Skip to content

Commit 9011ae8

Browse files
committed
M ghcide/src/Development/IDE/Spans/Documentation.hs
1 parent 29a8bc4 commit 9011ae8

File tree

1 file changed

+0
-227
lines changed

1 file changed

+0
-227
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,229 +1,3 @@
1-
<<<<<<< HEAD
2-
{-# LANGUAGE RankNTypes #-}
3-
-- Copyright (c) 2019 The DAML Authors. All rights reserved.
4-
-- SPDX-License-Identifier: Apache-2.0
5-
6-
{-# LANGUAGE CPP #-}
7-
8-
module Development.IDE.Spans.Documentation (
9-
getDocumentation
10-
, getDocumentationTryGhc
11-
, getDocumentationsTryGhc
12-
, DocMap
13-
, mkDocMap
14-
) where
15-
16-
import Control.Monad
17-
import Control.Monad.Extra (findM)
18-
import Control.Monad.IO.Class
19-
import Data.Either
20-
import Data.Foldable
21-
import Data.List.Extra
22-
import qualified Data.Map as M
23-
import Data.Maybe
24-
import qualified Data.Set as S
25-
import qualified Data.Text as T
26-
import Development.IDE.Core.Compile
27-
import Development.IDE.Core.RuleTypes
28-
import Development.IDE.GHC.Compat
29-
import Development.IDE.GHC.Compat.Util
30-
import Development.IDE.GHC.Error
31-
import Development.IDE.Spans.Common
32-
import System.Directory
33-
import System.FilePath
34-
35-
import Language.LSP.Types (filePathToUri, getUri)
36-
37-
mkDocMap
38-
:: HscEnv
39-
-> RefMap a
40-
-> TcGblEnv
41-
-> IO DocAndKindMap
42-
mkDocMap env rm this_mod =
43-
do
44-
#if MIN_VERSION_ghc(9,2,0)
45-
(_ , DeclDocMap this_docs, _) <- extractDocs this_mod
46-
#else
47-
let (_ , DeclDocMap this_docs, _) = extractDocs this_mod
48-
#endif
49-
d <- foldrM getDocs (mkNameEnv $ M.toList $ fmap (`SpanDocString` SpanDocUris Nothing Nothing) this_docs) names
50-
k <- foldrM getType (tcg_type_env this_mod) names
51-
pure $ DKMap d k
52-
where
53-
getDocs n map
54-
| maybe True (mod ==) $ nameModule_maybe n = pure map -- we already have the docs in this_docs, or they do not exist
55-
| otherwise = do
56-
doc <- getDocumentationTryGhc env mod n
57-
pure $ extendNameEnv map n doc
58-
getType n map
59-
| isTcOcc $ occName n = do
60-
kind <- lookupKind env mod n
61-
pure $ maybe map (extendNameEnv map n) kind
62-
| otherwise = pure map
63-
names = rights $ S.toList idents
64-
idents = M.keysSet rm
65-
mod = tcg_mod this_mod
66-
67-
lookupKind :: HscEnv -> Module -> Name -> IO (Maybe TyThing)
68-
lookupKind env mod =
69-
fmap (fromRight Nothing) . catchSrcErrors (hsc_dflags env) "span" . lookupName env mod
70-
71-
getDocumentationTryGhc :: HscEnv -> Module -> Name -> IO SpanDoc
72-
getDocumentationTryGhc env mod n = head <$> getDocumentationsTryGhc env mod [n]
73-
74-
getDocumentationsTryGhc :: HscEnv -> Module -> [Name] -> IO [SpanDoc]
75-
getDocumentationsTryGhc env mod names = do
76-
res <- catchSrcErrors (hsc_dflags env) "docs" $ getDocsBatch env mod names
77-
case res of
78-
Left _ -> return []
79-
Right res -> zipWithM unwrap res names
80-
where
81-
unwrap (Right (Just docs, _)) n = SpanDocString docs <$> getUris n
82-
unwrap _ n = mkSpanDocText n
83-
84-
mkSpanDocText name =
85-
SpanDocText [] <$> getUris name
86-
87-
-- Get the uris to the documentation and source html pages if they exist
88-
getUris name = do
89-
(docFu, srcFu) <-
90-
case nameModule_maybe name of
91-
Just mod -> liftIO $ do
92-
doc <- toFileUriText $ lookupDocHtmlForModule env mod
93-
src <- toFileUriText $ lookupSrcHtmlForModule env mod
94-
return (doc, src)
95-
Nothing -> pure (Nothing, Nothing)
96-
let docUri = (<> "#" <> selector <> showNameWithoutUniques name) <$> docFu
97-
srcUri = (<> "#" <> showNameWithoutUniques name) <$> srcFu
98-
selector
99-
| isValName name = "v:"
100-
| otherwise = "t:"
101-
return $ SpanDocUris docUri srcUri
102-
103-
toFileUriText = (fmap . fmap) (getUri . filePathToUri)
104-
105-
getDocumentation
106-
:: HasSrcSpan name
107-
=> [ParsedModule] -- ^ All of the possible modules it could be defined in.
108-
-> name -- ^ The name you want documentation for.
109-
-> [T.Text]
110-
-- This finds any documentation between the name you want
111-
-- documentation for and the one before it. This is only an
112-
-- approximately correct algorithm and there are easily constructed
113-
-- cases where it will be wrong (if so then usually slightly but there
114-
-- may be edge cases where it is very wrong).
115-
-- TODO : Build a version of GHC exactprint to extract this information
116-
-- more accurately.
117-
-- TODO : Implement this for GHC 9.2 with in-tree annotations
118-
-- (alternatively, just remove it and rely soley on GHC's parsing)
119-
getDocumentation sources targetName = fromMaybe [] $ do
120-
#if MIN_VERSION_ghc(9,2,0)
121-
Nothing
122-
#else
123-
-- Find the module the target is defined in.
124-
targetNameSpan <- realSpan $ getLoc targetName
125-
tc <-
126-
find ((==) (Just $ srcSpanFile targetNameSpan) . annotationFileName)
127-
$ reverse sources -- TODO : Is reversing the list here really neccessary?
128-
129-
-- Top level names bound by the module
130-
let bs = [ n | let L _ HsModule{hsmodDecls} = pm_parsed_source tc
131-
, L _ (ValD _ hsbind) <- hsmodDecls
132-
, Just n <- [name_of_bind hsbind]
133-
]
134-
-- Sort the names' source spans.
135-
let sortedSpans = sortedNameSpans bs
136-
-- Now go ahead and extract the docs.
137-
let docs = ann tc
138-
nameInd <- elemIndex targetNameSpan sortedSpans
139-
let prevNameSpan =
140-
if nameInd >= 1
141-
then sortedSpans !! (nameInd - 1)
142-
else zeroSpan $ srcSpanFile targetNameSpan
143-
-- Annoyingly "-- |" documentation isn't annotated with a location,
144-
-- so you have to pull it out from the elements.
145-
pure
146-
$ docHeaders
147-
$ filter (\(L target _) -> isBetween target prevNameSpan targetNameSpan)
148-
$ fold
149-
docs
150-
where
151-
-- Get the name bound by a binding. We only concern ourselves with
152-
-- @FunBind@ (which covers functions and variables).
153-
name_of_bind :: HsBind GhcPs -> Maybe (Located RdrName)
154-
name_of_bind FunBind {fun_id} = Just fun_id
155-
name_of_bind _ = Nothing
156-
-- Get source spans from names, discard unhelpful spans, remove
157-
-- duplicates and sort.
158-
sortedNameSpans :: [Located RdrName] -> [RealSrcSpan]
159-
sortedNameSpans ls = nubSort (mapMaybe (realSpan . getLoc) ls)
160-
isBetween target before after = before <= target && target <= after
161-
#if MIN_VERSION_ghc(9,0,0)
162-
ann = apiAnnComments . pm_annotations
163-
#else
164-
ann = fmap filterReal . snd . pm_annotations
165-
filterReal :: [Located a] -> [RealLocated a]
166-
filterReal = mapMaybe (\(L l v) -> (`L`v) <$> realSpan l)
167-
#endif
168-
annotationFileName :: ParsedModule -> Maybe FastString
169-
annotationFileName = fmap srcSpanFile . listToMaybe . map getRealSrcSpan . fold . ann
170-
171-
-- | Shows this part of the documentation
172-
docHeaders :: [RealLocated AnnotationComment]
173-
-> [T.Text]
174-
docHeaders = mapMaybe (\(L _ x) -> wrk x)
175-
where
176-
wrk = \case
177-
-- When `Opt_Haddock` is enabled.
178-
AnnDocCommentNext s -> Just $ T.pack s
179-
-- When `Opt_KeepRawTokenStream` enabled.
180-
AnnLineComment s -> if "-- |" `isPrefixOf` s
181-
then Just $ T.pack s
182-
else Nothing
183-
_ -> Nothing
184-
#endif
185-
186-
-- These are taken from haskell-ide-engine's Haddock plugin
187-
188-
-- | Given a module finds the local @doc/html/Foo-Bar-Baz.html@ page.
189-
-- An example for a cabal installed module:
190-
-- @~/.cabal/store/ghc-8.10.1/vctr-0.12.1.2-98e2e861/share/doc/html/Data-Vector-Primitive.html@
191-
lookupDocHtmlForModule :: HscEnv -> Module -> IO (Maybe FilePath)
192-
lookupDocHtmlForModule =
193-
lookupHtmlForModule (\pkgDocDir modDocName -> pkgDocDir </> modDocName <.> "html")
194-
195-
-- | Given a module finds the hyperlinked source @doc/html/src/Foo.Bar.Baz.html@ page.
196-
-- An example for a cabal installed module:
197-
-- @~/.cabal/store/ghc-8.10.1/vctr-0.12.1.2-98e2e861/share/doc/html/src/Data.Vector.Primitive.html@
198-
lookupSrcHtmlForModule :: HscEnv -> Module -> IO (Maybe FilePath)
199-
lookupSrcHtmlForModule =
200-
lookupHtmlForModule (\pkgDocDir modDocName -> pkgDocDir </> "src" </> modDocName <.> "html")
201-
202-
lookupHtmlForModule :: (FilePath -> FilePath -> FilePath) -> HscEnv -> Module -> IO (Maybe FilePath)
203-
lookupHtmlForModule mkDocPath hscEnv m = do
204-
-- try all directories
205-
let mfs = fmap (concatMap go) (lookupHtmls hscEnv ui)
206-
html <- findM doesFileExist (concat . maybeToList $ mfs)
207-
-- canonicalize located html to remove /../ indirection which can break some clients
208-
-- (vscode on Windows at least)
209-
traverse canonicalizePath html
210-
where
211-
go pkgDocDir = map (mkDocPath pkgDocDir) mns
212-
ui = moduleUnit m
213-
-- try to locate html file from most to least specific name e.g.
214-
-- first Language.LSP.Types.Uri.html and Language-Haskell-LSP-Types-Uri.html
215-
-- then Language.LSP.Types.html and Language-Haskell-LSP-Types.html etc.
216-
mns = do
217-
chunks <- (reverse . drop1 . inits . splitOn ".") $ (moduleNameString . moduleName) m
218-
-- The file might use "." or "-" as separator
219-
map (`intercalate` chunks) [".", "-"]
220-
221-
lookupHtmls :: HscEnv -> Unit -> Maybe [FilePath]
222-
lookupHtmls df ui =
223-
-- use haddockInterfaces instead of haddockHTMLs: GHC treats haddockHTMLs as URL not path
224-
-- and therefore doesn't expand $topdir on Windows
225-
map takeDirectory . unitHaddockInterfaces <$> lookupUnit df ui
226-
=======
2271
{-# LANGUAGE RankNTypes #-}
2282
-- Copyright (c) 2019 The DAML Authors. All rights reserved.
2293
-- SPDX-License-Identifier: Apache-2.0
@@ -448,4 +222,3 @@ lookupHtmls df ui =
448222
-- use haddockInterfaces instead of haddockHTMLs: GHC treats haddockHTMLs as URL not path
449223
-- and therefore doesn't expand $topdir on Windows
450224
map takeDirectory . unitHaddockInterfaces <$> lookupUnit df ui
451-
>>>>>>> bab90cc4 (ghcide: Core.Compile: getDocsBatch: return (Name,))

0 commit comments

Comments
 (0)