git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #3159 from jkr/refs
901045bJohn MacFarlane
MANUAL.txt+7
@@ -645,6 +645,13 @@ Options affecting specific writers
645: Use reference-style links, rather than inline links, in writing Markdown
645: Use reference-style links, rather than inline links, in writing Markdown
646 or reStructuredText. By default inline links are used.
646 or reStructuredText. By default inline links are used.
647
647
648+`--reference-location = block` | `section` | `document`
649+
650+: Specify whether footnotes (and references, if `reference-links` is
651+ set) are placed at the end of the current (top-level) block, the
652+ current section, or the document. The default is
653+ `document`. Currently only affects the markdown writer.
654+
648`--atx-headers`
655`--atx-headers`
649
656
650: Use ATX-style headers in Markdown and asciidoc output. The default is
657: Use ATX-style headers in Markdown and asciidoc output. The default is
pandoc.hs+17
@@ -196,6 +196,7 @@ data Opt = Opt
196 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
196 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
197 , optVerbose :: Bool -- ^ Verbose diagnostic output
197 , optVerbose :: Bool -- ^ Verbose diagnostic output
198 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
198 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
199+ , optReferenceLocation :: ReferenceLocation -- ^ location for footnotes and link references in markdown output
199 , optDpi :: Int -- ^ Dpi
200 , optDpi :: Int -- ^ Dpi
200 , optWrapText :: WrapOption -- ^ Options for wrapping text
201 , optWrapText :: WrapOption -- ^ Options for wrapping text
201 , optColumns :: Int -- ^ Line length in characters
202 , optColumns :: Int -- ^ Line length in characters
@@ -260,6 +261,7 @@ defaultOpts = Opt
260 , optIgnoreArgs = False
261 , optIgnoreArgs = False
261 , optVerbose = False
262 , optVerbose = False
262 , optReferenceLinks = False
263 , optReferenceLinks = False
264+ , optReferenceLocation = EndOfDocument
263 , optDpi = 96
265 , optDpi = 96
264 , optWrapText = WrapAuto
266 , optWrapText = WrapAuto
265 , optColumns = 72
267 , optColumns = 72
@@ -584,6 +586,19 @@ options =
584 (\opt -> return opt { optReferenceLinks = True } ))
586 (\opt -> return opt { optReferenceLinks = True } ))
585 "" -- "Use reference links in parsing HTML"
587 "" -- "Use reference links in parsing HTML"
586
588
589+ , Option "" ["reference-location"]
590+ (ReqArg
591+ (\arg opt -> do
592+ action <- case arg of
593+ "block" -> return EndOfBlock
594+ "section" -> return EndOfSection
595+ "document" -> return EndOfDocument
596+ _ -> err 6
597+ ("Unknown option for reference-location: " ++ arg)
598+ return opt { optReferenceLocation = action })
599+ "block|section|document")
600+ "" -- "Accepting or reject MS Word track-changes.""
601+
587 , Option "" ["atx-headers"]
602 , Option "" ["atx-headers"]
588 (NoArg
603 (NoArg
589 (\opt -> return opt { optSetextHeaders = False } ))
604 (\opt -> return opt { optSetextHeaders = False } ))
@@ -1120,6 +1135,7 @@ convertWithOpts opts args = do
1120 , optIgnoreArgs = ignoreArgs
1135 , optIgnoreArgs = ignoreArgs
1121 , optVerbose = verbose
1136 , optVerbose = verbose
1122 , optReferenceLinks = referenceLinks
1137 , optReferenceLinks = referenceLinks
1138+ , optReferenceLocation = referenceLocation
1123 , optDpi = dpi
1139 , optDpi = dpi
1124 , optWrapText = wrap
1140 , optWrapText = wrap
1125 , optColumns = columns
1141 , optColumns = columns
@@ -1360,6 +1376,7 @@ convertWithOpts opts args = do
1360 writerNumberOffset = numberFrom,
1376 writerNumberOffset = numberFrom,
1361 writerSectionDivs = sectionDivs,
1377 writerSectionDivs = sectionDivs,
1362 writerReferenceLinks = referenceLinks,
1378 writerReferenceLinks = referenceLinks,
1379+ writerReferenceLocation = referenceLocation,
1363 writerDpi = dpi,
1380 writerDpi = dpi,
1364 writerWrapText = wrap,
1381 writerWrapText = wrap,
1365 writerColumns = columns,
1382 writerColumns = columns,
src/Text/Pandoc/Options.hs+9
@@ -45,6 +45,7 @@ module Text.Pandoc.Options ( Extension(..)
45 , WrapOption (..)
45 , WrapOption (..)
46 , WriterOptions (..)
46 , WriterOptions (..)
47 , TrackChanges (..)
47 , TrackChanges (..)
48+ , ReferenceLocation (..)
48 , def
49 , def
49 , isEnabled
50 , isEnabled
50 ) where
51 ) where
@@ -336,6 +337,12 @@ data WrapOption = WrapAuto -- ^ Automatically wrap to width
336 | WrapPreserve -- ^ Preserve wrapping of input source
337 | WrapPreserve -- ^ Preserve wrapping of input source
337 deriving (Show, Read, Eq, Data, Typeable, Generic)
338 deriving (Show, Read, Eq, Data, Typeable, Generic)
338
339
340+-- | Locations for footnotes and references in markdown output
341+data ReferenceLocation = EndOfBlock -- ^ End of block
342+ | EndOfSection -- ^ prior to next section header (or end of document)
343+ | EndOfDocument -- ^ at end of document
344+ deriving (Show, Read, Eq, Data, Typeable, Generic)
345+
339-- | Options for writers
346-- | Options for writers
340data WriterOptions = WriterOptions
347data WriterOptions = WriterOptions
341 { writerStandalone :: Bool -- ^ Include header and footer
348 { writerStandalone :: Bool -- ^ Include header and footer
@@ -383,6 +390,7 @@ data WriterOptions = WriterOptions
383 , writerMediaBag :: MediaBag -- ^ Media collected by docx or epub reader
390 , writerMediaBag :: MediaBag -- ^ Media collected by docx or epub reader
384 , writerVerbose :: Bool -- ^ Verbose debugging output
391 , writerVerbose :: Bool -- ^ Verbose debugging output
385 , writerLaTeXArgs :: [String] -- ^ Flags to pass to latex-engine
392 , writerLaTeXArgs :: [String] -- ^ Flags to pass to latex-engine
393+ , writerReferenceLocation :: ReferenceLocation -- ^ Location of footnotes and references for writing markdown
386 } deriving (Show, Data, Typeable, Generic)
394 } deriving (Show, Data, Typeable, Generic)
387
395
388instance Default WriterOptions where
396instance Default WriterOptions where
@@ -430,6 +438,7 @@ instance Default WriterOptions where
430 , writerMediaBag = mempty
438 , writerMediaBag = mempty
431 , writerVerbose = False
439 , writerVerbose = False
432 , writerLaTeXArgs = []
440 , writerLaTeXArgs = []
441+ , writerReferenceLocation = EndOfDocument
433 }
442 }
434
443
435-- | Returns True if the given extension is enabled.
444-- | Returns True if the given extension is enabled.
src/Text/Pandoc/Writers/Markdown.hs+142−85
@@ -44,6 +44,7 @@ import Data.List ( group, stripPrefix, find, intersperse, transpose, sortBy )
44import Data.Char ( isSpace, isPunctuation, ord, chr )
44import Data.Char ( isSpace, isPunctuation, ord, chr )
45import Data.Ord ( comparing )
45import Data.Ord ( comparing )
46import Text.Pandoc.Pretty
46import Text.Pandoc.Pretty
47+import Control.Monad.Reader
47import Control.Monad.State
48import Control.Monad.State
48import Text.Pandoc.Writers.HTML (writeHtmlString)
49import Text.Pandoc.Writers.HTML (writeHtmlString)
49import Text.Pandoc.Readers.TeXMath (texMathToInlines)
50import Text.Pandoc.Readers.TeXMath (texMathToInlines)
@@ -60,30 +61,52 @@ import Network.HTTP ( urlEncode )
60type Notes = [[Block]]
61type Notes = [[Block]]
61type Ref = ([Inline], Target, Attr)
62type Ref = ([Inline], Target, Attr)
62type Refs = [Ref]
63type Refs = [Ref]
64+
65+type MD = ReaderT WriterEnv (State WriterState)
66+
67+evalMD :: MD a -> WriterEnv -> WriterState -> a
68+evalMD md env st = evalState (runReaderT md env) st
69+
70+data WriterEnv = WriterEnv { envInList :: Bool
71+ , envPlain :: Bool
72+ , envRefShortcutable :: Bool
73+ , envBlockLevel :: Int
74+ }
75+
76+instance Default WriterEnv
77+ where def = WriterEnv { envInList = False
78+ , envPlain = False
79+ , envRefShortcutable = True
80+ , envBlockLevel = 0
81+ }
82+
63−data WriterState = WriterState { stNotes :: Notes
83+data WriterState = WriterState { stNotes :: Notes
64− , stRefs :: Refs
84+ , stRefs :: Refs
65− , stRefShortcutable :: Bool
66− , stInList :: Bool
67− , stIds :: Set.Set String
85+ , stIds :: Set.Set String
68− , stPlain :: Bool }
86+ , stNoteNum :: Int
87+ }
88+
69instance Default WriterState
89instance Default WriterState
70− where def = WriterState{ stNotes = [], stRefs = [], stRefShortcutable = True,
90+ where def = WriterState{ stNotes = []
91+ , stRefs = []
71− stInList = False, stIds = Set.empty, stPlain = False }
92+ , stIds = Set.empty
93+ , stNoteNum = 1
94+ }
72
95
73-- | Convert Pandoc to Markdown.
96-- | Convert Pandoc to Markdown.
74writeMarkdown :: WriterOptions -> Pandoc -> String
97writeMarkdown :: WriterOptions -> Pandoc -> String
75writeMarkdown opts document =
98writeMarkdown opts document =
76− evalState (pandocToMarkdown opts{
99+ evalMD (pandocToMarkdown opts{
77− writerWrapText = if isEnabled Ext_hard_line_breaks opts
100+ writerWrapText = if isEnabled Ext_hard_line_breaks opts
78− then WrapNone
101+ then WrapNone
79− else writerWrapText opts }
102+ else writerWrapText opts }
80− document) def
103+ document) def def
81
104
82-- | Convert Pandoc to plain text (like markdown, but without links,
105-- | Convert Pandoc to plain text (like markdown, but without links,
83-- pictures, or inline formatting).
106-- pictures, or inline formatting).
84writePlain :: WriterOptions -> Pandoc -> String
107writePlain :: WriterOptions -> Pandoc -> String
85writePlain opts document =
108writePlain opts document =
86− evalState (pandocToMarkdown opts document) def{ stPlain = True }
109+ evalMD (pandocToMarkdown opts document) def{ envPlain = True } def
87
110
88pandocTitleBlock :: Doc -> [Doc] -> Doc -> Doc
111pandocTitleBlock :: Doc -> [Doc] -> Doc -> Doc
89pandocTitleBlock tit auths dat =
112pandocTitleBlock tit auths dat =
@@ -146,12 +169,12 @@ jsonToYaml (Number n) = text $ show n
146jsonToYaml _ = empty
169jsonToYaml _ = empty
147
170
148-- | Return markdown representation of document.
171-- | Return markdown representation of document.
149−pandocToMarkdown :: WriterOptions -> Pandoc -> State WriterState String
172+pandocToMarkdown :: WriterOptions -> Pandoc -> MD String
150pandocToMarkdown opts (Pandoc meta blocks) = do
173pandocToMarkdown opts (Pandoc meta blocks) = do
151 let colwidth = if writerWrapText opts == WrapAuto
174 let colwidth = if writerWrapText opts == WrapAuto
152 then Just $ writerColumns opts
175 then Just $ writerColumns opts
153 else Nothing
176 else Nothing
154− isPlain <- gets stPlain
177+ isPlain <- asks envPlain
155 metadata <- metaToJSON opts
178 metadata <- metaToJSON opts
156 (fmap (render colwidth) . blockListToMarkdown opts)
179 (fmap (render colwidth) . blockListToMarkdown opts)
157 (fmap (render colwidth) . inlineListToMarkdown opts)
180 (fmap (render colwidth) . inlineListToMarkdown opts)
@@ -201,13 +224,13 @@ pandocToMarkdown opts (Pandoc meta blocks) = do
201 else return main
224 else return main
202
225
203-- | Return markdown representation of reference key table.
226-- | Return markdown representation of reference key table.
204−refsToMarkdown :: WriterOptions -> Refs -> State WriterState Doc
227+refsToMarkdown :: WriterOptions -> Refs -> MD Doc
205refsToMarkdown opts refs = mapM (keyToMarkdown opts) refs >>= return . vcat
228refsToMarkdown opts refs = mapM (keyToMarkdown opts) refs >>= return . vcat
206
229
207-- | Return markdown representation of a reference key.
230-- | Return markdown representation of a reference key.
208keyToMarkdown :: WriterOptions
231keyToMarkdown :: WriterOptions
209 -> Ref
232 -> Ref
210− -> State WriterState Doc
233+ -> MD Doc
211keyToMarkdown opts (label, (src, tit), attr) = do
234keyToMarkdown opts (label, (src, tit), attr) = do
212 label' <- inlineListToMarkdown opts label
235 label' <- inlineListToMarkdown opts label
213 let tit' = if null tit
236 let tit' = if null tit
@@ -218,13 +241,15 @@ keyToMarkdown opts (label, (src, tit), attr) = do
218 <> linkAttributes opts attr
241 <> linkAttributes opts attr
219
242
220-- | Return markdown representation of notes.
243-- | Return markdown representation of notes.
221−notesToMarkdown :: WriterOptions -> [[Block]] -> State WriterState Doc
244+notesToMarkdown :: WriterOptions -> [[Block]] -> MD Doc
222−notesToMarkdown opts notes =
245+notesToMarkdown opts notes = do
246+ n <- gets stNoteNum
223− mapM (\(num, note) -> noteToMarkdown opts num note) (zip [1..] notes) >>=
247+ notes' <- mapM (\(num, note) -> noteToMarkdown opts num note) (zip [n..] notes)
248+ modify $ \st -> st { stNoteNum = stNoteNum st + length notes }
224− return . vsep
249+ return $ vsep notes'
225
250
226-- | Return markdown representation of a note.
251-- | Return markdown representation of a note.
227−noteToMarkdown :: WriterOptions -> Int -> [Block] -> State WriterState Doc
252+noteToMarkdown :: WriterOptions -> Int -> [Block] -> MD Doc
228noteToMarkdown opts num blocks = do
253noteToMarkdown opts num blocks = do
229 contents <- blockListToMarkdown opts blocks
254 contents <- blockListToMarkdown opts blocks
230 let num' = text $ writerIdentifierPrefix opts ++ show num
255 let num' = text $ writerIdentifierPrefix opts ++ show num
@@ -261,7 +286,7 @@ tableOfContents :: WriterOptions -> [Block] -> Doc
261tableOfContents opts headers =
286tableOfContents opts headers =
262 let opts' = opts { writerIgnoreNotes = True }
287 let opts' = opts { writerIgnoreNotes = True }
263 contents = BulletList $ map (elementToListItem opts) $ hierarchicalize headers
288 contents = BulletList $ map (elementToListItem opts) $ hierarchicalize headers
264− in evalState (blockToMarkdown opts' contents) def
289+ in evalMD (blockToMarkdown opts' contents) def def
265
290
266-- | Converts an Element to a list item for a table of contents,
291-- | Converts an Element to a list item for a table of contents,
267elementToListItem :: WriterOptions -> Element -> [Block]
292elementToListItem :: WriterOptions -> Element -> [Block]
@@ -315,19 +340,39 @@ beginsWithOrderedListMarker str =
315-- | Convert Pandoc block element to markdown.
340-- | Convert Pandoc block element to markdown.
316blockToMarkdown :: WriterOptions -- ^ Options
341blockToMarkdown :: WriterOptions -- ^ Options
317 -> Block -- ^ Block element
342 -> Block -- ^ Block element
318− -> State WriterState Doc
343+ -> MD Doc
344+blockToMarkdown opts blk =
345+ local (\env -> env {envBlockLevel = envBlockLevel env + 1}) $
346+ do doc <- blockToMarkdown' opts blk
347+ blkLevel <- asks envBlockLevel
348+ if writerReferenceLocation opts == EndOfBlock && blkLevel == 1
349+ then do st <- get
350+ notes' <- notesToMarkdown opts (reverse $ stNotes st)
351+ modify $ \s -> s { stNotes = [] }
352+ st' <- get -- note that the notes may contain refs
353+ refs' <- refsToMarkdown opts (reverse $ stRefs st')
354+ modify $ \s -> s { stRefs = [] }
355+ return $ doc <>
356+ (if isEmpty notes' then empty else blankline <> notes') <>
357+ (if isEmpty refs' then empty else blankline <> refs') <>
358+ (if (isEmpty notes' && isEmpty refs') then empty else blankline)
359+ else return doc
360+
361+blockToMarkdown' :: WriterOptions -- ^ Options
362+ -> Block -- ^ Block element
363+ -> MD Doc
319−blockToMarkdown _ Null = return empty
364+blockToMarkdown' _ Null = return empty
320−blockToMarkdown opts (Div attrs ils) = do
365+blockToMarkdown' opts (Div attrs ils) = do
321 contents <- blockListToMarkdown opts ils
366 contents <- blockListToMarkdown opts ils
322 return $ if isEnabled Ext_raw_html opts &&
367 return $ if isEnabled Ext_raw_html opts &&
323 isEnabled Ext_markdown_in_html_blocks opts
368 isEnabled Ext_markdown_in_html_blocks opts
324 then tagWithAttrs "div" attrs <> blankline <>
369 then tagWithAttrs "div" attrs <> blankline <>
325 contents <> blankline <> "</div>" <> blankline
370 contents <> blankline <> "</div>" <> blankline
326 else contents <> blankline
371 else contents <> blankline
327−blockToMarkdown opts (Plain inlines) = do
372+blockToMarkdown' opts (Plain inlines) = do
328 contents <- inlineListToMarkdown opts inlines
373 contents <- inlineListToMarkdown opts inlines
329 -- escape if para starts with ordered list marker
374 -- escape if para starts with ordered list marker
330− st <- get
375+ isPlain <- asks envPlain
331 let colwidth = if writerWrapText opts == WrapAuto
376 let colwidth = if writerWrapText opts == WrapAuto
332 then Just $ writerColumns opts
377 then Just $ writerColumns opts
333 else Nothing
378 else Nothing
@@ -336,34 +381,50 @@ blockToMarkdown opts (Plain inlines) = do
336 | otherwise = x : escapeDelimiter xs
381 | otherwise = x : escapeDelimiter xs
337 escapeDelimiter [] = []
382 escapeDelimiter [] = []
338 let contents' = if isEnabled Ext_all_symbols_escapable opts &&
383 let contents' = if isEnabled Ext_all_symbols_escapable opts &&
339− not (stPlain st) && beginsWithOrderedListMarker rendered
384+ not isPlain && beginsWithOrderedListMarker rendered
340 then text $ escapeDelimiter rendered
385 then text $ escapeDelimiter rendered
341 else contents
386 else contents
342 return $ contents' <> cr
387 return $ contents' <> cr
343-- title beginning with fig: indicates figure
388-- title beginning with fig: indicates figure
344−blockToMarkdown opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) =
389+blockToMarkdown' opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) =
345 blockToMarkdown opts (Para [Image attr alt (src,tit)])
390 blockToMarkdown opts (Para [Image attr alt (src,tit)])
346−blockToMarkdown opts (Para inlines) =
391+blockToMarkdown' opts (Para inlines) =
347 (<> blankline) `fmap` blockToMarkdown opts (Plain inlines)
392 (<> blankline) `fmap` blockToMarkdown opts (Plain inlines)
348−blockToMarkdown opts (RawBlock f str)
393+blockToMarkdown' opts (RawBlock f str)
349 | f == "markdown" = return $ text str <> text "\n"
394 | f == "markdown" = return $ text str <> text "\n"
350 | f == "html" && isEnabled Ext_raw_html opts = do
395 | f == "html" && isEnabled Ext_raw_html opts = do
351− plain <- gets stPlain
396+ plain <- asks envPlain
352 return $ if plain
397 return $ if plain
353 then empty
398 then empty
354 else if isEnabled Ext_markdown_attribute opts
399 else if isEnabled Ext_markdown_attribute opts
355 then text (addMarkdownAttribute str) <> text "\n"
400 then text (addMarkdownAttribute str) <> text "\n"
356 else text str <> text "\n"
401 else text str <> text "\n"
357 | f `elem` ["latex", "tex"] && isEnabled Ext_raw_tex opts = do
402 | f `elem` ["latex", "tex"] && isEnabled Ext_raw_tex opts = do
358− plain <- gets stPlain
403+ plain <- asks envPlain
359 return $ if plain
404 return $ if plain
360 then empty
405 then empty
361 else text str <> text "\n"
406 else text str <> text "\n"
362 | otherwise = return empty
407 | otherwise = return empty
363−blockToMarkdown opts HorizontalRule = do
408+blockToMarkdown' opts HorizontalRule = do
364 return $ blankline <> text (replicate (writerColumns opts) '-') <> blankline
409 return $ blankline <> text (replicate (writerColumns opts) '-') <> blankline
365−blockToMarkdown opts (Header level attr inlines) = do
410+blockToMarkdown' opts (Header level attr inlines) = do
366− plain <- gets stPlain
411+ -- first, if we're putting references at the end of a section, we
412+ -- put them here.
413+ blkLevel <- asks envBlockLevel
414+ refs <- if writerReferenceLocation opts == EndOfSection && blkLevel == 1
415+ then do st <- get
416+ notes' <- notesToMarkdown opts (reverse $ stNotes st)
417+ modify $ \s -> s { stNotes = [] }
418+ st' <- get -- note that the notes may contain refs
419+ refs' <- refsToMarkdown opts (reverse $ stRefs st')
420+ modify $ \s -> s { stRefs = [] }
421+ return $
422+ (if isEmpty notes' then empty else blankline <> notes') <>
423+ (if isEmpty refs' then empty else blankline <> refs') <>
424+ (if (isEmpty notes' && isEmpty refs') then empty else blankline)
425+ else return empty
426+
427+ plain <- asks envPlain
367 -- we calculate the id that would be used by auto_identifiers
428 -- we calculate the id that would be used by auto_identifiers
368 -- so we know whether to print an explicit identifier
429 -- so we know whether to print an explicit identifier
369 ids <- gets stIds
430 ids <- gets stIds
@@ -383,8 +444,7 @@ blockToMarkdown opts (Header level attr inlines) = do
383 then capitalize inlines
444 then capitalize inlines
384 else inlines
445 else inlines
385 let setext = writerSetextHeaders opts
446 let setext = writerSetextHeaders opts
386− return $ nowrap
387− $ case level of
447+ hdr = nowrap $ case level of
388 1 | plain -> blanklines 3 <> contents <> blanklines 2
448 1 | plain -> blanklines 3 <> contents <> blanklines 2
389 | setext ->
449 | setext ->
390 contents <> attr' <> cr <> text (replicate (offset contents) '=') <>
450 contents <> attr' <> cr <> text (replicate (offset contents) '=') <>
@@ -397,11 +457,13 @@ blockToMarkdown opts (Header level attr inlines) = do
397 _ | plain || isEnabled Ext_literate_haskell opts ->
457 _ | plain || isEnabled Ext_literate_haskell opts ->
398 contents <> blankline
458 contents <> blankline
399 _ -> text (replicate level '#') <> space <> contents <> attr' <> blankline
459 _ -> text (replicate level '#') <> space <> contents <> attr' <> blankline
460+
461+ return $ refs <> hdr
400−blockToMarkdown opts (CodeBlock (_,classes,_) str)
462+blockToMarkdown' opts (CodeBlock (_,classes,_) str)
401 | "haskell" `elem` classes && "literate" `elem` classes &&
463 | "haskell" `elem` classes && "literate" `elem` classes &&
402 isEnabled Ext_literate_haskell opts =
464 isEnabled Ext_literate_haskell opts =
403 return $ prefixed "> " (text str) <> blankline
465 return $ prefixed "> " (text str) <> blankline
404−blockToMarkdown opts (CodeBlock attribs str) = return $
466+blockToMarkdown' opts (CodeBlock attribs str) = return $
405 case attribs == nullAttr of
467 case attribs == nullAttr of
406 False | isEnabled Ext_backtick_code_blocks opts ->
468 False | isEnabled Ext_backtick_code_blocks opts ->
407 backticks <> attrs <> cr <> text str <> cr <> backticks <> blankline
469 backticks <> attrs <> cr <> text str <> cr <> backticks <> blankline
@@ -423,8 +485,8 @@ blockToMarkdown opts (CodeBlock attribs str) = return $
423 else case attribs of
485 else case attribs of
424 (_,(cls:_),_) -> " " <> text cls
486 (_,(cls:_),_) -> " " <> text cls
425 _ -> empty
487 _ -> empty
426−blockToMarkdown opts (BlockQuote blocks) = do
488+blockToMarkdown' opts (BlockQuote blocks) = do
427− plain <- gets stPlain
489+ plain <- asks envPlain
428 -- if we're writing literate haskell, put a space before the bird tracks
490 -- if we're writing literate haskell, put a space before the bird tracks
429 -- so they won't be interpreted as lhs...
491 -- so they won't be interpreted as lhs...
430 let leader = if isEnabled Ext_literate_haskell opts
492 let leader = if isEnabled Ext_literate_haskell opts
@@ -432,7 +494,7 @@ blockToMarkdown opts (BlockQuote blocks) = do
432 else if plain then " " else "> "
494 else if plain then " " else "> "
433 contents <- blockListToMarkdown opts blocks
495 contents <- blockListToMarkdown opts blocks
434 return $ (prefixed leader contents) <> blankline
496 return $ (prefixed leader contents) <> blankline
435−blockToMarkdown opts t@(Table caption aligns widths headers rows) = do
497+blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do
436 caption' <- inlineListToMarkdown opts caption
498 caption' <- inlineListToMarkdown opts caption
437 let caption'' = if null caption || not (isEnabled Ext_table_captions opts)
499 let caption'' = if null caption || not (isEnabled Ext_table_captions opts)
438 then empty
500 then empty
@@ -466,10 +528,10 @@ blockToMarkdown opts t@(Table caption aligns widths headers rows) = do
466 $ Pandoc nullMeta [t]
528 $ Pandoc nullMeta [t]
467 | otherwise -> return $ (id, text "[TABLE]")
529 | otherwise -> return $ (id, text "[TABLE]")
468 return $ nst $ tbl $$ blankline $$ caption'' $$ blankline
530 return $ nst $ tbl $$ blankline $$ caption'' $$ blankline
469−blockToMarkdown opts (BulletList items) = do
531+blockToMarkdown' opts (BulletList items) = do
470 contents <- inList $ mapM (bulletListItemToMarkdown opts) items
532 contents <- inList $ mapM (bulletListItemToMarkdown opts) items
471 return $ cat contents <> blankline
533 return $ cat contents <> blankline
472−blockToMarkdown opts (OrderedList (start,sty,delim) items) = do
534+blockToMarkdown' opts (OrderedList (start,sty,delim) items) = do
473 let start' = if isEnabled Ext_startnum opts then start else 1
535 let start' = if isEnabled Ext_startnum opts then start else 1
474 let sty' = if isEnabled Ext_fancy_lists opts then sty else DefaultStyle
536 let sty' = if isEnabled Ext_fancy_lists opts then sty else DefaultStyle
475 let delim' = if isEnabled Ext_fancy_lists opts then delim else DefaultDelim
537 let delim' = if isEnabled Ext_fancy_lists opts then delim else DefaultDelim
@@ -482,17 +544,12 @@ blockToMarkdown opts (OrderedList (start,sty,delim) items) = do
482 mapM (\(item, num) -> orderedListItemToMarkdown opts item num) $
544 mapM (\(item, num) -> orderedListItemToMarkdown opts item num) $
483 zip markers' items
545 zip markers' items
484 return $ cat contents <> blankline
546 return $ cat contents <> blankline
485−blockToMarkdown opts (DefinitionList items) = do
547+blockToMarkdown' opts (DefinitionList items) = do
486 contents <- inList $ mapM (definitionListItemToMarkdown opts) items
548 contents <- inList $ mapM (definitionListItemToMarkdown opts) items
487 return $ cat contents <> blankline
549 return $ cat contents <> blankline
488
550
489−inList :: State WriterState a -> State WriterState a
551+inList :: MD a -> MD a
490−inList p = do
552+inList p = local (\env -> env {envInList = True}) p
491− oldInList <- gets stInList
492− modify $ \st -> st{ stInList = True }
493− res <- p
494− modify $ \st -> st{ stInList = oldInList }
495− return res
496
553
497addMarkdownAttribute :: String -> String
554addMarkdownAttribute :: String -> String
498addMarkdownAttribute s =
555addMarkdownAttribute s =
@@ -503,7 +560,7 @@ addMarkdownAttribute s =
503 x /= "markdown"]
560 x /= "markdown"]
504 _ -> s
561 _ -> s
505
562
506−pipeTable :: Bool -> [Alignment] -> [Doc] -> [[Doc]] -> State WriterState Doc
563+pipeTable :: Bool -> [Alignment] -> [Doc] -> [[Doc]] -> MD Doc
507pipeTable headless aligns rawHeaders rawRows = do
564pipeTable headless aligns rawHeaders rawRows = do
508 let sp = text " "
565 let sp = text " "
509 let blockFor AlignLeft x y = lblock (x + 2) (sp <> y) <> lblock 0 empty
566 let blockFor AlignLeft x y = lblock (x + 2) (sp <> y) <> lblock 0 empty
@@ -532,7 +589,7 @@ pipeTable headless aligns rawHeaders rawRows = do
532 return $ header $$ border $$ body
589 return $ header $$ border $$ body
533
590
534pandocTable :: WriterOptions -> Bool -> [Alignment] -> [Double]
591pandocTable :: WriterOptions -> Bool -> [Alignment] -> [Double]
535− -> [Doc] -> [[Doc]] -> State WriterState Doc
592+ -> [Doc] -> [[Doc]] -> MD Doc
536pandocTable opts headless aligns widths rawHeaders rawRows = do
593pandocTable opts headless aligns widths rawHeaders rawRows = do
537 let isSimple = all (==0) widths
594 let isSimple = all (==0) widths
538 let alignHeader alignment = case alignment of
595 let alignHeader alignment = case alignment of
@@ -572,7 +629,7 @@ pandocTable opts headless aligns widths rawHeaders rawRows = do
572 return $ head'' $$ underline $$ body $$ bottom
629 return $ head'' $$ underline $$ body $$ bottom
573
630
574gridTable :: WriterOptions -> Bool -> [Alignment] -> [Double]
631gridTable :: WriterOptions -> Bool -> [Alignment] -> [Double]
575− -> [Doc] -> [[Doc]] -> State WriterState Doc
632+ -> [Doc] -> [[Doc]] -> MD Doc
576gridTable opts headless _aligns widths headers' rawRows = do
633gridTable opts headless _aligns widths headers' rawRows = do
577 let numcols = length headers'
634 let numcols = length headers'
578 let widths' = if all (==0) widths
635 let widths' = if all (==0) widths
@@ -599,7 +656,7 @@ gridTable opts headless _aligns widths headers' rawRows = do
599 return $ border '-' $$ head'' $$ body $$ border '-'
656 return $ border '-' $$ head'' $$ body $$ border '-'
600
657
601-- | Convert bullet list item (list of blocks) to markdown.
658-- | Convert bullet list item (list of blocks) to markdown.
602−bulletListItemToMarkdown :: WriterOptions -> [Block] -> State WriterState Doc
659+bulletListItemToMarkdown :: WriterOptions -> [Block] -> MD Doc
603bulletListItemToMarkdown opts items = do
660bulletListItemToMarkdown opts items = do
604 contents <- blockListToMarkdown opts items
661 contents <- blockListToMarkdown opts items
605 let sps = replicate (writerTabStop opts - 2) ' '
662 let sps = replicate (writerTabStop opts - 2) ' '
@@ -617,7 +674,7 @@ bulletListItemToMarkdown opts items = do
617orderedListItemToMarkdown :: WriterOptions -- ^ options
674orderedListItemToMarkdown :: WriterOptions -- ^ options
618 -> String -- ^ list item marker
675 -> String -- ^ list item marker
619 -> [Block] -- ^ list item (list of blocks)
676 -> [Block] -- ^ list item (list of blocks)
620− -> State WriterState Doc
677+ -> MD Doc
621orderedListItemToMarkdown opts marker items = do
678orderedListItemToMarkdown opts marker items = do
622 contents <- blockListToMarkdown opts items
679 contents <- blockListToMarkdown opts items
623 let sps = case length marker - writerTabStop opts of
680 let sps = case length marker - writerTabStop opts of
@@ -629,15 +686,15 @@ orderedListItemToMarkdown opts marker items = do
629-- | Convert definition list item (label, list of blocks) to markdown.
686-- | Convert definition list item (label, list of blocks) to markdown.
630definitionListItemToMarkdown :: WriterOptions
687definitionListItemToMarkdown :: WriterOptions
631 -> ([Inline],[[Block]])
688 -> ([Inline],[[Block]])
632− -> State WriterState Doc
689+ -> MD Doc
633definitionListItemToMarkdown opts (label, defs) = do
690definitionListItemToMarkdown opts (label, defs) = do
634 labelText <- inlineListToMarkdown opts label
691 labelText <- inlineListToMarkdown opts label
635 defs' <- mapM (mapM (blockToMarkdown opts)) defs
692 defs' <- mapM (mapM (blockToMarkdown opts)) defs
636 if isEnabled Ext_definition_lists opts
693 if isEnabled Ext_definition_lists opts
637 then do
694 then do
638 let tabStop = writerTabStop opts
695 let tabStop = writerTabStop opts
639− st <- get
696+ isPlain <- asks envPlain
640− let leader = if stPlain st then " " else ": "
697+ let leader = if isPlain then " " else ": "
641 let sps = case writerTabStop opts - 3 of
698 let sps = case writerTabStop opts - 3 of
642 n | n > 0 -> text $ replicate n ' '
699 n | n > 0 -> text $ replicate n ' '
643 _ -> text " "
700 _ -> text " "
@@ -661,7 +718,7 @@ definitionListItemToMarkdown opts (label, defs) = do
661-- | Convert list of Pandoc block elements to markdown.
718-- | Convert list of Pandoc block elements to markdown.
662blockListToMarkdown :: WriterOptions -- ^ Options
719blockListToMarkdown :: WriterOptions -- ^ Options
663 -> [Block] -- ^ List of block elements
720 -> [Block] -- ^ List of block elements
664− -> State WriterState Doc
721+ -> MD Doc
665blockListToMarkdown opts blocks =
722blockListToMarkdown opts blocks =
666 mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat
723 mapM (blockToMarkdown opts) (fixBlocks blocks) >>= return . cat
667 -- insert comment between list and indented code block, or the
724 -- insert comment between list and indented code block, or the
@@ -688,7 +745,7 @@ blockListToMarkdown opts blocks =
688
745
689-- | Get reference for target; if none exists, create unique one and return.
746-- | Get reference for target; if none exists, create unique one and return.
690-- Prefer label if possible; otherwise, generate a unique key.
747-- Prefer label if possible; otherwise, generate a unique key.
691−getReference :: Attr -> [Inline] -> Target -> State WriterState [Inline]
748+getReference :: Attr -> [Inline] -> Target -> MD [Inline]
692getReference attr label target = do
749getReference attr label target = do
693 st <- get
750 st <- get
694 case find (\(_,t,a) -> t == target && a == attr) (stRefs st) of
751 case find (\(_,t,a) -> t == target && a == attr) (stRefs st) of
@@ -706,9 +763,9 @@ getReference attr label target = do
706 return label'
763 return label'
707
764
708-- | Convert list of Pandoc inline elements to markdown.
765-- | Convert list of Pandoc inline elements to markdown.
709−inlineListToMarkdown :: WriterOptions -> [Inline] -> State WriterState Doc
766+inlineListToMarkdown :: WriterOptions -> [Inline] -> MD Doc
710inlineListToMarkdown opts lst = do
767inlineListToMarkdown opts lst = do
711− inlist <- gets stInList
768+ inlist <- asks envInList
712 go (if inlist then avoidBadWrapsInList lst else lst)
769 go (if inlist then avoidBadWrapsInList lst else lst)
713 where go [] = return empty
770 where go [] = return empty
714 go (i:is) = case i of
771 go (i:is) = case i of
@@ -731,9 +788,9 @@ inlineListToMarkdown opts lst = do
731 _ -> shortcutable
788 _ -> shortcutable
732 where shortcutable = liftM2 (<>) (inlineToMarkdown opts i) (go is)
789 where shortcutable = liftM2 (<>) (inlineToMarkdown opts i) (go is)
733 unshortcutable = do
790 unshortcutable = do
734− iMark <- withState (\s -> s { stRefShortcutable = False })
791+ iMark <- local
792+ (\env -> env { envRefShortcutable = False })
735− (inlineToMarkdown opts i)
793+ (inlineToMarkdown opts i)
736− modify (\s -> s {stRefShortcutable = True })
737 fmap (iMark <>) (go is)
794 fmap (iMark <>) (go is)
738
795
739isSp :: Inline -> Bool
796isSp :: Inline -> Bool
@@ -773,22 +830,22 @@ escapeSpaces SoftBreak = Str "\\ "
773escapeSpaces x = x
830escapeSpaces x = x
774
831
775-- | Convert Pandoc inline element to markdown.
832-- | Convert Pandoc inline element to markdown.
776−inlineToMarkdown :: WriterOptions -> Inline -> State WriterState Doc
833+inlineToMarkdown :: WriterOptions -> Inline -> MD Doc
777inlineToMarkdown opts (Span attrs ils) = do
834inlineToMarkdown opts (Span attrs ils) = do
778− plain <- gets stPlain
835+ plain <- asks envPlain
779 contents <- inlineListToMarkdown opts ils
836 contents <- inlineListToMarkdown opts ils
780 return $ if not plain &&
837 return $ if not plain &&
781 (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts)
838 (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts)
782 then tagWithAttrs "span" attrs <> contents <> text "</span>"
839 then tagWithAttrs "span" attrs <> contents <> text "</span>"
783 else contents
840 else contents
784inlineToMarkdown opts (Emph lst) = do
841inlineToMarkdown opts (Emph lst) = do
785− plain <- gets stPlain
842+ plain <- asks envPlain
786 contents <- inlineListToMarkdown opts lst
843 contents <- inlineListToMarkdown opts lst
787 return $ if plain
844 return $ if plain
788 then "_" <> contents <> "_"
845 then "_" <> contents <> "_"
789 else "*" <> contents <> "*"
846 else "*" <> contents <> "*"
790inlineToMarkdown opts (Strong lst) = do
847inlineToMarkdown opts (Strong lst) = do
791− plain <- gets stPlain
848+ plain <- asks envPlain
792 if plain
849 if plain
793 then inlineListToMarkdown opts $ capitalize lst
850 then inlineListToMarkdown opts $ capitalize lst
794 else do
851 else do
@@ -827,7 +884,7 @@ inlineToMarkdown opts (Subscript lst) = do
827 _ -> contents
884 _ -> contents
828 where toSubscript c = chr (0x2080 + (ord c - 48))
885 where toSubscript c = chr (0x2080 + (ord c - 48))
829inlineToMarkdown opts (SmallCaps lst) = do
886inlineToMarkdown opts (SmallCaps lst) = do
830− plain <- gets stPlain
887+ plain <- asks envPlain
831 if not plain &&
888 if not plain &&
832 (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts)
889 (isEnabled Ext_raw_html opts || isEnabled Ext_native_spans opts)
833 then do
890 then do
@@ -852,13 +909,13 @@ inlineToMarkdown opts (Code attr str) = do
852 let attrs = if isEnabled Ext_inline_code_attributes opts && attr /= nullAttr
909 let attrs = if isEnabled Ext_inline_code_attributes opts && attr /= nullAttr
853 then attrsToMarkdown attr
910 then attrsToMarkdown attr
854 else empty
911 else empty
855− plain <- gets stPlain
912+ plain <- asks envPlain
856 if plain
913 if plain
857 then return $ text str
914 then return $ text str
858 else return $ text (marker ++ spacer ++ str ++ spacer ++ marker) <> attrs
915 else return $ text (marker ++ spacer ++ str ++ spacer ++ marker) <> attrs
859inlineToMarkdown opts (Str str) = do
916inlineToMarkdown opts (Str str) = do
860− st <- get
917+ isPlain <- asks envPlain
861− if stPlain st
918+ if isPlain
862 then return $ text str
919 then return $ text str
863 else return $ text $ escapeString opts str
920 else return $ text $ escapeString opts str
864inlineToMarkdown opts (Math InlineMath str) =
921inlineToMarkdown opts (Math InlineMath str) =
@@ -873,7 +930,7 @@ inlineToMarkdown opts (Math InlineMath str) =
873 | isEnabled Ext_tex_math_double_backslash opts ->
930 | isEnabled Ext_tex_math_double_backslash opts ->
874 return $ "\\\\(" <> text str <> "\\\\)"
931 return $ "\\\\(" <> text str <> "\\\\)"
875 | otherwise -> do
932 | otherwise -> do
876− plain <- gets stPlain
933+ plain <- asks envPlain
877 inlineListToMarkdown opts $
934 inlineListToMarkdown opts $
878 (if plain then makeMathPlainer else id) $
935 (if plain then makeMathPlainer else id) $
879 texMathToInlines InlineMath str
936 texMathToInlines InlineMath str
@@ -887,7 +944,7 @@ inlineToMarkdown opts (Math DisplayMath str)
887 | otherwise = (\x -> cr <> x <> cr) `fmap`
944 | otherwise = (\x -> cr <> x <> cr) `fmap`
888 inlineListToMarkdown opts (texMathToInlines DisplayMath str)
945 inlineListToMarkdown opts (texMathToInlines DisplayMath str)
889inlineToMarkdown opts (RawInline f str) = do
946inlineToMarkdown opts (RawInline f str) = do
890− plain <- gets stPlain
947+ plain <- asks envPlain
891 if not plain &&
948 if not plain &&
892 ( f == "markdown" ||
949 ( f == "markdown" ||
893 (isEnabled Ext_raw_tex opts && (f == "latex" || f == "tex")) ||
950 (isEnabled Ext_raw_tex opts && (f == "latex" || f == "tex")) ||
@@ -895,7 +952,7 @@ inlineToMarkdown opts (RawInline f str) = do
895 then return $ text str
952 then return $ text str
896 else return empty
953 else return empty
897inlineToMarkdown opts (LineBreak) = do
954inlineToMarkdown opts (LineBreak) = do
898− plain <- gets stPlain
955+ plain <- asks envPlain
899 if plain || isEnabled Ext_hard_line_breaks opts
956 if plain || isEnabled Ext_hard_line_breaks opts
900 then return cr
957 then return cr
901 else return $
958 else return $
@@ -944,7 +1001,7 @@ inlineToMarkdown opts lnk@(Link attr txt (src, tit))
944 attr /= nullAttr = -- use raw HTML
1001 attr /= nullAttr = -- use raw HTML
945 return $ text $ trim $ writeHtmlString def $ Pandoc nullMeta [Plain [lnk]]
1002 return $ text $ trim $ writeHtmlString def $ Pandoc nullMeta [Plain [lnk]]
946 | otherwise = do
1003 | otherwise = do
947− plain <- gets stPlain
1004+ plain <- asks envPlain
948 linktext <- inlineListToMarkdown opts txt
1005 linktext <- inlineListToMarkdown opts txt
949 let linktitle = if null tit
1006 let linktitle = if null tit
950 then empty
1007 then empty
@@ -955,7 +1012,7 @@ inlineToMarkdown opts lnk@(Link attr txt (src, tit))
955 [Str s] | escapeURI s == srcSuffix -> True
1012 [Str s] | escapeURI s == srcSuffix -> True
956 _ -> False
1013 _ -> False
957 let useRefLinks = writerReferenceLinks opts && not useAuto
1014 let useRefLinks = writerReferenceLinks opts && not useAuto
958− shortcutable <- gets stRefShortcutable
1015+ shortcutable <- asks envRefShortcutable
959 let useShortcutRefLinks = shortcutable &&
1016 let useShortcutRefLinks = shortcutable &&
960 isEnabled Ext_shortcut_reference_links opts
1017 isEnabled Ext_shortcut_reference_links opts
961 ref <- if useRefLinks then getReference attr txt (src, tit) else return []
1018 ref <- if useRefLinks then getReference attr txt (src, tit) else return []
@@ -983,7 +1040,7 @@ inlineToMarkdown opts img@(Image attr alternate (source, tit))
983 attr /= nullAttr = -- use raw HTML
1040 attr /= nullAttr = -- use raw HTML
984 return $ text $ trim $ writeHtmlString def $ Pandoc nullMeta [Plain [img]]
1041 return $ text $ trim $ writeHtmlString def $ Pandoc nullMeta [Plain [img]]
985 | otherwise = do
1042 | otherwise = do
986− plain <- gets stPlain
1043+ plain <- asks envPlain
987 let txt = if null alternate || alternate == [Str source]
1044 let txt = if null alternate || alternate == [Str source]
988 -- to prevent autolinks
1045 -- to prevent autolinks
989 then [Str ""]
1046 then [Str ""]
@@ -995,7 +1052,7 @@ inlineToMarkdown opts img@(Image attr alternate (source, tit))
995inlineToMarkdown opts (Note contents) = do
1052inlineToMarkdown opts (Note contents) = do
996 modify (\st -> st{ stNotes = contents : stNotes st })
1053 modify (\st -> st{ stNotes = contents : stNotes st })
997 st <- get
1054 st <- get
998− let ref = text $ writerIdentifierPrefix opts ++ show (length $ stNotes st)
1055+ let ref = text $ writerIdentifierPrefix opts ++ show (stNoteNum st + (length $ stNotes st) - 1)
999 if isEnabled Ext_footnotes opts
1056 if isEnabled Ext_footnotes opts
1000 then return $ "[^" <> ref <> "]"
1057 then return $ "[^" <> ref <> "]"
1001 else return $ "[" <> ref <> "]"
1058 else return $ "[" <> ref <> "]"
tests/Tests/Writers/Markdown.hs+138−2
@@ -11,6 +11,9 @@ import Tests.Arbitrary()
11markdown :: (ToPandoc a) => a -> String
11markdown :: (ToPandoc a) => a -> String
12markdown = writeMarkdown def . toPandoc
12markdown = writeMarkdown def . toPandoc
13
13
14+markdownWithOpts :: (ToPandoc a) => WriterOptions -> a -> String
15+markdownWithOpts opts x = writeMarkdown opts $ toPandoc x
16+
14{-
17{-
15 "my test" =: X =?> Y
18 "my test" =: X =?> Y
16
19
@@ -36,13 +39,146 @@ tests = [ "indented code after list"
36 =: bulletList [ plain "foo" <> bulletList [ plain "bar" ],
39 =: bulletList [ plain "foo" <> bulletList [ plain "bar" ],
37 plain "baz" ]
40 plain "baz" ]
38 =?> "- foo\n - bar\n- baz\n"
41 =?> "- foo\n - bar\n- baz\n"
39− ] ++ [shortcutLinkRefsTests]
42+ ] ++ [noteTests] ++ [shortcutLinkRefsTests]
43+
44+{-
45+
46+Testing with the following text:
47+
48+First Header
49+============
50+
51+This is a footnote.[^1] And this is a [link](https://www.google.com).
52+
53+> A note inside a block quote.[^2]
54+>
55+> A second paragraph.
56+
57+Second Header
58+=============
59+
60+Some more text.
61+
62+
63+[^1]: Down here.
64+
65+[^2]: The second note.
66+
67+-}
68+
69+noteTestDoc :: Blocks
70+noteTestDoc =
71+ header 1 "First Header" <>
72+ para ("This is a footnote." <>
73+ note (para "Down here.") <>
74+ " And this is a " <>
75+ link "https://www.google.com" "" "link" <>
76+ ".") <>
77+ blockQuote (para ("A note inside a block quote." <>
78+ note (para "The second note.")) <>
79+ para ("A second paragraph.")) <>
80+ header 1 "Second Header" <>
81+ para "Some more text."
82+
83+
84+
85+noteTests :: Test
86+noteTests = testGroup "note and reference location"
87+ [ test (markdownWithOpts def)
88+ "footnotes at the end of a document" $
89+ noteTestDoc =?>
90+ (unlines $ [ "First Header"
91+ , "============"
92+ , ""
93+ , "This is a footnote.[^1] And this is a [link](https://www.google.com)."
94+ , ""
95+ , "> A note inside a block quote.[^2]"
96+ , ">"
97+ , "> A second paragraph."
98+ , ""
99+ , "Second Header"
100+ , "============="
101+ , ""
102+ , "Some more text."
103+ , ""
104+ , "[^1]: Down here."
105+ , ""
106+ , "[^2]: The second note."
107+ ])
108+ , test (markdownWithOpts def{writerReferenceLocation=EndOfBlock})
109+ "footnotes at the end of blocks" $
110+ noteTestDoc =?>
111+ (unlines $ [ "First Header"
112+ , "============"
113+ , ""
114+ , "This is a footnote.[^1] And this is a [link](https://www.google.com)."
115+ , ""
116+ , "[^1]: Down here."
117+ , ""
118+ , "> A note inside a block quote.[^2]"
119+ , ">"
120+ , "> A second paragraph."
121+ , ""
122+ , "[^2]: The second note."
123+ , ""
124+ , "Second Header"
125+ , "============="
126+ , ""
127+ , "Some more text."
128+ ])
129+ , test (markdownWithOpts def{writerReferenceLocation=EndOfBlock, writerReferenceLinks=True})
130+ "footnotes and reference links at the end of blocks" $
131+ noteTestDoc =?>
132+ (unlines $ [ "First Header"
133+ , "============"
134+ , ""
135+ , "This is a footnote.[^1] And this is a [link]."
136+ , ""
137+ , "[^1]: Down here."
138+ , ""
139+ , " [link]: https://www.google.com"
140+ , ""
141+ , "> A note inside a block quote.[^2]"
142+ , ">"
143+ , "> A second paragraph."
144+ , ""
145+ , "[^2]: The second note."
146+ , ""
147+ , "Second Header"
148+ , "============="
149+ , ""
150+ , "Some more text."
151+ ])
152+ , test (markdownWithOpts def{writerReferenceLocation=EndOfSection})
153+ "footnotes at the end of section" $
154+ noteTestDoc =?>
155+ (unlines $ [ "First Header"
156+ , "============"
157+ , ""
158+ , "This is a footnote.[^1] And this is a [link](https://www.google.com)."
159+ , ""
160+ , "> A note inside a block quote.[^2]"
161+ , ">"
162+ , "> A second paragraph."
163+ , ""
164+ , "[^1]: Down here."
165+ , ""
166+ , "[^2]: The second note."
167+ , ""
168+ , "Second Header"
169+ , "============="
170+ , ""
171+ , "Some more text."
172+ ])
173+
174+ ]
40
175
41shortcutLinkRefsTests :: Test
176shortcutLinkRefsTests :: Test
42shortcutLinkRefsTests =
177shortcutLinkRefsTests =
43 let infix 4 =:
178 let infix 4 =:
44 (=:) :: (ToString a, ToPandoc a)
179 (=:) :: (ToString a, ToPandoc a)
180+
45− => String -> (a, String) -> Test
181+ => String -> (a, String) -> Test
46 (=:) = test (writeMarkdown (def {writerReferenceLinks = True}) . toPandoc)
182 (=:) = test (writeMarkdown (def {writerReferenceLinks = True}) . toPandoc)
47 in testGroup "Shortcut reference links"
183 in testGroup "Shortcut reference links"
48 [ "Simple link (shortcutable)"
184 [ "Simple link (shortcutable)"