From 575014975e5ede206791c9389f07aeb2af9fe652 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Wed, 15 Feb 2017 17:35:51 +0100 Subject: Fix indirect hyperlink targets. Closes #512. --- src/Text/Pandoc/Readers/RST.hs | 56 +++++++++++++++++++++++++----------------- test/command/512.md | 41 +++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 test/command/512.md diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs index f981dd9ad..3ffe24a84 100644 --- a/src/Text/Pandoc/Readers/RST.hs +++ b/src/Text/Pandoc/Readers/RST.hs @@ -1232,43 +1232,53 @@ explicitLink = try $ do then B.str src else label' -- `link ` is a reference link to _google! - (src',tit,attr) <- case reverse src of - '_':xs -> do - keyTable <- stateKeys <$> getState - let key = toKey $ reverse xs - case M.lookup key keyTable of - Nothing -> do - pos <- getPosition - report $ ReferenceNotFound (show key) pos - return ("","",nullAttr) - Just ((s,t),a) -> return (s,t,a) - _ -> return (src, "", nullAttr) + ((src',tit),attr) <- case reverse src of + '_':xs -> lookupKey [] (toKey (reverse xs)) + _ -> return ((src, ""), nullAttr) return $ B.linkWith attr (escapeURI src') tit label'' referenceLink :: PandocMonad m => RSTParser m Inlines referenceLink = try $ do (label',ref) <- withRaw (quotedReferenceName <|> simpleReferenceName) <* char '_' - state <- getState - let keyTable = stateKeys state let isAnonKey (Key ('_':_)) = True isAnonKey _ = False + state <- getState + let keyTable = stateKeys state key <- option (toKey $ stripTicks ref) $ do char '_' let anonKeys = sort $ filter isAnonKey $ M.keys keyTable - if null anonKeys - then mzero - else return (head anonKeys) - ((src,tit), attr) <- case M.lookup key keyTable of - Nothing -> do - pos <- getPosition - report $ ReferenceNotFound (show key) pos - return (("",""),nullAttr) - Just val -> return val + case anonKeys of + [] -> mzero + (k:_) -> return k + ((src,tit), attr) <- lookupKey [] key -- if anonymous link, remove key so it won't be used again when (isAnonKey key) $ updateState $ \s -> s{ stateKeys = M.delete key keyTable } return $ B.linkWith attr src tit label' +-- We keep a list of oldkeys so we can detect lookup loops. +lookupKey :: PandocMonad m + => [Key] -> Key -> RSTParser m ((String, String), Attr) +lookupKey oldkeys key = do + pos <- getPosition + state <- getState + let keyTable = stateKeys state + case M.lookup key keyTable of + Nothing -> do + let Key key' = key + report $ ReferenceNotFound key' pos + return (("",""),nullAttr) + -- check for keys of the form link_, which need to be resolved: + Just ((u@(_:_),""),_) | last u == '_' -> do + let rawkey = init u + let newkey = toKey rawkey + if newkey `elem` oldkeys + then do + report $ CircularReference rawkey pos + return (("",""),nullAttr) + else lookupKey (key:oldkeys) newkey + Just val -> return val + autoURI :: Monad m => RSTParser m Inlines autoURI = do (orig, src) <- uri @@ -1305,7 +1315,7 @@ note = try $ do case lookup ref notes of Nothing -> do pos <- getPosition - report $ ReferenceNotFound (show ref) pos + report $ ReferenceNotFound ref pos return mempty Just raw -> do -- We temporarily empty the note list while parsing the note, diff --git a/test/command/512.md b/test/command/512.md new file mode 100644 index 000000000..a13c434f6 --- /dev/null +++ b/test/command/512.md @@ -0,0 +1,41 @@ +``` +% pandoc -f rst +`click here`__ or `click here`__ + +.. _link1: http://www.example.com/ +.. _link2: http://johnmacfarlane.net/pandoc/ + +__ link1_ +__ link2_ +^D +

click here or click here

+``` + +Multiple indirection: + +``` +% pandoc -f rst +`click here`__ + +.. _link1: link2_ +.. _link2: http://johnmacfarlane.net/pandoc/ + +__ link1_ +^D +

click here

+``` + +Loop detection: + +``` +% pandoc -f rst +`click here`__ + +.. _link1: link2_ +.. _link2: link1_ + +__ link1_ +^D +

click here

+``` + -- cgit v1.2.3