-
-
Notifications
You must be signed in to change notification settings - Fork 435
Expand file tree
/
Copy pathDocumentLink.hs
More file actions
129 lines (114 loc) · 6.49 KB
/
DocumentLink.hs
File metadata and controls
129 lines (114 loc) · 6.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TypeFamilies #-}
module Ide.Plugin.DocumentLink (descriptor, Log(..)) where
import Control.DeepSeq (NFData)
import Control.Monad.Trans.Maybe (MaybeT (runMaybeT),
hoistMaybe)
import Data.Hashable (Hashable)
import qualified Data.Map.Strict as Map
import Data.Maybe (maybeToList)
import Development.IDE (DocAndTyThingMap (DKMap, getDocMap),
GetDocMap (GetDocMap),
GetHieAst (GetHieAst),
HieAstResult (HAR, hieAst),
IdeState (shakeExtras),
Pretty (pretty), Range,
Recorder, RuleResult, Rules,
Uri (Uri), WithPriority,
cmapWithPrio,
defineNoDiagnostics,
fromNormalizedFilePath,
realSrcSpanToRange)
import Development.IDE.Core.PluginUtils (runIdeActionE, useMT,
useWithStaleFastE)
import qualified Development.IDE.Core.Shake as Shake
import Development.IDE.GHC.Compat (lookupNameEnv)
import Development.IDE.GHC.Compat.Util (mkFastString)
import Development.IDE.Spans.Common (DocMap,
SpanDoc (SpanDocString, SpanDocText),
spanDocUriDoc)
import GHC.Generics (Generic)
import GHC.Iface.Ext.Types (HieAST (nodeChildren, nodeSpan, sourcedNodeInfo),
HieASTs (getAsts),
Identifier,
NodeInfo (nodeIdentifiers),
NodeOrigin (SourceInfo),
SourcedNodeInfo (getSourcedNodeInfo),
Span, pattern HiePath)
import Ide.Plugin.Error (getNormalizedFilePathE)
import Ide.Types (PluginDescriptor (pluginHandlers),
PluginId,
PluginMethodHandler,
defaultPluginDescriptor,
mkPluginHandler, pluginRules)
import Language.LSP.Protocol.Message (Method (Method_TextDocumentDocumentLink),
SMethod (SMethod_TextDocumentDocumentLink))
import Language.LSP.Protocol.Types (DocumentLink (..),
DocumentLinkParams (DocumentLinkParams),
TextDocumentIdentifier (TextDocumentIdentifier),
type (|?) (InL))
newtype Log = LogShake Shake.Log
instance Pretty Log where
pretty = \case
LogShake shakeLog -> pretty shakeLog
descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState
descriptor recorder pluginId =
(defaultPluginDescriptor pluginId "Provide document link of symbols")
{ Ide.Types.pluginHandlers = mkPluginHandler SMethod_TextDocumentDocumentLink documentLinkProvider,
Ide.Types.pluginRules = getDocumentLinkRule recorder
}
documentLinkProvider :: PluginMethodHandler IdeState Method_TextDocumentDocumentLink
documentLinkProvider ideState _pluginId (DocumentLinkParams _ _ (TextDocumentIdentifier uri)) = do
nfp <- getNormalizedFilePathE uri
((DocumentLinks uris), _pm) <- runIdeActionE "DocumentLink" (shakeExtras ideState) $ useWithStaleFastE GetDocumentLinks nfp
pure $ InL (map mkDocumentLink uris)
mkDocumentLink :: (Range, Uri) -> DocumentLink
mkDocumentLink (range, target) =
DocumentLink
{ _range = range,
_target = Just target,
_tooltip = Nothing,
_data_ = Nothing
}
data GetDocumentLinks = GetDocumentLinks
deriving (Eq, Show, Generic)
instance Hashable GetDocumentLinks
instance NFData GetDocumentLinks
newtype DocumentLinks = DocumentLinks [(Range, Uri)]
deriving (Show, NFData)
type instance RuleResult GetDocumentLinks = DocumentLinks
getDocumentLinkRule :: Recorder (WithPriority Log) -> Rules ()
getDocumentLinkRule recorder =
defineNoDiagnostics (cmapWithPrio LogShake recorder) $ \GetDocumentLinks nfp -> runMaybeT $ do
HAR {hieAst} <- useMT GetHieAst nfp
DKMap {getDocMap} <- useMT GetDocMap nfp
ast <- hoistMaybe $ getAsts hieAst Map.!? HiePath (mkFastString $ fromNormalizedFilePath nfp)
let lookup = lookupDoc getDocMap
pure $ DocumentLinks (foldAst lookup ast)
-- | Recursively traverses the HieAST in depth-first order to collect information
-- from leaf nodes. For each leaf, it extracts all identifiers and their source
-- spans, applies the lookup function, and aggregates the results using the
-- Monoid instance.
foldAst :: forall a t. Monoid a => ((Identifier, Span) -> a) -> HieAST t -> a
foldAst lookup ast = case nodeChildren ast of
[] -> visitLeaf ast
asts -> foldMap (foldAst lookup) asts
where
visitLeaf leaf =
let span = nodeSpan leaf
mNodeInfo = Map.lookup SourceInfo $ getSourcedNodeInfo (sourcedNodeInfo leaf)
in flip foldMap mNodeInfo $ \nodeInfo ->
foldMap (\ident -> lookup (ident, span)) (Map.keys $ nodeIdentifiers nodeInfo)
lookupDoc :: DocMap -> (Identifier, Span) -> [(Range, Uri)]
lookupDoc dm (identifier, span) = do
Right name <- [identifier]
doc <- maybeToList $ lookupNameEnv dm name
uris <- maybeToList $ spanDocUriDoc $ case doc of
SpanDocString _ uris -> uris
SpanDocText _ uris -> uris
pure (realSrcSpanToRange span, Uri uris)