@@ -22,6 +22,7 @@ import qualified Data.Map as M
2222import Data.Maybe
2323import qualified Data.Set as S
2424import qualified Data.Text as T
25+ import Data.Version (showVersion )
2526import Development.IDE.Core.Compile
2627import Development.IDE.Core.RuleTypes
2728import Development.IDE.GHC.Compat
@@ -30,7 +31,9 @@ import Development.IDE.GHC.Error
3031import Development.IDE.GHC.Util (printOutputable )
3132import Development.IDE.Spans.Common
3233import GHC.Iface.Ext.Utils (RefMap )
33- import Language.LSP.Protocol.Types (filePathToUri , getUri )
34+ import GHC.Plugins (GenericUnitInfo (unitPackageName ))
35+ import Language.LSP.Protocol.Types (Uri (.. ), filePathToUri ,
36+ getUri )
3437import Prelude hiding (mod )
3538import System.Directory
3639import System.FilePath
@@ -40,8 +43,9 @@ mkDocMap
4043 :: HscEnv
4144 -> RefMap a
4245 -> TcGblEnv
46+ -> Bool
4347 -> IO DocAndTyThingMap
44- mkDocMap env rm this_mod =
48+ mkDocMap env rm this_mod linkToHackage =
4549 do
4650 (Just Docs {docs_decls = UniqMap this_docs, docs_args = UniqMap this_arg_docs}) <- extractDocs (hsc_dflags env) this_mod
4751 d <- foldrM getDocs (fmap (\ (_, x) -> (map hsDocString x) `SpanDocString ` SpanDocUris Nothing Nothing ) this_docs) names
@@ -52,7 +56,7 @@ mkDocMap env rm this_mod =
5256 getDocs n nameMap
5357 | maybe True (mod == ) $ nameModule_maybe n = pure nameMap -- we already have the docs in this_docs, or they do not exist
5458 | otherwise = do
55- (doc, _argDoc) <- getDocumentationTryGhc env n
59+ (doc, _argDoc) <- getDocumentationTryGhc env linkToHackage n
5660 pure $ extendNameEnv nameMap n doc
5761 getType n nameMap
5862 | Nothing <- lookupNameEnv nameMap n
@@ -62,7 +66,7 @@ mkDocMap env rm this_mod =
6266 getArgDocs n nameMap
6367 | maybe True (mod == ) $ nameModule_maybe n = pure nameMap
6468 | otherwise = do
65- (_doc, argDoc) <- getDocumentationTryGhc env n
69+ (_doc, argDoc) <- getDocumentationTryGhc env linkToHackage n
6670 pure $ extendNameEnv nameMap n argDoc
6771 names = rights $ S. toList idents
6872 idents = M. keysSet rm
@@ -72,13 +76,13 @@ lookupKind :: HscEnv -> Name -> IO (Maybe TyThing)
7276lookupKind env =
7377 fmap (fromRight Nothing ) . catchSrcErrors (hsc_dflags env) " span" . lookupName env
7478
75- getDocumentationTryGhc :: HscEnv -> Name -> IO (SpanDoc , IntMap SpanDoc )
76- getDocumentationTryGhc env n =
77- (fromMaybe (emptySpanDoc, mempty ) . listToMaybe <$> getDocumentationsTryGhc env [n])
79+ getDocumentationTryGhc :: HscEnv -> Bool -> Name -> IO (SpanDoc , IntMap SpanDoc )
80+ getDocumentationTryGhc env l2h n =
81+ (fromMaybe (emptySpanDoc, mempty ) . listToMaybe <$> getDocumentationsTryGhc env l2h [n])
7882 `catch` (\ (_ :: IOEnvFailure ) -> pure (emptySpanDoc, mempty ))
7983
80- getDocumentationsTryGhc :: HscEnv -> [Name ] -> IO [(SpanDoc , IntMap SpanDoc )]
81- getDocumentationsTryGhc env names = do
84+ getDocumentationsTryGhc :: HscEnv -> Bool -> [Name ] -> IO [(SpanDoc , IntMap SpanDoc )]
85+ getDocumentationsTryGhc env linkToHackage names = do
8286 resOr <- catchSrcErrors (hsc_dflags env) " docs" $ getDocsBatch env names
8387 case resOr of
8488 Left _ -> return []
@@ -95,18 +99,37 @@ getDocumentationsTryGhc env names = do
9599 (docFu, srcFu) <-
96100 case nameModule_maybe name of
97101 Just mod -> liftIO $ do
98- doc <- toFileUriText $ lookupDocHtmlForModule env mod
99- src <- toFileUriText $ lookupSrcHtmlForModule env mod
100- return (doc, src)
102+ doc <- lookupDocHtmlForModule env mod
103+ src <- lookupSrcHtmlForModule env mod
104+ -- If found, the local files are used as hints for the hackage links, this helps with symbols defined in an internal module but re-exported by another.
105+ if linkToHackage
106+ then return ( toHackageDocUriText env mod (takeFileName <$> doc)
107+ , toHackageSrcUriText env mod (takeFileName <$> src))
108+ else pure (toFileUriText doc, toFileUriText src)
101109 Nothing -> pure (Nothing , Nothing )
110+
102111 let docUri = (<> " #" <> selector <> printOutputable name) <$> docFu
103112 srcUri = (<> " #" <> printOutputable name) <$> srcFu
104113 selector
105114 | isValName name = " v:"
106115 | otherwise = " t:"
107116 return $ SpanDocUris docUri srcUri
108117
109- toFileUriText = (fmap . fmap ) (getUri . filePathToUri)
118+ toFileUriText = fmap (getUri . filePathToUri)
119+ toHackageUriText subdir sep env mod hint = do
120+ ui <- lookupUnit env (moduleUnit mod )
121+ let htmlFile = case hint of
122+ Nothing -> T. intercalate sep (map T. pack $ moduleNameChunks mod ) <> " .html"
123+ Just foundFile -> T. replace " -" sep $ T. pack foundFile
124+ pure $!
125+ mconcat $
126+ [ " http://hackage.haskell.org/package/"
127+ , printOutputable (unitPackageName ui), " -" , T. pack $ showVersion (unitPackageVersion ui), " /"
128+ , subdir , " /"
129+ , htmlFile
130+ ]
131+ toHackageDocUriText mod = toHackageUriText " docs" " -" mod
132+ toHackageSrcUriText mod = toHackageUriText " docs/src" " ." mod
110133
111134getDocumentation
112135 :: HasSrcSpan name
@@ -146,10 +169,13 @@ lookupHtmlForModule mkDocPath hscEnv m = do
146169 -- first Language.LSP.Types.Uri.html and Language-Haskell-LSP-Types-Uri.html
147170 -- then Language.LSP.Types.html and Language-Haskell-LSP-Types.html etc.
148171 mns = do
149- chunks <- (reverse . drop1 . inits . splitOn " . " ) $ (moduleNameString . moduleName) m
172+ chunks <- (reverse . drop1 . inits) $ moduleNameChunks m
150173 -- The file might use "." or "-" as separator
151174 map (`intercalate` chunks) [" ." , " -" ]
152175
176+ moduleNameChunks :: Module -> [String ]
177+ moduleNameChunks m = splitOn " ." $ (moduleNameString . moduleName) m
178+
153179lookupHtmls :: HscEnv -> Unit -> Maybe [FilePath ]
154180lookupHtmls df ui =
155181 -- use haddockInterfaces instead of haddockHTMLs: GHC treats haddockHTMLs as URL not path
0 commit comments