10
10
11
11
-- |
12
12
-- This module provides the core functionality of the plugin.
13
- module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull , getSemanticTokensRule , persistentGetSemanticTokensRule , semanticConfigProperties ) where
13
+ module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull , getSemanticTokensRule , semanticConfigProperties , semanticTokensFullDelta ) where
14
14
15
+ import Control.Concurrent.STM (stateTVar )
16
+ import Control.Concurrent.STM.Stats (atomically )
15
17
import Control.Lens ((^.) )
16
18
import Control.Monad.Except (ExceptT , liftEither ,
17
19
withExceptT )
20
+ import Control.Monad.IO.Class (MonadIO (.. ))
18
21
import Control.Monad.Trans (lift )
19
22
import Control.Monad.Trans.Except (runExceptT )
20
23
import qualified Data.Map.Strict as M
24
+ import Data.Text (Text )
25
+ import qualified Data.Text as T
21
26
import Development.IDE (Action ,
22
27
GetDocMap (GetDocMap ),
23
28
GetHieAst (GetHieAst ),
@@ -31,10 +36,10 @@ import Development.IDE (Action,
31
36
hieKind , use_ )
32
37
import Development.IDE.Core.PluginUtils (runActionE ,
33
38
useWithStaleE )
34
- import Development.IDE.Core.PositionMapping (idDelta )
35
39
import Development.IDE.Core.Rules (toIdeResult )
36
40
import Development.IDE.Core.RuleTypes (DocAndTyThingMap (.. ))
37
- import Development.IDE.Core.Shake (addPersistentRule ,
41
+ import Development.IDE.Core.Shake (ShakeExtras (.. ),
42
+ getShakeExtras ,
38
43
getVirtualFile ,
39
44
useWithStale_ )
40
45
import Development.IDE.GHC.Compat hiding (Warning )
@@ -51,11 +56,13 @@ import Ide.Plugin.SemanticTokens.Tokenize (computeRangeHsSemanti
51
56
import Ide.Plugin.SemanticTokens.Types
52
57
import Ide.Types
53
58
import qualified Language.LSP.Protocol.Lens as L
54
- import Language.LSP.Protocol.Message (Method (Method_TextDocumentSemanticTokensFull ))
59
+ import Language.LSP.Protocol.Message (MessageResult ,
60
+ Method (Method_TextDocumentSemanticTokensFull , Method_TextDocumentSemanticTokensFullDelta ))
55
61
import Language.LSP.Protocol.Types (NormalizedFilePath ,
56
62
SemanticTokens ,
57
- type (|? ) (InL ))
63
+ type (|? ) (InL , InR ))
58
64
import Prelude hiding (span )
65
+ import qualified StmContainers.Map as STM
59
66
60
67
61
68
$ mkSemanticConfigFunctions
@@ -68,14 +75,40 @@ computeSemanticTokens :: Recorder (WithPriority SemanticLog) -> PluginId -> IdeS
68
75
computeSemanticTokens recorder pid _ nfp = do
69
76
config <- lift $ useSemanticConfigAction pid
70
77
logWith recorder Debug (LogConfig config)
78
+ semanticId <- lift getAndIncreaseSemanticTokensId
71
79
(RangeHsSemanticTokenTypes {rangeSemanticList}, mapping) <- useWithStaleE GetSemanticTokens nfp
72
- withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens config mapping rangeSemanticList
80
+ withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens semanticId config mapping rangeSemanticList
73
81
74
82
semanticTokensFull :: Recorder (WithPriority SemanticLog ) -> PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull
75
- semanticTokensFull recorder state pid param = do
83
+ semanticTokensFull recorder state pid param = runActionE " SemanticTokens.semanticTokensFull" state computeSemanticTokensFull
84
+ where
85
+ computeSemanticTokensFull :: ExceptT PluginError Action (MessageResult Method_TextDocumentSemanticTokensFull )
86
+ computeSemanticTokensFull = do
87
+ nfp <- getNormalizedFilePathE (param ^. L. textDocument . L. uri)
88
+ items <- computeSemanticTokens recorder pid state nfp
89
+ lift $ setSemanticTokens nfp items
90
+ return $ InL items
91
+
92
+
93
+ semanticTokensFullDelta :: Recorder (WithPriority SemanticLog ) -> PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFullDelta
94
+ semanticTokensFullDelta recorder state pid param = do
76
95
nfp <- getNormalizedFilePathE (param ^. L. textDocument . L. uri)
77
- items <- runActionE " SemanticTokens.semanticTokensFull" state $ computeSemanticTokens recorder pid state nfp
78
- return $ InL items
96
+ let previousVersionFromParam = param ^. L. previousResultId
97
+ runActionE " SemanticTokens.semanticTokensFullDelta" state $ computeSemanticTokensFullDelta recorder previousVersionFromParam pid state nfp
98
+ where
99
+ computeSemanticTokensFullDelta :: Recorder (WithPriority SemanticLog ) -> Text -> PluginId -> IdeState -> NormalizedFilePath -> ExceptT PluginError Action (MessageResult Method_TextDocumentSemanticTokensFullDelta )
100
+ computeSemanticTokensFullDelta recorder previousVersionFromParam pid state nfp = do
101
+ semanticTokens <- computeSemanticTokens recorder pid state nfp
102
+ previousSemanticTokensMaybe <- lift $ getPreviousSemanticTokens nfp
103
+ lift $ setSemanticTokens nfp semanticTokens
104
+ case previousSemanticTokensMaybe of
105
+ Nothing -> return $ InL semanticTokens
106
+ Just previousSemanticTokens ->
107
+ if Just previousVersionFromParam == previousSemanticTokens^. L. resultId
108
+ then return $ InR $ InL $ makeSemanticTokensDeltaWithId (semanticTokens^. L. resultId) previousSemanticTokens semanticTokens
109
+ else do
110
+ logWith recorder Warning (LogSemanticTokensDeltaMisMatch previousVersionFromParam (previousSemanticTokens^. L. resultId))
111
+ return $ InL semanticTokens
79
112
80
113
-- | Defines the 'getSemanticTokensRule' function, compute semantic tokens for a Haskell source file.
81
114
--
@@ -98,9 +131,6 @@ getSemanticTokensRule recorder =
98
131
let hsFinder = idSemantic getTyThingMap (hieKindFunMasksKind hieKind) refMap
99
132
return $ computeRangeHsSemanticTokenTypeList hsFinder virtualFile ast
100
133
101
- -- | Persistent rule to ensure that semantic tokens doesn't block on startup
102
- persistentGetSemanticTokensRule :: Rules ()
103
- persistentGetSemanticTokensRule = addPersistentRule GetSemanticTokens $ \ _ -> pure $ Just (RangeHsSemanticTokenTypes mempty , idDelta, Nothing )
104
134
105
135
-- taken from /haskell-language-server/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs
106
136
@@ -113,3 +143,22 @@ handleError recorder action' = do
113
143
logWith recorder Warning msg
114
144
pure $ toIdeResult (Left [] )
115
145
Right value -> pure $ toIdeResult (Right value)
146
+
147
+ -----------------------
148
+ -- helper functions
149
+ -----------------------
150
+
151
+ -- keep track of the semantic tokens response id
152
+ -- so that we can compute the delta between two versions
153
+ getAndIncreaseSemanticTokensId :: Action SemanticTokenId
154
+ getAndIncreaseSemanticTokensId = do
155
+ ShakeExtras {semanticTokensId} <- getShakeExtras
156
+ liftIO $ atomically $ do
157
+ i <- stateTVar semanticTokensId (\ val -> (val, val+ 1 ))
158
+ return $ T. pack $ show i
159
+
160
+ getPreviousSemanticTokens :: NormalizedFilePath -> Action (Maybe SemanticTokens )
161
+ getPreviousSemanticTokens uri = getShakeExtras >>= liftIO . atomically . STM. lookup uri . semanticTokensCache
162
+
163
+ setSemanticTokens :: NormalizedFilePath -> SemanticTokens -> Action ()
164
+ setSemanticTokens uri tokens = getShakeExtras >>= liftIO . atomically . STM. insert tokens uri . semanticTokensCache
0 commit comments