packages feed

asciidoc 0.1.0.5 → 0.1.1

raw patch · 39 files changed

+2366/−873 lines, 39 filesdep +tasty-benchdep −attoparsecPVP ok

version bump matches the API change (PVP)

Dependencies added: tasty-bench

Dependencies removed: attoparsec

API changes (from Hackage documentation)

+ AsciiDoc.Generic: instance (AsciiDoc.Generic.HasBlocks a, AsciiDoc.Generic.HasBlocks b) => AsciiDoc.Generic.HasBlocks (a, b)
+ AsciiDoc.Generic: instance (AsciiDoc.Generic.HasInlines a, AsciiDoc.Generic.HasInlines b) => AsciiDoc.Generic.HasInlines (a, b)
+ AsciiDoc.Parse: instance GHC.Show.Show AsciiDoc.Parse.ParseFlags

Files

CHANGELOG.md view
@@ -1,5 +1,71 @@ # Revision history for asciidoc-hs +## 0.1.1 -- 2026-08-27++  * Fix counter type for lowercase alpha start values.+    `{counter:name:a}` produced UpperAlphaCounter due to a copy-paste error.++  * Fix Meta Semigroup to keep title attributes with the title.+    Both branches of the docTitleAttributes case returned m2's attributes,+    so concatenating documents paired the first document's title with the+    second document's title attributes.++  * Allow empty cells in CSV/TSV tables.++  * Require ';' to terminate character entity references.++  * Traverse both components of definition list items in generic traversals.++  * Require a space or end of line after definition list markers.++  * Fix infinite loop on unterminated fenced and literal delimited blocks+    at end of document.++  * Consume extra backticks of a longer closing fence.+    A closing fence longer than the opening one (e.g. closing a ``` block+    with ````) was only matched up to the opening length, leaking the+    remaining backticks into the following block.++  * Guard against include cycles. A file that (transitively) included+    itself caused infinite recursion in `handleIncludes`.++  * Resolve attribute references at the point of use.+    Attribute references were only substituted in a post-processing pass+    using the end-of-document attribute values, so a reference before a+    redefinition incorrectly picked up the later value.++  * Infer implicit table headers from layout like Asciidoctor.+    Tables were given a header row by default unless the noheader option+    was set.  Asciidoctor instead only implies a header when the first row+    sits on a single line directly after the opening border and is+    followed by a blank line.++  * Add a benchmark suite.++  * Consume whole letter runs in the inline parser.++  * Try macro, autolink and email starts once per letter run.++  * Avoid the full cell-separator lookahead at every PSV cell character.++  * Speed up the inline, table and post-processing paths.++  * Flatten the parser monad stack. Replace the derived+    ReaderT/StateT-over-attoparsec stack with a hand-rolled, inlined+    equivalent, so primitive operations no longer pay for two layers+    of transformer binds. Backtracking semantics are unchanged: state+    changes made by a failed branch of `<|>` are discarded.++  * Reject non-definition-list lines with a substring check.++  * Skip typographic replacement when no trigger is present.++  * Scan plain inline text in chunks, not per word.++  * Replace attoparsec with a hand-rolled CPS parser.++  * Do typographic replacement in one pass, without a String round trip.+ ## 0.1.0.5 -- 2026-08-27      Tables: skip whitespace before cell spec (#13).
asciidoc.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.4 name:               asciidoc-version:            0.1.0.5+version:            0.1.1 synopsis:           AsciiDoc parser. description:        A parser for AsciiDoc syntax. license:            BSD-3-Clause@@ -33,7 +33,6 @@     build-depends:    base >=4.14 && <5                     , text                     , mtl-                    , attoparsec                     , filepath                     , containers                     , tagsoup@@ -76,3 +75,16 @@         directory,         filepath,         process++benchmark asciidoc-bench+    import:           warnings+    default-language: Haskell2010+    type:             exitcode-stdio-1.0+    hs-source-dirs:   bench+    main-is:          Main.hs+    ghc-options:      -rtsopts "-with-rtsopts=-A32m"+    build-depends:+        base >=4.14 && <5,+        asciidoc,+        tasty-bench,+        text
+ bench/Main.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}+import Test.Tasty.Bench+import qualified Data.Text as T+import Data.Text (Text)+import Data.Functor.Identity (Identity (..))+import Data.Monoid (Sum (..))+import AsciiDoc++main :: IO ()+main = defaultMain+  [ bgroup "prose"+      [ bench (show n <> "KB") $ nf parseSize (prose n)+      | n <- [32, 64, 128 :: Int]+      ]+  , bgroup "longword"+      [ bench (show n) $ nf parseSize (longword n)+      | n <- [1000, 2000, 4000 :: Int]+      ]+  , bgroup "formatting"+      [ bench "32KB" $ nf parseSize (formatting 32) ]+  , bgroup "typography"+      [ bench "32KB" $ nf parseSize (typography 32) ]+  , bgroup "table"+      [ bench (show n <> "rows") $ nf parseSize (table n)+      | n <- [250, 500, 1000 :: Int]+      ]+  ]++-- Parse a document and force its interesting parts, returning a size.+parseSize :: Text -> Int+parseSize t = docSize $ runIdentity $+  parseDocument (const (Identity "")) raiseError "bench.adoc" t+ where+  raiseError fp pos msg =+    error $ fp <> "@" <> show pos <> ": " <> msg++docSize :: Document -> Int+docSize d = getSum (foldBlocks (const (Sum 1)) d) ++            getSum (foldInlines inlineSize d)+ where+  inlineSize (Inline _ (Str s)) = Sum (T.length s)+  inlineSize _ = Sum 1++-- n KB of plain prose paragraphs.+prose :: Int -> Text+prose n = T.replicate (n * 16) paragraph+ where+  paragraph = T.replicate 7 sentence <> "\n\n"  -- ~64 bytes/sentence+  sentence = "The quick brown fox jumps over one lazy dog every morning. "++-- A single unbroken run of letters (worst case for per-letter lookahead).+longword :: Int -> Text+longword n = T.replicate n "a" <> "\n"++-- n KB of text with plenty of inline formatting.+formatting :: Int -> Text+formatting n = T.replicate (n * 16) paragraph+ where+  paragraph = T.replicate 8 chunk <> "\n\n"  -- 8 * 8 bytes+  chunk = "a *b* `c` _d_ "++-- n KB of prose with plenty of typographic replacements.+typography :: Int -> Text+typography n = T.replicate (n * 16) paragraph+ where+  paragraph = T.replicate 7 sentence <> "\n\n"  -- ~64 bytes/sentence+  sentence = "It's odd -- the dog's list... (C) 2024 -> next <= prev now. "++-- A PSV table with n rows of five cells.+table :: Int -> Text+table n =+  "|===\n" <> T.replicate n row <> "|===\n"+ where+  row = "| alpha | beta | gamma | delta | epsilon\n"
src/AsciiDoc/AST.hs view
@@ -120,7 +120,7 @@                   , docTitleAttributes =                                case docTitle m1 of                                  [] -> docTitleAttributes m2-                                 _ -> docTitleAttributes m2+                                 _ -> docTitleAttributes m1                   , docAuthors = docAuthors m1 <> docAuthors m2                   , docRevision = docRevision m1 `mplus` docRevision m2                   , docAttributes = docAttributes m1 <> docAttributes m2
src/AsciiDoc/Generic.hs view
@@ -60,6 +60,15 @@   foldInlines f = foldMap (foldInlines f)   mapInlines f = mapM (mapInlines f) +-- Note: this instance is needed because the (t a) instance above would+-- otherwise match pairs via the Traversable instance for ((,) a), which+-- traverses only the second component.  Definition list items are pairs+-- of ([Inline], [Block]), and both components must be traversed.+instance {-# OVERLAPPING #-} (HasInlines a, HasInlines b)+         => HasInlines (a, b) where+  foldInlines f (x, y) = foldInlines f x <> foldInlines f y+  mapInlines f (x, y) = liftM2 (,) (mapInlines f x) (mapInlines f y)+ instance HasInlines Inline where   foldInlines f i@(Inline _ ty) =     f i <> foldInlines f ty@@ -123,6 +132,11 @@ instance (HasBlocks a, Traversable t, Foldable t) => HasBlocks (t a) where   foldBlocks f = foldMap (foldBlocks f)   mapBlocks f = mapM (mapBlocks f)++instance {-# OVERLAPPING #-} (HasBlocks a, HasBlocks b)+         => HasBlocks (a, b) where+  foldBlocks f (x, y) = foldBlocks f x <> foldBlocks f y+  mapBlocks f (x, y) = liftM2 (,) (mapBlocks f x) (mapBlocks f y)  instance HasBlocks Block where   foldBlocks f i@(Block _ _ ty) =
src/AsciiDoc/Parse.hs view
@@ -1,7 +1,9 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -19,15 +21,16 @@ import qualified Data.Text as T import qualified Data.Text.Read as TR import Data.Text (Text)-import Data.List (foldl', intersperse, isPrefixOf)-import qualified Data.Attoparsec.Text as A+import Data.List (foldl', intersperse, isPrefixOf, sortOn)+import qualified Data.Text.Internal as TI+import qualified Data.Text.Unsafe as TU import System.FilePath import Control.Applicative import Control.Monad import Control.Monad.State import Control.Monad.Reader import Data.Char (isAlphaNum, isAscii, isSpace, isLetter, isPunctuation, chr, isDigit,-                  isUpper, isLower, ord)+                  isHexDigit, digitToInt, isUpper, isLower, ord) import AsciiDoc.AST import AsciiDoc.Generic -- import Debug.Trace@@ -42,10 +45,31 @@                   -- ^ Path of file containing the text               -> Text -- ^ Text to convert               -> m Document-parseDocument getFileContents raiseError path t =-   handleResult (parse pDocument path t) >>= handleIncludes-     >>= resolveAttributeReferences . addIdentifiers-     >>= resolveCrossReferences+parseDocument getFileContents raiseError path t = do+  -- The parser records which constructs occurred, so that the+  -- post-processing passes (each a full traversal of the AST) can be+  -- skipped when they would do nothing.  When includes are expanded,+  -- their contents are not reflected in the flags, so all passes run.+  (doc0, flags) <- case parse ((,) <$> pDocument <*> gets parseFlags) path t of+                     Left err -> do+                       -- raiseError may return a fallback document whose+                       -- contents we know nothing about, so run all passes.+                       d <- raiseError path (errorPosition err)+                                            (errorMessage err)+                       pure (d, ParseFlags True True True True)+                     Right r -> pure r+  doc1 <- if sawInclude flags+             then handleIncludes doc0+             else pure doc0+  let doc2 = if sawInclude flags || sawSection flags+                then addIdentifiers doc1+                else doc1+  doc3 <- if sawInclude flags || sawAttributeReference flags+             then resolveAttributeReferences doc2+             else pure doc2+  if sawInclude flags || sawCrossReference flags+     then resolveCrossReferences doc3+     else pure doc3  where   handleResult (Left err) =     raiseError path (errorPosition err) (errorMessage err)@@ -80,20 +104,25 @@        Just x -> return $ Inline attr (Str x)   goAttref _ il = return il -  handleIncludes = mapBlocks handleIncludeBlock+  handleIncludes = mapBlocks (handleIncludeBlock [path]) -  handleIncludeBlock (Block attr mbtitle (Include fp Nothing)) =-    (do contents <- getFileContents fp-        Block attr mbtitle . Include fp . Just . docBlocks <$>-          handleResult (parse pDocument fp contents))-      >>= mapBlocks handleIncludeBlock-  handleIncludeBlock (Block attr mbtitle-                         (IncludeListing mblang fp Nothing)) =+  -- The first argument is the chain of files being included; a file+  -- that (transitively) includes itself is left unexpanded instead of+  -- recursing forever.+  handleIncludeBlock seen b@(Block attr mbtitle (Include fp Nothing))+    | fp `elem` seen = pure b+    | otherwise =+        (do contents <- getFileContents fp+            Block attr mbtitle . Include fp . Just . docBlocks <$>+              handleResult (parse pDocument fp contents))+          >>= mapBlocks (handleIncludeBlock (fp : seen))+  handleIncludeBlock seen (Block attr mbtitle+                             (IncludeListing mblang fp Nothing)) =     (do contents <- getFileContents fp         pure $ Block attr mbtitle $ IncludeListing mblang fp              $ Just (map (`SourceLine` []) (T.lines contents)))-      >>= mapBlocks handleIncludeBlock-  handleIncludeBlock x = pure x+      >>= mapBlocks (handleIncludeBlock (fp : seen))+  handleIncludeBlock _ x = pure x  -- | Make a relative path relative to a parent's directory. -- Leaves absolute paths alone.@@ -105,16 +134,96 @@  --- Wrapped parser type: -newtype P a = P { unP :: ReaderT ParserConfig (StateT ParserState A.Parser) a }-  deriving (Functor, Applicative, Alternative, Monad, MonadPlus,-            MonadFail, MonadReader ParserConfig, MonadState ParserState)+-- A parser in continuation-passing style over the whole input Text.+-- The config, state and remaining input are threaded by hand and+-- passed directly to a success continuation, so binds allocate no+-- intermediate results.  Incremental (chunked) input is not+-- supported -- the whole document is in memory anyway -- which makes+-- backtracking cheap: (<|>) simply re-runs the second parser with the+-- state and input it saved.  As with StateT over a backtracking+-- parser, state changes made by a failed branch of (<|>) are+-- discarded.  The failure continuation receives the message and the+-- remaining input at the failure site (used to report a position).+newtype P a = P { unP :: forall r. ParserConfig -> ParserState -> Text+                      -> (String -> Text -> r)              -- failure+                      -> (a -> ParserState -> Text -> r)    -- success+                      -> r } +instance Functor P where+  fmap f (P m) = P $ \c s t kf ks -> m c s t kf (\a s' t' -> ks (f a) s' t')+  {-# INLINE fmap #-}++instance Applicative P where+  pure a = P $ \_ s t _ ks -> ks a s t+  {-# INLINE pure #-}+  P mf <*> P ma = P $ \c s t kf ks ->+    mf c s t kf (\f s' t' ->+      ma c s' t' kf (\a s'' t'' -> ks (f a) s'' t''))+  {-# INLINE (<*>) #-}+  P ma *> P mb = P $ \c s t kf ks ->+    ma c s t kf (\_ s' t' -> mb c s' t' kf ks)+  {-# INLINE (*>) #-}+  P ma <* P mb = P $ \c s t kf ks ->+    ma c s t kf (\a s' t' ->+      mb c s' t' kf (\_ s'' t'' -> ks a s'' t''))+  {-# INLINE (<*) #-}++instance Monad P where+  P m >>= f = P $ \c s t kf ks ->+    m c s t kf (\a s' t' -> unP (f a) c s' t' kf ks)+  {-# INLINE (>>=) #-}++instance MonadFail P where+  fail msg = P $ \_ _ t kf _ -> kf ("Failed reading: " <> msg) t++instance Alternative P where+  empty = P $ \_ _ t kf _ -> kf "empty" t+  {-# INLINE empty #-}+  -- Note that the success continuation is passed through unchanged:+  -- once a branch succeeds, a later failure calls the failure+  -- continuation in scope at that point, not the saved one, so it+  -- does not backtrack into the right branch.+  P a <|> P b = P $ \c s t kf ks -> a c s t (\_ _ -> b c s t kf ks) ks+  {-# INLINE (<|>) #-}++instance MonadPlus P++instance MonadReader ParserConfig P where+  ask = P $ \c s t _ ks -> ks c s t+  {-# INLINE ask #-}+  local f (P m) = P $ \c -> m (f c)+  {-# INLINE local #-}++instance MonadState ParserState P where+  get = P $ \_ s t _ ks -> ks s s t+  {-# INLINE get #-}+  put s = P $ \_ _ t _ ks -> ks () s t+  {-# INLINE put #-}+  state f = P $ \_ s t _ ks -> case f s of (a, s') -> ks a s' t+  {-# INLINE state #-}+ data ParserState = ParserState                      { counterMap :: M.Map Text (CounterType, Int)                      , docAttrs :: M.Map Text Text+                     , parseFlags :: !ParseFlags                      }         deriving (Show) +-- | Which constructs occurred during the parse; used to skip+-- post-processing passes that would have no effect.+data ParseFlags = ParseFlags+  { sawInclude :: !Bool+  , sawSection :: !Bool+  , sawAttributeReference :: !Bool+  , sawCrossReference :: !Bool+  } deriving (Show)++noParseFlags :: ParseFlags+noParseFlags = ParseFlags False False False False++setFlag :: (ParseFlags -> ParseFlags) -> P ()+setFlag f = modify $ \s -> s{ parseFlags = f (parseFlags s) }+ defaultDocAttrs :: M.Map Text Text defaultDocAttrs = M.insert "sectids" "" mempty @@ -135,23 +244,24 @@                                  })                     (ParserState { counterMap = mempty                                  , docAttrs = defaultDocAttrs+                                 , parseFlags = noParseFlags                                  })                     p  parse' :: ParserConfig -> ParserState        -> P a -> T.Text -> Either ParseError a parse' cfg st p t =-  go $ A.parse (evalStateT ( runReaderT (unP p) cfg ) st) t+  unP p cfg st t failure success  where-  go (A.Fail i _ msg) = Left $ ParseError (T.length t - T.length i)-                             $ if "endOfInput" `isPrefixOf` msg-                                  then "Unexpected " <> show (T.take 20 i)-                                  else msg-  go (A.Partial continue) = go (continue "")-  go (A.Done _i r) = Right r+  failure msg i = Left $ ParseError (T.length t - T.length i)+                       $ if "endOfInput" `isPrefixOf` msg+                            then "Unexpected " <> show (T.take 20 i)+                            else msg+  success a _ _ = Right a  localP :: (ParserConfig -> ParserConfig) -> P a -> P a-localP f (P p) = P (local f p)+localP f (P p) = P $ \c -> p (f c)+{-# INLINE localP #-}  withBlockContext :: BlockContext -> P a -> P a withBlockContext bc =@@ -160,80 +270,157 @@ withHardBreaks :: P a -> P a withHardBreaks = localP (\conf -> conf{ hardBreaks = True }) -liftP :: A.Parser a -> P a-liftP = P . lift . lift+failP :: String -> P a+failP msg = P $ \_ _ t kf _ -> kf msg t  vchar :: Char -> P ()-vchar = liftP . void . A.char+vchar c = P $ \_ s t kf ks ->+  case T.uncons t of+    Just (c', t') | c' == c -> ks () s t'+    _ -> kf "satisfy" t+{-# INLINE vchar #-}  char :: Char -> P Char-char = liftP . A.char+char c = P $ \_ s t kf ks ->+  case T.uncons t of+    Just (c', t') | c' == c -> ks c s t'+    _ -> kf "satisfy" t+{-# INLINE char #-}  peekChar :: P (Maybe Char)-peekChar = liftP A.peekChar+peekChar = P $ \_ s t _ ks ->+  case T.uncons t of+    Just (c, _) -> ks (Just c) s t+    Nothing -> ks Nothing s t+{-# INLINE peekChar #-}  peekChar' :: P Char-peekChar' = liftP A.peekChar'+peekChar' = P $ \_ s t kf ks ->+  case T.uncons t of+    Just (c, _) -> ks c s t+    Nothing -> kf "not enough input" t+{-# INLINE peekChar' #-}  anyChar :: P Char-anyChar = liftP A.anyChar+anyChar = P $ \_ s t kf ks ->+  case T.uncons t of+    Just (c, t') -> ks c s t'+    Nothing -> kf "not enough input" t+{-# INLINE anyChar #-}  satisfy :: (Char -> Bool) -> P Char-satisfy = liftP . A.satisfy+satisfy f = P $ \_ s t kf ks ->+  case T.uncons t of+    Just (c, t') | f c -> ks c s t'+    _ -> kf "satisfy" t+{-# INLINE satisfy #-}  space :: P Char-space = liftP A.space+space = satisfy isSpace  isEndOfLine :: Char -> Bool-isEndOfLine = A.isEndOfLine+isEndOfLine c = c == '\n' || c == '\r' +-- The parser only ever advances by taking suffixes of the input, all+-- slices of one underlying array, so the text consumed between two+-- points is the prefix of the earlier remainder whose length is the+-- difference of the remainders' lengths.+consumed :: Text -> Text -> Text+consumed (TI.Text arr off len) (TI.Text _ _ len') =+  TI.text arr off (len - len')+{-# INLINE consumed #-}+ match :: P a -> P (T.Text, a)-match p = P $ do-  parseInfo <- ask-  parserState <- get-  lift . lift $ A.match (evalStateT (runReaderT (unP p) parseInfo) parserState)+match p = P $ \c s t kf ks ->+  unP p c s t kf (\x _ t' -> ks (consumed t t', x) s t') +-- Like match, but keeps the parser state changes made by the inner+-- parser instead of discarding them.+matchKeepingState :: P a -> P (T.Text, a)+matchKeepingState p = P $ \c s t kf ks ->+  unP p c s t kf (\x s' t' -> ks (consumed t t', x) s' t')++-- Run a parser, then restore the input (and state) as they were.+lookAhead :: P a -> P a+lookAhead (P m) = P $ \c s t kf ks -> m c s t kf (\a _ _ -> ks a s t)+ string :: T.Text -> P T.Text-string = liftP . A.string+string pat = P $ \_ s t kf ks ->+  case T.stripPrefix pat t of+    Just t' -> ks pat s t'+    Nothing -> kf "string" t+{-# INLINE string #-}  decimal :: Integral a => P a-decimal = liftP A.decimal+decimal = P $ \_ s t kf ks ->+  case T.span isDigit t of+    (ds, t') | T.null ds -> kf "decimal" t+             | otherwise -> ks (T.foldl' step 0 ds) s t'+ where+  step n d = n * 10 + fromIntegral (ord d - 48) +hexadecimal :: Integral a => P a+hexadecimal = P $ \_ s t kf ks ->+  case T.span isHexDigit t of+    (ds, t') | T.null ds -> kf "hexadecimal" t+             | otherwise -> ks (T.foldl' step 0 ds) s t'+ where+  step n d = n * 16 + fromIntegral (digitToInt d)+ endOfInput :: P ()-endOfInput = liftP A.endOfInput+endOfInput = P $ \_ s t kf ks ->+  if T.null t then ks () s t else kf "endOfInput" t  endOfLine :: P ()-endOfLine = liftP A.endOfLine+endOfLine = P $ \_ s t kf ks ->+  case T.uncons t of+    Just ('\n', t') -> ks () s t'+    Just ('\r', t') | Just ('\n', t'') <- T.uncons t' -> ks () s t''+    _ -> kf "endOfLine" t+{-# INLINE endOfLine #-}  takeWhile :: (Char -> Bool) -> P T.Text-takeWhile f = liftP (A.takeWhile f)+takeWhile f = P $ \_ s t _ ks ->+  case T.span f t of (a, t') -> ks a s t'+{-# INLINE takeWhile #-}  takeWhile1 :: (Char -> Bool) -> P T.Text-takeWhile1 f = liftP (A.takeWhile1 f)+takeWhile1 f = P $ \_ s t kf ks ->+  case T.span f t of+    (a, t') | T.null a -> kf "takeWhile1" t+            | otherwise -> ks a s t'+{-# INLINE takeWhile1 #-}  skipWhile :: (Char -> Bool) -> P ()-skipWhile f = liftP (A.skipWhile f)+skipWhile f = P $ \_ s t _ ks -> ks () s (T.dropWhile f t)+{-# INLINE skipWhile #-}  skipMany :: P a -> P ()-skipMany = A.skipMany+skipMany p = go+ where+  go = (p *> go) <|> pure ()  option :: Alternative f => a -> f a -> f a-option = A.option+option x p = p <|> pure x  choice :: [P a] -> P a-choice = A.choice+choice = foldr (<|>) (failP "choice")  count :: Int -> P a -> P [a]-count = A.count+count = replicateM  manyTill :: P a -> P b  -> P [a]-manyTill = A.manyTill+manyTill p end = go+ where+  go = ([] <$ end) <|> liftA2 (:) p go  sepBy :: P a -> P b -> P [a]-sepBy = A.sepBy+sepBy p s = sepBy1 p s <|> pure []  sepBy1 :: P a -> P b -> P [a]-sepBy1 = A.sepBy1+sepBy1 p s = go+ where+  go = liftA2 (:) p ((s *> go) <|> pure [])  --- Block parsing: @@ -544,6 +731,7 @@         attr' <- pAttributes         fp <- asks filePath         let path = resolvePath fp (T.unpack target)+        setFlag $ \f -> f{ sawInclude = True }         pure $ Block (attr' <> attr) mbtitle $ Include path Nothing)   ] @@ -562,6 +750,7 @@       -- ==== bar       -- ==== baz       -- bar is a level-3 section and will contain baz!+      setFlag $ \f -> f{ sawSection = True }       pure $ Section (Level (sectionLevel + 1)) title contents     _ -> mzero @@ -615,10 +804,20 @@ pDefinitionListItem :: P ([Inline],[Block]) pDefinitionListItem = do   contexts <- asks blockContexts+  -- The term/definition separator must occur before the end of the+  -- line, so ordinary paragraph text can be rejected with a single+  -- substring check instead of the chunked term scan below.+  restOfLine <- lookAhead (takeWhile (not . isEndOfLine))+  guard $ "::" `T.isInfixOf` restOfLine   let marker = (do t <- takeWhile1 (== ':')                    case contexts of                        ListContext ':' n : _ -> guard (T.length t == n + 2)-                       _ -> guard (T.length t == 2))+                       _ -> guard (T.length t == 2)+                   -- The marker must be followed by a space or the end of+                   -- the line, so that e.g. std::vector is not mistaken+                   -- for a term/definition separator.+                   mbc <- peekChar+                   guard $ maybe True (\c -> c == ' ' || isEndOfLine c) mbc)   skipWhile (== ' ')   term <- manyTill (takeWhile1 (\c -> not (isEndOfLine c || c == ':'))                               <|> takeWhile1 (==':')) marker@@ -707,7 +906,11 @@ pDelimitedLiteralBlock c minimumNumber = do   len <- length <$> some (vchar c) <* pBlankLine   guard $ len >= minimumNumber-  let endFence = count len (vchar c) *> (pBlankLine <|> endOfInput)+  -- The bare endOfInput alternative makes an unterminated block extend+  -- to the end of input; without it, manyTill would loop forever at end+  -- of input because pLine succeeds there without consuming anything.+  let endFence = (count len (vchar c) *> (pBlankLine <|> endOfInput))+                 <|> endOfInput   manyTill pLine endFence  pDelimitedBlock :: Char -> Int -> P [Block]@@ -755,7 +958,13 @@   let mblang = case T.strip lang' of                  "" -> Nothing                  l -> Just (Language l)-  lns <- toSourceLines <$> manyTill pLine (string ticks)+  -- An unterminated block extends to the end of input; without the+  -- endOfInput alternative, manyTill would loop forever at end of input+  -- because pLine succeeds there without consuming anything.  A closing+  -- fence may be longer than the opening one; consume the extra+  -- backticks so they don't leak into the following block.+  lns <- toSourceLines <$>+    manyTill pLine ((string ticks *> skipWhile (== '`')) <|> endOfInput)   pure $ Block attr mbtitle $ Listing mblang lns  pListing :: Maybe BlockTitle -> Attr -> P Block@@ -767,12 +976,14 @@           _ -> (Nothing, attr)   lns <- toSourceLines <$> pDelimitedLiteralBlock '-' 4   fp <- asks filePath-  pure $ Block attr' mbtitle $-    case lns of+  bt <- case lns of       [SourceLine x []] | "include::" `T.isPrefixOf` x           , Right ("include", target) <- parse pBlockMacro' fp x-          -> IncludeListing mbLang (resolvePath fp (T.unpack target)) Nothing-      _ -> Listing mbLang lns)+          -> do setFlag $ \f -> f{ sawInclude = True }+                pure $ IncludeListing mbLang+                         (resolvePath fp (T.unpack target)) Nothing+      _ -> pure $ Listing mbLang lns+  pure $ Block attr' mbtitle bt)  <|>   (case attr of     Attr ("listing":ps) kvs -> do@@ -928,7 +1139,15 @@   case contexts of     ListContext{} : _ -> do       guard $ t' /= "+"-      guard $ not $ "::" `T.isInfixOf` t'+      -- A definition list marker is a run of 2 to 4 colons followed by a+      -- space or the end of the line.  A mere "::" infix (e.g. in+      -- std::vector) does not start a definition list.+      let isDlistMarker (_, post) =+            let colons = T.takeWhile (== ':') post+                rest = T.drop (T.length colons) post+            in T.length colons >= 2 && T.length colons <= 4 &&+               (T.null rest || T.head rest == ' ')+      guard $ not $ any isDlistMarker (T.breakOnAll "::" t')       guard $ case parse pAnyListItemStart fp (T.strip t) of                 Left _ -> True                 _ -> False@@ -944,12 +1163,14 @@   void $ string "==="   skipWhile (=='=')   pBlankLine-  skipMany pBlankLine   pure syntax  pTable :: Maybe BlockTitle -> Attr -> P Block pTable mbtitle (Attr ps kvs) = do   syntax' <- pTableBorder+  -- Record whether a blank line separates the opening border from the+  -- first row; if so, no header row is implied.+  leadingBlank <- not . null <$> many pBlankLine   mbcolspecs <- maybe (pure Nothing) (fmap Just . parseColspecs)                   (M.lookup "cols" kvs)   let options = maybe [] T.words $ M.lookup "options" kvs@@ -967,8 +1188,7 @@                  _ -> Nothing   let tableOpts = TableOpts { tableSyntax = syntax                             , tableSeparator = mbsep-                            , tableHeader = "header" `elem` options ||-                                "noheader" `notElem` options+                            , tableHeader = "header" `elem` options                             , tableFooter = "footer" `elem` options                             }   let getRows mbspecs rowspans = (([],[]) <$ pTableBorder) <|>@@ -996,13 +1216,24 @@                                                  [] -> specs                                                  _ -> colspecs'))                                      <$> getRows (Just specs) rowspans'-  (rows, colspecs') <- getRows mbcolspecs (repeat (0 :: Int))+  (rawRows, (rows, colspecs')) <-+    matchKeepingState (getRows mbcolspecs (repeat (0 :: Int)))   let attr' = Attr ps $ M.delete "format" .                         M.delete "separator" .                         M.delete "cols" .                         M.delete "options" $ kvs+  -- Like Asciidoctor, imply a header row when the first row sits on a+  -- single line directly after the opening border and is followed by a+  -- blank line.+  let isBlankLine = T.all (\c -> c == ' ' || c == '\t')+  let headerImplied = not leadingBlank && not (null rows) &&+        case T.lines rawRows of+          _ : l2 : _ -> isBlankLine l2+          _ -> False+  let hasHeader = tableHeader tableOpts ||+        ("noheader" `notElem` options && headerImplied)   let (mbHead, rest)-        | tableHeader tableOpts = (Just (take 1 rows), drop 1 rows)+        | hasHeader = (Just (take 1 rows), drop 1 rows)         | otherwise = (Nothing, rows)   let (mbFoot, bodyRows)         | tableFooter tableOpts@@ -1033,7 +1264,7 @@ pColspec = ColumnSpec <$> optional pHorizAlign                       <*> optional pVertAlign                       <*> (pWidth <|> pure Nothing)-                      <*> (toCellStyle <$> satisfy (A.inClass "adehlms")+                      <*> (toCellStyle <$> satisfy isCellStyleChar                              <|> pure Nothing)  pHorizAlign :: P HorizAlign@@ -1107,7 +1338,7 @@           (T.pack <$>             manyTill (satisfy (/='"') <|> ('"' <$ string "\"\"")) (vchar '"'))     _ -> T.strip . T.replace "\"\"" "\"" <$>-           takeWhile1 (\c -> c /= delim && not (isEndOfLine c))+           takeWhile (\c -> c /= delim && not (isEndOfLine c))  -- no "; escape delim with backslash pDSVTableRow:: Char -> Maybe [ColumnSpec] -> P [TableCell]@@ -1139,14 +1370,42 @@ pTableCellPSV mbsep allowNewlines colspecs = do   let sep = fromMaybe '|' mbsep   cellData <- pCellSep sep-  t <- T.pack <$>+  -- A cell separator match can only begin at the separator itself or+  -- at one of the characters that may precede it in a cell spec+  -- (whitespace, duplicate/span numbers, alignments, styles); a table+  -- border only at its delimiter.  Runs of other characters can be+  -- consumed at once, and the expensive lookahead for a separator or+  -- border is only needed at characters that could begin one.+  let couldStartCellSep c = c == sep || c == ' ' || c == '\t' ||+        isDigit c || isCellSpecChar c+  let couldStartBorder c = c == '|' || c == ':' || c == ','+  let isPlainCellChar c = not (couldStartCellSep c) &&+        not (couldStartBorder c) && c /= '\\' && not (isEndOfLine c)+  t <- mconcat <$>          many-          (notFollowedBy (void (pCellSep sep) <|> void pTableBorder) *>-           ((vchar '\\' *> char sep)-             <|> satisfy (not . isEndOfLine)-             <|> if allowNewlines-                    then satisfy isEndOfLine-                    else satisfy isEndOfLine <* notFollowedBy (pCellSep sep)))+          (takeWhile1 isPlainCellChar+           <|>+           (do mbc <- peekChar+               case mbc of+                 Nothing -> mzero+                 Just c -> do+                   when (couldStartCellSep c) $+                     notFollowedBy (void (pCellSep sep))+                   when (couldStartBorder c) $+                     notFollowedBy (void pTableBorder)+                   -- pCellSep skips leading whitespace itself, so if it+                   -- failed at the first space of a run it fails at+                   -- every position within it; the whole run can be+                   -- consumed after a single lookahead.+                   if c == ' ' || c == '\t'+                      then takeWhile1 (\d -> d == ' ' || d == '\t')+                      else T.singleton <$>+                        ((vchar '\\' *> char sep)+                          <|> satisfy (not . isEndOfLine)+                          <|> if allowNewlines+                                 then satisfy isEndOfLine+                                 else satisfy isEndOfLine+                                        <* notFollowedBy (pCellSep sep))))   let cell' = TableCell                { cellContent = []                , cellHorizAlign = cHorizAlign cellData@@ -1194,6 +1453,29 @@   , cStyle :: Maybe CellStyle }   deriving (Show) +-- The letters that may denote a cell style ("adehlms").+isCellStyleChar :: Char -> Bool+isCellStyleChar c =+  case c of+    'a' -> True+    'd' -> True+    'e' -> True+    'h' -> True+    'l' -> True+    'm' -> True+    's' -> True+    _   -> False++-- The characters that may occur in a cell spec (".<^>adehlms").+isCellSpecChar :: Char -> Bool+isCellSpecChar c =+  case c of+    '.' -> True+    '<' -> True+    '^' -> True+    '>' -> True+    _   -> isCellStyleChar c+ toCellStyle :: Char -> Maybe CellStyle toCellStyle 'a' = Just AsciiDocStyle toCellStyle 'd' = Just DefaultStyle@@ -1212,6 +1494,12 @@ pCellSep :: Char -> P CellData pCellSep sep = do   skipWhile (\c -> c == ' ' || c == '\t')+  -- Fail fast unless the next character can actually begin a cell+  -- separator, so that speculative lookaheads stay cheap.+  mbc <- peekChar+  case mbc of+    Just c | c == sep || isDigit c || isCellSpecChar c -> pure ()+    _ -> mzero   mult <- option 1 pMultiplier   (colspan, rowspan) <- option (Nothing, Nothing) $ do     a <- optional decimal@@ -1221,7 +1509,7 @@     pure (a, b)   halign <- optional pHorizAlign   valign <- optional pVertAlign-  sty <- (toCellStyle <$> satisfy (A.inClass "adehlms")) <|> pure Nothing+  sty <- (toCellStyle <$> satisfy isCellStyleChar) <|> pure Nothing   notFollowedBy pTableBorder <* vchar sep   pure $ CellData     { cDuplicate = mult@@ -1236,7 +1524,7 @@ --- Inline parsing:  pInlines :: P [Inline]-pInlines = pInlines' []+pInlines = pInlines' False []  pComma :: P () pComma = vchar ',' <* skipWhile isSpace@@ -1303,41 +1591,249 @@    vchar '"'    pure $ T.pack result -pInlines' :: [Char] -> P [Inline]-pInlines' cs = do-  (pLineComment *> pInlines' cs)-    <|> (do il' <- pInline cs-            let il = case il' of-                       Inline (Attr ps kvs) (Span ils)-                         | Nothing <- M.lookup "role" kvs-                         -> Inline (Attr ps kvs) (Highlight ils)-                       _ -> il'-            addStr . (il:) <$> pInlines' [])-    <|> (do c <- anyChar-            pInlines' (c:cs))-    <|> (addStr [] <$ endOfInput)+-- The [Text] argument accumulates the plain text seen so far, in+-- reverse chunk order; prependStr turns it into a Str inline.  The+-- Bool records whether the accumulated text can contain the start of+-- a typographic replacement, so that prependStr can skip+-- replaceCharsText without rescanning the text.+pInlines' :: Bool -> [Text] -> P [Inline]+pInlines' !trig cs = P $ \cfg st t@(TI.Text arr off len) kf ks ->+  -- Consume a whole run of plain characters in a single scan.  Only a+  -- few characters can require anything other than plain text: the+  -- characters that can begin an inline element or line comment, the+  -- ':' that ends a macro or autolink name, and the '@' of an email+  -- autolink.  Everything in between (letters, spaces, ordinary+  -- punctuation) is consumed here without trying any parsers.  The+  -- scan also notes replacement triggers ('-', '=', or a ".." pair).+  let chunk i = if i == 0 then cs else TI.text arr off i : cs+      go !i !tr !prevDot+        | i >= len = ks (prependStr tr (chunk i) []) st T.empty+        | otherwise =+            case TU.iter t i of+              TU.Iter c d+                | isPlainInlineChar c ->+                    go (i + d)+                       (tr || c == '-' || c == '=' || (prevDot && c == '.'))+                       (c == '.')+                | otherwise ->+                    unP (pInlineBoundary c tr (chunk i)) cfg st+                        (TI.text arr (off + i) (len - i)) kf ks+  in go 0 trig False++-- Handle a stop character of the plain-text scan (not yet consumed).+pInlineBoundary :: Char -> Bool -> [Text] -> P [Inline]+pInlineBoundary c !trig cs+  | c == ':' = pMacroAtColon trig cs plainChar+  | c == '@' = pEmailAtBoundary trig cs plainChar+  | c == '/' = (pLineComment *> pInlines' trig cs) <|> plainChar+  | otherwise =+      -- An inline start character.  '+' and '_' can also occur inside+      -- the local part of an email autolink, whose attempt must come+      -- first (it can only succeed when a '@' with a valid domain+      -- follows, in which case the formatting parse would misfire).+      (if isEmailLocalChar c then pEmailAtBoundary trig cs else id) $+      (do il' <- pInline cs+          let il = case il' of+                     Inline (Attr ps kvs) (Span ils)+                       | Nothing <- M.lookup "role" kvs+                       -> Inline (Attr ps kvs) (Highlight ils)+                     _ -> il'+          prependStr trig cs . (il:) <$> pInlines' False [])+      <|> plainChar  where-  addStr = case cs of-              [] -> id-              _  -> (Inline mempty (Str (T.pack (replaceChars $ reverse cs))):)+  -- Consume the stop character (which failed to begin anything+  -- special) as a chunk of its own; the next pInlines' scan picks up+  -- the plain run that follows.+  plainChar = do+    _ <- anyChar+    pInlines' (trig || isTriggerStop c) (T.singleton c : cs) -replaceChars :: [Char] -> [Char]-replaceChars [] = []-replaceChars ('(':'C':')':cs) = '\169':replaceChars cs-replaceChars ('(':'R':')':cs) = '\174':replaceChars cs-replaceChars ('(':'T':'M':')':cs) = '\8482':replaceChars cs-replaceChars (x:'-':'-':y:cs)-  | x == ' ', y == ' ' = '\8201':'\8212':'\8201':replaceChars cs-  | isAlphaNum x, isAlphaNum y = x:'\8212':'\8203':replaceChars (y:cs)-  | otherwise = x:'-':'-':replaceChars (y:cs)-replaceChars ('.':'.':'.':cs) = '\8230':replaceChars cs-replaceChars ('-':'>':cs) = '\8594':replaceChars cs-replaceChars ('=':'>':cs) = '\8658':replaceChars cs-replaceChars ('<':'-':cs) = '\8592':replaceChars cs-replaceChars ('<':'=':cs) = '\8656':replaceChars cs-replaceChars ('\'':cs) = '\8217':replaceChars cs-replaceChars (c:cs) = c:replaceChars cs+-- Stop characters of the plain scan that are also replacement+-- triggers.  ('-', '=' and '.' are not stop characters, so they are+-- detected by the pInlines' scan instead; a ".." pair cannot+-- straddle two chunks, since the character between them is a stop+-- character and hence not a '.'.)+isTriggerStop :: Char -> Bool+isTriggerStop c = c == '\'' || c == '(' || c == '<' +-- Characters that cannot begin an inline element or line comment, end+-- a macro or autolink name, or start the domain of an email autolink.+-- A run of them can be consumed at once without trying any parsers.+isPlainInlineChar :: Char -> Bool+isPlainInlineChar c =+  case c of+    '*'  -> False+    '_'  -> False+    '`'  -> False+    '#'  -> False+    '~'  -> False+    '^'  -> False+    '+'  -> False+    '"'  -> False+    '\'' -> False+    '('  -> False+    '{'  -> False+    '\\' -> False+    '<'  -> False+    '&'  -> False+    '['  -> False+    '/'  -> False+    ':'  -> False+    '@'  -> False+    _    -> True++-- The Bool says whether the text can contain the start of a+-- typographic replacement; it is tracked during scanning so that no+-- extra pass over the text is needed here.+prependStr :: Bool -> [Text] -> [Inline] -> [Inline]+prependStr _ [] = id+prependStr trig cs =+  (Inline mempty (Str (replaced (T.concat (reverse cs)))):)+ where+  replaced = if trig then replaceCharsText else id++-- A macro or autolink name ends at a ':'.  A name contains no+-- plain-scan stop characters, so it must be a suffix of the plain+-- text accumulated since the last boundary; only the few candidates+-- whose last character matches the character before the ':' need to+-- be checked, leftmost (i.e. longest) first.  This also covers names+-- with a non-letter tail like indexterm2, whose '2' was consumed by+-- the plain scan.+pMacroAtColon :: Bool -> [Text] -> P [Inline] -> P [Inline]+pMacroAtColon trig (piece : rest) alt+  | not (T.null piece)+  , Just candidates <- M.lookup (T.last piece) nestedInlineStartsByLastChar+  = foldr tryCandidate alt candidates+ where+  tryCandidate (name, p) alt'+    | name `T.isSuffixOf` piece =+        (do vchar ':'+            il <- p+            let pre = T.dropEnd (T.length name) piece+            let cs' = if T.null pre then rest else pre : rest+            prependStr trig cs' . (il:) <$> pInlines' False []) <|> alt'+    | otherwise = alt'+pMacroAtColon _ _ alt = alt++-- Try email autolinks at a '@' (or at a '+' or '_', which can occur+-- inside a local part).  A local part starts at the beginning of a+-- run of letters; every candidate start lies in the trailing run of+-- email-local characters of the accumulated plain text.  Try each,+-- leftmost first.  Starts in earlier chunks need not be considered:+-- any that could reach this position was already tried, with the same+-- local part and input position, at the boundary ending its chunk.+pEmailAtBoundary :: Bool -> [Text] -> P [Inline] -> P [Inline]+pEmailAtBoundary trig (piece : rest) alt+  | not (T.null localSpan) = foldr tryStart alt (emailStarts localSpan)+ where+  localSpan = T.takeWhileEnd isEmailLocalChar piece+  tryStart sfx alt' =+    (do more <- takeWhile isEmailLocalChar+        il <- pEmailAutolinkRest (sfx <> more)+        let pre = T.dropEnd (T.length sfx) piece+        let cs' = if T.null pre then rest else pre : rest+        prependStr trig cs' . (il:) <$> pInlines' False []) <|> alt'+pEmailAtBoundary _ _ alt = alt++-- Suffixes of the given text beginning at the start of a run of+-- letters, leftmost first: the candidate starts of an email+-- autolink's local part.+emailStarts :: Text -> [Text]+emailStarts t+  | T.null t' = []+  | otherwise = t' : emailStarts (T.dropWhile isLetter t')+ where+  t' = T.dropWhile (not . isLetter) t++-- Possible macro and autolink names, as (name, parser for what+-- follows the name and ':').  Sorted by decreasing name length, so+-- that the leftmost match within a run of plain text wins; macros+-- come before autolink schemes of the same name.+nestedInlineStarts :: [(Text, P Inline)]+nestedInlineStarts =+  sortOn (\(name, _) -> negate (T.length name)) $+    [ (name, pInlineMacroTarget name) | name <- M.keys inlineMacros ] +++    [ (scheme, pAutolinkTarget (scheme <> ":")) | scheme <- autolinkSchemes ]++-- The same candidates indexed by the last character of the name, so+-- that a ':' boundary only has to check the few candidates that could+-- end just before it, preserving the order of nestedInlineStarts+-- within a bucket.+nestedInlineStartsByLastChar :: M.Map Char [(Text, P Inline)]+nestedInlineStartsByLastChar =+  M.fromListWith (flip (++))+    [ (T.last name, [x]) | x@(name, _) <- nestedInlineStarts ]++-- Apply typographic replacements in a single pass, splicing+-- replacements between unchanged slices of the input.  Equivalent to+-- matching, at each position, the first of these patterns (x and y+-- are arbitrary characters):+--+--   (C) (R) (TM)             -> copyright, registered, trademark sign+--   " -- "                   -> thin space, em dash, thin space+--   x--y  (x, y alphanumeric)-> x, em dash, zero-width space, y...+--   x--y  (otherwise)        -> unchanged (consuming x "--")+--   ...                      -> ellipsis+--   ->  =>  <-  <=           -> arrows+--   '                        -> right single quotation mark+replaceCharsText :: Text -> Text+replaceCharsText t@(TI.Text arr toff len) = go [] 0 0 '\0'+ where+  slice s e = TI.text arr (toff + s) (e - s)+  charAt j = case TU.iter t j of TU.Iter c _ -> c+  -- acc: finished output pieces in reverse order; s: start of the+  -- current unchanged run; i: current position (byte offsets); prev:+  -- the character ending at i (only meaningful when i > s).+  go acc !s !i !prev+    | i >= len =+        case acc of+          [] -> t                              -- nothing was replaced+          _ -> T.concat (reverse (slice s i : acc))+    | otherwise =+        case c0 of+          '-' | i1 < len, charAt i1 == '-' ->  -- a "--" pair at (i, i1)+                  if i > s && i2 < len+                    then                       -- x--y with x = prev+                      let y = charAt i2+                      in if prev == ' ' && y == ' '+                           then emit (i - 1) "\8201\8212\8201" (i + 3)+                         else if isAlphaNum prev && isAlphaNum y+                           then emit i "\8212\8203" i2+                         else go acc s i2 '-'  -- x "--" kept as-is+                    else if i == s && i2 < len && charAt i2 == '-'+                            && i3 < len+                           then go acc s i3 '-' -- x--y, x a dash itself+                           else plain+              | i1 < len, charAt i1 == '>' -> emit i "\8594" i2+          '(' | i2 < len, charAt i2 == ')', charAt i1 == 'C' ->+                  emit i "\169" i3+              | i2 < len, charAt i2 == ')', charAt i1 == 'R' ->+                  emit i "\174" i3+              | i3 < len, charAt i1 == 'T', charAt i2 == 'M',+                charAt i3 == ')' -> emit i "\8482" (i + 4)+          '.' | i2 < len, charAt i1 == '.', charAt i2 == '.' ->+                  emit i "\8230" i3+          '=' | i1 < len, charAt i1 == '>' -> emit i "\8658" i2+          '<' | i1 < len, charAt i1 == '-' ->+                  if i2 < len && charAt i2 == '-' && i3 < len+                    then go acc s i3 '-'       -- x--y with x = '<'+                    else emit i "\8592" i2+              | i1 < len, charAt i1 == '=' -> emit i "\8656" i2+          '\'' | i2 < len, charAt i1 == '-', charAt i2 == '-',+                 i3 < len -> go acc s i3 '-'   -- x--y with x = '\''+               | otherwise -> emit i "\8217" i1+          _ -> plain+    where+      TU.Iter c0 d0 = TU.iter t i+      i1 = i + 1+      i2 = i + 2+      i3 = i + 3+      plain = go acc s (i + d0) c0+      emit e piece j =+        let acc' | e > s = piece : slice s e : acc+                 | otherwise = piece : acc+        in go acc' j j '\0'+ pShorthandAttributes :: P Attr pShorthandAttributes = do   attr <- mconcat <$>@@ -1358,10 +1854,12 @@            _ -> mzero   pure (key, val) -pInline :: [Char] -> P Inline+pInline :: [Text] -> P Inline pInline prevChars = do+  -- The chunks in prevChars are non-empty by construction.   let maybeUnconstrained = case prevChars of-                              (d:_) -> isSpace d || isPunctuation d || d == '+'+                              (t:_) -> let d = T.last t+                                       in isSpace d || isPunctuation d || d == '+'                               [] -> True   let inMatched = pInMatched maybeUnconstrained   (do attr <- pFormattedTextAttributes <|> pure mempty@@ -1387,8 +1885,9 @@                '<' -> pBracedAutolink <|> pCrossReference                '&' -> pCharacterReference                '[' -> pBibAnchor <|> pInlineAnchor-               _ | isLetter c -> pInlineMacro <|> pAutolink <|> pEmailAutolink-                 | otherwise -> mzero)+               -- macros, autolinks and email autolinks are handled+               -- at the ':' and '@' boundaries in pInlineBoundary+               _ -> mzero)  pIndexEntry :: Attr -> P Inline pIndexEntry attr = do@@ -1415,7 +1914,9 @@   let ts = T.split (==',') t   case ts of     [] -> mzero-    [x] -> pure $ Inline mempty $ CrossReference x Nothing+    [x] -> do+      setFlag $ \f -> f{ sawCrossReference = True }+      pure $ Inline mempty $ CrossReference x Nothing     (x:xs) -> Inline mempty . CrossReference x . Just                        <$> parseInlines (T.intercalate "," xs) @@ -1436,18 +1937,32 @@   isDoubled <- option False (True <$ vchar delim)   followedBySpace <- maybe True isSpace <$> peekChar   guard $ isDoubled || (maybeUnconstrained && not followedBySpace)-  cs <- manyTill ( (vchar '\\' *> char delim) <|> anyChar )-                   (if isDoubled-                       then vchar delim *> vchar delim-                       else vchar delim)-  guard $ not $ null cs+  t <- pMatchedContent isDoubled delim+  guard $ not $ T.null t   when (not isDoubled && maybeUnconstrained) $ do     mbc <- peekChar     case mbc of       Nothing -> pure ()       Just c -> guard $ isSpace c || isPunctuation c || c == '+'-  Inline attr <$> toInlineType (T.pack cs)+  Inline attr <$> toInlineType t +-- Scan the content of a delimited span up to and including the closing+-- delimiter (doubled or single), consuming runs of plain characters in+-- chunks rather than character by character.  A backslash escapes the+-- delimiter; in doubled mode a lone delimiter is content.+pMatchedContent :: Bool -> Char -> P Text+pMatchedContent isDoubled delim = mconcat <$> go+ where+  closing = if isDoubled+               then vchar delim *> vchar delim+               else vchar delim+  go = ([] <$ closing) <|> ((:) <$> piece <*> go)+  piece = takeWhile1 (\c -> c /= delim && c /= '\\')+      <|> (vchar '\\' *> ((T.singleton <$> char delim) <|> pure "\\"))+      <|> (T.singleton <$> char delim)+          -- only reachable in doubled mode, when the delimiter is not+          -- part of a closing pair+ pInlineAnchor :: P Inline pInlineAnchor = do   void $ string "[["@@ -1478,13 +1993,13 @@   vchar '#' *> (((vchar 'x' <|> vchar 'X') *> pHexReference) <|> pDecimalReference)  where   pHexReference =-    Inline mempty . Str . T.singleton . chr <$> (liftP A.hexadecimal <* vchar ';')+    Inline mempty . Str . T.singleton . chr <$> (hexadecimal <* vchar ';')   pDecimalReference =     Inline mempty . Str . T.singleton . chr <$> (decimal <* vchar ';')  pCharacterEntityReference :: P Inline pCharacterEntityReference = do-  xs <- manyTill (satisfy isAlphaNum) (char ';' <|> space)+  xs <- manyTill (satisfy isAlphaNum) (char ';')   case lookupNamedEntity xs of     Just s -> pure $ Inline mempty (Str (T.pack s))     Nothing -> mzero@@ -1500,9 +2015,9 @@ pApostrophe '`' = Inline mempty (Str "’") <$ string "`'" pApostrophe _ = mzero -pInlineMacro :: P Inline-pInlineMacro = do-  name <- choice (map (\n -> string n <* vchar ':') (M.keys inlineMacros))+-- Parse the part of an inline macro after the name and ':'.+pInlineMacroTarget :: Text -> P Inline+pInlineMacroTarget name = do   let targetChars = mconcat <$> some        ( (string "pass:" *> vchar '[' *> takeWhile1 (/=']') <* vchar ']')          <|>@@ -1583,7 +2098,9 @@       Inline (Attr mempty kvs) . Footnote fnid <$> parseInlines contents)   , ("xref", \target -> do         ils <- pBracketedText >>= parseInlines-        let mbtext = if null ils then Nothing else Just ils+        mbtext <- if null ils+                     then Nothing <$ setFlag (\f -> f{ sawCrossReference = True })+                     else pure (Just ils)         pure $ Inline mempty $ CrossReference target mbtext)   , ("image", \target -> do         (Attr ps kvs) <- pAttributes@@ -1628,9 +2145,12 @@   in (description, Attr (drop 1 ps) kvs)  -pEmailAutolink :: P Inline-pEmailAutolink = do-  a <- takeWhile1 (\c -> isAlphaNum c || c == '_' || c == '.' || c == '+')+isEmailLocalChar :: Char -> Bool+isEmailLocalChar c = isAlphaNum c || c == '_' || c == '.' || c == '+'++-- Parse the part of an email autolink after the local part.+pEmailAutolinkRest :: Text -> P Inline+pEmailAutolinkRest a = do   vchar '@'   b <- takeWhile1 isLetter   vchar '.'@@ -1643,10 +2163,18 @@                   then pure [Inline mempty (Str email)]                   else parseInlines description +autolinkSchemes :: [Text]+autolinkSchemes = ["http", "https", "irc", "ftp", "mailto"]+ pAutolink :: P Inline pAutolink = do-  scheme <- choice (map string-               ["http:", "https:", "irc:", "ftp:", "mailto:"])+  scheme <- choice (map (\s -> string (s <> ":")) autolinkSchemes)+  pAutolinkTarget scheme++-- Parse the part of an autolink after the scheme (which includes the+-- trailing ':').+pAutolinkTarget :: Text -> P Inline+pAutolinkTarget scheme = do   let isSpecialPunct ',' = True       isSpecialPunct '.' = True       isSpecialPunct '?' = True@@ -1728,7 +2256,7 @@      pure (UpperAlphaCounter, 1 + (ord c - ord 'A'))    pLowerValue = do      c <- satisfy (\c -> isAscii c && isLower c)-     pure (UpperAlphaCounter, 1 + (ord c - ord 'a'))+     pure (LowerAlphaCounter, 1 + (ord c - ord 'a'))    pDecimalValue = do      n <- decimal      pure (DecimalCounter, n)@@ -1740,7 +2268,17 @@   vchar '}'   case M.lookup name replacements of     Just r -> pure $ Inline mempty (Str r)-    Nothing -> pure $ Inline mempty $ AttributeReference (AttributeName name)+    Nothing -> do+      -- Resolve document attributes at the point of use, so that a+      -- reference sees the value in effect where it occurs.  References+      -- to attributes defined later are left unresolved here and get+      -- the end-of-document value in a post-processing pass.+      attrs <- gets docAttrs+      case M.lookup name attrs of+        Just v -> pure $ Inline mempty (Str v)+        Nothing -> do+          setFlag $ \f -> f{ sawAttributeReference = True }+          pure $ Inline mempty $ AttributeReference (AttributeName name)  replacements :: M.Map Text Text replacements = M.fromList
test/Main.hs view
@@ -21,7 +21,9 @@   asciidoctorTests <- goldenTests "asciidoctor"   featureTests <- goldenTests "feature"   regressionTests <- goldenTests "regression"-  defaultMain $ testGroup "Tests"+  -- A global per-test timeout so that parser non-termination bugs make+  -- tests fail instead of hanging the suite.+  defaultMain $ localOption (mkTimeout 10000000) $ testGroup "Tests"     [ testGroup "Asciidoctor" asciidoctorTests     , testGroup "Feature" featureTests     , testGroup "Regression" regressionTests@@ -30,7 +32,11 @@        , foldBlockTest        , mapInlineTest        , mapBlockTest+       , definitionListTermsTest        ]+    , testGroup "AST"+       [ metaSemigroupTest+       ]     ]  goldenTests :: FilePath -> IO [TestTree]@@ -89,6 +95,51 @@   Just (_, '\n') -> xs   _              -> xs <> "\n" ++definitionListTermsTest :: TestTree+definitionListTermsTest = testCase "definitionListTerms" $ do+  let dlistDoc = Document+        { docMeta = mempty+        , docBlocks =+            [ Block mempty Nothing+                (DefinitionList+                   [ ( [Inline mempty (Str "term")]+                     , [ Block mempty Nothing+                           (Paragraph [Inline mempty (Str "def")]) ] )+                   ])+            ]+        }+  -- foldInlines and mapInlines must reach the term as well as the definition+  foldInlines (\case+                  Inline _ (Str s) -> s+                  _ -> "") dlistDoc+    @?= "termdef"+  d <- mapInlines (\case+                      Inline _ (Str _) -> pure $ Inline mempty (Str "X")+                      x -> pure x) dlistDoc+  foldInlines (\case+                  Inline _ (Str s) -> s+                  _ -> "") d+    @?= "XX"+  -- foldBlocks must reach the blocks in the definition+  foldBlocks (\case+                 Block _ _ (Paragraph _) -> [()]+                 _ -> []) dlistDoc+    @?= [()]++metaSemigroupTest :: TestTree+metaSemigroupTest = testCase "metaSemigroup" $ do+  let attr1 = Attr ["one"] mempty+  let attr2 = Attr ["two"] mempty+  let meta1 = mempty{ docTitle = [Inline mempty (Str "First")]+                    , docTitleAttributes = Just attr1 }+  let meta2 = mempty{ docTitle = [Inline mempty (Str "Second")]+                    , docTitleAttributes = Just attr2 }+  -- title and its attributes are taken from the same document+  docTitle (meta1 <> meta2) @?= [Inline mempty (Str "First")]+  docTitleAttributes (meta1 <> meta2) @?= Just attr1+  docTitle (mempty <> meta2) @?= [Inline mempty (Str "Second")]+  docTitleAttributes (mempty <> meta2) @?= Just attr2  testDoc :: Document testDoc = Document
test/asciidoctor/table/aligns-per-cell.test view
@@ -33,29 +33,28 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph-                                   [ Inline mempty (Str "Prefix the ")-                                   , Inline mempty (Str "{vbar}")-                                   , Inline mempty (Str " with ")-                                   , Inline mempty (Str "{caret}")-                                   , Inline mempty (Str " to center content horizontally")-                                   ])-                            ]-                        , cellHorizAlign = Just AlignCenter-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])+             Nothing              [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline mempty (Str "Prefix the ")+                                , Inline mempty (Str "{vbar}")+                                , Inline mempty (Str " with ")+                                , Inline mempty (Str "{caret}")+                                , Inline mempty (Str " to center content horizontally")+                                ])+                         ]+                     , cellHorizAlign = Just AlignCenter+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow                  [ TableCell                      { cellContent =                          [ Block
test/asciidoctor/table/basic.test view
@@ -30,35 +30,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])+             Nothing              [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow                  [ TableCell                      { cellContent =                          [ Block
test/asciidoctor/table/cell-with-paragraphs.test view
@@ -28,23 +28,22 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Single paragraph on row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])+             Nothing              [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Single paragraph on row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow                  [ TableCell                      { cellContent =                          [ Block
test/asciidoctor/table/colspan.test view
@@ -39,47 +39,46 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])+             Nothing              [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow                  [ TableCell                      { cellContent =                          [ Block
test/asciidoctor/table/insane-cells-formatting.test view
@@ -44,87 +44,84 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph-                                   [ Inline-                                       mempty-                                       (Monospace-                                          [ Inline-                                              mempty-                                              (Str "This content is duplicated across two columns.")-                                          ])-                                   ])-                            , Block-                                mempty-                                Nothing-                                (Paragraph-                                   [ Inline-                                       mempty-                                       (Monospace-                                          [ Inline mempty (Str "It is aligned right horizontally.")-                                          ])-                                   ])-                            , Block-                                mempty-                                Nothing-                                (Paragraph-                                   [ Inline-                                       mempty-                                       (Monospace [ Inline mempty (Str "And it is monospaced.") ])-                                   ])-                            ]-                        , cellHorizAlign = Just AlignRight-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph-                                   [ Inline-                                       mempty-                                       (Monospace-                                          [ Inline-                                              mempty-                                              (Str "This content is duplicated across two columns.")-                                          ])-                                   ])-                            , Block-                                mempty-                                Nothing-                                (Paragraph-                                   [ Inline-                                       mempty-                                       (Monospace-                                          [ Inline mempty (Str "It is aligned right horizontally.")-                                          ])-                                   ])-                            , Block-                                mempty-                                Nothing-                                (Paragraph-                                   [ Inline-                                       mempty-                                       (Monospace [ Inline mempty (Str "And it is monospaced.") ])-                                   ])-                            ]-                        , cellHorizAlign = Just AlignRight-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])+             Nothing              [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline+                                    mempty+                                    (Monospace+                                       [ Inline+                                           mempty+                                           (Str "This content is duplicated across two columns.")+                                       ])+                                ])+                         , Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline+                                    mempty+                                    (Monospace+                                       [ Inline mempty (Str "It is aligned right horizontally.") ])+                                ])+                         , Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline+                                    mempty+                                    (Monospace [ Inline mempty (Str "And it is monospaced.") ])+                                ])+                         ]+                     , cellHorizAlign = Just AlignRight+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline+                                    mempty+                                    (Monospace+                                       [ Inline+                                           mempty+                                           (Str "This content is duplicated across two columns.")+                                       ])+                                ])+                         , Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline+                                    mempty+                                    (Monospace+                                       [ Inline mempty (Str "It is aligned right horizontally.") ])+                                ])+                         , Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline+                                    mempty+                                    (Monospace [ Inline mempty (Str "And it is monospaced.") ])+                                ])+                         ]+                     , cellHorizAlign = Just AlignRight+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow                  [ TableCell                      { cellContent =                          [ Block
test/asciidoctor/table/rowspan.test view
@@ -42,47 +42,46 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])+             Nothing              [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow                  [ TableCell                      { cellContent =                          [ Block
test/asciidoctor/table/with-autowidth-and-width.test view
@@ -31,35 +31,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-autowidth.test view
@@ -30,35 +30,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-cols-halign.test view
@@ -38,47 +38,46 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-cols-styles.test view
@@ -59,90 +59,88 @@                  , colStyle = Just StrongStyle                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (BlockImage-                                   (Target "sunset.jpg")-                                   (Just (AltText "AsciiDoc content"))-                                   Nothing-                                   Nothing)-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph-                                   [ Inline-                                       mempty (Italic [ Inline mempty (Str "Emphasized text") ])-                                   ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Styled like a header") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block mempty Nothing (LiteralBlock "Literal block\n") ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph-                                   [ Inline-                                       mempty (Monospace [ Inline mempty (Str "Monospaced text") ])-                                   ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (BlockImage+                                (Target "sunset.jpg")+                                (Just (AltText "AsciiDoc content"))                                 Nothing-                                (Paragraph-                                   [ Inline mempty (Bold [ Inline mempty (Str "Strong text") ]) ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+                                Nothing)+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline mempty (Italic [ Inline mempty (Str "Emphasized text") ])+                                ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Styled like a header") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (LiteralBlock "Literal block\n") ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline+                                    mempty (Monospace [ Inline mempty (Str "Monospaced text") ])+                                ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph+                                [ Inline mempty (Bold [ Inline mempty (Str "Strong text") ]) ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-cols-valign.test view
@@ -38,47 +38,46 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-cols-width.test view
@@ -38,47 +38,46 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 3, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-float.test view
@@ -31,35 +31,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-footer.test view
@@ -32,35 +32,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])+             Nothing              [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow                  [ TableCell                      { cellContent =                          [ Block
test/asciidoctor/table/with-frame-sides.test view
@@ -31,35 +31,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-grid-cols.test view
@@ -31,35 +31,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-id-and-role.test view
@@ -31,35 +31,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-title.test view
@@ -30,35 +30,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/asciidoctor/table/with-width.test view
@@ -31,35 +31,34 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block-                                mempty-                                Nothing-                                (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])-                            ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])-             []+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 1, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block+                             mempty+                             Nothing+                             (Paragraph [ Inline mempty (Str "Cell in column 2, row 1") ])+                         ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]              Nothing)       ]   }
test/feature/table/longborder.test view
@@ -30,27 +30,26 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a") ]) ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b") ]) ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])+             Nothing              [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow                  [ TableCell                      { cellContent =                          [ Block mempty Nothing (Paragraph [ Inline mempty (Str "c") ]) ]
+ test/regression/attribute_point_of_use.test view
@@ -0,0 +1,33 @@+:x: first++{x}++:x: second++{x} and {y}++:y: late+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes =+            fromList+              [ ( "sectids" , "" ) , ( "x" , "second" ) , ( "y" , "late" ) ]+        }+  , docBlocks =+      [ Block mempty Nothing (Paragraph [ Inline mempty (Str "first") ])+      , Block+          mempty+          Nothing+          (Paragraph+             [ Inline mempty (Str "second")+             , Inline mempty (Str " and ")+             , Inline mempty (Str "late")+             ])+      ]+  }
+ test/regression/counter_lowercase.test view
@@ -0,0 +1,25 @@+one {counter:foo:a} two {counter:foo} upper {counter:bar:C}+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          mempty+          Nothing+          (Paragraph+             [ Inline mempty (Str "one ")+             , Inline mempty (Counter "foo" LowerAlphaCounter 1)+             , Inline mempty (Str " two ")+             , Inline mempty (Counter "foo" LowerAlphaCounter 2)+             , Inline mempty (Str " upper ")+             , Inline mempty (Counter "bar" UpperAlphaCounter 3)+             ])+      ]+  }
+ test/regression/csv_empty_cell.test view
@@ -0,0 +1,93 @@+[format=csv,options="noheader"]+|===+a,,b+,d,+|===+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          mempty+          Nothing+          (Table+             [ ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             , ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             , ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             ]+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent = []+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow+                 [ TableCell+                     { cellContent = []+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "d") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent = []+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]+             Nothing)+      ]+  }
+ test/regression/dlist_marker_needs_space.test view
@@ -0,0 +1,54 @@+* use std::vector here+* second item++A paragraph mentioning std::vector too.++term:: a real definition+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          mempty+          Nothing+          (List+             (BulletList (Level 1))+             [ ListItem+                 Nothing+                 [ Block+                     mempty+                     Nothing+                     (Paragraph [ Inline mempty (Str "use std::vector here") ])+                 ]+             , ListItem+                 Nothing+                 [ Block+                     mempty Nothing (Paragraph [ Inline mempty (Str "second item") ])+                 ]+             ])+      , Block+          mempty+          Nothing+          (Paragraph+             [ Inline mempty (Str "A paragraph mentioning std::vector too.") ])+      , Block+          mempty+          Nothing+          (DefinitionList+             [ ( [ Inline mempty (Str "term") ]+               , [ Block+                     mempty+                     Nothing+                     (Paragraph [ Inline mempty (Str "a real definition") ])+                 ]+               )+             ])+      ]+  }
+ test/regression/dlist_term_traversal.test view
@@ -0,0 +1,48 @@+[#target]+== Section Title++<<target>> in term:: definition with <<target>>+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          Attr+          ( [] , fromList [ ( "id" , "target" ) ] )+          Nothing+          (Section+             (Level 1)+             [ Inline mempty (Str "Section Title") ]+             [ Block+                 mempty+                 Nothing+                 (DefinitionList+                    [ ( [ Inline+                            mempty+                            (CrossReference+                               "target" (Just [ Inline mempty (Str "Section Title") ]))+                        , Inline mempty (Str " in term")+                        ]+                      , [ Block+                            mempty+                            Nothing+                            (Paragraph+                               [ Inline mempty (Str "definition with ")+                               , Inline+                                   mempty+                                   (CrossReference+                                      "target" (Just [ Inline mempty (Str "Section Title") ]))+                               ])+                        ]+                      )+                    ])+             ])+      ]+  }
+ test/regression/entity_needs_semicolon.test view
@@ -0,0 +1,22 @@+AT&amp;T but AT&amp T keeps its space.+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          mempty+          Nothing+          (Paragraph+             [ Inline mempty (Str "AT")+             , Inline mempty (Str "&")+             , Inline mempty (Str "T but AT&amp T keeps its space.")+             ])+      ]+  }
+ test/regression/fenced_longer_close.test view
@@ -0,0 +1,24 @@+```+code line+````++A paragraph after the block.+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          mempty Nothing (Listing Nothing [ SourceLine "code line" [] ])+      , Block+          mempty+          Nothing+          (Paragraph [ Inline mempty (Str "A paragraph after the block.") ])+      ]+  }
+ test/regression/fenced_unterminated.test view
@@ -0,0 +1,20 @@+```+foo+bar+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          mempty+          Nothing+          (Listing Nothing [ SourceLine "foo" [] , SourceLine "bar" [] ])+      ]+  }
+ test/regression/include_cycle.test view
@@ -0,0 +1,33 @@+include::include_cycle_helper.adoc[]+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          mempty+          Nothing+          (Include+             "test/regression/include_cycle_helper.adoc"+             (Just+                [ Block+                    mempty+                    Nothing+                    (Paragraph [ Inline mempty (Str "Before the self-include.") ])+                , Block+                    mempty+                    Nothing+                    (Include "test/regression/include_cycle_helper.adoc" Nothing)+                , Block+                    mempty+                    Nothing+                    (Paragraph [ Inline mempty (Str "After the self-include.") ])+                ]))+      ]+  }
test/regression/issue_5.test view
@@ -33,27 +33,26 @@                  , colStyle = Nothing                  }              ]-             (Just-                [ TableRow-                    [ TableCell-                        { cellContent =-                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "col1") ]) ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    , TableCell-                        { cellContent =-                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "col2") ]) ]-                        , cellHorizAlign = Nothing-                        , cellVertAlign = Nothing-                        , cellColspan = 1-                        , cellRowspan = 1-                        }-                    ]-                ])+             Nothing              [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "col1") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "col2") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow                  [ TableCell                      { cellContent =                          [ Block mempty Nothing (Paragraph [ Inline mempty (Str "11") ]) ]
+ test/regression/listing_unterminated.test view
@@ -0,0 +1,20 @@+----+foo+bar+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          mempty+          Nothing+          (Listing Nothing [ SourceLine "foo" [] , SourceLine "bar" [] ])+      ]+  }
+ test/regression/table_implicit_header.test view
@@ -0,0 +1,389 @@+Implied header (first row on one line, then blank line):++|===+|A |B++|a1 |b1+|a2 |b2+|===++No blank line after first row, so no header:++|===+|a1 |b1+|a2 |b2+|===++Explicit header option without header layout:++[options="header"]+|===+|a1 |b1+|a2 |b2+|===++Header layout suppressed by noheader option:++[options="noheader"]+|===+|A |B++|a1 |b1+|===++Blank line directly after the border, so no header:++|===++|a1 |b1+|a2 |b2+|===+>>>+Document+  { docMeta =+      Meta+        { docTitle = []+        , docTitleAttributes = Nothing+        , docAuthors = []+        , docRevision = Nothing+        , docAttributes = fromList [ ( "sectids" , "" ) ]+        }+  , docBlocks =+      [ Block+          mempty+          Nothing+          (Paragraph+             [ Inline+                 mempty+                 (Str "Implied header (first row on one line, then blank line):")+             ])+      , Block+          mempty+          Nothing+          (Table+             [ ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             , ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             ]+             (Just+                [ TableRow+                    [ TableCell+                        { cellContent =+                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "A") ]) ]+                        , cellHorizAlign = Nothing+                        , cellVertAlign = Nothing+                        , cellColspan = 1+                        , cellRowspan = 1+                        }+                    , TableCell+                        { cellContent =+                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "B") ]) ]+                        , cellHorizAlign = Nothing+                        , cellVertAlign = Nothing+                        , cellColspan = 1+                        , cellRowspan = 1+                        }+                    ]+                ])+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a2") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b2") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]+             Nothing)+      , Block+          mempty+          Nothing+          (Paragraph+             [ Inline+                 mempty (Str "No blank line after first row, so no header:")+             ])+      , Block+          mempty+          Nothing+          (Table+             [ ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             , ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             ]+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a2") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b2") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]+             Nothing)+      , Block+          mempty+          Nothing+          (Paragraph+             [ Inline+                 mempty (Str "Explicit header option without header layout:")+             ])+      , Block+          mempty+          Nothing+          (Table+             [ ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             , ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             ]+             (Just+                [ TableRow+                    [ TableCell+                        { cellContent =+                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]+                        , cellHorizAlign = Nothing+                        , cellVertAlign = Nothing+                        , cellColspan = 1+                        , cellRowspan = 1+                        }+                    , TableCell+                        { cellContent =+                            [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]+                        , cellHorizAlign = Nothing+                        , cellVertAlign = Nothing+                        , cellColspan = 1+                        , cellRowspan = 1+                        }+                    ]+                ])+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a2") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b2") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]+             Nothing)+      , Block+          mempty+          Nothing+          (Paragraph+             [ Inline+                 mempty (Str "Header layout suppressed by noheader option:")+             ])+      , Block+          mempty+          Nothing+          (Table+             [ ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             , ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             ]+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "A") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "B") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]+             Nothing)+      , Block+          mempty+          Nothing+          (Paragraph+             [ Inline+                 mempty (Str "Blank line directly after the border, so no header:")+             ])+      , Block+          mempty+          Nothing+          (Table+             [ ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             , ColumnSpec+                 { colHorizAlign = Nothing+                 , colVertAlign = Nothing+                 , colWidth = Nothing+                 , colStyle = Nothing+                 }+             ]+             Nothing+             [ TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a1") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b1") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             , TableRow+                 [ TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "a2") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 , TableCell+                     { cellContent =+                         [ Block mempty Nothing (Paragraph [ Inline mempty (Str "b2") ]) ]+                     , cellHorizAlign = Nothing+                     , cellVertAlign = Nothing+                     , cellColspan = 1+                     , cellRowspan = 1+                     }+                 ]+             ]+             Nothing)+      ]+  }