1
- {-# LANGUAGE DataKinds #-}
2
- {-# LANGUAGE DerivingStrategies #-}
3
- {-# LANGUAGE OverloadedLabels #-}
4
- {-# LANGUAGE OverloadedRecordDot #-}
5
- {-# LANGUAGE OverloadedStrings #-}
6
- {-# LANGUAGE PatternSynonyms #-}
7
- {-# LANGUAGE RecordWildCards #-}
8
- {-# LANGUAGE TemplateHaskell #-}
9
- {-# LANGUAGE TypeFamilies #-}
10
- {-# LANGUAGE UnicodeSyntax #-}
1
+ {-# LANGUAGE BlockArguments #-}
2
+ {-# LANGUAGE DataKinds #-}
3
+ {-# LANGUAGE DerivingStrategies #-}
4
+ {-# LANGUAGE ImpredicativeTypes #-}
5
+ {-# LANGUAGE LiberalTypeSynonyms #-}
6
+ {-# LANGUAGE MultiWayIf #-}
7
+ {-# LANGUAGE OverloadedLabels #-}
8
+ {-# LANGUAGE OverloadedRecordDot #-}
9
+ {-# LANGUAGE OverloadedStrings #-}
10
+ {-# LANGUAGE PatternSynonyms #-}
11
+ {-# LANGUAGE QuantifiedConstraints #-}
12
+ {-# LANGUAGE RecordWildCards #-}
13
+ {-# LANGUAGE TemplateHaskell #-}
14
+ {-# LANGUAGE TypeFamilies #-}
15
+ {-# LANGUAGE UnicodeSyntax #-}
16
+ {-# LANGUAGE ViewPatterns #-}
11
17
12
18
-- |
13
19
-- This module provides the core functionality of the plugin.
14
- module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull , getSemanticTokensRule , semanticConfigProperties , semanticTokensFullDelta ) where
20
+ module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull , getSemanticTokensRule , getSyntacticTokensRule , semanticConfigProperties , semanticTokensFullDelta ) where
15
21
16
22
import Control.Concurrent.STM (stateTVar )
17
23
import Control.Concurrent.STM.Stats (atomically )
@@ -21,20 +27,28 @@ import Control.Monad.Except (ExceptT, liftEither,
21
27
import Control.Monad.IO.Class (MonadIO (.. ))
22
28
import Control.Monad.Trans (lift )
23
29
import Control.Monad.Trans.Except (runExceptT )
30
+ import Control.Monad.Trans.Maybe
31
+ import Data.Data (Data (.. ))
32
+ import Data.List
24
33
import qualified Data.Map.Strict as M
34
+ import Data.Maybe
35
+ import Data.Semigroup (First (.. ))
25
36
import Data.Text (Text )
26
37
import qualified Data.Text as T
27
38
import Development.IDE (Action ,
28
39
GetDocMap (GetDocMap ),
29
40
GetHieAst (GetHieAst ),
41
+ GetParsedModuleWithComments (.. ),
30
42
HieAstResult (HAR , hieAst , hieModule , refMap ),
31
43
IdeResult , IdeState ,
32
44
Priority (.. ),
33
45
Recorder , Rules ,
34
46
WithPriority ,
35
47
cmapWithPrio , define ,
36
- fromNormalizedFilePath ,
37
- hieKind )
48
+ hieKind ,
49
+ srcSpanToRange ,
50
+ toNormalizedUri ,
51
+ useWithStale )
38
52
import Development.IDE.Core.PluginUtils (runActionE , useE ,
39
53
useWithStaleE )
40
54
import Development.IDE.Core.Rules (toIdeResult )
@@ -44,10 +58,11 @@ import Development.IDE.Core.Shake (ShakeExtras (..),
44
58
getVirtualFile )
45
59
import Development.IDE.GHC.Compat hiding (Warning )
46
60
import Development.IDE.GHC.Compat.Util (mkFastString )
61
+ import GHC.Parser.Annotation
47
62
import GHC.Iface.Ext.Types (HieASTs (getAsts ),
48
63
pattern HiePath )
49
64
import Ide.Logger (logWith )
50
- import Ide.Plugin.Error (PluginError (PluginInternalError ),
65
+ import Ide.Plugin.Error (PluginError (PluginInternalError , PluginRuleFailed ),
51
66
getNormalizedFilePathE ,
52
67
handleMaybe ,
53
68
handleMaybeM )
@@ -61,10 +76,17 @@ import qualified Language.LSP.Protocol.Lens as L
61
76
import Language.LSP.Protocol.Message (MessageResult ,
62
77
Method (Method_TextDocumentSemanticTokensFull , Method_TextDocumentSemanticTokensFullDelta ))
63
78
import Language.LSP.Protocol.Types (NormalizedFilePath ,
79
+ Range ,
64
80
SemanticTokens ,
81
+ fromNormalizedFilePath ,
65
82
type (|? ) (InL , InR ))
66
83
import Prelude hiding (span )
67
84
import qualified StmContainers.Map as STM
85
+ import Type.Reflection (Typeable , eqTypeRep ,
86
+ pattern App ,
87
+ type (:~~: ) (HRefl ),
88
+ typeOf , typeRep ,
89
+ withTypeable )
68
90
69
91
70
92
$ mkSemanticConfigFunctions
@@ -78,8 +100,17 @@ computeSemanticTokens recorder pid _ nfp = do
78
100
config <- lift $ useSemanticConfigAction pid
79
101
logWith recorder Debug (LogConfig config)
80
102
semanticId <- lift getAndIncreaseSemanticTokensId
81
- (RangeHsSemanticTokenTypes {rangeSemanticList}, mapping) <- useWithStaleE GetSemanticTokens nfp
82
- withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens semanticId config mapping rangeSemanticList
103
+
104
+ (sortOn fst -> tokenList, First mapping) <- do
105
+ rangesyntacticTypes <- lift $ useWithStale GetSyntacticTokens nfp
106
+ rangesemanticTypes <- lift $ useWithStale GetSemanticTokens nfp
107
+ let mk w u (toks, mapping) = (map (fmap w) $ u toks, First mapping)
108
+ maybeToExceptT (PluginRuleFailed " no syntactic nor semantic tokens" ) $ hoistMaybe $
109
+ (mk HsSyntacticTokenType rangeSyntacticList <$> rangesyntacticTypes)
110
+ <> (mk HsSemanticTokenType rangeSemanticList <$> rangesemanticTypes)
111
+
112
+ -- NOTE: rangeSemanticsSemanticTokens actually assumes that the tokesn are in order. that means they have to be sorted by position
113
+ withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens semanticId config mapping tokenList
83
114
84
115
semanticTokensFull :: Recorder (WithPriority SemanticLog ) -> PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull
85
116
semanticTokensFull recorder state pid param = runActionE " SemanticTokens.semanticTokensFull" state computeSemanticTokensFull
@@ -133,6 +164,87 @@ getSemanticTokensRule recorder =
133
164
let hsFinder = idSemantic getTyThingMap (hieKindFunMasksKind hieKind) refMap
134
165
return $ computeRangeHsSemanticTokenTypeList hsFinder virtualFile ast
135
166
167
+ getSyntacticTokensRule :: Recorder (WithPriority SemanticLog ) -> Rules ()
168
+ getSyntacticTokensRule recorder =
169
+ define (cmapWithPrio LogShake recorder) $ \ GetSyntacticTokens nfp -> handleError recorder $ do
170
+ (parsedModule, _) <- withExceptT LogDependencyError $ useWithStaleE GetParsedModuleWithComments nfp
171
+ let tokList = computeRangeHsSyntacticTokenTypeList parsedModule
172
+ logWith recorder Debug $ LogSyntacticTokens tokList
173
+ pure tokList
174
+
175
+ astTraversalWith :: forall b r . Data b => b -> (forall a . Data a => a -> [r ]) -> [r ]
176
+ astTraversalWith ast f = mconcat $ flip gmapQ ast \ y -> f y <> astTraversalWith y f
177
+
178
+ {-# inline extractTyToTy #-}
179
+ extractTyToTy :: forall f a . (Typeable f , Data a ) => a -> Maybe (forall r . (forall b . Typeable b => f b -> r ) -> r )
180
+ extractTyToTy node
181
+ | App conRep argRep <- typeOf node
182
+ , Just HRefl <- eqTypeRep conRep (typeRep @ f )
183
+ = Just $ withTypeable argRep $ (\ k -> k node)
184
+ | otherwise = Nothing
185
+
186
+ {-# inline extractTy #-}
187
+ extractTy :: forall b a . (Typeable b , Data a ) => a -> Maybe b
188
+ extractTy node
189
+ | Just HRefl <- eqTypeRep (typeRep @ b ) (typeOf node)
190
+ = Just node
191
+ | otherwise = Nothing
192
+
193
+ computeRangeHsSyntacticTokenTypeList :: ParsedModule -> RangeHsSyntacticTokenTypes
194
+ computeRangeHsSyntacticTokenTypeList ParsedModule {pm_parsed_source} =
195
+ let toks = astTraversalWith pm_parsed_source \ node -> mconcat
196
+ [ maybeToList $ mkFromLocatable TKeyword . (\ k -> k \ x k' -> k' x) =<< extractTyToTy @ EpToken node
197
+ -- FIXME: probably needs to be commented out for ghc > 9.10
198
+ , maybeToList $ mkFromLocatable TKeyword . (\ x k -> k x) =<< extractTy @ AddEpAnn node
199
+ , do
200
+ EpAnnImportDecl i p s q pkg a <- maybeToList $ extractTy @ EpAnnImportDecl node
201
+
202
+ mapMaybe (mkFromLocatable TKeyword . (\ x k -> k x)) $ catMaybes $ [Just i, s, q, pkg, a] <> foldMap (\ (l, l') -> [Just l, Just l']) p
203
+ , maybeToList $ mkFromLocatable TComment . (\ x k -> k x) =<< extractTy @ LEpaComment node
204
+ , do
205
+ L loc expr <- maybeToList $ extractTy @ (LHsExpr GhcPs ) node
206
+ let fromSimple = maybeToList . flip mkFromLocatable \ k -> k loc
207
+ case expr of
208
+ HsOverLabel {} -> fromSimple TStringLit
209
+ HsOverLit _ (OverLit _ lit) -> fromSimple case lit of
210
+ HsIntegral {} -> TNumberLit
211
+ HsFractional {} -> TNumberLit
212
+
213
+ HsIsString {} -> TStringLit
214
+ HsLit _ lit -> fromSimple case lit of
215
+ HsChar {} -> TCharLit
216
+ HsCharPrim {} -> TCharLit
217
+
218
+ HsInt {} -> TNumberLit
219
+ HsInteger {} -> TNumberLit
220
+ HsIntPrim {} -> TNumberLit
221
+ HsWordPrim {} -> TNumberLit
222
+ HsWord8Prim {} -> TNumberLit
223
+ HsWord16Prim {} -> TNumberLit
224
+ HsWord32Prim {} -> TNumberLit
225
+ HsWord64Prim {} -> TNumberLit
226
+ HsInt8Prim {} -> TNumberLit
227
+ HsInt16Prim {} -> TNumberLit
228
+ HsInt32Prim {} -> TNumberLit
229
+ HsInt64Prim {} -> TNumberLit
230
+ HsFloatPrim {} -> TNumberLit
231
+ HsDoublePrim {} -> TNumberLit
232
+ HsRat {} -> TNumberLit
233
+
234
+ HsString {} -> TStringLit
235
+ HsStringPrim {} -> TStringLit
236
+ HsGetField _ _ field -> maybeToList $ mkFromLocatable TRecordSelector \ k -> k field
237
+ HsProjection _ projs -> foldMap (\ proj -> maybeToList $ mkFromLocatable TRecordSelector \ k -> k proj) projs
238
+ _ -> []
239
+ ]
240
+ in RangeHsSyntacticTokenTypes toks
241
+
242
+ {-# inline mkFromLocatable #-}
243
+ mkFromLocatable
244
+ :: HsSyntacticTokenType
245
+ -> (forall r . (forall a . HasSrcSpan a => a -> r ) -> r )
246
+ -> Maybe (Range , HsSyntacticTokenType )
247
+ mkFromLocatable tt w = w \ tok -> let mrange = srcSpanToRange $ getLoc tok in fmap (, tt) mrange
136
248
137
249
-- taken from /haskell-language-server/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs
138
250
0 commit comments