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 +66/−0
- asciidoc.cabal +14/−2
- bench/Main.hs +75/−0
- src/AsciiDoc/AST.hs +1/−1
- src/AsciiDoc/Generic.hs +14/−0
- src/AsciiDoc/Parse.hs +675/−137
- test/Main.hs +52/−1
- test/asciidoctor/table/aligns-per-cell.test +21/−22
- test/asciidoctor/table/basic.test +27/−28
- test/asciidoctor/table/cell-with-paragraphs.test +15/−16
- test/asciidoctor/table/colspan.test +39/−40
- test/asciidoctor/table/insane-cells-formatting.test +77/−80
- test/asciidoctor/table/rowspan.test +39/−40
- test/asciidoctor/table/with-autowidth-and-width.test +28/−29
- test/asciidoctor/table/with-autowidth.test +28/−29
- test/asciidoctor/table/with-cols-halign.test +40/−41
- test/asciidoctor/table/with-cols-styles.test +81/−83
- test/asciidoctor/table/with-cols-valign.test +40/−41
- test/asciidoctor/table/with-cols-width.test +40/−41
- test/asciidoctor/table/with-float.test +28/−29
- test/asciidoctor/table/with-footer.test +27/−28
- test/asciidoctor/table/with-frame-sides.test +28/−29
- test/asciidoctor/table/with-grid-cols.test +28/−29
- test/asciidoctor/table/with-id-and-role.test +28/−29
- test/asciidoctor/table/with-title.test +28/−29
- test/asciidoctor/table/with-width.test +28/−29
- test/feature/table/longborder.test +19/−20
- test/regression/attribute_point_of_use.test +33/−0
- test/regression/counter_lowercase.test +25/−0
- test/regression/csv_empty_cell.test +93/−0
- test/regression/dlist_marker_needs_space.test +54/−0
- test/regression/dlist_term_traversal.test +48/−0
- test/regression/entity_needs_semicolon.test +22/−0
- test/regression/fenced_longer_close.test +24/−0
- test/regression/fenced_unterminated.test +20/−0
- test/regression/include_cycle.test +33/−0
- test/regression/issue_5.test +19/−20
- test/regression/listing_unterminated.test +20/−0
- test/regression/table_implicit_header.test +389/−0
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) ] }
@@ -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&T but AT& 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& 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)+ ]+ }