{-# OPTIONS_HADDOCK not-home #-}

-- | Attachment of comments and empty lines to the nodes of a document.
--
-- The rules are in the documentation of "Yamlet.Syntax". The parser skips
-- comments, so this module finds them again in the input, outside of the
-- scalars, and gives each one to a node.
--
-- This module is intended for internal use only, and may change without warning
-- in subsequent releases.
module Yamlet.Internal.Comments
  ( attachComments
  , gapEnd
  , linesAbove
  ) where

import Control.Applicative
import Control.DeepSeq
import Data.Maybe
import Data.Text qualified as T
import Data.Text.Array qualified as A

import Yamlet.Internal.Chars
import Yamlet.Internal.Parser.Monad hiding ((<|>))
import Yamlet.Internal.Parser.Scan
import Yamlet.Internal.Syntax
import Yamlet.Internal.Utils

-- | A comment or empty lines. The indices are offsets of the input.
data Item = Item
  { Item -> Int
at :: !Int
  -- ^ The index of the @#@, or of the start of the first empty line.
  , Item -> Int
lineStart :: !Int
  , Item -> Bool
own :: !Bool
  -- ^ Nothing else is on the line.
  , Item -> Line
line :: !Line
  , Item -> Int
count :: !Int
  -- ^ The number of lines. One item holds the empty lines that follow each
  -- other, because the nested collections hand the empty lines at their end
  -- to each other, and one by one the time would be quadratic.
  }

-- | The lines of the item.
itemLines :: Item -> [Line]
itemLines :: Item -> [Line]
itemLines Item
i = Int -> Line -> [Line]
forall a. Int -> a -> [a]
replicate Item
i.count Item
i.line

-- | Attach the comments of a document, and return the lines at its end that
-- belong to the next document. The flags tell if the document is the first
-- one and if another one follows it. The indices are the start of the lines
-- that belong to the document, its @---@ marker, the end of its root and its
-- end.
attachComments
  :: Env
  -> Bool
  -> Bool
  -> Int
  -> Maybe Int
  -> Int
  -> Int
  -> Document
  -> (Document, [Line])
attachComments :: Env
-> Bool
-> Bool
-> Int
-> Maybe Int
-> Int
-> Int
-> Document
-> (Document, [Line])
attachComments Env
e Bool
first Bool
hasNext Int
start Maybe Int
marker Int
rootEnd Int
end Document
doc
  | Bool -> Bool
not Bool
mayHaveItems = (Document
doc, [])
  | [Item] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Item]
items = (Document
doc, [])
  | Bool
otherwise =
      ( Document
doc
          { docComments =
              strictComments
                ( (if first then dropWhile (== EmptyLine) else id)
                    (concatMap itemLines docItems)
                )
                markerComment
                docEnd
          , root = root''
          }
      , [Line]
next
      )
  where
    -- Nothing is above the first document, so the empty lines at its start
    -- separate it from nothing.
    items :: [Item]
    items :: [Item]
items
      | Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust Maybe Int
marker Bool -> Bool -> Bool
|| Bool -> Bool
not Bool
first = [Item]
scanned
      | Bool
otherwise = (Item -> Bool) -> [Item] -> [Item]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (\Item
i -> Item -> Bool
isEmptyLine Item
i Bool -> Bool -> Bool
&& Item
i.at Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
rootStart) [Item]
scanned
      where
        scanned :: [Item]
        scanned :: [Item]
scanned = [(Int, Int)] -> [Item]
scanItems (Env -> Node -> [(Int, Int)]
skipRanges Env
e Document
doc.root)

    ([Item]
docItems, [Item]
afterMarker) = case Maybe Int
marker of
      Just Int
m -> (Item -> Bool) -> [Item] -> ([Item], [Item])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (\Item
i -> Item
i.at Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
m Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base) [Item]
items
      Maybe Int
Nothing -> ([], [Item]
items)

    rootStart, rootLine :: Int
    rootStart :: Int
rootStart = Offset -> Int
offsetOf Document
doc.root.offset
    rootLine :: Int
rootLine = Env -> Int -> Int
skipBoms Env
e (Env -> Int -> Int
lineStartAt Env
e (Int
rootStart Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base)) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base

    -- The comment on the line of the marker, unless the root starts there.
    (Maybe Text
markerComment, [Item]
rest) = case (Maybe Int
marker, [Item]
afterMarker) of
      (Just Int
m, Item
i : [Item]
is)
        | Bool -> Bool
not Item
i.own
        , Item
i.lineStart Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
m Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base
        , Int
rootLine Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
m Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base
        , Comment Text
t <- Item
i.line ->
            (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
t, [Item]
is)
      (Maybe Int, [Item])
_ -> (Maybe Text
forall a. Maybe a
Nothing, [Item]
afterMarker)

    (Node
root', [Item]
leftover) =
      Env
-> Int
-> Int
-> (Int, Int)
-> [Item]
-> Node
-> [Item]
-> (Node, [Item])
attachNode Env
e (Int
rootEnd Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base) Int
0 (Int
rootStart, Int
rootLine) [] Document
doc.root [Item]
rest

    ([Item]
below, [Item]
afterEnd) = (Item -> Bool) -> [Item] -> ([Item], [Item])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (\Item
i -> Item
i.at Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
rootEnd Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base) [Item]
leftover

    -- The lines of a flow collection are inside its brackets, so the lines
    -- below a flow collection root belong to the document.
    holdsLines :: Bool
    holdsLines :: Bool
holdsLines = case Node
root'.content of
      SequenceContent CollectionStyle
Flow [Node]
_ -> Bool
False
      MappingContent CollectionStyle
Flow [(Node, Node)]
_ -> Bool
False
      Content
_ -> Bool
True

    -- Without a @...@ marker, the first empty line ends the lines of the
    -- document if another document follows it.
    endLines, next :: [Line]
    ([Line]
endLines, [Line]
next)
      | Document
doc.explicitEnd Bool -> Bool -> Bool
|| Bool -> Bool
not Bool
hasNext = ([Line]
rootLines, [])
      | Bool
otherwise = (Line -> Bool) -> [Line] -> ([Line], [Line])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Line -> Line -> Bool
forall a. Eq a => a -> a -> Bool
== Line
EmptyLine) [Line]
rootLines
      where
        rootLines :: [Line]
        rootLines :: [Line]
rootLines =
          (if Bool
holdsLines then Node
root'.comments.after else []) [Line] -> [Line] -> [Line]
forall a. [a] -> [a] -> [a]
++ (Item -> [Line]) -> [Item] -> [Line]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Item -> [Line]
itemLines [Item]
below

    root'' :: Node
    root'' :: Node
root''
      | Bool
holdsLines =
          let c :: Comments
c = Node
root'.comments
          in Node
               { offset :: Offset
offset = Node
root'.offset
               , endOffset :: Offset
endOffset = Node
root'.endOffset
               , props :: Props
props = Node
root'.props
               , comments :: Comments
comments =
                   [Line] -> Maybe Text -> [Line] -> Comments
strictComments
                     Comments
c.before
                     Comments
c.inline
                     (if Document
doc.explicitEnd then [Line]
endLines else [Line] -> [Line]
atEnd [Line]
endLines)
               , content :: Content
content = Node
root'.content
               }
      | Bool
otherwise = Node
root'

    -- Only the lines below a @...@ marker can be at the end of the stream.
    docEnd :: [Line]
    docEnd :: [Line]
docEnd
      | Bool
holdsLines = [Line] -> [Line]
atEnd ((Item -> [Line]) -> [Item] -> [Line]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Item -> [Line]
itemLines [Item]
afterEnd)
      | Document
doc.explicitEnd = [Line]
endLines [Line] -> [Line] -> [Line]
forall a. [a] -> [a] -> [a]
++ [Line] -> [Line]
atEnd ((Item -> [Line]) -> [Item] -> [Line]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Item -> [Line]
itemLines [Item]
afterEnd)
      | Bool
otherwise = [Line] -> [Line]
atEnd ([Line]
endLines [Line] -> [Line] -> [Line]
forall a. [a] -> [a] -> [a]
++ (Item -> [Line]) -> [Item] -> [Line]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Item -> [Line]
itemLines [Item]
afterEnd)

    -- The empty lines at the end of the stream belong to no node.
    atEnd :: [Line] -> [Line]
    atEnd :: [Line] -> [Line]
atEnd [Line]
ls
      | Bool
hasNext = [Line]
ls
      | Bool
otherwise = [Line] -> [Line]
forall a. [a] -> [a]
reverse ((Line -> Bool) -> [Line] -> [Line]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Line -> Line -> Bool
forall a. Eq a => a -> a -> Bool
== Line
EmptyLine) ([Line] -> [Line]
forall a. [a] -> [a]
reverse [Line]
ls))

    -- A quick check for a comment or an empty line in the document.
    mayHaveItems :: Bool
    mayHaveItems :: Bool
mayHaveItems = Bool -> Int -> Bool
go Bool
True Int
start
      where
        go :: Bool -> Int -> Bool
        go :: Bool -> Int -> Bool
go Bool
blank Int
i
          | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
end = Bool
False
          | Bool
otherwise = case Array -> Int -> Word8
A.unsafeIndex Env
e.array Int
i of
              Word8
HASH -> Bool
True
              Word8
w
                | Word8 -> Bool
isBreak Word8
w -> Bool
blank Bool -> Bool -> Bool
|| Bool -> Int -> Bool
go Bool
True (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
                | Word8 -> Bool
isWhite Word8
w -> Bool -> Int -> Bool
go Bool
blank (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
                | Bool
otherwise -> Bool -> Int -> Bool
go Bool
False (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)

    -- The comments and the empty lines of the document, outside the given
    -- ranges. The offsets of the items are relative to the start of the
    -- input.
    scanItems :: [(Int, Int)] -> [Item]
    scanItems :: [(Int, Int)] -> [Item]
scanItems = [Item] -> [Item]
runs ([Item] -> [Item])
-> ([(Int, Int)] -> [Item]) -> [(Int, Int)] -> [Item]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> Bool -> [(Int, Int)] -> [Item]
go Int
start Int
start Bool
False
      where
        runs :: [Item] -> [Item]
        runs :: [Item] -> [Item]
runs = \case
          Item
i : [Item]
is | Item -> Bool
isEmptyLine Item
i -> Item -> Int -> Int -> [Item] -> [Item]
run Item
i Int
1 Item
i.at [Item]
is
          Item
i : [Item]
is -> Item
i Item -> [Item] -> [Item]
forall a. a -> [a] -> [a]
: [Item] -> [Item]
runs [Item]
is
          [] -> []

        -- The empty lines from the first item, with the start of the last
        -- one. A line joins them only if it comes right after the last one,
        -- so that no node is between them.
        run :: Item -> Int -> Int -> [Item] -> [Item]
        run :: Item -> Int -> Int -> [Item] -> [Item]
run Item
i0 Int
n Int
lastAt = \case
          Item
i : [Item]
is
            | Item -> Bool
isEmptyLine Item
i
            , Item
i.at Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Env -> Int -> Int
breakEnd Env
e (Env -> Int -> Int
skipWhites Env
e (Int
lastAt Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base)) ->
                Item -> Int -> Int -> [Item] -> [Item]
run Item
i0 (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Item
i.at [Item]
is
          [Item]
is ->
            Item
              { at :: Int
at = Item
i0.at
              , lineStart :: Int
lineStart = Item
i0.lineStart
              , own :: Bool
own = Bool
True
              , line :: Line
line = Line
EmptyLine
              , count :: Int
count = Int
n
              }
              Item -> [Item] -> [Item]
forall a. a -> [a] -> [a]
: [Item] -> [Item]
runs [Item]
is

        -- The flag tells if the line has something other than white space.
        go :: Int -> Int -> Bool -> [(Int, Int)] -> [Item]
        go :: Int -> Int -> Bool -> [(Int, Int)] -> [Item]
go Int
i Int
ls Bool
content [(Int, Int)]
ranges
          | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
end = []
          | (Int
rs, Int
re) : [(Int, Int)]
others <- [(Int, Int)]
ranges
          , Int
rs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
i =
              if Int
re Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
i
                -- A block scalar can end at the start of a line.
                then let ls' :: Int
ls' = Int -> Int -> Int -> Int
lineBefore Int
i Int
re Int
ls in Int -> Int -> Bool -> [(Int, Int)] -> [Item]
go Int
re Int
ls' (Int
ls' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
re) [(Int, Int)]
others
                else Int -> Int -> Bool -> [(Int, Int)] -> [Item]
go Int
i Int
ls Bool
content [(Int, Int)]
others
          -- The parser allows byte order marks at the start of a line only
          -- between documents, where a comment can follow them.
          | Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
ls
          , Env -> Int -> Bool
isBom Env
e Int
i =
              let j :: Int
j = Env -> Int -> Int
skipBoms Env
e Int
i in Int -> Int -> Bool -> [(Int, Int)] -> [Item]
go Int
j Int
j Bool
content [(Int, Int)]
ranges
          | Bool
otherwise = case Array -> Int -> Word8
A.unsafeIndex Env
e.array Int
i of
              Word8
w
                | Word8 -> Bool
isBreak Word8
w ->
                    let j :: Int
j =
                          if Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
CR Bool -> Bool -> Bool
&& Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
end Bool -> Bool -> Bool
&& Array -> Int -> Word8
A.unsafeIndex Env
e.array (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LF
                            then Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2
                            else Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1
                        item :: [Item]
item =
                          [ Item
                              { at :: Int
at = Int
ls Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base
                              , lineStart :: Int
lineStart = Int
ls Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base
                              , own :: Bool
own = Bool
True
                              , line :: Line
line = Line
EmptyLine
                              , count :: Int
count = Int
1
                              }
                          | Bool -> Bool
not Bool
content
                          ]
                    in [Item]
item [Item] -> [Item] -> [Item]
forall a. [a] -> [a] -> [a]
++ Int -> Int -> Bool -> [(Int, Int)] -> [Item]
go Int
j Int
j Bool
False [(Int, Int)]
ranges
                | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH Bool -> Bool -> Bool
&& (Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
ls Bool -> Bool -> Bool
|| Word8 -> Bool
isWhite (Array -> Int -> Word8
A.unsafeIndex Env
e.array (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))) ->
                    let eol :: Int
eol = Int -> Int
lineEnd Int
i
                        -- A comment at the end of a line keeps its text after
                        -- the first #, because 'Comments' has no count for it.
                        textStart :: Int
textStart = if Bool
content then Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 else Int -> Int
hashesEnd Int
i
                        text :: Text
text = Text -> Text
T.stripEnd (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
dropSpace (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ Env -> Int -> Int -> Text
slice Env
e Int
textStart Int
eol
                    in Item
                         { at :: Int
at = Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base
                         , lineStart :: Int
lineStart = Int
ls Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base
                         , own :: Bool
own = Bool -> Bool
not Bool
content
                         , line :: Line
line = Int -> Text -> Line
CommentLine (Int
textStart Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
i) Text
text
                         , count :: Int
count = Int
1
                         }
                         Item -> [Item] -> [Item]
forall a. a -> [a] -> [a]
: Int -> Int -> Bool -> [(Int, Int)] -> [Item]
go Int
eol Int
ls Bool
True [(Int, Int)]
ranges
                | Word8 -> Bool
isWhite Word8
w -> Int -> Int -> Bool -> [(Int, Int)] -> [Item]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int
ls Bool
content [(Int, Int)]
ranges
                | Bool
otherwise -> Int -> Int -> Bool -> [(Int, Int)] -> [Item]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int
ls Bool
True [(Int, Int)]
ranges

        hashesEnd :: Int -> Int
        hashesEnd :: Int -> Int
hashesEnd Int
i
          | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
end Bool -> Bool -> Bool
&& Array -> Int -> Word8
A.unsafeIndex Env
e.array Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH = Int -> Int
hashesEnd (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
          | Bool
otherwise = Int
i

        lineEnd :: Int -> Int
        lineEnd :: Int -> Int
lineEnd Int
i
          | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
end Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isBreak (Array -> Int -> Word8
A.unsafeIndex Env
e.array Int
i)) = Int -> Int
lineEnd (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
          | Bool
otherwise = Int
i

        -- The start of the line of the second index, or the given start if no
        -- line break is between the indices.
        lineBefore :: Int -> Int -> Int -> Int
        lineBefore :: Int -> Int -> Int -> Int
lineBefore Int
i Int
j Int
ls
          | Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
i = Int
ls
          | Word8 -> Bool
isBreak (Array -> Int -> Word8
A.unsafeIndex Env
e.array (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)) = Int
j
          | Bool
otherwise = Int -> Int -> Int -> Int
lineBefore Int
i (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int
ls

        dropSpace :: T.Text -> T.Text
        dropSpace :: Text -> Text
dropSpace Text
t = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
t (Text -> Text -> Maybe Text
textStripPrefix Text
" " Text
t)

-- | The start of the first line from the index that is empty or has more than
-- a comment or a @...@ marker. The index is the start of a line.
gapEnd :: Env -> Int -> Int
gapEnd :: Env -> Int -> Int
gapEnd Env
e Int
i
  | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Env
e.end Bool -> Bool -> Bool
&& Env -> Int -> Bool
isEndMarker Env
e Int
b = Env -> Int -> Int
gapEnd Env
e (Env -> Int -> Int
nextLineStart Env
e Int
b)
  | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Env
e.end Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteAt Env
e (Env -> Int -> Int
skipWhites Env
e Int
b) Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH = Env -> Int -> Int
gapEnd Env
e (Env -> Int -> Int
nextLineStart Env
e Int
b)
  | Bool
otherwise = Int
i
  where
    -- A byte order mark can start a line between documents.
    b :: Int
    b :: Int
b = Env -> Int -> Int
skipBoms Env
e Int
i

-- | The documents with the lines above the first one.
linesAbove :: [Line] -> [Document] -> [Document]
linesAbove :: [Line] -> [Document] -> [Document]
linesAbove [Line]
ls = \case
  Document
d : [Document]
ds
    | Bool -> Bool
not ([Line] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Line]
ls) ->
        let c :: Comments
c = Document
d.docComments
            !d' :: Document
d' = Document
d {docComments = strictComments (ls ++ c.before) c.inline c.after}
        in Document
d' Document -> [Document] -> [Document]
forall a. a -> [a] -> [a]
: [Document]
ds
  [Document]
ds -> [Document]
ds

-- | Comments with their lists evaluated. The parser returns a document
-- without thunks, and a lazy list would keep the items of the input alive.
strictComments :: [Line] -> Maybe T.Text -> [Line] -> Comments
strictComments :: [Line] -> Maybe Text -> [Line] -> Comments
strictComments [Line]
before Maybe Text
inline [Line]
after =
  Comments {before :: [Line]
before = [Line] -> [Line]
forall a. NFData a => a -> a
force [Line]
before, inline :: Maybe Text
inline = Maybe Text
inline, after :: [Line]
after = [Line] -> [Line]
forall a. NFData a => a -> a
force [Line]
after}

isEmptyLine :: Item -> Bool
isEmptyLine :: Item -> Bool
isEmptyLine Item
i = case Item
i.line of
  Line
EmptyLine -> Bool
True
  Comment Text
_ -> Bool
False

offsetOf :: Offset -> Int
offsetOf :: Offset -> Int
offsetOf (Offset Int
o) = Int
o

-- | Attach the comments to a node and the nodes inside it. The limit is the
-- offset of the next node, and the column is the smallest one for the lines
-- after the last entry of a block collection. The pair is an offset at or
-- before the node and the start of its line. The first list holds the lines
-- on their own above the node that its parent gave to it, in reverse. They
-- are not in the items, so that a chain of nested first entries passes them
-- down without a walk over them at each level, which would make the time
-- quadratic.
attachNode
  :: Env -> Int -> Int -> (Int, Int) -> [Item] -> Node -> [Item] -> (Node, [Item])
attachNode :: Env
-> Int
-> Int
-> (Int, Int)
-> [Item]
-> Node
-> [Item]
-> (Node, [Item])
attachNode Env
e Int
limit Int
minColumn (Int, Int)
known [Item]
above Node
n [Item]
items0 = Node
node Node -> (Node, [Item]) -> (Node, [Item])
forall a b. a -> b -> b
`seq` [Item]
items5 [Item] -> (Node, [Item]) -> (Node, [Item])
forall a b. a -> b -> b
`seq` (Node
node, [Item]
items5)
  where
    node :: Node
    node :: Node
node =
      Node
n
        { comments =
            strictComments
              [l | i <- pre, isJust own || not (isFallback i), l <- itemLines i]
              (own <|> fallback)
              afterLines
        , content = content'
        }

    s, en :: Int
    s :: Int
s = Offset -> Int
offsetOf Node
n.offset
    en :: Int
en = Offset -> Int
offsetOf Node
n.endOffset

    lineStart, column :: Int
    lineStart :: Int
lineStart = (Int, Int) -> Int -> Int
lineFrom (Int, Int)
known Int
s
    column :: Int
column = Int
s Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
lineStart

    -- The lines above the node. A comment at the end of a line that no node
    -- took, e.g. in "- # comment" above a mapping, belongs to the node. It is
    -- a line above the node if the node has a comment on its own line.
    ([Item]
pre, [Item]
toEntry, [Item]
items1) =
      let ([Item]
ls, [Item]
rest) = (Item -> Bool) -> [Item] -> ([Item], [Item])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (\Item
i -> Item
i.at Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
s) [Item]
items0
      in case Node
n.content of
           SequenceContent CollectionStyle
Block (Node
_ : [Node]
_) | Bool
startsLine -> [Item] -> [Item] -> ([Item], [Item], [Item])
toFirstEntry [Item]
ls [Item]
rest
           MappingContent CollectionStyle
Block ((Node, Node)
_ : [(Node, Node)]
_) | Bool
startsLine -> [Item] -> [Item] -> ([Item], [Item], [Item])
toFirstEntry [Item]
ls [Item]
rest
           Content
_ -> ([Item] -> [Item]
forall a. [a] -> [a]
reverse [Item]
above [Item] -> [Item] -> [Item]
forall a. [a] -> [a] -> [a]
++ [Item]
ls, [], [Item]
rest)

    -- A collection after "- " on the same line keeps the lines above the
    -- indicator, so that a comment above an item stays with the item. The walk
    -- goes back from the node, so that it stops at the indicator of an outer
    -- collection. A walk from the start of the line would cross the whole
    -- indentation for each nested collection, and the time would be quadratic.
    startsLine :: Bool
    startsLine :: Bool
startsLine = Int -> Bool
go (Int
s Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base)
      where
        go :: Int -> Bool
        go :: Int -> Bool
go Int
i
          | Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
lineStart Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base = Bool
True
          | Word8 -> Bool
isWhite (Array -> Int -> Word8
A.unsafeIndex Env
e.array (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)) = Int -> Bool
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
          | Bool
otherwise = Bool
False

    -- The lines on their own after the last empty line go to the first entry,
    -- in reverse.
    toFirstEntry :: [Item] -> [Item] -> ([Item], [Item], [Item])
    toFirstEntry :: [Item] -> [Item] -> ([Item], [Item], [Item])
toFirstEntry [Item]
ls [Item]
rest =
      let ([Item]
ownLines, [Item]
others) = (Item -> Bool) -> [Item] -> ([Item], [Item])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (.own) ([Item] -> [Item]
forall a. [a] -> [a]
reverse [Item]
ls)
          ([Item]
entry, [Item]
kept) = (Item -> Bool) -> [Item] -> ([Item], [Item])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Item -> Bool
isEmptyLine [Item]
ownLines
      in case ([Item]
kept, [Item]
others) of
           ([], []) -> ([], [Item]
entry [Item] -> [Item] -> [Item]
forall a. [a] -> [a] -> [a]
++ [Item]
above, [Item]
rest)
           ([Item], [Item])
_ -> ([Item] -> [Item]
forall a. [a] -> [a]
reverse ([Item]
kept [Item] -> [Item] -> [Item]
forall a. [a] -> [a] -> [a]
++ [Item]
others [Item] -> [Item] -> [Item]
forall a. [a] -> [a] -> [a]
++ [Item]
above), [Item]
entry, [Item]
rest)

    fallbackItem :: Maybe Item
    fallbackItem :: Maybe Item
fallbackItem = case [Item] -> [Item]
forall a. [a] -> [a]
reverse ((Item -> Bool) -> [Item] -> [Item]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Item -> Bool) -> Item -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (.own)) [Item]
pre) of
      Item
i : [Item]
_ -> Item -> Maybe Item
forall a. a -> Maybe a
Just Item
i
      [] -> Maybe Item
forall a. Maybe a
Nothing

    fallback :: Maybe T.Text
    fallback :: Maybe Text
fallback = Maybe Item
fallbackItem Maybe Item -> (Item -> Maybe Text) -> Maybe Text
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Item -> Maybe Text
comment

    isFallback :: Item -> Bool
    isFallback :: Item -> Bool
isFallback Item
i = Bool -> (Item -> Bool) -> Maybe Item -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (\Item
f -> Item
f.at Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Item
i.at) Maybe Item
fallbackItem

    own :: Maybe T.Text
    own :: Maybe Text
own = Maybe Text
header Maybe Text -> Maybe Text -> Maybe Text
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe Text
trailing

    -- The comment on the line of a block scalar header.
    (Maybe Text
header, [Item]
items2) = case (Node
n.content, [Item]
items1) of
      (ScalarContent ScalarStyle
style Text
_, Item
i : [Item]
is)
        | ScalarStyle -> Bool
isBlockScalar ScalarStyle
style
        , Bool -> Bool
not Item
i.own
        , Item
i.lineStart Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
lineStart ->
            (Item -> Maybe Text
comment Item
i, [Item]
is)
      (Content, [Item])
_ -> (Maybe Text
forall a. Maybe a
Nothing, [Item]
items1)

    (Content
content', [Item]
items3) = case Node
n.content of
      SequenceContent CollectionStyle
style [Node]
xs ->
        let !([Node]
xs', [Item]
is) = CollectionStyle -> [Node] -> [Item] -> ([Node], [Item])
sequenceItems CollectionStyle
style [Node]
xs [Item]
items2
        in (CollectionStyle -> [Node] -> Content
SequenceContent CollectionStyle
style [Node]
xs', [Item]
is)
      MappingContent CollectionStyle
style [(Node, Node)]
kvs ->
        let !([(Node, Node)]
kvs', [Item]
is) = CollectionStyle
-> [(Node, Node)] -> [Item] -> ([(Node, Node)], [Item])
mappingEntries CollectionStyle
style [(Node, Node)]
kvs [Item]
items2
        in (CollectionStyle -> [(Node, Node)] -> Content
MappingContent CollectionStyle
style [(Node, Node)]
kvs', [Item]
is)
      Content
c -> (Content
c, [Item]
items2)

    -- The lines before the closing bracket come before the comment after it.
    (Maybe Text
trailing, [Line]
afterLines, [Item]
items5) = case Node
n.content of
      SequenceContent CollectionStyle
Block (Node
_ : [Node]
_) -> (Maybe Text, [Line], [Item])
blockEnd
      MappingContent CollectionStyle
Block ((Node, Node)
_ : [(Node, Node)]
_) -> (Maybe Text, [Line], [Item])
blockEnd
      SequenceContent CollectionStyle
Flow [Node]
_ -> (Maybe Text, [Line], [Item])
flowEnd
      MappingContent CollectionStyle
Flow [(Node, Node)]
_ -> (Maybe Text, [Line], [Item])
flowEnd
      Content
_ -> let (Maybe Text
t, [Item]
is) = [Item] -> (Maybe Text, [Item])
trailingComment [Item]
items3 in (Maybe Text
t, [], [Item]
is)

    blockEnd, flowEnd :: (Maybe T.Text, [Line], [Item])
    blockEnd :: (Maybe Text, [Line], [Item])
blockEnd =
      let (Maybe Text
t, [Item]
is) = [Item] -> (Maybe Text, [Item])
trailingComment [Item]
items3
          ([Line]
ls, [Item]
is') = [Item] -> ([Line], [Item])
blockAfter [Item]
is
      in (Maybe Text
t, [Line]
ls, [Item]
is')
    -- The empty lines before the bracket go to the node below, as after the
    -- last entry of a block collection. They go back after the comment
    -- after the bracket, which 'trailingComment' takes from the front.
    flowEnd :: (Maybe Text, [Line], [Item])
flowEnd =
      let ([Line]
ls, [Item]
empties, [Item]
is) = [Item] -> ([Line], [Item], [Item])
flowAfter [Item]
items3
          (Maybe Text
t, [Item]
is') = [Item] -> (Maybe Text, [Item])
trailingComment [Item]
is
      in (Maybe Text
t, [Line]
ls, [Item]
empties [Item] -> [Item] -> [Item]
forall a. [a] -> [a] -> [a]
++ [Item]
is')

    -- The comment at the end of the line of the node's end. A node that ends
    -- at the start of a line, e.g. a block scalar, ends on the line before,
    -- unless it is empty.
    trailingComment :: [Item] -> (Maybe T.Text, [Item])
    trailingComment :: [Item] -> (Maybe Text, [Item])
trailingComment = \case
      Item
i : [Item]
is
        | Bool -> Bool
not Item
i.own
        , Item
i.at Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
en
        , Item
i.at Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
limit
        , Item
i.lineStart Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
en Bool -> Bool -> Bool
|| Int
s Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
en
        , (Char -> Bool) -> Text -> Bool
T.all (\Char
c -> forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem @[] Char
c [Char]
" \t,:") (Int -> Int -> Text
between Int
en Item
i.at) ->
            (Item -> Maybe Text
comment Item
i, [Item]
is)
      [Item]
is -> (Maybe Text
forall a. Maybe a
Nothing, [Item]
is)

    -- The lines after the last entry, indented deep enough, and the empty
    -- lines between them.
    blockAfter :: [Item] -> ([Line], [Item])
    blockAfter :: [Item] -> ([Line], [Item])
blockAfter =
      (Item -> Bool) -> [Item] -> ([Line], [Item])
takeLines ((Item -> Bool) -> [Item] -> ([Line], [Item]))
-> (Item -> Bool) -> [Item] -> ([Line], [Item])
forall a b. (a -> b) -> a -> b
$ \Item
i ->
        Item
i.at Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
limit
          Bool -> Bool -> Bool
&& Item
i.own
          Bool -> Bool -> Bool
&& (Item -> Bool
isEmptyLine Item
i Bool -> Bool -> Bool
|| Item
i.at Int -> Int -> Int
forall a. Num a => a -> a -> a
- Item
i.lineStart Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
column Int
minColumn)

    -- The lines of the items from the start that pass the check, without the
    -- empty lines at their end, which stay with the next items.
    takeLines :: (Item -> Bool) -> [Item] -> ([Line], [Item])
    takeLines :: (Item -> Bool) -> [Item] -> ([Line], [Item])
takeLines Item -> Bool
ok [Item]
is =
      let ([Item]
taken, [Item]
rest) = (Item -> Bool) -> [Item] -> ([Item], [Item])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span Item -> Bool
ok [Item]
is
          ([Item]
empties, [Item]
taken') = (Item -> Bool) -> [Item] -> ([Item], [Item])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span Item -> Bool
isEmptyLine ([Item] -> [Item]
forall a. [a] -> [a]
reverse [Item]
taken)
      in ((Item -> [Line]) -> [Item] -> [Line]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Item -> [Line]
itemLines ([Item] -> [Item]
forall a. [a] -> [a]
reverse [Item]
taken'), [Item] -> [Item]
forall a. [a] -> [a]
reverse [Item]
empties [Item] -> [Item] -> [Item]
forall a. [a] -> [a] -> [a]
++ [Item]
rest)

    -- The lines before the closing bracket.
    flowAfter :: [Item] -> ([Line], [Item], [Item])
    flowAfter :: [Item] -> ([Line], [Item], [Item])
flowAfter [Item]
is =
      let ([Item]
taken, [Item]
rest) = (Item -> Bool) -> [Item] -> ([Item], [Item])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (\Item
i -> Item
i.at Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
en) [Item]
is
          ([Item]
empties, [Item]
taken') = (Item -> Bool) -> [Item] -> ([Item], [Item])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span Item -> Bool
isEmptyLine ([Item] -> [Item]
forall a. [a] -> [a]
reverse [Item]
taken)
      in ((Item -> [Line]) -> [Item] -> [Line]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Item -> [Line]
itemLines ([Item] -> [Item]
forall a. [a] -> [a]
reverse [Item]
taken'), [Item] -> [Item]
forall a. [a] -> [a]
reverse [Item]
empties, [Item]
rest)

    sequenceItems :: CollectionStyle -> [Node] -> [Item] -> ([Node], [Item])
    sequenceItems :: CollectionStyle -> [Node] -> [Item] -> ([Node], [Item])
sequenceItems CollectionStyle
style = [Node] -> [Item] -> [Node] -> [Item] -> ([Node], [Item])
go [] [Item]
toEntry
      where
        -- The nodes are in reverse, so that the list is evaluated when the
        -- result is.
        go :: [Node] -> [Item] -> [Node] -> [Item] -> ([Node], [Item])
        go :: [Node] -> [Item] -> [Node] -> [Item] -> ([Node], [Item])
go [Node]
acc [Item]
_ [] [Item]
is = let !xs :: [Node]
xs = [Node] -> [Node]
forall a. [a] -> [a]
reverse [Node]
acc in ([Node]
xs, [Item]
is)
        go [Node]
acc [Item]
xAbove (Node
x : [Node]
rest) [Item]
is =
          let next :: Int
next = Node -> [Node] -> Int
nextStart Node
x [Node]
rest
              !(Node
x', [Item]
is') =
                Env
-> Int
-> Int
-> (Int, Int)
-> [Item]
-> Node
-> [Item]
-> (Node, [Item])
attachNode Env
e Int
next (CollectionStyle -> Int
entryColumn CollectionStyle
style) (Int
s, Int
lineStart) [Item]
xAbove Node
x [Item]
is
              !(Node
x'', [Item]
is'')
                -- A list without indentation has no column of its own for the
                -- lines after its last item, so they stay with the list.
                | CollectionStyle
style CollectionStyle -> CollectionStyle -> Bool
forall a. Eq a => a -> a -> Bool
== CollectionStyle
Block Bool -> Bool -> Bool
&& Bool -> Bool
not ([Node] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Node]
rest Bool -> Bool -> Bool
&& Int
minColumn Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
column) =
                    Int -> Node -> [Item] -> (Node, [Item])
linesBelow Int
next Node
x' [Item]
is'
                | Bool
otherwise = (Node
x', [Item]
is')
          in [Node] -> [Item] -> [Node] -> [Item] -> ([Node], [Item])
go (Node
x'' Node -> [Node] -> [Node]
forall a. a -> [a] -> [a]
: [Node]
acc) [] [Node]
rest [Item]
is''

        nextStart :: Node -> [Node] -> Int
        nextStart :: Node -> [Node] -> Int
nextStart Node
x = \case
          Node
y : [Node]
_
            | CollectionStyle
style CollectionStyle -> CollectionStyle -> Bool
forall a. Eq a => a -> a -> Bool
== CollectionStyle
Block -> Int -> Int -> Int
entryStart (Offset -> Int
offsetOf Node
x.endOffset) (Offset -> Int
offsetOf Node
y.offset)
            | Bool
otherwise -> Offset -> Int
offsetOf Node
y.offset
          [] -> if CollectionStyle
style CollectionStyle -> CollectionStyle -> Bool
forall a. Eq a => a -> a -> Bool
== CollectionStyle
Flow then Int
en else Int
limit

    mappingEntries
      :: CollectionStyle -> [(Node, Node)] -> [Item] -> ([(Node, Node)], [Item])
    mappingEntries :: CollectionStyle
-> [(Node, Node)] -> [Item] -> ([(Node, Node)], [Item])
mappingEntries CollectionStyle
style = [(Node, Node)]
-> [Item] -> [(Node, Node)] -> [Item] -> ([(Node, Node)], [Item])
go [] [Item]
toEntry
      where
        -- The entries are in reverse, as in 'sequenceItems'.
        go
          :: [(Node, Node)]
          -> [Item]
          -> [(Node, Node)]
          -> [Item]
          -> ([(Node, Node)], [Item])
        go :: [(Node, Node)]
-> [Item] -> [(Node, Node)] -> [Item] -> ([(Node, Node)], [Item])
go [(Node, Node)]
acc [Item]
_ [] [Item]
is = let !kvs :: [(Node, Node)]
kvs = [(Node, Node)] -> [(Node, Node)]
forall a. [a] -> [a]
reverse [(Node, Node)]
acc in ([(Node, Node)]
kvs, [Item]
is)
        go [(Node, Node)]
acc [Item]
kAbove ((Node
k, Node
v) : [(Node, Node)]
rest) [Item]
is =
          let next :: Int
next = Node -> [(Node, Node)] -> Int
nextStart Node
v [(Node, Node)]
rest
              !(Node
k', [Item]
is') =
                Env
-> Int
-> Int
-> (Int, Int)
-> [Item]
-> Node
-> [Item]
-> (Node, [Item])
attachNode Env
e (Node -> Node -> Int
keyLimit Node
k Node
v) (CollectionStyle -> Int
entryColumn CollectionStyle
style) (Int
s, Int
lineStart) [Item]
kAbove Node
k [Item]
is
              !(Node
v', [Item]
is'') = Env
-> Int
-> Int
-> (Int, Int)
-> [Item]
-> Node
-> [Item]
-> (Node, [Item])
attachNode Env
e Int
next (CollectionStyle -> Int
entryColumn CollectionStyle
style) (Int
s, Int
lineStart) [] Node
v [Item]
is'
              !(Node
v'', [Item]
is''')
                | CollectionStyle
style CollectionStyle -> CollectionStyle -> Bool
forall a. Eq a => a -> a -> Bool
== CollectionStyle
Block = Int -> Node -> [Item] -> (Node, [Item])
linesBelow Int
next Node
v' [Item]
is''
                | Bool
otherwise = (Node
v', [Item]
is'')
          in [(Node, Node)]
-> [Item] -> [(Node, Node)] -> [Item] -> ([(Node, Node)], [Item])
go ((Node
k', Node
v'') (Node, Node) -> [(Node, Node)] -> [(Node, Node)]
forall a. a -> [a] -> [a]
: [(Node, Node)]
acc) [] [(Node, Node)]
rest [Item]
is'''

        nextStart :: Node -> [(Node, Node)] -> Int
        nextStart :: Node -> [(Node, Node)] -> Int
nextStart Node
v = \case
          (Node
k, Node
_) : [(Node, Node)]
_
            | CollectionStyle
style CollectionStyle -> CollectionStyle -> Bool
forall a. Eq a => a -> a -> Bool
== CollectionStyle
Block -> Int -> Int -> Int
entryStart (Offset -> Int
offsetOf Node
v.endOffset) (Offset -> Int
offsetOf Node
k.offset)
            | Bool
otherwise -> Offset -> Int
offsetOf Node
k.offset
          [] -> if CollectionStyle
style CollectionStyle -> CollectionStyle -> Bool
forall a. Eq a => a -> a -> Bool
== CollectionStyle
Flow then Int
en else Int
limit

        -- The key takes the comment on the line of the colon, but not the
        -- lines below it, e.g. in ": &a".
        keyLimit :: Node -> Node -> Int
        keyLimit :: Node -> Node -> Int
keyLimit Node
k Node
v
          | CollectionStyle
style CollectionStyle -> CollectionStyle -> Bool
forall a. Eq a => a -> a -> Bool
== CollectionStyle
Block =
              Int -> Int -> Int
lineEnd
                (Offset -> Int
offsetOf Node
v.offset)
                (Int -> Int -> Int
entryStart (Offset -> Int
offsetOf Node
k.endOffset) (Offset -> Int
offsetOf Node
v.offset))
          | Bool
otherwise = Offset -> Int
offsetOf Node
v.offset

    -- The start of the next entry of a block collection, between the end of
    -- the previous entry and the content of the next one: the first indicator
    -- or property, or the content. Lines between an indicator and the content,
    -- e.g. in "- &a", belong to the next entry.
    entryStart :: Int -> Int -> Int
    entryStart :: Int -> Int -> Int
entryStart Int
from Int
to = Int -> Int -> Int
go Int
from Int
from
      where
        go :: Int -> Int -> Int
        go :: Int -> Int -> Int
go Int
i Int
ls
          | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
to = Int
to
          | Bool
otherwise = case Array -> Int -> Word8
A.unsafeIndex Env
e.array (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base) of
              Word8
w
                | Word8 -> Bool
isBreak Word8
w -> Int -> Int -> Int
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
                | Word8 -> Bool
isWhite Word8
w -> Int -> Int -> Int
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int
ls
                | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH Bool -> Bool -> Bool
&& (Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
ls Bool -> Bool -> Bool
|| Word8 -> Bool
isWhite (Array -> Int -> Word8
A.unsafeIndex Env
e.array (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))) ->
                    Int -> Int -> Int
go (Int -> Int -> Int
lineEnd Int
to Int
i) Int
ls
                | Bool
otherwise -> Int
i

    -- The end of the line of the second offset, or the first offset if it
    -- comes first.
    lineEnd :: Int -> Int -> Int
    lineEnd :: Int -> Int -> Int
lineEnd Int
to Int
i
      | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
to Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isBreak (Array -> Int -> Word8
A.unsafeIndex Env
e.array (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base))) = Int -> Int -> Int
lineEnd Int
to (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
      | Bool
otherwise = Int
i

    -- The column of 'attachNode' for an entry of a collection in the style.
    entryColumn :: CollectionStyle -> Int
    entryColumn :: CollectionStyle -> Int
entryColumn CollectionStyle
style = if CollectionStyle
style CollectionStyle -> CollectionStyle -> Bool
forall a. Eq a => a -> a -> Bool
== CollectionStyle
Flow then Int
0 else Int
column Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1

    -- The lines below a scalar or an alias in a block collection, before the
    -- limit, that are indented deeper than its entry, and the empty lines
    -- between them. They go after the node, as 'blockAfter' does for a
    -- collection. Below a block scalar, such a line is part of the scalar
    -- if it is indented as deep as its content.
    linesBelow :: Int -> Node -> [Item] -> (Node, [Item])
    linesBelow :: Int -> Node -> [Item] -> (Node, [Item])
linesBelow Int
lim Node
x [Item]
is = case Node
x.content of
      SequenceContent {} -> (Node
x, [Item]
is)
      MappingContent {} -> (Node
x, [Item]
is)
      ScalarContent ScalarStyle
style Text
_ | ScalarStyle -> Bool
isBlockScalar ScalarStyle
style -> (Node
x, [Item]
is)
      Content
_ -> case (Item -> Bool) -> [Item] -> ([Line], [Item])
takeLines (\Item
i -> Item
i.at Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
lim Bool -> Bool -> Bool
&& Item
i.own Bool -> Bool -> Bool
&& (Item -> Bool
isEmptyLine Item
i Bool -> Bool -> Bool
|| Item
i.at Int -> Int -> Int
forall a. Num a => a -> a -> a
- Item
i.lineStart Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
column)) [Item]
is of
        ([], [Item]
_) -> (Node
x, [Item]
is)
        ([Line]
ls, [Item]
rest) ->
          let !x' :: Node
x' = Comments -> Node -> Node
withComments ([Line] -> Maybe Text -> [Line] -> Comments
strictComments Node
x.comments.before Node
x.comments.inline [Line]
ls) Node
x
          in (Node
x', [Item]
rest)

    between :: Int -> Int -> T.Text
    between :: Int -> Int -> Text
between Int
i Int
j = Env -> Int -> Int -> Text
slice Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base) (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base)

    comment :: Item -> Maybe T.Text
    comment :: Item -> Maybe Text
comment Item
i = case Item
i.line of
      Comment Text
t -> Text -> Maybe Text
forall a. a -> Maybe a
Just Text
t
      Line
EmptyLine -> Maybe Text
forall a. Maybe a
Nothing

    -- The offset of the start of the line with the second offset, after the
    -- byte order marks, as for the items. The walk stops at the first offset
    -- of the pair, and the pair gives the start of its line. Without it, each
    -- nested block collection of a long line would walk back to the start of
    -- the line, and the time would be quadratic.
    lineFrom :: (Int, Int) -> Int -> Int
    lineFrom :: (Int, Int) -> Int -> Int
lineFrom (Int
p, Int
ls) Int
o = Int -> Int
go (Int
o Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base)
      where
        go :: Int -> Int
        go :: Int -> Int
go Int
i
          | Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
p Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base = Int
ls
          | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Env
e.base Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isBreak (Array -> Int -> Word8
A.unsafeIndex Env
e.array (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))) = Int -> Int
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
          | Bool
otherwise = Env -> Int -> Int
skipBoms Env
e Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env
e.base

-- | The ranges of the scalars, which cannot contain comments, in the order of
-- the input. The range of a block scalar starts after its header.
skipRanges :: Env -> Node -> [(Int, Int)]
skipRanges :: Env -> Node -> [(Int, Int)]
skipRanges Env
e Node
root = Node -> [(Int, Int)] -> [(Int, Int)]
go Node
root []
  where
    go :: Node -> [(Int, Int)] -> [(Int, Int)]
    go :: Node -> [(Int, Int)] -> [(Int, Int)]
go Node
n [(Int, Int)]
acc = case Node
n.content of
      ScalarContent ScalarStyle
style Text
_
        | ScalarStyle -> Bool
isBlockScalar ScalarStyle
style ->
            let s :: Int
s = Env -> Int -> Int
nextLineStart Env
e (Offset -> Int
offsetOf Node
n.offset Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base)
            in if Int
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
en then (Int
s, Int
en) (Int, Int) -> [(Int, Int)] -> [(Int, Int)]
forall a. a -> [a] -> [a]
: [(Int, Int)]
acc else [(Int, Int)]
acc
        | Offset -> Int
offsetOf Node
n.offset Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
en -> (Offset -> Int
offsetOf Node
n.offset Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base, Int
en) (Int, Int) -> [(Int, Int)] -> [(Int, Int)]
forall a. a -> [a] -> [a]
: [(Int, Int)]
acc
        where
          en :: Int
          en :: Int
en = Offset -> Int
offsetOf Node
n.endOffset Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Env
e.base
      SequenceContent CollectionStyle
_ [Node]
xs -> (Node -> [(Int, Int)] -> [(Int, Int)])
-> [(Int, Int)] -> [Node] -> [(Int, Int)]
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Node -> [(Int, Int)] -> [(Int, Int)]
go [(Int, Int)]
acc [Node]
xs
      MappingContent CollectionStyle
_ [(Node, Node)]
kvs -> ((Node, Node) -> [(Int, Int)] -> [(Int, Int)])
-> [(Int, Int)] -> [(Node, Node)] -> [(Int, Int)]
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(Node
k, Node
v) [(Int, Int)]
a -> Node -> [(Int, Int)] -> [(Int, Int)]
go Node
k (Node -> [(Int, Int)] -> [(Int, Int)]
go Node
v [(Int, Int)]
a)) [(Int, Int)]
acc [(Node, Node)]
kvs
      Content
_ -> [(Int, Int)]
acc