diff --git a/CHANGES b/CHANGES index 1fe0249b594a8f887d27165839c6eaff570d2519..3deee1fd774143a22c7658378066493b591f49e4 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ Changes in version 2.14.3 * Fix parsing of identifiers with ^ or ⋆ in them (#298) + * Fix anchors (#308) + Changes in version 2.14.2 * Always drop --split-objs GHC flag for performance reasons (#292) diff --git a/doc/haddock.xml b/doc/haddock.xml index 98df2c975160b0d0bdb3709122ab2f589fd9e330..1ed7fb6e8ef4e2d1723131b0b281ff4ac2e6f8ec 100644 --- a/doc/haddock.xml +++ b/doc/haddock.xml @@ -2101,8 +2101,12 @@ This belongs to the list above! <literal>"<replaceable>module</replaceable>#<replaceable>label</replaceable>"</literal> where <replaceable>module</replaceable> is the module name containing the anchor, and <replaceable>label</replaceable> is - the anchor label. The module does not have to be local, it - can be imported via an interface.</para> + the anchor label. The module does not have to be local, it can + be imported via an interface. Please note that in Haddock + versions 2.13.x and earlier, the syntax was + <literal>"<replaceable>module</replaceable>\#<replaceable>label</replaceable>"</literal>. + This is deprecated as of version 2.14.3 and will be removed in + future versions.</para> </section> <section> diff --git a/html-test/ref/Bug308.html b/html-test/ref/Bug308.html new file mode 100644 index 0000000000000000000000000000000000000000..517a1cd9784422e2a8bb4f052c87834654da29e5 --- /dev/null +++ b/html-test/ref/Bug308.html @@ -0,0 +1,111 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" +><head + ><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" + /><title + >Bug308</title + ><link href="ocean.css" rel="stylesheet" type="text/css" title="Ocean" + /><script src="haddock-util.js" type="text/javascript" + ></script + ><script type="text/javascript" + >//<![CDATA[ +window.onload = function () {pageLoad();setSynopsis("mini_Bug308.html");}; +//]]> +</script + ></head + ><body + ><div id="package-header" + ><ul class="links" id="page-menu" + ><li + ><a href="" + >Contents</a + ></li + ><li + ><a href="" + >Index</a + ></li + ></ul + ><p class="caption empty" + > </p + ></div + ><div id="content" + ><div id="module-header" + ><table class="info" + ><tr + ><th + >Safe Haskell</th + ><td + >Safe-Inferred</td + ></tr + ></table + ><p class="caption" + >Bug308</p + ></div + ><div id="synopsis" + ><p id="control.syn" class="caption expander" onclick="toggleSection('syn')" + >Synopsis</p + ><ul id="section.syn" class="hide" onclick="toggleSection('syn')" + ><li class="src short" + ><a href="" + >f</a + > :: ()</li + ><li class="src short" + ><a href="" + >g</a + > :: ()</li + ></ul + ></div + ><div id="interface" + ><h1 + >Documentation</h1 + ><div class="top" + ><p class="src" + ><a name="v:f" class="def" + >f</a + > :: ()</p + ><div class="doc" + ><p + >start<a name="startAnchor" + ></a + > followed by middle<a name="middleAnchor" + ></a + > and end<a name="endAnchor" + ></a + ></p + ></div + ></div + ><div class="top" + ><p class="src" + ><a name="v:g" class="def" + >g</a + > :: ()</p + ><div class="doc" + ><p + >start <a href="" + >Bug308</a + ></p + ><p + >startOldStyle <a href="" + >Bug308</a + ></p + ><p + >middle <a href="" + >Bug308</a + ></p + ><p + >end <a href="" + >Bug308</a + ></p + ></div + ></div + ></div + ></div + ><div id="footer" + ><p + >Produced by <a href="" + >Haddock</a + > version 2.15.0</p + ></div + ></body + ></html +> diff --git a/html-test/ref/Bug308CrossModule.html b/html-test/ref/Bug308CrossModule.html new file mode 100644 index 0000000000000000000000000000000000000000..6a053d3c9a22f443ea399eb84af5b96e64178953 --- /dev/null +++ b/html-test/ref/Bug308CrossModule.html @@ -0,0 +1,91 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" +><head + ><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" + /><title + >Bug308CrossModule</title + ><link href="ocean.css" rel="stylesheet" type="text/css" title="Ocean" + /><script src="haddock-util.js" type="text/javascript" + ></script + ><script type="text/javascript" + >//<![CDATA[ +window.onload = function () {pageLoad();setSynopsis("mini_Bug308CrossModule.html");}; +//]]> +</script + ></head + ><body + ><div id="package-header" + ><ul class="links" id="page-menu" + ><li + ><a href="" + >Contents</a + ></li + ><li + ><a href="" + >Index</a + ></li + ></ul + ><p class="caption empty" + > </p + ></div + ><div id="content" + ><div id="module-header" + ><table class="info" + ><tr + ><th + >Safe Haskell</th + ><td + >Safe-Inferred</td + ></tr + ></table + ><p class="caption" + >Bug308CrossModule</p + ></div + ><div id="synopsis" + ><p id="control.syn" class="caption expander" onclick="toggleSection('syn')" + >Synopsis</p + ><ul id="section.syn" class="hide" onclick="toggleSection('syn')" + ><li class="src short" + ><a href="" + >h</a + > :: ()</li + ></ul + ></div + ><div id="interface" + ><h1 + >Documentation</h1 + ><div class="top" + ><p class="src" + ><a name="v:h" class="def" + >h</a + > :: ()</p + ><div class="doc" + ><p + >start <a href="" + >Bug308</a + ></p + ><p + >startOldStyle <a href="" + >Bug308</a + ></p + ><p + >middle <a href="" + >Bug308</a + ></p + ><p + >end <a href="" + >Bug308</a + ></p + ></div + ></div + ></div + ></div + ><div id="footer" + ><p + >Produced by <a href="" + >Haddock</a + > version 2.15.0</p + ></div + ></body + ></html +> diff --git a/html-test/src/Bug308.hs b/html-test/src/Bug308.hs new file mode 100644 index 0000000000000000000000000000000000000000..3adb37468a9950d0c8f78cd560733a10c840728d --- /dev/null +++ b/html-test/src/Bug308.hs @@ -0,0 +1,21 @@ +-- From 2.14.x onwards we were forgetting to swallow ‘#’ as a special +-- character resulting in broken anchors if they accured +-- mid-paragraph. Here we check that anchors get generated as +-- expected. +module Bug308 where + +-- | start#startAnchor# followed by middle#middleAnchor# and end#endAnchor# +f :: () +f = () + +{-| +start "Bug308#startAnchor" + +startOldStyle "Bug308\#startAnchor" + +middle "Bug308#middleAnchor" + +end "Bug308#middleAnchor" +-} +g :: () +g = () diff --git a/html-test/src/Bug308CrossModule.hs b/html-test/src/Bug308CrossModule.hs new file mode 100644 index 0000000000000000000000000000000000000000..589aa69ed67bb50ad750702be9e5887ab45750c7 --- /dev/null +++ b/html-test/src/Bug308CrossModule.hs @@ -0,0 +1,17 @@ +-- Just like Bug308 module but here we test that referring to anchors +-- from other modules works. +module Bug308CrossModule where + +import Bug308 + +{-| +start "Bug308#startAnchor" + +startOldStyle "Bug308\#startAnchor" + +middle "Bug308#middleAnchor" + +end "Bug308#middleAnchor" +-} +h :: () +h = () diff --git a/src/Haddock/Backends/Xhtml/DocMarkup.hs b/src/Haddock/Backends/Xhtml/DocMarkup.hs index 16d771cac85a6f4f54e1fc38a8a9dce76dde793f..5e27d9b035e7a15104963ba02af5f7ef3693ff0a 100644 --- a/src/Haddock/Backends/Xhtml/DocMarkup.hs +++ b/src/Haddock/Backends/Xhtml/DocMarkup.hs @@ -42,7 +42,12 @@ parHtmlMarkup qual insertAnchors ppId = Markup { markupIdentifier = thecode . ppId insertAnchors, markupIdentifierUnchecked = thecode . ppUncheckedLink qual, markupModule = \m -> let (mdl,ref) = break (=='#') m - in ppModuleRef (mkModuleName mdl) ref, + -- Accomodate for old style + -- foo\#bar anchors + mdl' = case reverse mdl of + '\\':_ -> init mdl + _ -> mdl + in ppModuleRef (mkModuleName mdl') ref, markupWarning = thediv ! [theclass "warning"], markupEmphasis = emphasize, markupBold = strong, diff --git a/src/Haddock/Parser.hs b/src/Haddock/Parser.hs index ece9291f08763c0eca1c1d60242eb7d5a867f589..1372e2cd8561b10aef3d2a42b9f77563cdcb9b18 100644 --- a/src/Haddock/Parser.hs +++ b/src/Haddock/Parser.hs @@ -85,8 +85,11 @@ encodedChar = "&#" *> c <* ";" num = hex <|> decimal hex = ("x" <|> "X") *> hexadecimal +-- | List of characters that we use to delimit any special markup. +-- Once we have checked for any of these and tried to parse the +-- relevant markup, we can assume they are used as regular text. specialChar :: [Char] -specialChar = "_/<@\"&'`" +specialChar = "_/<@\"&'`#" -- | Plain, regular parser for text. Called as one of the last parsers -- to ensure that we have already given a chance to more meaningful parsers @@ -140,7 +143,8 @@ takeWhile1_ = mfilter (not . BS.null) . takeWhile_ -- >>> parseOnly anchor "#Hello world#" -- Right (DocAName "Hello world") anchor :: Parser (Doc a) -anchor = DocAName . decodeUtf8 <$> ("#" *> takeWhile1 (`notElem` "#\n") <* "#") +anchor = DocAName . decodeUtf8 <$> + disallowNewline ("#" *> takeWhile1_ (/= '#') <* "#") -- | Monospaced strings. -- @@ -158,7 +162,8 @@ moduleName = DocModule <$> (char '"' *> modid <* char '"') -- NOTE: According to Haskell 2010 we shouldd actually only -- accept {small | large | digit | ' } here. But as we can't -- match on unicode characters, this is currently not possible. - <*> (decodeUtf8 <$> takeWhile (`notElem` " .&[{}(=*)+]!#|@/;,^?\"\n")) + -- Note that we allow ‘#’ to suport anchors. + <*> (decodeUtf8 <$> takeWhile (`notElem` " .&[{}(=*)+]!|@/;,^?\"\n")) -- | Picture parser, surrounded by \<\< and \>\>. It's possible to specify -- a title for the picture. diff --git a/test/Haddock/ParserSpec.hs b/test/Haddock/ParserSpec.hs index f44b7d0f0eaa9af74bfbe1e53210b3b5e0fa812e..074fd1481c5a114f248aa2c5c5d69ec0ae8f4bbc 100644 --- a/test/Haddock/ParserSpec.hs +++ b/test/Haddock/ParserSpec.hs @@ -185,6 +185,13 @@ spec = before initStaticOpts $ do it "does not accept newlines in anchors" $ do "#foo\nbar#" `shouldParseTo` "#foo\nbar#" + it "accepts anchors mid-paragraph" $ do + "Hello #someAnchor# world!" + `shouldParseTo` "Hello " <> DocAName "someAnchor" <> " world!" + + it "does not accept empty anchors" $ do + "##" `shouldParseTo` "##" + context "when parsing emphasised text" $ do it "emphasises a word on its own" $ do "/foo/" `shouldParseTo` DocEmphasis "foo" @@ -308,6 +315,12 @@ spec = before initStaticOpts $ do it "treats empty module name as regular double quotes" $ do "\"\"" `shouldParseTo` "\"\"" + it "accepts anchor reference syntax as DocModule" $ do + "\"Foo#bar\"" `shouldParseTo` DocModule "Foo#bar" + + it "accepts old anchor reference syntax as DocModule" $ do + "\"Foo\\#bar\"" `shouldParseTo` DocModule "Foo\\#bar" + describe "parseParas" $ do let infix 1 `shouldParseTo` shouldParseTo :: String -> Doc RdrName -> Expectation