Markdown reader: Made implicit header references case-insensitive.

Added `stateHeaderKeys` to `ParserState`; this is a `KeyTable`
like `stateKeys`, but it only gets consulted if we don't find
a match in `stateKeys`, and if `Ext_implicit_header_references`
is enabled.

Closes #1606.
This commit is contained in:
John MacFarlane 2015-05-13 23:02:54 -07:00
parent dc51114320
commit 24ee1ab4f7
6 changed files with 45 additions and 16 deletions

13
README
View file

@ -1269,10 +1269,17 @@ If there are multiple headers with identical text, the corresponding
reference will link to the first one only, and you will need to use explicit
links to link to the others, as described above.
Unlike regular reference links, these references are case-sensitive.
Like regular reference links, these references are case-insensitive.
Note: if you have defined an explicit identifier for a header,
then implicit references to it will not work.
Explicit link reference definitions always take priority over
implicit header references. So, in the following example, the
link will point to `bar`, not to `#foo`:
# Foo
[foo]: bar
See [foo]
Block quotations
----------------

View file

@ -35,6 +35,8 @@ pandoc (1.14)
* Markdown reader:
+ Reference links with `implicit_header_references` are no longer
case-sensitive (#1606).
+ Definition lists: don't require indent for first line (#2087).
Previously the body of the definition (after the `:` or `~` marker)
needed to be in column 4. This commit relaxes that requirement,
@ -439,7 +441,10 @@ pandoc (1.14)
`extra-source-files`. This should make maintenance of these components
easier going forward.
* `Text.Pandoc.Parsing`: Added new `<+?>` combinator (Nikolay Yakimov).
* `Text.Pandoc.Parsing`:
+ Added new `<+?>` combinator (Nikolay Yakimov).
+ Added `stateHeaderKeys` to `ParserState`.
* `make_deb.sh` fixes:

View file

@ -903,7 +903,8 @@ data ParserState = ParserState
stateAllowLinks :: Bool, -- ^ Allow parsing of links
stateMaxNestingLevel :: Int, -- ^ Max # of nested Strong/Emph
stateLastStrPos :: Maybe SourcePos, -- ^ Position after last str parsed
stateKeys :: KeyTable, -- ^ List of reference keys (with fallbacks)
stateKeys :: KeyTable, -- ^ List of reference keys
stateHeaderKeys :: KeyTable, -- ^ List of implicit header ref keys
stateSubstitutions :: SubstTable, -- ^ List of substitution references
stateNotes :: NoteTable, -- ^ List of notes (raw bodies)
stateNotes' :: NoteTable', -- ^ List of notes (parsed bodies)
@ -1001,6 +1002,7 @@ defaultParserState =
stateMaxNestingLevel = 6,
stateLastStrPos = Nothing,
stateKeys = M.empty,
stateHeaderKeys = M.empty,
stateSubstitutions = M.empty,
stateNotes = [],
stateNotes' = [],

View file

@ -509,9 +509,12 @@ atxHeader = try $ do
notFollowedBy $ guardEnabled Ext_fancy_lists >>
(char '.' <|> char ')') -- this would be a list
skipSpaces
text <- trimInlinesF . mconcat <$> many (notFollowedBy atxClosing >> inline)
(text, raw) <- withRaw $
trimInlinesF . mconcat <$> many (notFollowedBy atxClosing >> inline)
attr <- atxClosing
attr' <- registerHeader attr (runF text defaultParserState)
attr'@(ident,_,_) <- registerHeader attr (runF text defaultParserState)
guardDisabled Ext_implicit_header_references
<|> registerImplicitHeader raw ident
return $ B.headerWith attr' level <$> text
atxClosing :: MarkdownParser Attr
@ -544,15 +547,24 @@ setextHeader = try $ do
-- This lookahead prevents us from wasting time parsing Inlines
-- unless necessary -- it gives a significant performance boost.
lookAhead $ anyLine >> many1 (oneOf setextHChars) >> blankline
text <- trimInlinesF . mconcat <$> many1 (notFollowedBy setextHeaderEnd >> inline)
(text, raw) <- withRaw $
trimInlinesF . mconcat <$> many1 (notFollowedBy setextHeaderEnd >> inline)
attr <- setextHeaderEnd
underlineChar <- oneOf setextHChars
many (char underlineChar)
blanklines
let level = (fromMaybe 0 $ findIndex (== underlineChar) setextHChars) + 1
attr' <- registerHeader attr (runF text defaultParserState)
attr'@(ident,_,_) <- registerHeader attr (runF text defaultParserState)
guardDisabled Ext_implicit_header_references
<|> registerImplicitHeader raw ident
return $ B.headerWith attr' level <$> text
registerImplicitHeader :: String -> String -> MarkdownParser ()
registerImplicitHeader raw ident = do
let key = toKey $ "[" ++ raw ++ "]"
updateState (\s -> s { stateHeaderKeys =
M.insert key ('#':ident,"") (stateHeaderKeys s) })
--
-- hrule block
--
@ -1700,7 +1712,7 @@ referenceLink :: (String -> String -> Inlines -> Inlines)
-> (F Inlines, String) -> MarkdownParser (F Inlines)
referenceLink constructor (lab, raw) = do
sp <- (True <$ lookAhead (char ' ')) <|> return False
(ref,raw') <- option (mempty, "") $
(_,raw') <- option (mempty, "") $
lookAhead (try (spnl >> normalCite >> return (mempty, "")))
<|>
try (spnl >> reference)
@ -1720,13 +1732,13 @@ referenceLink constructor (lab, raw) = do
return $ do
keys <- asksF stateKeys
case M.lookup key keys of
Nothing -> do
headers <- asksF stateHeaders
ref' <- if labIsRef then lab else ref
Nothing ->
if implicitHeaderRefs
then case M.lookup ref' headers of
Just ident -> constructor ('#':ident) "" <$> lab
Nothing -> makeFallback
then do
headerKeys <- asksF stateHeaderKeys
case M.lookup key headerKeys of
Just (src, tit) -> constructor src tit <$> lab
Nothing -> makeFallback
else makeFallback
Just (src,tit) -> constructor src tit <$> lab

View file

@ -76,6 +76,7 @@
,Header 3 ("my-other-header",[],[]) [Str "My",Space,Str "other",Space,Str "header"]
,Para [Str "A",Space,Str "link",Space,Str "to",Space,Link [Str "My",Space,Str "header"] ("#my-header-1",""),Str "."]
,Para [Str "Another",Space,Str "link",Space,Str "to",Space,Link [Str "it"] ("#my-header-1",""),Str "."]
,Para [Str "Should",Space,Str "be",Space,Link [Str "case",Space,Str "insensitive"] ("#my-header-1",""),Str "."]
,Para [Str "Link",Space,Str "to",Space,Link [Str "Explicit",Space,Str "header",Space,Str "attributes"] ("#foobar",""),Str "."]
,Para [Str "But",Space,Str "this",Space,Str "is",Space,Str "not",Space,Str "a",Space,Str "link",Space,Str "to",Space,Link [Str "My",Space,Str "other",Space,Str "header"] ("/foo",""),Str ",",Space,Str "since",Space,Str "the",Space,Str "reference",Space,Str "is",Space,Str "defined."]
,Header 2 ("foobar",["baz"],[("key","val")]) [Str "Explicit",Space,Str "header",Space,Str "attributes"]

View file

@ -168,6 +168,8 @@ A link to [My header].
Another link to [it][My header].
Should be [case insensitive][my header].
Link to [Explicit header attributes].
[my other header]: /foo