git.iko.soy/typeable/pandoc
on master help sign in
Markdown writer: Allow footnotes/refs at the end of blocks, sections

This allows footnotes and refs to be placed at the end of blocks and sections. Note that we only place them at the end of blocks that are at the top level and before headers that are the top level. We add an environment variable to keep track of this. Because we clear the footnotes and refs when we use them, we also add a state variable to keep track of the starting number. Finally, note that we still add any remaining footnotes at the end. This takes care of the final section, if we are placing at the end of a section, and will always come after a final block as well.

commitca50deeeeee3bf0900cb91d39671d8dc5aaceb1b authorJesse Rosenthal <jrosenthal@jhu.edu> dateTue 11 Oct 2016, 14:57 (10 years ago) parent6914808
1 file changed+64−21
src/Text/Pandoc/Writers/Markdown.hs+64−21
src/Text/Pandoc/Writers/Markdown.hs+64−21
@@ -70,23 +70,27 @@ evalMD md env st = evalState (runReaderT md env) st
7070data WriterEnv = WriterEnv { envInList :: Bool
7171 , envPlain :: Bool
7272 , envRefShortcutable :: Bool
73+ , envBlockLevel :: Int
7374 }
7475
7576instance Default WriterEnv
7677 where def = WriterEnv { envInList = False
7778 , envPlain = False
7879 , envRefShortcutable = True
80+ , envBlockLevel = 0
7981 }
8082
8183data WriterState = WriterState { stNotes :: Notes
8284 , stRefs :: Refs
8385 , stIds :: Set.Set String
86+ , stNoteNum :: Int
8487 }
8588
8689instance Default WriterState
8790 where def = WriterState{ stNotes = []
8891 , stRefs = []
8992 , stIds = Set.empty
93+ , stNoteNum = 1
9094 }
9195
9296-- | Convert Pandoc to Markdown.
@@ -238,9 +242,11 @@ keyToMarkdown opts (label, (src, tit), attr) = do
238242
239243-- | Return markdown representation of notes.
240244notesToMarkdown :: WriterOptions -> [[Block]] -> MD Doc
241−notesToMarkdown opts notes =
242− mapM (\(num, note) -> noteToMarkdown opts num note) (zip [1..] notes) >>=
243− return . vsep
245+notesToMarkdown opts notes = do
246+ n <- gets stNoteNum
247+ notes' <- mapM (\(num, note) -> noteToMarkdown opts num note) (zip [n..] notes)
248+ modify $ \st -> st { stNoteNum = stNoteNum st + length notes }
249+ return $ vsep notes'
244250
245251-- | Return markdown representation of a note.
246252noteToMarkdown :: WriterOptions -> Int -> [Block] -> MD Doc
@@ -335,15 +341,35 @@ beginsWithOrderedListMarker str =
335341blockToMarkdown :: WriterOptions -- ^ Options
336342 -> Block -- ^ Block element
337343 -> MD Doc
338−blockToMarkdown _ Null = return empty
339−blockToMarkdown opts (Div attrs ils) = do
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
364+blockToMarkdown' _ Null = return empty
365+blockToMarkdown' opts (Div attrs ils) = do
340366 contents <- blockListToMarkdown opts ils
341367 return $ if isEnabled Ext_raw_html opts &&
342368 isEnabled Ext_markdown_in_html_blocks opts
343369 then tagWithAttrs "div" attrs <> blankline <>
344370 contents <> blankline <> "</div>" <> blankline
345371 else contents <> blankline
346−blockToMarkdown opts (Plain inlines) = do
372+blockToMarkdown' opts (Plain inlines) = do
347373 contents <- inlineListToMarkdown opts inlines
348374 -- escape if para starts with ordered list marker
349375 isPlain <- asks envPlain
@@ -360,11 +386,11 @@ blockToMarkdown opts (Plain inlines) = do
360386 else contents
361387 return $ contents' <> cr
362388-- title beginning with fig: indicates figure
363−blockToMarkdown opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) =
389+blockToMarkdown' opts (Para [Image attr alt (src,'f':'i':'g':':':tit)]) =
364390 blockToMarkdown opts (Para [Image attr alt (src,tit)])
365−blockToMarkdown opts (Para inlines) =
391+blockToMarkdown' opts (Para inlines) =
366392 (<> blankline) `fmap` blockToMarkdown opts (Plain inlines)
367−blockToMarkdown opts (RawBlock f str)
393+blockToMarkdown' opts (RawBlock f str)
368394 | f == "markdown" = return $ text str <> text "\n"
369395 | f == "html" && isEnabled Ext_raw_html opts = do
370396 plain <- asks envPlain
@@ -379,9 +405,25 @@ blockToMarkdown opts (RawBlock f str)
379405 then empty
380406 else text str <> text "\n"
381407 | otherwise = return empty
382−blockToMarkdown opts HorizontalRule = do
408+blockToMarkdown' opts HorizontalRule = do
383409 return $ blankline <> text (replicate (writerColumns opts) '-') <> blankline
384−blockToMarkdown opts (Header level attr inlines) = do
410+blockToMarkdown' opts (Header level attr inlines) = do
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+
385427 plain <- asks envPlain
386428 -- we calculate the id that would be used by auto_identifiers
387429 -- so we know whether to print an explicit identifier
@@ -402,8 +444,7 @@ blockToMarkdown opts (Header level attr inlines) = do
402444 then capitalize inlines
403445 else inlines
404446 let setext = writerSetextHeaders opts
405− return $ nowrap
406− $ case level of
447+ hdr = nowrap $ case level of
407448 1 | plain -> blanklines 3 <> contents <> blanklines 2
408449 | setext ->
409450 contents <> attr' <> cr <> text (replicate (offset contents) '=') <>
@@ -416,11 +457,13 @@ blockToMarkdown opts (Header level attr inlines) = do
416457 _ | plain || isEnabled Ext_literate_haskell opts ->
417458 contents <> blankline
418459 _ -> text (replicate level '#') <> space <> contents <> attr' <> blankline
419−blockToMarkdown opts (CodeBlock (_,classes,_) str)
460+
461+ return $ refs <> hdr
462+blockToMarkdown' opts (CodeBlock (_,classes,_) str)
420463 | "haskell" `elem` classes && "literate" `elem` classes &&
421464 isEnabled Ext_literate_haskell opts =
422465 return $ prefixed "> " (text str) <> blankline
423−blockToMarkdown opts (CodeBlock attribs str) = return $
466+blockToMarkdown' opts (CodeBlock attribs str) = return $
424467 case attribs == nullAttr of
425468 False | isEnabled Ext_backtick_code_blocks opts ->
426469 backticks <> attrs <> cr <> text str <> cr <> backticks <> blankline
@@ -442,7 +485,7 @@ blockToMarkdown opts (CodeBlock attribs str) = return $
442485 else case attribs of
443486 (_,(cls:_),_) -> " " <> text cls
444487 _ -> empty
445−blockToMarkdown opts (BlockQuote blocks) = do
488+blockToMarkdown' opts (BlockQuote blocks) = do
446489 plain <- asks envPlain
447490 -- if we're writing literate haskell, put a space before the bird tracks
448491 -- so they won't be interpreted as lhs...
@@ -451,7 +494,7 @@ blockToMarkdown opts (BlockQuote blocks) = do
451494 else if plain then " " else "> "
452495 contents <- blockListToMarkdown opts blocks
453496 return $ (prefixed leader contents) <> blankline
454−blockToMarkdown opts t@(Table caption aligns widths headers rows) = do
497+blockToMarkdown' opts t@(Table caption aligns widths headers rows) = do
455498 caption' <- inlineListToMarkdown opts caption
456499 let caption'' = if null caption || not (isEnabled Ext_table_captions opts)
457500 then empty
@@ -485,10 +528,10 @@ blockToMarkdown opts t@(Table caption aligns widths headers rows) = do
485528 $ Pandoc nullMeta [t]
486529 | otherwise -> return $ (id, text "[TABLE]")
487530 return $ nst $ tbl $$ blankline $$ caption'' $$ blankline
488−blockToMarkdown opts (BulletList items) = do
531+blockToMarkdown' opts (BulletList items) = do
489532 contents <- inList $ mapM (bulletListItemToMarkdown opts) items
490533 return $ cat contents <> blankline
491−blockToMarkdown opts (OrderedList (start,sty,delim) items) = do
534+blockToMarkdown' opts (OrderedList (start,sty,delim) items) = do
492535 let start' = if isEnabled Ext_startnum opts then start else 1
493536 let sty' = if isEnabled Ext_fancy_lists opts then sty else DefaultStyle
494537 let delim' = if isEnabled Ext_fancy_lists opts then delim else DefaultDelim
@@ -501,7 +544,7 @@ blockToMarkdown opts (OrderedList (start,sty,delim) items) = do
501544 mapM (\(item, num) -> orderedListItemToMarkdown opts item num) $
502545 zip markers' items
503546 return $ cat contents <> blankline
504−blockToMarkdown opts (DefinitionList items) = do
547+blockToMarkdown' opts (DefinitionList items) = do
505548 contents <- inList $ mapM (definitionListItemToMarkdown opts) items
506549 return $ cat contents <> blankline
507550
@@ -1009,7 +1052,7 @@ inlineToMarkdown opts img@(Image attr alternate (source, tit))
10091052inlineToMarkdown opts (Note contents) = do
10101053 modify (\st -> st{ stNotes = contents : stNotes st })
10111054 st <- get
1012− let ref = text $ writerIdentifierPrefix opts ++ show (length $ stNotes st)
1055+ let ref = text $ writerIdentifierPrefix opts ++ show (stNoteNum st + (length $ stNotes st) - 1)
10131056 if isEnabled Ext_footnotes opts
10141057 then return $ "[^" <> ref <> "]"
10151058 else return $ "[" <> ref <> "]"