git.iko.soy/typeable/pandoc
on master help sign in
Issue warning for duplicate header identifiers.
2f8f8f0John MacFarlane
src/Text/Pandoc/Logging.hs+10−1
@@ -62,6 +62,7 @@ data LogMessage =
62 | CouldNotParseYamlMetadata String SourcePos
62 | CouldNotParseYamlMetadata String SourcePos
63 | DuplicateLinkReference String SourcePos
63 | DuplicateLinkReference String SourcePos
64 | DuplicateNoteReference String SourcePos
64 | DuplicateNoteReference String SourcePos
65+ | DuplicateIdentifier String SourcePos
65 | ReferenceNotFound String SourcePos
66 | ReferenceNotFound String SourcePos
66 | CircularReference String SourcePos
67 | CircularReference String SourcePos
67 | ParsingUnescaped String SourcePos
68 | ParsingUnescaped String SourcePos
@@ -106,6 +107,11 @@ instance ToJSON LogMessage where
106 "source" .= Text.pack (sourceName pos),
107 "source" .= Text.pack (sourceName pos),
107 "line" .= toJSON (sourceLine pos),
108 "line" .= toJSON (sourceLine pos),
108 "column" .= toJSON (sourceColumn pos)]
109 "column" .= toJSON (sourceColumn pos)]
110+ DuplicateIdentifier s pos ->
111+ ["contents" .= Text.pack s,
112+ "source" .= Text.pack (sourceName pos),
113+ "line" .= toJSON (sourceLine pos),
114+ "column" .= toJSON (sourceColumn pos)]
109 ReferenceNotFound s pos ->
115 ReferenceNotFound s pos ->
110 ["contents" .= Text.pack s,
116 ["contents" .= Text.pack s,
111 "source" .= Text.pack (sourceName pos),
117 "source" .= Text.pack (sourceName pos),
@@ -184,6 +190,8 @@ showLogMessage msg =
184 "Duplicate link reference '" ++ s ++ "' at " ++ showPos pos
190 "Duplicate link reference '" ++ s ++ "' at " ++ showPos pos
185 DuplicateNoteReference s pos ->
191 DuplicateNoteReference s pos ->
186 "Duplicate note reference '" ++ s ++ "' at " ++ showPos pos
192 "Duplicate note reference '" ++ s ++ "' at " ++ showPos pos
193+ DuplicateIdentifier s pos ->
194+ "Duplicate identifier '" ++ s ++ "' at " ++ showPos pos
187 ReferenceNotFound s pos ->
195 ReferenceNotFound s pos ->
188 "Reference not found for '" ++ s ++ "' at " ++ showPos pos
196 "Reference not found for '" ++ s ++ "' at " ++ showPos pos
189 CircularReference s pos ->
197 CircularReference s pos ->
@@ -233,6 +241,7 @@ messageVerbosity msg =
233 CouldNotParseYamlMetadata{} -> WARNING
241 CouldNotParseYamlMetadata{} -> WARNING
234 DuplicateLinkReference{} -> WARNING
242 DuplicateLinkReference{} -> WARNING
235 DuplicateNoteReference{} -> WARNING
243 DuplicateNoteReference{} -> WARNING
244+ DuplicateIdentifier{} -> WARNING
236 ReferenceNotFound{} -> WARNING
245 ReferenceNotFound{} -> WARNING
237 CircularReference{} -> WARNING
246 CircularReference{} -> WARNING
238 CouldNotLoadIncludeFile{} -> WARNING
247 CouldNotLoadIncludeFile{} -> WARNING
@@ -249,4 +258,4 @@ messageVerbosity msg =
249 CouldNotParseCSS{} -> WARNING
258 CouldNotParseCSS{} -> WARNING
250 Fetching{} -> INFO
259 Fetching{} -> INFO
251 NoTitleElement{} -> WARNING
260 NoTitleElement{} -> WARNING
252− NoLangSpecified -> INFO
261+ NoLangSpecified -> INFO
src/Text/Pandoc/Parsing.hs+8−2
@@ -1112,8 +1112,11 @@ type SubstTable = M.Map Key Inlines
1112-- with its associated identifier. If the identifier is null
1112-- with its associated identifier. If the identifier is null
1113-- and the auto_identifers extension is set, generate a new
1113-- and the auto_identifers extension is set, generate a new
1114-- unique identifier, and update the list of identifiers
1114-- unique identifier, and update the list of identifiers
1115−-- in state.
1115+-- in state. Issue a warning if an explicit identifier
1116+-- is encountered that duplicates an earlier identifier
1117+-- (explict or automatically generated).
1116−registerHeader :: (Stream s m a, HasReaderOptions st, HasHeaderMap st, HasIdentifierList st)
1118+registerHeader :: (Stream s m a, HasReaderOptions st,
1119+ HasHeaderMap st, HasLogMessages st, HasIdentifierList st)
1117 => Attr -> Inlines -> ParserT s st m Attr
1120 => Attr -> Inlines -> ParserT s st m Attr
1118registerHeader (ident,classes,kvs) header' = do
1121registerHeader (ident,classes,kvs) header' = do
1119 ids <- extractIdentifierList <$> getState
1122 ids <- extractIdentifierList <$> getState
@@ -1131,6 +1134,9 @@ registerHeader (ident,classes,kvs) header' = do
1131 return (id'',classes,kvs)
1134 return (id'',classes,kvs)
1132 else do
1135 else do
1133 unless (null ident) $ do
1136 unless (null ident) $ do
1137+ when (ident `Set.member` ids) $ do
1138+ pos <- getPosition
1139+ logMessage $ DuplicateIdentifier ident pos
1134 updateState $ updateIdentifierList $ Set.insert ident
1140 updateState $ updateIdentifierList $ Set.insert ident
1135 updateState $ updateHeaderMap $ insert' header' ident
1141 updateState $ updateHeaderMap $ insert' header' ident
1136 return (ident,classes,kvs)
1142 return (ident,classes,kvs)
src/Text/Pandoc/Readers/HTML.hs+11−5
@@ -83,14 +83,15 @@ readHtml opts inp = do
83 blocks <- (fixPlains False) . mconcat <$> manyTill block eof
83 blocks <- (fixPlains False) . mconcat <$> manyTill block eof
84 meta <- stateMeta . parserState <$> getState
84 meta <- stateMeta . parserState <$> getState
85 bs' <- replaceNotes (B.toList blocks)
85 bs' <- replaceNotes (B.toList blocks)
86+ reportLogMessages
86 return $ Pandoc meta bs'
87 return $ Pandoc meta bs'
87 getError (errorMessages -> ms) = case ms of
88 getError (errorMessages -> ms) = case ms of
88 [] -> ""
89 [] -> ""
89 (m:_) -> messageString m
90 (m:_) -> messageString m
90 result <- flip runReaderT def $
91 result <- flip runReaderT def $
91− runParserT parseDoc
92+ runParserT parseDoc
92− (HTMLState def{ stateOptions = opts } [] Nothing Set.empty M.empty)
93+ (HTMLState def{ stateOptions = opts } [] Nothing Set.empty M.empty [])
93− "source" tags
94+ "source" tags
94 case result of
95 case result of
95 Right doc -> return doc
96 Right doc -> return doc
96 Left err -> throwError $ PandocParseError $ getError err
97 Left err -> throwError $ PandocParseError $ getError err
@@ -110,7 +111,8 @@ data HTMLState =
110 noteTable :: [(String, Blocks)],
111 noteTable :: [(String, Blocks)],
111 baseHref :: Maybe URI,
112 baseHref :: Maybe URI,
112 identifiers :: Set.Set String,
113 identifiers :: Set.Set String,
113− headerMap :: M.Map Inlines String
114+ headerMap :: M.Map Inlines String,
115+ logMessages :: [LogMessage]
114 }
116 }
115
117
116data HTMLLocal = HTMLLocal { quoteContext :: QuoteContext
118data HTMLLocal = HTMLLocal { quoteContext :: QuoteContext
@@ -376,7 +378,7 @@ ignore raw = do
376 -- raw can be null for tags like <!DOCTYPE>; see paRawTag
378 -- raw can be null for tags like <!DOCTYPE>; see paRawTag
377 -- in this case we don't want a warning:
379 -- in this case we don't want a warning:
378 unless (null raw) $
380 unless (null raw) $
379− report $ SkippedContent raw pos
381+ logMessage $ SkippedContent raw pos
380 return mempty
382 return mempty
381
383
382pHtmlBlock :: PandocMonad m => String -> TagParser m String
384pHtmlBlock :: PandocMonad m => String -> TagParser m String
@@ -1092,6 +1094,10 @@ instance HasHeaderMap HTMLState where
1092 extractHeaderMap = headerMap
1094 extractHeaderMap = headerMap
1093 updateHeaderMap f s = s{ headerMap = f (headerMap s) }
1095 updateHeaderMap f s = s{ headerMap = f (headerMap s) }
1094
1096
1097+instance HasLogMessages HTMLState where
1098+ addLogMessage m s = s{ logMessages = m : logMessages s }
1099+ getLogMessages = reverse . logMessages
1100+
1095-- This signature should be more general
1101-- This signature should be more general
1096-- MonadReader HTMLLocal m => HasQuoteContext st m
1102-- MonadReader HTMLLocal m => HasQuoteContext st m
1097instance PandocMonad m => HasQuoteContext HTMLState (ReaderT HTMLLocal m) where
1103instance PandocMonad m => HasQuoteContext HTMLState (ReaderT HTMLLocal m) where
src/Text/Pandoc/Readers/MediaWiki.hs+7
@@ -73,6 +73,7 @@ readMediaWiki opts s = do
73 , mwCategoryLinks = []
73 , mwCategoryLinks = []
74 , mwHeaderMap = M.empty
74 , mwHeaderMap = M.empty
75 , mwIdentifierList = Set.empty
75 , mwIdentifierList = Set.empty
76+ , mwLogMessages = []
76 }
77 }
77 (s ++ "\n")
78 (s ++ "\n")
78 case parsed of
79 case parsed of
@@ -85,6 +86,7 @@ data MWState = MWState { mwOptions :: ReaderOptions
85 , mwCategoryLinks :: [Inlines]
86 , mwCategoryLinks :: [Inlines]
86 , mwHeaderMap :: M.Map Inlines String
87 , mwHeaderMap :: M.Map Inlines String
87 , mwIdentifierList :: Set.Set String
88 , mwIdentifierList :: Set.Set String
89+ , mwLogMessages :: [LogMessage]
88 }
90 }
89
91
90type MWParser m = ParserT [Char] MWState m
92type MWParser m = ParserT [Char] MWState m
@@ -100,6 +102,10 @@ instance HasIdentifierList MWState where
100 extractIdentifierList = mwIdentifierList
102 extractIdentifierList = mwIdentifierList
101 updateIdentifierList f st = st{ mwIdentifierList = f $ mwIdentifierList st }
103 updateIdentifierList f st = st{ mwIdentifierList = f $ mwIdentifierList st }
102
104
105+instance HasLogMessages MWState where
106+ addLogMessage m s = s{ mwLogMessages = m : mwLogMessages s }
107+ getLogMessages = reverse . mwLogMessages
108+
103--
109--
104-- auxiliary functions
110-- auxiliary functions
105--
111--
@@ -187,6 +193,7 @@ parseMediaWiki = do
187 let categories = if null categoryLinks
193 let categories = if null categoryLinks
188 then mempty
194 then mempty
189 else B.para $ mconcat $ intersperse B.space categoryLinks
195 else B.para $ mconcat $ intersperse B.space categoryLinks
196+ reportLogMessages
190 return $ B.doc $ bs <> categories
197 return $ B.doc $ bs <> categories
191
198
192--
199--
src/Text/Pandoc/Readers/Org.hs+2
@@ -35,6 +35,7 @@ import Text.Pandoc.Class (PandocMonad)
35import Text.Pandoc.Definition
35import Text.Pandoc.Definition
36import Text.Pandoc.Error
36import Text.Pandoc.Error
37import Text.Pandoc.Options
37import Text.Pandoc.Options
38+import Text.Pandoc.Parsing (reportLogMessages)
38
39
39import Control.Monad.Except (throwError)
40import Control.Monad.Except (throwError)
40import Control.Monad.Reader (runReaderT)
41import Control.Monad.Reader (runReaderT)
@@ -59,4 +60,5 @@ parseOrg :: PandocMonad m => OrgParser m Pandoc
59parseOrg = do
60parseOrg = do
60 blocks' <- blockList
61 blocks' <- blockList
61 meta' <- meta
62 meta' <- meta
63+ reportLogMessages
62 return $ Pandoc meta' blocks'
64 return $ Pandoc meta' blocks'
src/Text/Pandoc/Readers/Org/ParserState.hs+8
@@ -60,7 +60,9 @@ import qualified Data.Set as Set
60import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
60import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
61import Text.Pandoc.Definition (Meta (..), nullMeta)
61import Text.Pandoc.Definition (Meta (..), nullMeta)
62import Text.Pandoc.Options (ReaderOptions (..))
62import Text.Pandoc.Options (ReaderOptions (..))
63+import Text.Pandoc.Logging
63import Text.Pandoc.Parsing (HasHeaderMap (..), HasIdentifierList (..),
64import Text.Pandoc.Parsing (HasHeaderMap (..), HasIdentifierList (..),
65+ HasLogMessages (..),
64 HasLastStrPosition (..), HasQuoteContext (..),
66 HasLastStrPosition (..), HasQuoteContext (..),
65 HasReaderOptions (..), ParserContext (..),
67 HasReaderOptions (..), ParserContext (..),
66 QuoteContext (..), SourcePos)
68 QuoteContext (..), SourcePos)
@@ -104,6 +106,7 @@ data OrgParserState = OrgParserState
104 , orgStateOptions :: ReaderOptions
106 , orgStateOptions :: ReaderOptions
105 , orgStateParserContext :: ParserContext
107 , orgStateParserContext :: ParserContext
106 , orgStateTodoSequences :: [TodoSequence]
108 , orgStateTodoSequences :: [TodoSequence]
109+ , orgLogMessages :: [LogMessage]
107 }
110 }
108
111
109data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext }
112data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext }
@@ -130,6 +133,10 @@ instance HasHeaderMap OrgParserState where
130 extractHeaderMap = orgStateHeaderMap
133 extractHeaderMap = orgStateHeaderMap
131 updateHeaderMap f s = s{ orgStateHeaderMap = f (orgStateHeaderMap s) }
134 updateHeaderMap f s = s{ orgStateHeaderMap = f (orgStateHeaderMap s) }
132
135
136+instance HasLogMessages OrgParserState where
137+ addLogMessage msg st = st{ orgLogMessages = msg : orgLogMessages st }
138+ getLogMessages st = reverse $ orgLogMessages st
139+
133instance Default OrgParserState where
140instance Default OrgParserState where
134 def = defaultOrgParserState
141 def = defaultOrgParserState
135
142
@@ -150,6 +157,7 @@ defaultOrgParserState = OrgParserState
150 , orgStateOptions = def
157 , orgStateOptions = def
151 , orgStateParserContext = NullState
158 , orgStateParserContext = NullState
152 , orgStateTodoSequences = []
159 , orgStateTodoSequences = []
160+ , orgLogMessages = []
153 }
161 }
154
162
155optionsToParserState :: ReaderOptions -> OrgParserState
163optionsToParserState :: ReaderOptions -> OrgParserState