Markdown reader: make definition lists behave like other lists.

If the `four_space_rule` extension is not enabled,
figure out the indentation needed for child blocks dynamically,
by looking at the first nonspace content after the `:` marker.

Previously the four-space rule was always obeyed.

Remove the old `compact_definition_lists` extension. This was
neded to preserve backwards compatibility after pandoc 1.12
was released, but at this point we can get rid of it.

T.P.Extensions: remove `Ext_compact_definition_lists` constructor
for `Extension` [API change].

Fix tight/loose detection for definition lists, to conform to
the documentation.

Closes #10889.
This commit is contained in:
John MacFarlane
2025-06-02 23:29:47 -07:00
parent 0f79a1f627
commit add83e8169
7 changed files with 51 additions and 111 deletions
-30
View File
@@ -4502,9 +4502,6 @@ definition:
~ Definition 2b
Note that space between items in a definition list is required.
(A variant that loosens this requirement, but disallows "lazy"
hard wrapping, can be activated with the [`compact_definition_lists`
extension][Extension: `compact_definition_lists`].)
[^3]: I have been influenced by the suggestions of [David
Wheeler](https://justatheory.com/2009/02/modest-markdown-proposal/).
@@ -6109,33 +6106,6 @@ and image references. This extension should not be confused with the
Parses MultiMarkdown-style heading identifiers (in square brackets,
after the heading but before any trailing `#`s in an ATX heading).
### Extension: `compact_definition_lists` ###
Activates the definition list syntax of pandoc 1.12.x and earlier.
This syntax differs from the one described above under [Definition lists]
in several respects:
- No blank line is required between consecutive items of the
definition list.
- To get a "tight" or "compact" list, omit space between consecutive
items; the space between a term and its definition does not affect
anything.
- Lazy wrapping of paragraphs is not allowed: the entire definition must
be indented four spaces.[^6]
[^6]: To see why laziness is incompatible with relaxing the requirement
of a blank line between items, consider the following example:
bar
: definition
foo
: definition
Is this a single list item with two definitions of "bar," the first of
which is lazily wrapped, or two list items? To remove the ambiguity
we must either disallow lazy wrapping or require a blank line between
list items.
### Extension: `gutenberg` ###
Use [Project Gutenberg] conventions for `plain` output:
-3
View File
@@ -59,8 +59,6 @@ data Extension =
| Ext_blank_before_header -- ^ Require blank line before a header
| Ext_bracketed_spans -- ^ Bracketed spans with attributes
| Ext_citations -- ^ Pandoc/citeproc citations
| Ext_compact_definition_lists -- ^ Definition lists without space between items,
-- and disallow laziness
| Ext_definition_lists -- ^ Definition lists as in pandoc, mmd, php
| Ext_east_asian_line_breaks -- ^ Newlines in paragraphs are ignored between
-- East Asian wide characters. Note: this extension
@@ -511,7 +509,6 @@ getAllExtensions f = universalExtensions <> getAll f
, Ext_mark
, Ext_mmd_link_attributes
, Ext_mmd_header_identifiers
, Ext_compact_definition_lists
, Ext_gutenberg
, Ext_smart
, Ext_literate_haskell
+24 -50
View File
@@ -845,7 +845,9 @@ orderedListStart mbstydelim = try $ do
return (num, style, delim))
listStart :: PandocMonad m => MarkdownParser m ()
listStart = bulletListStart <|> Control.Monad.void (orderedListStart Nothing)
listStart = bulletListStart
<|> Control.Monad.void (orderedListStart Nothing)
<|> defListStart
listLine :: PandocMonad m => Int -> MarkdownParser m Text
listLine continuationIndent = try $ do
@@ -967,67 +969,39 @@ bulletList = do
-- definition lists
defListMarker :: PandocMonad m => MarkdownParser m ()
defListMarker = do
sps <- nonindentSpaces
defListStart :: PandocMonad m => MarkdownParser m ()
defListStart = do
nonindentSpaces
char ':' <|> char '~'
tabStop <- getOption readerTabStop
let remaining = tabStop - (T.length sps + 1)
if remaining > 0
then try (count remaining (char ' ')) <|> string "\t" <|> many1 spaceChar
else mzero
return ()
gobbleSpaces 1 <|> () <$ lookAhead newline
try (gobbleAtMostSpaces 3 >> notFollowedBy spaceChar) <|> return ()
definitionListItem :: PandocMonad m => Bool -> MarkdownParser m (F (Inlines, [Blocks]))
definitionListItem compact = try $ do
definitionListItem :: PandocMonad m => MarkdownParser m (F (Inlines, [Blocks]))
definitionListItem = try $ do
rawLine' <- anyLine
raw <- many1 $ defRawBlock compact
term <- parseFromString' (trimInlinesF <$> inlines) rawLine'
contents <- mapM (parseFromString' parseBlocks . (<> "\n")) raw
isTight <- (False <$ blanklines) <|> pure True
fourSpaceRule <- (True <$ guardEnabled Ext_four_space_rule) <|> pure False
contents <- many1 $ listItem fourSpaceRule defListStart
optional blanklines
return $ liftM2 (,) term (sequence contents)
return $ liftM2 (,)
term
((if isTight
then fmap (fmap (fmap paraToPlain))
else id) (sequence contents))
defRawBlock :: PandocMonad m => Bool -> MarkdownParser m Text
defRawBlock compact = try $ do
hasBlank <- option False $ blankline >> return True
defListMarker
firstline <- anyLineNewline
let dline = try
( do notFollowedBy blankline
notFollowedByHtmlCloser
notFollowedByDivCloser
if compact -- laziness not compatible with compact
then () <$ indentSpaces
else (() <$ indentSpaces)
<|> notFollowedBy defListMarker
anyLine )
rawlines <- many dline
cont <- fmap T.concat $ many $ try $ do
trailing <- option "" blanklines
ln <- indentSpaces >> notFollowedBy blankline >> anyLine
lns <- many dline
return $ trailing <> T.unlines (ln:lns)
return $ trimr (firstline <> T.unlines rawlines <> cont) <>
if hasBlank || not (T.null cont) then "\n\n" else ""
paraToPlain :: Block -> Block
paraToPlain (Para ils) = Plain ils
paraToPlain x = x
definitionList :: PandocMonad m => MarkdownParser m (F Blocks)
definitionList = try $ do
guardEnabled Ext_definition_lists
lookAhead (anyLine >>
optional (blankline >> notFollowedBy (Control.Monad.void table)) >>
-- don't capture table caption as def list!
defListMarker)
compactDefinitionList <|> normalDefinitionList
compactDefinitionList :: PandocMonad m => MarkdownParser m (F Blocks)
compactDefinitionList = do
guardEnabled Ext_compact_definition_lists
items <- fmap sequence $ many1 $ definitionListItem True
return $ B.definitionList <$> fmap compactifyDL items
normalDefinitionList :: PandocMonad m => MarkdownParser m (F Blocks)
normalDefinitionList = do
guardEnabled Ext_definition_lists
items <- fmap sequence $ many1 $ definitionListItem False
defListStart)
items <- fmap sequence $ many1 definitionListItem
return $ B.definitionList <$> items
--
+5 -11
View File
@@ -860,17 +860,11 @@ definitionListItemToMarkdown opts (label, defs) = do
let isTight = case defs of
((Plain _ : _): _) -> True
_ -> False
if isEnabled Ext_compact_definition_lists opts
then do
let contents = vcat $ map (\d -> hang tabStop (leader <> sps)
$ vcat d <> cr) defs'
return $ nowrap labelText <> cr <> contents <> cr
else do
let contents = (if isTight then vcat else vsep) $ map
(\d -> hang tabStop (leader <> sps) $ vcat d)
defs'
return $ blankline <> nowrap labelText $$
(if isTight then empty else blankline) <> contents <> blankline
let contents = (if isTight then vcat else vsep) $ map
(\d -> hang tabStop (leader <> sps) $ vcat d)
defs'
return $ blankline <> nowrap labelText $$
(if isTight then empty else blankline) <> contents <> blankline
else
return $ nowrap (chomp labelText <> literal " " <> cr) <>
vsep (map vsep defs') <> blankline
+5 -17
View File
@@ -29,10 +29,6 @@ markdownSmart :: Text -> Pandoc
markdownSmart = purely $ readMarkdown def { readerExtensions =
enableExtension Ext_smart pandocExtensions }
markdownCDL :: Text -> Pandoc
markdownCDL = purely $ readMarkdown def { readerExtensions = enableExtension
Ext_compact_definition_lists pandocExtensions }
markdownGH :: Text -> Pandoc
markdownGH = purely $ readMarkdown def {readerExtensions = enableExtension
Ext_wikilinks_title_before_pipe githubMarkdownExtensions }
@@ -461,14 +457,14 @@ tests = [ testGroup "inline code"
, "blank space before first def" =:
"foo1\n\n : bar\n\nfoo2\n\n : bar2\n : bar3\n" =?>
definitionList [ (text "foo1", [para (text "bar")])
, (text "foo2", [para (text "bar2"),
plain (text "bar3")])
, (text "foo2", [plain (text "bar2"),
para (text "bar3")])
]
, "blank space before second def" =:
"foo1\n : bar\n\nfoo2\n : bar2\n\n : bar3\n" =?>
definitionList [ (text "foo1", [plain (text "bar")])
, (text "foo2", [plain (text "bar2"),
para (text "bar3")])
plain (text "bar3")])
]
, "laziness" =:
"foo1\n : bar\nbaz\n : bar2\n" =?>
@@ -478,8 +474,8 @@ tests = [ testGroup "inline code"
]
, "no blank space before first of two paragraphs" =:
"foo1\n : bar\n\n baz\n" =?>
definitionList [ (text "foo1", [para (text "bar") <>
para (text "baz")])
definitionList [ (text "foo1", [plain (text "bar") <>
plain (text "baz")])
]
, "first line not indented" =:
"foo\n: bar\n" =?>
@@ -492,14 +488,6 @@ tests = [ testGroup "inline code"
divWith nullAttr (definitionList
[ (text "foo", [bulletList [plain (text "bar")]]) ])
]
, testGroup "+compact_definition_lists"
[ test markdownCDL "basic compact list" $
"foo1\n: bar\n baz\nfoo2\n: bar2\n" =?>
definitionList [ (text "foo1", [plain (text "bar" <> softbreak <>
text "baz")])
, (text "foo2", [plain (text "bar2")])
]
]
, testGroup "lists"
[ "issue #1154" =:
" - <div>\n first div breaks\n </div>\n\n <button>if this button exists</button>\n\n <div>\n with this div too.\n </div>\n"
+16
View File
@@ -0,0 +1,16 @@
```
% pandoc
apple
: pomaceous
fruit
^D
<dl>
<dt>apple</dt>
<dd>
<p>pomaceous</p>
<p>fruit</p>
</dd>
</dl>
```
+1
View File
@@ -33,6 +33,7 @@ Definition
2. list
^D
\begin{description}
\tightlist
\item[Definition]
Foo