on master help sign in
Enhancements to hover (#317)

* Show kinds in hover * Documentation on hover * Enable kind tests * Fix tests * Print literals * Show (some) overloaded literals * Fix for 8.4 * Fix tests * Do not consider literals for definitions * Suggestions by @cocreature * No warning for 8.4 * More fixes for 8.4 * Make it work with ghc-lib * More fixes for warnings when compiled with ghc-lib * More fixes to build in ghc-lib * Try once again to build with ghc-lib * More fixes for ghc-lib * Fix warning with ghc-lib

commit6d0b6ea18a8081bff139ef57d8bf44cc7f0ad7da authorAlejandro Serrano <trupill@gmail.com> dateTue 21 Jan 2020, 09:15 (7 years ago) parent2d9314a
11 files changed+301−215
.hlint.yaml+1 ghcide.cabal+1 src/Development/IDE/Core/Completions.hs+4−22 src/Development/IDE/Core/CompletionsTypes.hs+3−7 src/Development/IDE/Core/Rules.hs+4−4 src/Development/IDE/Spans/AtPoint.hs+19−16 src/Development/IDE/Spans/Calculate.hs+83−62 src/Development/IDE/Spans/Common.hs+160 src/Development/IDE/Spans/Documentation.hs+11−95 src/Development/IDE/Spans/Type.hs+9−3 test/exe/Main.hs+6−6
.hlint.yaml+1
@@ -85,6 +85,7 @@
8585 - Development.IDE.LSP.CodeAction
8686 - Development.IDE.Spans.Calculate
8787 - Development.IDE.Spans.Documentation
88+ - Development.IDE.Spans.Common
8889 - Main
8990
9091- flags:
ghcide.cabal+1
@@ -135,6 +135,7 @@ library
135135 Development.IDE.LSP.Outline
136136 Development.IDE.Spans.AtPoint
137137 Development.IDE.Spans.Calculate
138+ Development.IDE.Spans.Common
138139 Development.IDE.Spans.Documentation
139140 Development.IDE.Spans.Type
140141 ghc-options: -Wall -Wno-name-shadowing
src/Development/IDE/Core/Completions.hs+4−22
@@ -25,8 +25,6 @@ import Type
2525import Var
2626import Packages
2727import DynFlags
28−import ConLike
29−import DataCon
3028
3129import Language.Haskell.LSP.Types
3230import Language.Haskell.LSP.Types.Capabilities
@@ -35,25 +33,9 @@ import Development.IDE.Core.CompletionsTypes
3533import Development.IDE.Spans.Documentation
3634import Development.IDE.GHC.Error
3735import Development.IDE.Types.Options
38−
39−#ifndef GHC_LIB
36+import Development.IDE.Spans.Common
4037import Development.IDE.GHC.Util
4138
42−
43−safeTyThingType :: TyThing -> Maybe Type
44−safeTyThingType thing
45− | Just i <- safeTyThingId thing = Just (varType i)
46−safeTyThingType (ATyCon tycon) = Just (tyConKind tycon)
47−safeTyThingType _ = Nothing
48−#endif
49−
50−-- From haskell-ide-engine/src/Haskell/Ide/Engine/Support/HieExtras.hs
51−
52−safeTyThingId :: TyThing -> Maybe Id
53−safeTyThingId (AnId i) = Just i
54−safeTyThingId (AConLike (RealDataCon dc)) = Just $ dataConWrapId dc
55−safeTyThingId _ = Nothing
56−
5739-- From haskell-ide-engine/hie-plugin-api/Haskell/Ide/Engine/Context.hs
5840
5941-- | A context of a declaration in the program
@@ -158,7 +140,7 @@ mkCompl IdeOptions{..} CI{origName,importedFrom,thingType,label,isInfix,docs} =
158140 typeText
159141 | Just t <- thingType = Just . stripForall $ T.pack (showGhc t)
160142 | otherwise = Nothing
161− docs' = ("*Defined in '" <> importedFrom <> "'*\n") : docs
143+ docs' = ("*Defined in '" <> importedFrom <> "'*\n") : spanDocToMarkdown docs
162144 colon = if optNewColonConvention then ": " else ":: "
163145
164146stripForall :: T.Text -> T.Text
@@ -275,12 +257,12 @@ cacheDataProducer packageState dflags tm tcs = do
275257 let typ = Just $ varType var
276258 name = Var.varName var
277259 label = T.pack $ showGhc name
278− docs <- getDocumentationTryGhc packageState (tm:tcs) name
260+ docs <- runGhcEnv packageState $ getDocumentationTryGhc (tm:tcs) name
279261 return $ CI name (showModName curMod) typ label Nothing docs
280262
281263 toCompItem :: ModuleName -> Name -> IO CompItem
282264 toCompItem mn n = do
283− docs <- getDocumentationTryGhc packageState (tm:tcs) n
265+ docs <- runGhcEnv packageState $ getDocumentationTryGhc (tm:tcs) n
284266-- lookupName uses runInteractiveHsc, i.e., GHCi stuff which does not work with GHCi
285267-- and leads to fun errors like "Cannot continue after interface file error".
286268#ifdef GHC_LIB
src/Development/IDE/Core/CompletionsTypes.hs+3−7
@@ -5,15 +5,11 @@ module Development.IDE.Core.CompletionsTypes (
55import Control.DeepSeq
66import qualified Data.Map as Map
77import qualified Data.Text as T
8−
98import GHC
10−import Outputable
11−import DynFlags
129
13−-- From haskell-ide-engine/src/Haskell/Ide/Engine/LSP/Completions.hs
10+import Development.IDE.Spans.Common
1411
15−showGhc :: Outputable a => a -> String
16−showGhc = showPpr unsafeGlobalDynFlags
12+-- From haskell-ide-engine/src/Haskell/Ide/Engine/LSP/Completions.hs
1713
1814data Backtick = Surrounded | LeftSide deriving Show
1915data CompItem = CI
@@ -23,7 +19,7 @@ data CompItem = CI
2319 , label :: T.Text -- ^ Label to display to the user.
2420 , isInfix :: Maybe Backtick -- ^ Did the completion happen
2521 -- in the context of an infix notation.
26− , docs :: [T.Text] -- ^ Available documentation.
22+ , docs :: SpanDoc -- ^ Available documentation.
2723 }
2824instance Show CompItem where
2925 show CI { .. } = "CompItem { origName = \"" ++ showGhc origName ++ "\""
src/Development/IDE/Core/Rules.hs+4−4
@@ -105,9 +105,7 @@ getAtPoint :: NormalizedFilePath -> Position -> Action (Maybe (Maybe Range, [T.T
105105getAtPoint file pos = fmap join $ runMaybeT $ do
106106 opts <- lift getIdeOptions
107107 spans <- useE GetSpanInfo file
108− files <- transitiveModuleDeps <$> useE GetDependencies file
109− tms <- usesE TypeCheck (file : files)
110− return $ AtPoint.atPoint opts (map tmrModule tms) spans pos
108+ return $ AtPoint.atPoint opts spans pos
111109
112110-- | Goto Definition.
113111getDefinition :: NormalizedFilePath -> Position -> Action (Maybe Location)
@@ -263,9 +261,11 @@ getSpanInfoRule :: Rules ()
263261getSpanInfoRule =
264262 define $ \GetSpanInfo file -> do
265263 tc <- use_ TypeCheck file
264+ deps <- maybe (TransitiveDependencies [] []) fst <$> useWithStale GetDependencies file
265+ tms <- mapMaybe (fmap fst) <$> usesWithStale TypeCheck (transitiveModuleDeps deps)
266266 (fileImports, _) <- use_ GetLocatedImports file
267267 packageState <- hscEnv <$> use_ GhcSession file
268− x <- liftIO $ getSrcSpanInfos packageState fileImports tc
268+ x <- liftIO $ getSrcSpanInfos packageState fileImports tc tms
269269 return ([], Just x)
270270
271271-- Typechecks a module.
src/Development/IDE/Spans/AtPoint.hs+19−16
@@ -8,7 +8,6 @@ module Development.IDE.Spans.AtPoint (
88 , gotoDefinition
99 ) where
1010
11−import Development.IDE.Spans.Documentation
1211import Development.IDE.GHC.Error
1312import Development.IDE.GHC.Orphans()
1413import Development.IDE.Types.Location
@@ -18,7 +17,8 @@ import Development.Shake
1817import Development.IDE.GHC.Util
1918import Development.IDE.GHC.Compat
2019import Development.IDE.Types.Options
21−import Development.IDE.Spans.Type as SpanInfo
20+import Development.IDE.Spans.Type as SpanInfo
21+import Development.IDE.Spans.Common (spanDocToMarkdown)
2222
2323-- GHC API imports
2424import Avail
@@ -50,40 +50,42 @@ gotoDefinition getHieFile ideOpts pkgState srcSpans pos =
5050-- | Synopsis for the name at a given position.
5151atPoint
5252 :: IdeOptions
53− -> [TypecheckedModule]
5453 -> [SpanInfo]
5554 -> Position
5655 -> Maybe (Maybe Range, [T.Text])
57−atPoint IdeOptions{..} tcs srcSpans pos = do
56+atPoint IdeOptions{..} srcSpans pos = do
5857 firstSpan <- listToMaybe $ deEmpasizeGeneratedEqShow $ spansAtPoint pos srcSpans
5958 return (Just (range firstSpan), hoverInfo firstSpan)
6059 where
6160 -- Hover info for types, classes, type variables
62− hoverInfo SpanInfo{spaninfoType = Nothing , ..} =
63− documentation <> (wrapLanguageSyntax <$> name <> kind) <> location
61+ hoverInfo SpanInfo{spaninfoType = Nothing , spaninfoDocs = docs , ..} =
62+ (wrapLanguageSyntax <$> name) <> location <> spanDocToMarkdown docs
6463 where
65− documentation = findDocumentation mbName
6664 name = [maybe shouldNotHappen showName mbName]
6765 location = [maybe shouldNotHappen definedAt mbName]
68− kind = [] -- TODO
6966 shouldNotHappen = "ghcide: did not expect a type level component without a name"
7067 mbName = getNameM spaninfoSource
7168
7269 -- Hover info for values/data
73− hoverInfo SpanInfo{spaninfoType = (Just typ), ..} =
74− documentation <> (wrapLanguageSyntax <$> nameOrSource <> typeAnnotation) <> location
70+ hoverInfo SpanInfo{spaninfoType = (Just typ), spaninfoDocs = docs , ..} =
71+ (wrapLanguageSyntax <$> nameOrSource) <> location <> spanDocToMarkdown docs
7572 where
7673 mbName = getNameM spaninfoSource
77− documentation = findDocumentation mbName
78− typeAnnotation = [colon <> showName typ]
79− nameOrSource = [maybe literalSource qualifyNameIfPossible mbName]
80− literalSource = "" -- TODO: literals: display (length-limited) source
74+ typeAnnotation = colon <> showName typ
75+ expr = case spaninfoSource of
76+ Named n -> qualifyNameIfPossible n
77+ Lit l -> crop $ T.pack l
78+ _ -> ""
79+ nameOrSource = [expr <> "\n" <> typeAnnotation]
8180 qualifyNameIfPossible name' = modulePrefix <> showName name'
8281 where modulePrefix = maybe "" (<> ".") (getModuleNameAsText name')
8382 location = [maybe "" definedAt mbName]
8483
85− findDocumentation = maybe [] (getDocumentation tcs)
86− definedAt name = "**Defined " <> T.pack (showSDocUnsafe $ pprNameDefnLoc name) <> "**\n"
84+ definedAt name = "*Defined " <> T.pack (showSDocUnsafe $ pprNameDefnLoc name) <> "*\n"
85+
86+ crop txt
87+ | T.length txt > 50 = T.take 46 txt <> " ..."
88+ | otherwise = txt
8789
8890 range SpanInfo{..} = Range
8991 (Position spaninfoStartLine spaninfoStartCol)
@@ -112,6 +114,7 @@ locationsAtPoint getHieFile IdeOptions{..} pkgState pos =
112114 where getSpan :: SpanSource -> m (Maybe SrcSpan)
113115 getSpan NoSource = pure Nothing
114116 getSpan (SpanS sp) = pure $ Just sp
117+ getSpan (Lit _) = pure Nothing
115118 getSpan (Named name) = case nameSrcSpan name of
116119 sp@(RealSrcSpan _) -> pure $ Just sp
117120 sp@(UnhelpfulSpan _) -> runMaybeT $ do
src/Development/IDE/Spans/Calculate.hs+83−62
@@ -9,13 +9,11 @@
99
1010-- | Get information on modules, identifiers, etc.
1111
12−module Development.IDE.Spans.Calculate(getSrcSpanInfos,listifyAllSpans) where
12+module Development.IDE.Spans.Calculate(getSrcSpanInfos) where
1313
1414import ConLike
1515import Control.Monad
1616import qualified CoreUtils
17−import Data.Data
18−import qualified Data.Generics
1917import Data.List
2018import Data.Maybe
2119import DataCon
@@ -26,14 +24,19 @@ import FastString (mkFastString)
2624import OccName
2725import Development.IDE.Types.Location
2826import Development.IDE.Spans.Type
27+#ifdef GHC_LIB
2928import Development.IDE.GHC.Error (zeroSpan)
29+#else
30+import Development.IDE.GHC.Error (zeroSpan, catchSrcErrors)
31+#endif
3032import Prelude hiding (mod)
3133import TcHsSyn
3234import Var
3335import Development.IDE.Core.Compile
3436import qualified Development.IDE.GHC.Compat as Compat
3537import Development.IDE.GHC.Util
36−
38+import Development.IDE.Spans.Common
39+import Development.IDE.Spans.Documentation
3740
3841-- A lot of things gained an extra X argument in GHC 8.6, which we mostly ignore
3942-- this U ignores that arg in 8.6, but is hidden in 8.4
@@ -48,37 +51,41 @@ getSrcSpanInfos
4851 :: HscEnv
4952 -> [(Located ModuleName, Maybe NormalizedFilePath)]
5053 -> TcModuleResult
54+ -> [TcModuleResult]
5155 -> IO [SpanInfo]
52−getSrcSpanInfos env imports tc =
53− runGhcEnv env
54− . getSpanInfo imports
55− $ tmrModule tc
56+getSrcSpanInfos env imports tc tms =
57+ runGhcEnv env $
58+ getSpanInfo imports (tmrModule tc) (map tmrModule tms)
5659
5760-- | Get ALL source spans in the module.
5861getSpanInfo :: GhcMonad m
5962 => [(Located ModuleName, Maybe NormalizedFilePath)] -- ^ imports
6063 -> TypecheckedModule
64+ -> [TypecheckedModule]
6165 -> m [SpanInfo]
62−getSpanInfo mods tcm =
66+getSpanInfo mods tcm tcms =
6367 do let tcs = tm_typechecked_source tcm
6468 bs = listifyAllSpans tcs :: [LHsBind GhcTc]
6569 es = listifyAllSpans tcs :: [LHsExpr GhcTc]
6670 ps = listifyAllSpans' tcs :: [Pat GhcTc]
6771 ts = listifyAllSpans $ tm_renamed_source tcm :: [LHsType GhcRn]
68− let funBinds = funBindMap $ tm_parsed_module tcm
69− bts <- mapM (getTypeLHsBind funBinds) bs -- binds
70− ets <- mapM (getTypeLHsExpr tcm) es -- expressions
71− pts <- mapM (getTypeLPat tcm) ps -- patterns
72− tts <- mapM (getLHsType tcm) ts -- types
72+ allModules = tcm:tcms
73+ funBinds = funBindMap $ tm_parsed_module tcm
74+ bts <- mapM (getTypeLHsBind allModules funBinds) bs -- binds
75+ ets <- mapM (getTypeLHsExpr allModules) es -- expressions
76+ pts <- mapM (getTypeLPat allModules) ps -- patterns
77+ tts <- mapM (getLHsType allModules) ts -- types
7378 let imports = importInfo mods
7479 let exports = getExports tcm
75− let exprs = exports ++ imports ++ concat bts ++ concat tts ++ catMaybes (ets ++ pts)
80+ let exprs = addEmptyInfo exports ++ addEmptyInfo imports ++ concat bts ++ concat tts ++ catMaybes (ets ++ pts)
7681 return (mapMaybe toSpanInfo (sortBy cmp exprs))
77− where cmp (_,a,_) (_,b,_)
82+ where cmp (_,a,_,_) (_,b,_,_)
7883 | a `isSubspanOf` b = LT
7984 | b `isSubspanOf` a = GT
8085 | otherwise = compare (srcSpanStart a) (srcSpanStart b)
8186
87+ addEmptyInfo = map (\(a,b) -> (a,b,Nothing,emptySpanDoc))
88+
8289-- | The locations in the typechecked module are slightly messed up in some cases (e.g. HsMatchContext always
8390-- points to the first match) whereas the parsed module has the correct locations.
8491-- Therefore we build up a map from OccName to the corresponding definition in the parsed module
@@ -88,10 +95,10 @@ getSpanInfo mods tcm =
8895funBindMap :: ParsedModule -> OccEnv (HsBind GhcPs)
8996funBindMap pm = mkOccEnv $ [ (occName $ unLoc f, bnd) | L _ (Compat.ValD bnd@FunBind{fun_id = f}) <- hsmodDecls $ unLoc $ pm_parsed_source pm ]
9097
91−getExports :: TypecheckedModule -> [(SpanSource, SrcSpan, Maybe Type)]
98+getExports :: TypecheckedModule -> [(SpanSource, SrcSpan)]
9299getExports m
93100 | Just (_, _, Just exports, _) <- renamedSource m =
94− [ (Named $ unLoc n, getLoc n, Nothing)
101+ [ (Named $ unLoc n, getLoc n)
95102 | (e, _) <- exports
96103 , n <- ieLNames $ unLoc e
97104 ]
@@ -107,47 +114,60 @@ ieLNames _ = []
107114
108115-- | Get the name and type of a binding.
109116getTypeLHsBind :: (GhcMonad m)
110− => OccEnv (HsBind GhcPs)
117+ => [TypecheckedModule]
118+ -> OccEnv (HsBind GhcPs)
111119 -> LHsBind GhcTc
112− -> m [(SpanSource, SrcSpan, Maybe Type)]
113−getTypeLHsBind funBinds (L _spn FunBind{fun_id = pid})
114− | Just FunBind {fun_matches = MG{mg_alts=L _ matches}} <- lookupOccEnv funBinds (occName $ unLoc pid) =
115− return [(Named (getName (unLoc pid)), getLoc mc_fun, Just (varType (unLoc pid))) | match <- matches, FunRhs{mc_fun = mc_fun} <- [m_ctxt $ unLoc match] ]
120+ -> m [(SpanSource, SrcSpan, Maybe Type, SpanDoc)]
121+getTypeLHsBind tms funBinds (L _spn FunBind{fun_id = pid})
122+ | Just FunBind {fun_matches = MG{mg_alts=L _ matches}} <- lookupOccEnv funBinds (occName $ unLoc pid) = do
123+ let name = getName (unLoc pid)
124+ docs <- getDocumentationTryGhc tms name
125+ return [(Named name, getLoc mc_fun, Just (varType (unLoc pid)), docs) | match <- matches, FunRhs{mc_fun = mc_fun} <- [m_ctxt $ unLoc match] ]
116126-- In theory this shouldn’t ever fail but if it does, we can at least show the first clause.
117−getTypeLHsBind _ (L _spn FunBind{fun_id = pid,fun_matches = MG{}}) =
118− return [(Named $ getName (unLoc pid), getLoc pid, Just (varType (unLoc pid)))]
119−getTypeLHsBind _ _ = return []
127+getTypeLHsBind tms _ (L _spn FunBind{fun_id = pid,fun_matches = MG{}}) = do
128+ let name = getName (unLoc pid)
129+ docs <- getDocumentationTryGhc tms name
130+ return [(Named name, getLoc pid, Just (varType (unLoc pid)), docs)]
131+getTypeLHsBind _ _ _ = return []
120132
121133-- | Get the name and type of an expression.
122134getTypeLHsExpr :: (GhcMonad m)
123− => TypecheckedModule
135+ => [TypecheckedModule]
124136 -> LHsExpr GhcTc
125− -> m (Maybe (SpanSource, SrcSpan, Maybe Type))
126−getTypeLHsExpr _ e = do
137+ -> m (Maybe (SpanSource, SrcSpan, Maybe Type, SpanDoc))
138+getTypeLHsExpr tms e = do
127139 hs_env <- getSession
128140 (_, mbe) <- liftIO (deSugarExpr hs_env e)
129− return $
130− case mbe of
131− Just expr ->
132− Just (getSpanSource (unLoc e), getLoc e, Just (CoreUtils.exprType expr))
133− Nothing -> Nothing
141+ case mbe of
142+ Just expr -> do
143+ let ss = getSpanSource (unLoc e)
144+ docs <- case ss of
145+ Named n -> getDocumentationTryGhc tms n
146+ _ -> return emptySpanDoc
147+ return $ Just (ss, getLoc e, Just (CoreUtils.exprType expr), docs)
148+ Nothing -> return Nothing
134149 where
135150 getSpanSource :: HsExpr GhcTc -> SpanSource
151+ getSpanSource (HsLit U lit) = Lit (showGhc lit)
152+ getSpanSource (HsOverLit U lit) = Lit (showGhc lit)
136153 getSpanSource (HsVar U (L _ i)) = Named (getName i)
137154 getSpanSource (HsConLikeOut U (RealDataCon dc)) = Named (dataConName dc)
138155 getSpanSource RecordCon {rcon_con_name} = Named (getName rcon_con_name)
139156 getSpanSource (HsWrap U _ xpr) = getSpanSource xpr
140157 getSpanSource (HsPar U xpr) = getSpanSource (unLoc xpr)
141− getSpanSource _ = NoSource
158+ getSpanSource _ = NoSource
142159
143160-- | Get the name and type of a pattern.
144161getTypeLPat :: (GhcMonad m)
145− => TypecheckedModule
162+ => [TypecheckedModule]
146163 -> Pat GhcTc
147− -> m (Maybe (SpanSource, SrcSpan, Maybe Type))
148−getTypeLPat _ pat =
149− let (src, spn) = getSpanSource pat in
150− return $ Just (src, spn, Just (hsPatType pat))
164+ -> m (Maybe (SpanSource, SrcSpan, Maybe Type, SpanDoc))
165+getTypeLPat tms pat = do
166+ let (src, spn) = getSpanSource pat
167+ docs <- case src of
168+ Named n -> getDocumentationTryGhc tms n
169+ _ -> return emptySpanDoc
170+ return $ Just (src, spn, Just (hsPatType pat), docs)
151171 where
152172 getSpanSource :: Pat GhcTc -> (SpanSource, SrcSpan)
153173 getSpanSource (VarPat U (L spn vid)) = (Named (getName vid), spn)
@@ -157,40 +177,40 @@ getTypeLPat _ pat =
157177
158178getLHsType
159179 :: GhcMonad m
160− => TypecheckedModule
180+ => [TypecheckedModule]
161181 -> LHsType GhcRn
162− -> m [(SpanSource, SrcSpan, Maybe Type)]
163−getLHsType _ (L spn (HsTyVar U _ v)) = pure [(Named $ unLoc v, spn, Nothing)]
182+ -> m [(SpanSource, SrcSpan, Maybe Type, SpanDoc)]
183+getLHsType tms (L spn (HsTyVar U _ v)) = do
184+ let n = unLoc v
185+ docs <- getDocumentationTryGhc tms n
186+#ifdef GHC_LIB
187+ let ty = Right Nothing
188+#else
189+ ty <- catchSrcErrors "completion" $ do
190+ name' <- lookupName n
191+ return $ name' >>= safeTyThingType
192+#endif
193+ let ty' = case ty of
194+ Right (Just x) -> Just x
195+ _ -> Nothing
196+ pure [(Named n, spn, ty', docs)]
164197getLHsType _ _ = pure []
165198
166199importInfo :: [(Located ModuleName, Maybe NormalizedFilePath)]
167− -> [(SpanSource, SrcSpan, Maybe Type)]
200+ -> [(SpanSource, SrcSpan)]
168201importInfo = mapMaybe (uncurry wrk) where
169− wrk :: Located ModuleName -> Maybe NormalizedFilePath -> Maybe (SpanSource, SrcSpan, Maybe Type)
202+ wrk :: Located ModuleName -> Maybe NormalizedFilePath -> Maybe (SpanSource, SrcSpan)
170203 wrk modName = \case
171204 Nothing -> Nothing
172− Just fp -> Just (fpToSpanSource $ fromNormalizedFilePath fp, getLoc modName, Nothing)
205+ Just fp -> Just (fpToSpanSource $ fromNormalizedFilePath fp, getLoc modName)
173206
174207 -- TODO make this point to the module name
175208 fpToSpanSource :: FilePath -> SpanSource
176209 fpToSpanSource fp = SpanS $ RealSrcSpan $ zeroSpan $ mkFastString fp
177210
178−-- | Get ALL source spans in the source.
179−listifyAllSpans :: (Typeable a, Data m) => m -> [Located a]
180−listifyAllSpans tcs =
181− Data.Generics.listify p tcs
182− where p (L spn _) = isGoodSrcSpan spn
183−-- This is a version of `listifyAllSpans` specialized on picking out
184−-- patterns. It comes about since GHC now defines `type LPat p = Pat
185−-- p` (no top-level locations).
186−listifyAllSpans' :: Typeable a
187− => TypecheckedSource -> [Pat a]
188−listifyAllSpans' tcs = Data.Generics.listify (const True) tcs
189−
190−
191211-- | Pretty print the types into a 'SpanInfo'.
192−toSpanInfo :: (SpanSource, SrcSpan, Maybe Type) -> Maybe SpanInfo
193−toSpanInfo (name,mspan,typ) =
212+toSpanInfo :: (SpanSource, SrcSpan, Maybe Type, SpanDoc) -> Maybe SpanInfo
213+toSpanInfo (name,mspan,typ,docs) =
194214 case mspan of
195215 RealSrcSpan spn ->
196216 -- GHC’s line and column numbers are 1-based while LSP’s line and column
@@ -200,5 +220,6 @@ toSpanInfo (name,mspan,typ) =
200220 (srcSpanEndLine spn - 1)
201221 (srcSpanEndCol spn - 1)
202222 typ
203− name)
223+ name
224+ docs)
204225 _ -> Nothing
src/Development/IDE/Spans/Common.hsnew file+160
@@ -0,0 +1,160 @@
1+{-# LANGUAGE CPP #-}
2+#include "ghc-api-version.h"
3+
4+module Development.IDE.Spans.Common (
5+ showGhc
6+, listifyAllSpans
7+, listifyAllSpans'
8+, safeTyThingId
9+#ifndef GHC_LIB
10+, safeTyThingType
11+#endif
12+, SpanDoc(..)
13+, emptySpanDoc
14+, spanDocToMarkdown
15+) where
16+
17+import Data.Data
18+import qualified Data.Generics
19+import qualified Data.Text as T
20+
21+import GHC
22+import Outputable
23+import DynFlags
24+import ConLike
25+import DataCon
26+#ifndef GHC_LIB
27+import Var
28+#endif
29+
30+#if MIN_GHC_API_VERSION(8,6,0)
31+import Data.Char (isSpace)
32+import qualified Documentation.Haddock.Parser as H
33+import qualified Documentation.Haddock.Types as H
34+#endif
35+
36+showGhc :: Outputable a => a -> String
37+showGhc = showPpr unsafeGlobalDynFlags
38+
39+-- | Get ALL source spans in the source.
40+listifyAllSpans :: (Typeable a, Data m) => m -> [Located a]
41+listifyAllSpans tcs =
42+ Data.Generics.listify p tcs
43+ where p (L spn _) = isGoodSrcSpan spn
44+-- This is a version of `listifyAllSpans` specialized on picking out
45+-- patterns. It comes about since GHC now defines `type LPat p = Pat
46+-- p` (no top-level locations).
47+listifyAllSpans' :: Typeable a
48+ => TypecheckedSource -> [Pat a]
49+listifyAllSpans' tcs = Data.Generics.listify (const True) tcs
50+
51+#ifndef GHC_LIB
52+-- From haskell-ide-engine/src/Haskell/Ide/Engine/Support/HieExtras.hs
53+safeTyThingType :: TyThing -> Maybe Type
54+safeTyThingType thing
55+ | Just i <- safeTyThingId thing = Just (varType i)
56+safeTyThingType (ATyCon tycon) = Just (tyConKind tycon)
57+safeTyThingType _ = Nothing
58+#endif
59+
60+safeTyThingId :: TyThing -> Maybe Id
61+safeTyThingId (AnId i) = Just i
62+safeTyThingId (AConLike (RealDataCon dc)) = Just $ dataConWrapId dc
63+safeTyThingId _ = Nothing
64+
65+-- Possible documentation for an element in the code
66+data SpanDoc
67+ = SpanDocString HsDocString
68+ | SpanDocText [T.Text]
69+ deriving Show
70+
71+emptySpanDoc :: SpanDoc
72+emptySpanDoc = SpanDocText []
73+
74+spanDocToMarkdown :: SpanDoc -> [T.Text]
75+#if MIN_GHC_API_VERSION(8,6,0)
76+spanDocToMarkdown (SpanDocString docs)
77+ = [T.pack $ haddockToMarkdown $ H.toRegular $ H._doc $ H.parseParas Nothing $ unpackHDS docs]
78+#else
79+spanDocToMarkdown (SpanDocString _)
80+ = []
81+#endif
82+spanDocToMarkdown (SpanDocText txt) = txt
83+
84+#if MIN_GHC_API_VERSION(8,6,0)
85+-- Simple (and a bit hacky) conversion from Haddock markup to Markdown
86+haddockToMarkdown
87+ :: H.DocH String String -> String
88+
89+haddockToMarkdown H.DocEmpty
90+ = ""
91+haddockToMarkdown (H.DocAppend d1 d2)
92+ = haddockToMarkdown d1 Prelude.<> haddockToMarkdown d2
93+haddockToMarkdown (H.DocString s)
94+ = s
95+haddockToMarkdown (H.DocParagraph p)
96+ = "\n\n" ++ haddockToMarkdown p
97+haddockToMarkdown (H.DocIdentifier i)
98+ = "`" ++ i ++ "`"
99+haddockToMarkdown (H.DocIdentifierUnchecked i)
100+ = "`" ++ i ++ "`"
101+haddockToMarkdown (H.DocModule i)
102+ = "`" ++ i ++ "`"
103+haddockToMarkdown (H.DocWarning w)
104+ = haddockToMarkdown w
105+haddockToMarkdown (H.DocEmphasis d)
106+ = "*" ++ haddockToMarkdown d ++ "*"
107+haddockToMarkdown (H.DocBold d)
108+ = "**" ++ haddockToMarkdown d ++ "**"
109+haddockToMarkdown (H.DocMonospaced d)
110+ = "`" ++ escapeBackticks (haddockToMarkdown d) ++ "`"
111+ where
112+ escapeBackticks "" = ""
113+ escapeBackticks ('`':ss) = '\\':'`':escapeBackticks ss
114+ escapeBackticks (s :ss) = s:escapeBackticks ss
115+haddockToMarkdown (H.DocCodeBlock d)
116+ = "\n```haskell\n" ++ haddockToMarkdown d ++ "\n```\n"
117+haddockToMarkdown (H.DocExamples es)
118+ = "\n```haskell\n" ++ unlines (map exampleToMarkdown es) ++ "\n```\n"
119+ where
120+ exampleToMarkdown (H.Example expr result)
121+ = ">>> " ++ expr ++ "\n" ++ unlines result
122+haddockToMarkdown (H.DocHyperlink (H.Hyperlink url Nothing))
123+ = "<" ++ url ++ ">"
124+#if MIN_VERSION_haddock_library(1,8,0)
125+haddockToMarkdown (H.DocHyperlink (H.Hyperlink url (Just label)))
126+ = "[" ++ haddockToMarkdown label ++ "](" ++ url ++ ")"
127+#else
128+haddockToMarkdown (H.DocHyperlink (H.Hyperlink url (Just label)))
129+ = "[" ++ label ++ "](" ++ url ++ ")"
130+#endif
131+haddockToMarkdown (H.DocPic (H.Picture url Nothing))
132+ = "![](" ++ url ++ ")"
133+haddockToMarkdown (H.DocPic (H.Picture url (Just label)))
134+ = "![" ++ label ++ "](" ++ url ++ ")"
135+haddockToMarkdown (H.DocAName aname)
136+ = "[" ++ aname ++ "]:"
137+haddockToMarkdown (H.DocHeader (H.Header level title))
138+ = replicate level '#' ++ " " ++ haddockToMarkdown title
139+
140+haddockToMarkdown (H.DocUnorderedList things)
141+ = '\n' : (unlines $ map (\thing -> "+ " ++ dropWhile isSpace (haddockToMarkdown thing)) things)
142+haddockToMarkdown (H.DocOrderedList things)
143+ = '\n' : (unlines $ map (\thing -> "1. " ++ dropWhile isSpace (haddockToMarkdown thing)) things)
144+haddockToMarkdown (H.DocDefList things)
145+ = '\n' : (unlines $ map (\(term, defn) -> "+ **" ++ haddockToMarkdown term ++ "**: " ++ haddockToMarkdown defn) things)
146+
147+-- we cannot render math by default
148+haddockToMarkdown (H.DocMathInline _)
149+ = "*cannot render inline math formula*"
150+haddockToMarkdown (H.DocMathDisplay _)
151+ = "\n\n*cannot render display math formula*\n\n"
152+
153+-- TODO: render tables
154+haddockToMarkdown (H.DocTable _t)
155+ = "\n\n*tables are not yet supported*\n\n"
156+
157+-- things I don't really know how to handle
158+haddockToMarkdown (H.DocProperty _)
159+ = "" -- don't really know what to do
160+#endif
src/Development/IDE/Spans/Documentation.hs+11−95
@@ -15,32 +15,26 @@ import qualified Data.Map as M
1515import Data.Maybe
1616import qualified Data.Text as T
1717import Development.IDE.GHC.Error
18−import Development.IDE.Spans.Calculate
18+import Development.IDE.Spans.Common
1919import FastString
2020import GHC
2121import SrcLoc
2222
23−#if MIN_GHC_API_VERSION(8,6,0)
24−import Data.Char (isSpace)
25−import Development.IDE.GHC.Util
26−import qualified Documentation.Haddock.Parser as H
27−import qualified Documentation.Haddock.Types as H
28−#endif
2923
3024getDocumentationTryGhc
31− :: HscEnv
32− -> [TypecheckedModule]
25+ :: GhcMonad m
26+ => [TypecheckedModule]
3327 -> Name
34− -> IO [T.Text]
28+ -> m SpanDoc
3529#if MIN_GHC_API_VERSION(8,6,0)
36−getDocumentationTryGhc packageState tcs name = do
37− res <- runGhcEnv packageState $ catchSrcErrors "docs" $ getDocs name
30+getDocumentationTryGhc tcs name = do
31+ res <- catchSrcErrors "docs" $ getDocs name
3832 case res of
39− Right (Right (Just docs, _)) -> return [T.pack $ haddockToMarkdown $ H.toRegular $ H._doc $ H.parseParas Nothing $ unpackHDS docs]
40− _ -> return $ getDocumentation tcs name
33+ Right (Right (Just docs, _)) -> return $ SpanDocString docs
34+ _ -> return $ SpanDocText $ getDocumentation tcs name
4135#else
42−getDocumentationTryGhc _packageState tcs name = do
43− return $ getDocumentation tcs name
36+getDocumentationTryGhc tcs name = do
37+ return $ SpanDocText $ getDocumentation tcs name
4438#endif
4539
4640getDocumentation
@@ -114,82 +108,4 @@ docHeaders = mapMaybe (\(L _ x) -> wrk x)
114108 AnnLineComment s -> if "-- |" `isPrefixOf` s
115109 then Just $ T.pack s
116110 else Nothing
117− _ -> Nothing
118−
119−#if MIN_GHC_API_VERSION(8,6,0)
120−-- Simple (and a bit hacky) conversion from Haddock markup to Markdown
121−haddockToMarkdown
122− :: H.DocH String String -> String
123−
124−haddockToMarkdown H.DocEmpty
125− = ""
126−haddockToMarkdown (H.DocAppend d1 d2)
127− = haddockToMarkdown d1 <> haddockToMarkdown d2
128−haddockToMarkdown (H.DocString s)
129− = s
130−haddockToMarkdown (H.DocParagraph p)
131− = "\n\n" ++ haddockToMarkdown p
132−haddockToMarkdown (H.DocIdentifier i)
133− = "`" ++ i ++ "`"
134−haddockToMarkdown (H.DocIdentifierUnchecked i)
135− = "`" ++ i ++ "`"
136−haddockToMarkdown (H.DocModule i)
137− = "`" ++ i ++ "`"
138−haddockToMarkdown (H.DocWarning w)
139− = haddockToMarkdown w
140−haddockToMarkdown (H.DocEmphasis d)
141− = "*" ++ haddockToMarkdown d ++ "*"
142−haddockToMarkdown (H.DocBold d)
143− = "**" ++ haddockToMarkdown d ++ "**"
144−haddockToMarkdown (H.DocMonospaced d)
145− = "`" ++ escapeBackticks (haddockToMarkdown d) ++ "`"
146− where
147− escapeBackticks "" = ""
148− escapeBackticks ('`':ss) = '\\':'`':escapeBackticks ss
149− escapeBackticks (s :ss) = s:escapeBackticks ss
150−haddockToMarkdown (H.DocCodeBlock d)
151− = "\n```haskell\n" ++ haddockToMarkdown d ++ "\n```\n"
152−haddockToMarkdown (H.DocExamples es)
153− = "\n```haskell\n" ++ unlines (map exampleToMarkdown es) ++ "\n```\n"
154− where
155− exampleToMarkdown (H.Example expr result)
156− = ">>> " ++ expr ++ "\n" ++ unlines result
157−haddockToMarkdown (H.DocHyperlink (H.Hyperlink url Nothing))
158− = "<" ++ url ++ ">"
159−#if MIN_VERSION_haddock_library(1,8,0)
160−haddockToMarkdown (H.DocHyperlink (H.Hyperlink url (Just label)))
161− = "[" ++ haddockToMarkdown label ++ "](" ++ url ++ ")"
162−#else
163−haddockToMarkdown (H.DocHyperlink (H.Hyperlink url (Just label)))
164− = "[" ++ label ++ "](" ++ url ++ ")"
165−#endif
166−haddockToMarkdown (H.DocPic (H.Picture url Nothing))
167− = "![](" ++ url ++ ")"
168−haddockToMarkdown (H.DocPic (H.Picture url (Just label)))
169− = "![" ++ label ++ "](" ++ url ++ ")"
170−haddockToMarkdown (H.DocAName aname)
171− = "[" ++ aname ++ "]:"
172−haddockToMarkdown (H.DocHeader (H.Header level title))
173− = replicate level '#' ++ " " ++ haddockToMarkdown title
174−
175−haddockToMarkdown (H.DocUnorderedList things)
176− = '\n' : (unlines $ map (\thing -> "+ " ++ dropWhile isSpace (haddockToMarkdown thing)) things)
177−haddockToMarkdown (H.DocOrderedList things)
178− = '\n' : (unlines $ map (\thing -> "1. " ++ dropWhile isSpace (haddockToMarkdown thing)) things)
179−haddockToMarkdown (H.DocDefList things)
180− = '\n' : (unlines $ map (\(term, defn) -> "+ **" ++ haddockToMarkdown term ++ "**: " ++ haddockToMarkdown defn) things)
181−
182−-- we cannot render math by default
183−haddockToMarkdown (H.DocMathInline _)
184− = "*cannot render inline math formula*"
185−haddockToMarkdown (H.DocMathDisplay _)
186− = "\n\n*cannot render display math formula*\n\n"
187−
188−-- TODO: render tables
189−haddockToMarkdown (H.DocTable _t)
190− = "\n\n*tables are not yet supported*\n\n"
191−
192−-- things I don't really know how to handle
193−haddockToMarkdown (H.DocProperty _)
194− = "" -- don't really know what to do
195−#endif
111+ _ -> Nothing
src/Development/IDE/Spans/Type.hs+9−3
@@ -15,6 +15,7 @@ import GHC
1515import Control.DeepSeq
1616import OccName
1717import Development.IDE.GHC.Util
18+import Development.IDE.Spans.Common
1819
1920-- | Type of some span of source code. Most of these fields are
2021-- unboxed but Haddock doesn't show that.
@@ -34,11 +35,14 @@ data SpanInfo =
3435 -- any. This can be useful for accessing a variety of
3536 -- information about the identifier such as module,
3637 -- locality, definition location, etc.
38+ ,spaninfoDocs :: !SpanDoc
39+ -- ^ Documentation for the element
3740 }
3841instance Show SpanInfo where
39− show (SpanInfo sl sc el ec t n) =
42+ show (SpanInfo sl sc el ec t n docs) =
4043 unwords ["(SpanInfo", show sl, show sc, show el, show ec
41− , show $ maybe "NoType" prettyPrint t, "(" <> show n <> "))"]
44+ , show $ maybe "NoType" prettyPrint t, "(" <> show n <> "))"
45+ , "docs(" <> show docs <> ")"]
4246
4347instance NFData SpanInfo where
4448 rnf = rwhnf
@@ -47,6 +51,7 @@ instance NFData SpanInfo where
4751-- we don't always get a name out so sometimes manually annotating source is more appropriate
4852data SpanSource = Named Name
4953 | SpanS SrcSpan
54+ | Lit String
5055 | NoSource
5156 deriving (Eq)
5257
@@ -54,9 +59,10 @@ instance Show SpanSource where
5459 show = \case
5560 Named n -> "Named " ++ occNameString (occName n)
5661 SpanS sp -> "Span " ++ show sp
62+ Lit lit -> "Lit " ++ lit
5763 NoSource -> "NoSource"
5864
5965getNameM :: SpanSource -> Maybe Name
6066getNameM = \case
6167 Named name -> Just name
62− _ -> Nothing
68+ _ -> Nothing
test/exe/Main.hs+6−6
@@ -1084,7 +1084,7 @@ findDefinitionAndHoverTests = let
10841084 _ -> liftIO $ assertFailure $ "test not expecting this kind of hover info" <> show hover
10851085
10861086 extractLineColFromHoverMsg :: T.Text -> [T.Text]
1087− extractLineColFromHoverMsg = T.splitOn ":" . head . T.splitOn "**" . last . T.splitOn (sourceFileName <> ":")
1087+ extractLineColFromHoverMsg = T.splitOn ":" . head . T.splitOn "*" . last . T.splitOn (sourceFileName <> ":")
10881088
10891089 checkHoverRange :: Range -> Maybe Range -> T.Text -> Session ()
10901090 checkHoverRange expectedRange rangeInHover msg =
@@ -1151,7 +1151,7 @@ findDefinitionAndHoverTests = let
11511151 intL40 = Position 40 34 ; kindI = [ExpectHoverText [":: *\n"]]
11521152 tvrL40 = Position 40 37 ; kindV = [ExpectHoverText [":: * -> *\n"]]
11531153 intL41 = Position 41 20 ; litI = [ExpectHoverText ["7518"]]
1154− chrL36 = Position 36 25 ; litC = [ExpectHoverText ["'t'"]]
1154+ chrL36 = Position 37 24 ; litC = [ExpectHoverText ["'f'"]]
11551155 txtL8 = Position 8 14 ; litT = [ExpectHoverText ["\"dfgv\""]]
11561156 lstL43 = Position 43 12 ; litL = [ExpectHoverText ["[ 8391 :: Int, 6268 ]"]]
11571157 outL45 = Position 45 3 ; outSig = [ExpectHoverText ["outer", "Bool"], mkR 46 0 46 5]
@@ -1184,11 +1184,11 @@ findDefinitionAndHoverTests = let
11841184 , test yes yes mclL37 mcl "top-level fn 2nd clause #246"
11851185 , test yes yes spaceL37 space "top-level fn on space #315"
11861186 , test no broken docL41 doc "documentation #7"
1187− , test no broken eitL40 kindE "kind of Either #273"
1188− , test no broken intL40 kindI "kind of Int #273"
1187+ , test no yes eitL40 kindE "kind of Either #273"
1188+ , test no yes intL40 kindI "kind of Int #273"
11891189 , test no broken tvrL40 kindV "kind of (* -> *) type variable #273"
1190− , test no broken intL41 litI "literal Int in hover info #274"
1191− , test no broken chrL36 litC "literal Char in hover info #274"
1190+ , test no yes intL41 litI "literal Int in hover info #274"
1191+ , test no yes chrL36 litC "literal Char in hover info #274"
11921192 , test no broken txtL8 litT "literal Text in hover info #274"
11931193 , test no broken lstL43 litL "literal List in hover info #274"
11941194 , test no broken docL41 constr "type constraint in hover info #283"