spectacle-1.0.0: src/Language/Spectacle/Interaction/Pos.hs
{-# LANGUAGE RecordWildCards #-}
-- | 'Pos' is pair of integers representing a position in grid. The implementation for 'compare' on 'Pos' is biased to
-- the row-value of the position, i.e.
--
-- @
-- ∀ (x y u v :: Int) -> x ≢ u -> Pos x y `compare` Pos u v ≡ compare x u
-- @
--
-- This definition of 'compare' enables collections of 'Pos' to preserve most of the structural information in a rose
-- tree when kept in ordered-containers who manage their internal structure with 'compare' such as 'Set'. To motivate
-- why this is useful, it is worth noting that the model checker works exclusively with nested structures like rose
-- trees. Rendering output from trees is difficult since it is not always clear how to traverse a tree to extract a
-- flattened presentation of information in a tree that can be easily rendered as output.
--
-- Traversing a ('Set' 'Pos') is trivial, though, since 'Set' is (for all intent and purposes) a flat collection of
-- elements, and more specifically one which manages its structure with 'compare' internally. As a result of the way in
-- which 'compare' is defined, column and row values of 'Pos' can simultaneously be viewed as file locations and
-- node positions in a tree. Sets of Pos are flat organizations of information that can be rendered to output
-- straightforward way while still being able to store/recover the shape information of the original tree we're
-- interested in logging:
--
-- @
-- 1 2 3 4 5 6 7 8 9
-- ┌╌🮻╌╌🮻╌╌🮻╌ ○ ╌🮻╌╌🮻╌╌🮻╌╌🮻╌╌🮻╌┐
-- ┆ ┆ ┆ ╱ ┃ ╲ ┆ ┆ ┆ ┆ ┆
-- ├╌🮻╌╌🮻╌ ○ ○ ○ ╌╌🮻╌╌🮻╌╌🮻╌┤ 1
-- ┆ ┆ ┆ ┃ ┃ ┃ ╲ ┆ ┆ ┆ ┆
-- ├╌🮻╌╌🮻╌ ○ ○ ○ ○ ╌🮻╌╌🮻╌╌🮻╌┤ 2
-- ┆ ┆ ┆ ╲ ┃ ┃ ╱ ┆ ┆ ┆ ┆
-- ├╌🮻╌╌🮻╌ ○ ○ ╌╌🮻╌╌🮻╌╌🮻╌┤ 3
-- ┆ ┆ ┆ ┆ ┃ ┃ ┆ ┆ ┆ ┆ ┆
-- ├╌🮻╌╌🮻╌╌🮻╌ ○ ○ ╌🮻╌╌🮻╌╌🮻╌╌🮻╌┤ 4
-- ┆ ┆ ┆ ┆ ┃ ╱ ┆ ┆ ┆ ┆ ┆
-- ├╌🮻╌╌🮻╌╌🮻╌ ○ ╌🮻╌╌🮻╌╌🮻╌╌🮻╌╌🮻╌┤ 5
-- ┆ ┆ ┆ ┆ ┃ ┆ ┆ ┆ ┆ ┆ ┆
-- └╌🮻╌╌🮻╌╌🮻╌ ○ ╌🮻╌╌🮻╌╌🮻╌╌🮻╌╌🮻╌┘
-- @
--
-- This example would be the set with elements ordered according to their depth, and then to their span:
--
-- @
-- fromList [(0,4), (1,3), (1,4), (1,5), (2,3), (2,4), (2,5), ..., (5,4), (6,4)]
-- @
--
-- @since 1.0.0
module Language.Spectacle.Interaction.Pos
( -- * Locations
Loc (Loc),
locPos,
locExt,
-- * Positions
Pos (MkPos),
pattern Pos,
getPos,
posRow,
posCol,
-- ** Lenses
prow,
pcol,
)
where
import Data.Function (on)
import Data.Semigroup (Sum (Sum))
import Lens.Micro (Lens', lens, set, _1, _2)
import Lens.Micro.Extras (view)
-- ---------------------------------------------------------------------------------------------------------------------
-- | A 'Loc' is a position annotated with an length/span.
--
-- @since 1.0.0
data Loc = Loc
{ locPos :: {-# UNPACK #-} !Pos
, locExt :: {-# UNPACK #-} !Int
}
deriving (Eq)
-- | @since 1.0.0
instance Ord Loc where
compare = compare `on` locPos
-- | Buffer row/column positions.
--
-- @since 1.0.0
newtype Pos = MkPos {getPos :: (Int, Int)}
deriving (Eq, Semigroup, Monoid) via (Sum Int, Sum Int)
pattern Pos :: Int -> Int -> Pos
pattern Pos {posRow, posCol} = MkPos (posRow, posCol)
{-# COMPLETE Pos #-}
-- | @since 1.0.0
instance Ord Pos where
-- The derived implementation of 'Ord' is equivalent to what is given here, but its defined explicitly anyway
-- to emphasize the row value superseding the order of the column value in this trichotomy.
Pos x y `compare` Pos u v = case compare x u of
EQ -> compare y v
ordering -> ordering
-- | @since 1.0.0
instance Show Pos where
show (Pos r c) = "Pos(" ++ show r ++ ":" ++ show c ++ ")"
prow :: Lens' Pos Int
prow = lens (view _1 . getPos) \ ~(MkPos p) x -> MkPos (set _1 x p)
{-# INLINE prow #-}
pcol :: Lens' Pos Int
pcol = lens (view _2 . getPos) \ ~(MkPos p) x -> MkPos (set _2 x p)
{-# INLINE pcol #-}