diff --git a/ghcide/src/Development/IDE/Spans/Documentation.hs b/ghcide/src/Development/IDE/Spans/Documentation.hs index 8afe4f72fe..2e4a8691a8 100644 --- a/ghcide/src/Development/IDE/Spans/Documentation.hs +++ b/ghcide/src/Development/IDE/Spans/Documentation.hs @@ -15,6 +15,7 @@ module Development.IDE.Spans.Documentation ( import Control.Monad import Control.Monad.IO.Class import Control.Monad.Extra (findM) +import Data.Char import Data.Either import Data.Foldable import Data.List.Extra @@ -87,7 +88,7 @@ getDocumentationsTryGhc env mod names = do src <- toFileUriText $ lookupSrcHtmlForModule env mod return (doc, src) Nothing -> pure (Nothing, Nothing) - let docUri = (<> "#" <> selector <> showNameWithoutUniques name) <$> docFu + let docUri = (<> "#" <> selector <> makeDocAnchor (showNameWithoutUniques name)) <$> docFu srcUri = (<> "#" <> showNameWithoutUniques name) <$> srcFu selector | isValName name = "v:" @@ -96,6 +97,27 @@ getDocumentationsTryGhc env mod names = do toFileUriText = (fmap . fmap) (getUri . filePathToUri) +-- | Takes an arbitrary string and makes it a valid anchor ID. This is ported +-- from the function @Haddock.Utils.makeAnchorId@ in the @haddock-api@ package, +-- which seems to be the function in haddock that does the original encoding +-- that we reproduce here. +-- +-- Note that this isn't just necessary for operators, it also affects function +-- names like @foldl'@ with apostrophes in them. +makeDocAnchor :: T.Text -> T.Text +makeDocAnchor = T.concatMap docEscapeChar + where + docEscapeChar :: Char -> T.Text + docEscapeChar c + | isLegal c = T.singleton c + | otherwise = T.pack $ '-' : show (ord c) ++ "-" + + isLegal :: Char -> Bool + isLegal ':' = True + isLegal '_' = True + isLegal '.' = True + isLegal c = isAscii c && isAlphaNum c + getDocumentation :: HasSrcSpan name => [ParsedModule] -- ^ All of the possible modules it could be defined in. diff --git a/ghcide/test/data/hover/GotoHover.hs b/ghcide/test/data/hover/GotoHover.hs index ae261c6bdf..84ac3cad3e 100644 --- a/ghcide/test/data/hover/GotoHover.hs +++ b/ghcide/test/data/hover/GotoHover.hs @@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings, TemplateHaskell #-} {- HLINT ignore -} module GotoHover ( module GotoHover) where -import Data.Text (Text, pack) +import Data.Text (Text, pack, foldl') import Foo (Bar, foo) @@ -61,3 +61,6 @@ aa2 = $(id [| True |]) hole :: Int hole = _ + +externalFuncWithPrime :: Char +externalFuncWithPrime = foldl' max 'a' "word" diff --git a/ghcide/test/exe/Main.hs b/ghcide/test/exe/Main.hs index 9e221a8e08..ed96433ae0 100644 --- a/ghcide/test/exe/Main.hs +++ b/ghcide/test/exe/Main.hs @@ -3987,6 +3987,8 @@ findDefinitionAndHoverTests = let innL48 = Position 52 5 ; innSig = [ExpectHoverText ["inner", "Char"], mkR 49 2 49 7] holeL60 = Position 62 7 ; hleInfo = [ExpectHoverText ["_ ::"]] cccL17 = Position 17 16 ; docLink = [ExpectHoverText ["[Documentation](file:///"]] + opL25 = Position 24 18 ; docOpEnc = [ExpectHoverText ["[Documentation](file:///", "#v:-60--62-)"]] + funcL66 = Position 65 26 ; docFuncEnc = [ExpectHoverText ["[Documentation](file:///", "#v:foldl-39-)"]] imported = Position 56 13 ; importedSig = getDocUri "Foo.hs" >>= \foo -> return [ExpectHoverText ["foo", "Foo", "Haddock"], mkL foo 5 0 5 3] reexported = Position 55 14 ; reexportedSig = getDocUri "Bar.hs" >>= \bar -> return [ExpectHoverText ["Bar", "Bar", "Haddock"], mkL bar 3 0 3 14] thLocL57 = Position 59 10 ; thLoc = [ExpectHoverText ["Identity"]] @@ -4041,6 +4043,8 @@ findDefinitionAndHoverTests = let , test broken broken innL48 innSig "inner signature #767" , test no yes holeL60 hleInfo "hole without internal name #831" , test no skip cccL17 docLink "Haddock html links" + , test yes skip opL25 docOpEnc "Haddock html doc link operator #2542" + , test yes skip funcL66 docFuncEnc "Haddock html doc link function #2542" , testM yes yes imported importedSig "Imported symbol" , testM yes yes reexported reexportedSig "Imported symbol (reexported)" , if ghcVersion == GHC90 && isWindows then