@@ -28,14 +28,15 @@ import Development.IDE.Spans.Common
28
28
import Development.IDE.Core.RuleTypes
29
29
import System.Directory
30
30
import System.FilePath
31
-
31
+ import qualified Documentation.Haddock as H
32
32
import FastString
33
33
import SrcLoc (RealLocated )
34
34
import GhcMonad
35
35
import Packages
36
36
import Name
37
37
import Language.Haskell.LSP.Types (getUri , filePathToUri )
38
38
import Data.Either
39
+ import Control.Arrow (Arrow ((&&&) ))
39
40
40
41
mkDocMap
41
42
:: GhcMonad m
@@ -47,12 +48,15 @@ mkDocMap
47
48
mkDocMap sources rm hmi deps =
48
49
do mapM_ (`loadDepModule` Nothing ) (reverse deps)
49
50
loadDepModule hmi Nothing
50
- d <- foldrM getDocs M. empty names
51
+ df <- getSessionDynFlags
52
+ nml <- findNameToHaddockModuleLinks df names
53
+
54
+ d <- foldrM (getDocs nml) M. empty names
51
55
k <- foldrM getType M. empty names
52
56
pure $ DKMap d k
53
57
where
54
- getDocs n map = do
55
- doc <- getDocumentationTryGhc mod sources n
58
+ getDocs nml n map = do
59
+ doc <- getDocumentationTryGhc nml mod sources n
56
60
pure $ M. insert n doc map
57
61
getType n map
58
62
| isTcOcc $ occName n = do
@@ -67,13 +71,13 @@ lookupKind :: GhcMonad m => Module -> Name -> m (Maybe Type)
67
71
lookupKind mod =
68
72
fmap (either (const Nothing ) (safeTyThingType =<< )) . catchSrcErrors " span" . lookupName mod
69
73
70
- getDocumentationTryGhc :: GhcMonad m => Module -> [ParsedModule ] -> Name -> m SpanDoc
71
- getDocumentationTryGhc mod deps n = head <$> getDocumentationsTryGhc mod deps [n]
74
+ getDocumentationTryGhc :: GhcMonad m => M. Map Name ( FilePath , ModuleName ) -> Module -> [ParsedModule ] -> Name -> m SpanDoc
75
+ getDocumentationTryGhc nml mod deps n = head <$> getDocumentationsTryGhc nml mod deps [n]
72
76
73
- getDocumentationsTryGhc :: GhcMonad m => Module -> [ParsedModule ] -> [Name ] -> m [SpanDoc ]
77
+ getDocumentationsTryGhc :: GhcMonad m => M. Map Name ( FilePath , ModuleName ) -> Module -> [ParsedModule ] -> [Name ] -> m [SpanDoc ]
74
78
-- Interfaces are only generated for GHC >= 8.6.
75
79
-- In older versions, interface files do not embed Haddocks anyway
76
- getDocumentationsTryGhc mod sources names = do
80
+ getDocumentationsTryGhc nml mod sources names = do
77
81
res <- catchSrcErrors " docs" $ getDocsBatch mod names
78
82
case res of
79
83
Left _ -> mapM mkSpanDocText names
@@ -91,7 +95,8 @@ getDocumentationsTryGhc mod sources names = do
91
95
(docFu, srcFu) <-
92
96
case nameModule_maybe name of
93
97
Just mod -> liftIO $ do
94
- doc <- toFileUriText $ lookupDocHtmlForModule df mod
98
+ -- doc <- toFileUriText $ lookupDocHtmlForModule df mod
99
+ doc <- toFileUriText $ lookupHtmlDocForName (\ pkgDocDir modDocName -> pkgDocDir </> modDocName <.> " html" ) nml name
95
100
src <- toFileUriText $ lookupSrcHtmlForModule df mod
96
101
return (doc, src)
97
102
Nothing -> pure (Nothing , Nothing )
@@ -215,6 +220,51 @@ lookupHtmlForModule mkDocPath df m = do
215
220
-- The file might use "." or "-" as separator
216
221
map (`intercalate` chunks) [" ." , " -" ]
217
222
223
+
224
+ lookupHtmlDocForName :: (FilePath -> FilePath -> FilePath ) -> M. Map Name (FilePath , ModuleName ) -> Name -> IO (Maybe FilePath )
225
+ lookupHtmlDocForName mkDocPath nml n = do
226
+ let mfs = concatMap go dirs
227
+ html <- findM doesFileExist mfs
228
+ -- canonicalize located html to remove /../ indirection which can break some clients
229
+ -- (vscode on Windows at least)
230
+ traverse canonicalizePath html
231
+ where
232
+ go pkgDocDir = map (mkDocPath pkgDocDir) mns
233
+ x = M. lookup n nml
234
+ dirs = fromMaybe [] $ (: [] ) . fst <$> x
235
+ chunks = splitOn " ." $ fromMaybe " " $ (moduleNameString . snd <$> x)
236
+ mns = map (`intercalate` chunks) [" ." , " -" ]
237
+
238
+
239
+ findNameToHaddockModuleLinks :: GhcMonad m => DynFlags -> [Name ] -> m (M. Map Name (FilePath , ModuleName ))
240
+ findNameToHaddockModuleLinks df names = M. fromList . concat <$> mapM findNameUris fins
241
+ where
242
+ findNameUris (fi, ns) = do
243
+ -- TODO: resolve mangled html file name and anchor using Haddock api? is it possible?
244
+ -- TODO: fall back to old guesswork solution if haddock doesn't work ?
245
+ -- TODO : clean up / ugly
246
+
247
+ let dir = takeDirectory fi
248
+ exists <- liftIO $ doesFileExist fi
249
+ if exists
250
+ then do
251
+ ioe <- H. readInterfaceFile H. nameCacheFromGhc fi False
252
+ case ioe of
253
+ Left _ -> return []
254
+ Right (i :: H. InterfaceFile ) ->
255
+ let le = H. ifLinkEnv i
256
+ in return $ catMaybes $ map (\ n -> (n,) . (dir,) . moduleName <$> (M. lookup n le)) ns
257
+ else
258
+ return []
259
+
260
+ nameHaddockInterface_maybe n = do
261
+ m <- nameModule_maybe n
262
+ p <- lookupPackage df $ moduleUnitId m
263
+ i <- listToMaybe $ haddockInterfaces p
264
+ return (i, n)
265
+ fins = map (fst . head &&& map snd ) $ groupOn fst $ catMaybes $ map nameHaddockInterface_maybe names
266
+
267
+
218
268
lookupHtmls :: DynFlags -> UnitId -> Maybe [FilePath ]
219
269
lookupHtmls df ui =
220
270
-- use haddockInterfaces instead of haddockHTMLs: GHC treats haddockHTMLs as URL not path
0 commit comments