git.iko.soy/typeable/pandoc
on master help sign in
Markdown reader: warn for notes defined but not used.
8f2c803John MacFarlane
src/Text/Pandoc/Logging.hs+10
@@ -72,6 +72,7 @@ data LogMessage =
72 | CouldNotParseYamlMetadata String SourcePos
72 | CouldNotParseYamlMetadata String SourcePos
73 | DuplicateLinkReference String SourcePos
73 | DuplicateLinkReference String SourcePos
74 | DuplicateNoteReference String SourcePos
74 | DuplicateNoteReference String SourcePos
75+ | NoteDefinedButNotUsed String SourcePos
75 | DuplicateIdentifier String SourcePos
76 | DuplicateIdentifier String SourcePos
76 | ReferenceNotFound String SourcePos
77 | ReferenceNotFound String SourcePos
77 | CircularReference String SourcePos
78 | CircularReference String SourcePos
@@ -113,6 +114,11 @@ instance ToJSON LogMessage where
113 "source" .= Text.pack (sourceName pos),
114 "source" .= Text.pack (sourceName pos),
114 "line" .= toJSON (sourceLine pos),
115 "line" .= toJSON (sourceLine pos),
115 "column" .= toJSON (sourceColumn pos)]
116 "column" .= toJSON (sourceColumn pos)]
117+ NoteDefinedButNotUsed s pos ->
118+ ["key" .= Text.pack s,
119+ "source" .= Text.pack (sourceName pos),
120+ "line" .= toJSON (sourceLine pos),
121+ "column" .= toJSON (sourceColumn pos)]
116 DuplicateNoteReference s pos ->
122 DuplicateNoteReference s pos ->
117 ["contents" .= Text.pack s,
123 ["contents" .= Text.pack s,
118 "source" .= Text.pack (sourceName pos),
124 "source" .= Text.pack (sourceName pos),
@@ -203,6 +209,9 @@ showLogMessage msg =
203 "Duplicate link reference '" ++ s ++ "' at " ++ showPos pos
209 "Duplicate link reference '" ++ s ++ "' at " ++ showPos pos
204 DuplicateNoteReference s pos ->
210 DuplicateNoteReference s pos ->
205 "Duplicate note reference '" ++ s ++ "' at " ++ showPos pos
211 "Duplicate note reference '" ++ s ++ "' at " ++ showPos pos
212+ NoteDefinedButNotUsed s pos ->
213+ "Note with key '" ++ s ++ "' defined at " ++ showPos pos ++
214+ " but not used."
206 DuplicateIdentifier s pos ->
215 DuplicateIdentifier s pos ->
207 "Duplicate identifier '" ++ s ++ "' at " ++ showPos pos
216 "Duplicate identifier '" ++ s ++ "' at " ++ showPos pos
208 ReferenceNotFound s pos ->
217 ReferenceNotFound s pos ->
@@ -256,6 +265,7 @@ messageVerbosity msg =
256 CouldNotParseYamlMetadata{} -> WARNING
265 CouldNotParseYamlMetadata{} -> WARNING
257 DuplicateLinkReference{} -> WARNING
266 DuplicateLinkReference{} -> WARNING
258 DuplicateNoteReference{} -> WARNING
267 DuplicateNoteReference{} -> WARNING
268+ NoteDefinedButNotUsed{} -> WARNING
259 DuplicateIdentifier{} -> WARNING
269 DuplicateIdentifier{} -> WARNING
260 ReferenceNotFound{} -> WARNING
270 ReferenceNotFound{} -> WARNING
261 CircularReference{} -> WARNING
271 CircularReference{} -> WARNING
src/Text/Pandoc/Parsing.hs+5−2
@@ -983,6 +983,7 @@ data ParserState = ParserState
983 stateSubstitutions :: SubstTable, -- ^ List of substitution references
983 stateSubstitutions :: SubstTable, -- ^ List of substitution references
984 stateNotes :: NoteTable, -- ^ List of notes (raw bodies)
984 stateNotes :: NoteTable, -- ^ List of notes (raw bodies)
985 stateNotes' :: NoteTable', -- ^ List of notes (parsed bodies)
985 stateNotes' :: NoteTable', -- ^ List of notes (parsed bodies)
986+ stateNoteRefs :: Set.Set String, -- ^ List of note references used
986 stateMeta :: Meta, -- ^ Document metadata
987 stateMeta :: Meta, -- ^ Document metadata
987 stateMeta' :: F Meta, -- ^ Document metadata
988 stateMeta' :: F Meta, -- ^ Document metadata
988 stateCitations :: M.Map String String, -- ^ RST-style citations
989 stateCitations :: M.Map String String, -- ^ RST-style citations
@@ -1099,7 +1100,8 @@ defaultParserState =
1099 stateHeaderKeys = M.empty,
1100 stateHeaderKeys = M.empty,
1100 stateSubstitutions = M.empty,
1101 stateSubstitutions = M.empty,
1101 stateNotes = [],
1102 stateNotes = [],
1102− stateNotes' = [],
1103+ stateNotes' = M.empty,
1104+ stateNoteRefs = Set.empty,
1103 stateMeta = nullMeta,
1105 stateMeta = nullMeta,
1104 stateMeta' = return nullMeta,
1106 stateMeta' = return nullMeta,
1105 stateCitations = M.empty,
1107 stateCitations = M.empty,
@@ -1166,7 +1168,8 @@ data QuoteContext
1166
1168
1167type NoteTable = [(String, String)]
1169type NoteTable = [(String, String)]
1168
1170
1171+type NoteTable' = M.Map String (SourcePos, F Blocks)
1169−type NoteTable' = [(String, F Blocks)] -- used in markdown reader
1172+-- used in markdown reader
1170
1173
1171newtype Key = Key String deriving (Show, Read, Eq, Ord)
1174newtype Key = Key String deriving (Show, Read, Eq, Ord)
1172
1175
src/Text/Pandoc/Readers/Markdown.hs+14−6
@@ -362,6 +362,14 @@ parseMarkdown = do
362 optional titleBlock
362 optional titleBlock
363 blocks <- parseBlocks
363 blocks <- parseBlocks
364 st <- getState
364 st <- getState
365+ -- check for notes with no corresponding note references
366+ let notesUsed = stateNoteRefs st
367+ let notesDefined = M.keys (stateNotes' st)
368+ mapM_ (\n -> unless (n `Set.member` notesUsed) $ do
369+ -- lookup to get sourcepos
370+ case M.lookup n (stateNotes' st) of
371+ Just (pos, _) -> report (NoteDefinedButNotUsed n pos)
372+ Nothing -> error "The impossible happened.") notesDefined
365 let doc = runF (do Pandoc _ bs <- B.doc <$> blocks
373 let doc = runF (do Pandoc _ bs <- B.doc <$> blocks
366 meta <- stateMeta' st
374 meta <- stateMeta' st
367 return $ Pandoc meta bs) st
375 return $ Pandoc meta bs) st
@@ -469,12 +477,11 @@ noteBlock = try $ do
469 let raw = unlines (first:rest) ++ "\n"
477 let raw = unlines (first:rest) ++ "\n"
470 optional blanklines
478 optional blanklines
471 parsed <- parseFromString' parseBlocks raw
479 parsed <- parseFromString' parseBlocks raw
472− let newnote = (ref, parsed)
473 oldnotes <- stateNotes' <$> getState
480 oldnotes <- stateNotes' <$> getState
474− case lookup ref oldnotes of
481+ case M.lookup ref oldnotes of
475 Just _ -> logMessage $ DuplicateNoteReference ref pos
482 Just _ -> logMessage $ DuplicateNoteReference ref pos
476 Nothing -> return ()
483 Nothing -> return ()
477− updateState $ \s -> s { stateNotes' = newnote : oldnotes }
484+ updateState $ \s -> s { stateNotes' = M.insert ref (pos, parsed) oldnotes }
478 return mempty
485 return mempty
479
486
480--
487--
@@ -1816,16 +1823,17 @@ note :: PandocMonad m => MarkdownParser m (F Inlines)
1816note = try $ do
1823note = try $ do
1817 guardEnabled Ext_footnotes
1824 guardEnabled Ext_footnotes
1818 ref <- noteMarker
1825 ref <- noteMarker
1826+ updateState $ \st -> st{ stateNoteRefs = Set.insert ref (stateNoteRefs st) }
1819 return $ do
1827 return $ do
1820 notes <- asksF stateNotes'
1828 notes <- asksF stateNotes'
1821− case lookup ref notes of
1829+ case M.lookup ref notes of
1822 Nothing -> return $ B.str $ "[^" ++ ref ++ "]"
1830 Nothing -> return $ B.str $ "[^" ++ ref ++ "]"
1823− Just contents -> do
1831+ Just (_pos, contents) -> do
1824 st <- askF
1832 st <- askF
1825 -- process the note in a context that doesn't resolve
1833 -- process the note in a context that doesn't resolve
1826 -- notes, to avoid infinite looping with notes inside
1834 -- notes, to avoid infinite looping with notes inside
1827 -- notes:
1835 -- notes:
1828− let contents' = runF contents st{ stateNotes' = [] }
1836+ let contents' = runF contents st{ stateNotes' = M.empty }
1829 return $ B.note contents'
1837 return $ B.note contents'
1830
1838
1831inlineNote :: PandocMonad m => MarkdownParser m (F Inlines)
1839inlineNote :: PandocMonad m => MarkdownParser m (F Inlines)