module Data.Loc ( -- * Concepts -- $concepts -- * Imports -- $imports -- * Core types Line, Column, Loc, Span, SpanOrLoc, Area, -- * Constructing -- ** Loc loc, origin, -- ** Span spanFromTo, spanFromToMay, -- ** SpanOrLoc spanOrLocFromTo, -- ** Area areaFromTo, spanArea, -- * Deconstructing -- ** Loc locLine, locColumn, -- ** Span spanStart, spanEnd, -- ** SpanOrLoc spanOrLocStart, spanOrLocEnd, -- ** Area areaStart, areaEnd, areaSpansAsc, -- * Combining -- ** Span spanUnion, spanDifference, -- ** Area areaUnion, areaDifference, -- * Miscellaneous Pos, OneToTwo, ZeroToTwo, ToNat (..), LocException (..), ) where import Data.Loc.Internal.Prelude import Data.Loc.Area (Area) import Data.Loc.Exception (LocException (..)) import Data.Loc.List.OneToTwo (OneToTwo) import Data.Loc.List.ZeroToTwo (ZeroToTwo) import Data.Loc.Loc (Loc) import Data.Loc.Pos (Column, Line, Pos, ToNat (..)) import Data.Loc.Span (Span) import Data.Loc.SpanOrLoc (SpanOrLoc) import qualified Data.Loc.Area as Area import qualified Data.Loc.Loc as Loc import qualified Data.Loc.Span as Span import qualified Data.Loc.SpanOrLoc as SpanOrLoc {- | The smallest location: @'loc' 1 1@. /This is an alias for 'Loc.origin'./ -} origin :: Loc origin = Loc.origin {- | Create a 'Loc' from a line number and column number. /This is an alias for 'Loc.loc'./ -} loc :: Line -> Column -> Loc loc = Loc.loc -- | /This is an alias for 'Loc.line'./ locLine :: Loc -> Line locLine = Loc.line -- | /This is an alias for 'Loc.column'./ locColumn :: Loc -> Column locColumn = Loc.column {- | Attempt to construct a 'Span' from two 'Loc's. The lesser loc will be the start, and the greater loc will be the end. The two locs must not be equal, or else this throws 'EmptySpan'. /The safe version of this function is 'spanFromToMay'./ /This is an alias for 'Span.fromTo'./ -} spanFromTo :: Loc -> Loc -> Span spanFromTo = Span.fromTo {- | Attempt to construct a 'Span' from two 'Loc's. The lesser loc will be the start, and the greater loc will be the end. If the two locs are equal, the result is 'Nothing', because a span cannot be empty. /This is the safe version of 'spanFromTo', which throws an exception instead./ /This is an alias for 'Span.fromToMay'./ -} spanFromToMay :: Loc -> Loc -> Maybe Span spanFromToMay = Span.fromToMay {- | Construct a 'SpanOrLoc' from two 'Loc's. If the two locs are not equal, the lesser loc will be the start, and the greater loc will be the end. /This is an alias for 'SpanOrLoc.fromTo'./ -} spanOrLocFromTo :: Loc -> Loc -> SpanOrLoc spanOrLocFromTo = SpanOrLoc.fromTo -- | /This is an alias for 'SpanOrLoc.start'./ spanOrLocStart :: SpanOrLoc -> Loc spanOrLocStart = SpanOrLoc.start -- | /This is an alias for 'SpanOrLoc.end'./ spanOrLocEnd :: SpanOrLoc -> Loc spanOrLocEnd = SpanOrLoc.end {- | Construct a contiguous 'Area' consisting of a single 'Span' specified by two 'Loc's. The lesser loc will be the start, and the greater loc will be the end. If the two locs are equal, the area will be empty. /This is an alias for 'Area.fromTo'./ -} areaFromTo :: Loc -> Loc -> Area areaFromTo = Area.fromTo {- | The union of two 'Area's. Spans that overlap or abut will be merged in the result. /This is an alias for 'Area.+'./ -} areaUnion :: Area -> Area -> Area areaUnion = (Area.+) {- | The difference between two 'Area's. @a `'areaDifference'` b@ contains what is covered by @a@ and not covered by @b@. /This is an alias for 'Area.-'./ -} areaDifference :: Area -> Area -> Area areaDifference = (Area.-) {- | A list of the 'Span's that constitute an 'Area', sorted in ascending order. /This is an alias for 'Area.spansAsc'./ -} areaSpansAsc :: Area -> [Span] areaSpansAsc = Area.spansAsc {- | Construct an 'Area' consisting of a single 'Span'. /This is an alias for 'Area.spanArea'./ -} spanArea :: Span -> Area spanArea = Area.spanArea {- | Combine two 'Span's, merging them if they abut or overlap. /This is an alias for 'Span.+'./ -} spanUnion :: Span -> Span -> OneToTwo Span spanUnion = (Span.+) {- | The difference between two 'Spans's. @a '-' b@ contains what is covered by @a@ and not covered by @b@. /This is an alias for 'Span.-'./ -} spanDifference :: Span -> Span -> ZeroToTwo Span spanDifference = (Span.-) {- | /This is an alias for 'Span.start'./ -} spanStart :: Span -> Loc spanStart = Span.start {- | /This is an alias for 'Span.end'./ -} spanEnd :: Span -> Loc spanEnd = Span.end {- | /This is an alias for 'Area.start'./ -} areaStart :: Area -> Maybe Loc areaStart = Area.start {- | /This is an alias for 'Area.end'./ -} areaEnd :: Area -> Maybe Loc areaEnd = Area.end {- $concepts 'Line' and 'Column' are positive integers representing line and column numbers. The product of 'Line' and 'Column' is a 'Loc', which represents a position between characters in multiline text. The smallest loc is 'origin': line 1, column 1. Here's a small piece of text for illustration: > 1 2 > 12345678901234567890123456789 > ┌───────────────────────────────┐ > 1 │ I have my reasons, you │ > 2 │ have yours. What's obvious │ > 3 │ to me isn't to everyone else, │ > 4 │ and vice versa. │ > └───────────────────────────────┘ In this example, the word “obvious” starts at line 2, column 20, and it ends at line 2, column 27. The 'Show' instance uses a shorthand notation denoting these locs as @2:20@ and @2:27@. A 'Span' is a nonempty contiguous region of text between two locs; think of it like a highlighted area in a simple text editor. In the above example, a span that covers the word “obvious” starts at @2:20@ and ends at @2:27@. The 'Show' instance describes this tersely as @2:20-2:27@. Multiple non-overlapping regions form an 'Area'. You may also think of an area like a span that can be empty or have “gaps”. In the example above, the first three words “I have my”, and not the spaces between them, are covered by the area @[1:1-1:2,1:3-1:7,1:8-1:10]@. -} {- $imports Recommended import: > import Data.Loc.Types > import qualified Data.Loc as Loc -}