1010
1111-- |
1212-- 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
1414
15+ import Control.Concurrent.STM (stateTVar )
16+ import Control.Concurrent.STM.Stats (atomically )
1517import Control.Lens ((^.) )
1618import Control.Monad.Except (ExceptT , liftEither ,
1719 withExceptT )
20+ import Control.Monad.IO.Class (MonadIO (.. ))
1821import Control.Monad.Trans (lift )
1922import Control.Monad.Trans.Except (runExceptT )
2023import qualified Data.Map.Strict as M
24+ import Data.Text (Text )
25+ import qualified Data.Text as T
2126import Development.IDE (Action ,
2227 GetDocMap (GetDocMap ),
2328 GetHieAst (GetHieAst ),
@@ -31,10 +36,10 @@ import Development.IDE (Action,
3136 hieKind , use_ )
3237import Development.IDE.Core.PluginUtils (runActionE ,
3338 useWithStaleE )
34- import Development.IDE.Core.PositionMapping (idDelta )
3539import Development.IDE.Core.Rules (toIdeResult )
3640import Development.IDE.Core.RuleTypes (DocAndTyThingMap (.. ))
37- import Development.IDE.Core.Shake (addPersistentRule ,
41+ import Development.IDE.Core.Shake (ShakeExtras (.. ),
42+ getShakeExtras ,
3843 getVirtualFile ,
3944 useWithStale_ )
4045import Development.IDE.GHC.Compat hiding (Warning )
@@ -51,11 +56,13 @@ import Ide.Plugin.SemanticTokens.Tokenize (computeRangeHsSemanti
5156import Ide.Plugin.SemanticTokens.Types
5257import Ide.Types
5358import 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 ))
5561import Language.LSP.Protocol.Types (NormalizedFilePath ,
5662 SemanticTokens ,
57- type (|? ) (InL ))
63+ type (|? ) (InL , InR ))
5864import Prelude hiding (span )
65+ import qualified StmContainers.Map as STM
5966
6067
6168$ mkSemanticConfigFunctions
@@ -68,14 +75,40 @@ computeSemanticTokens :: Recorder (WithPriority SemanticLog) -> PluginId -> IdeS
6875computeSemanticTokens recorder pid _ nfp = do
6976 config <- lift $ useSemanticConfigAction pid
7077 logWith recorder Debug (LogConfig config)
78+ semanticId <- lift getAndIncreaseSemanticTokensId
7179 (RangeHsSemanticTokenTypes {rangeSemanticList}, mapping) <- useWithStaleE GetSemanticTokens nfp
72- withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens config mapping rangeSemanticList
80+ withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens semanticId config mapping rangeSemanticList
7381
7482semanticTokensFull :: 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
7695 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
79112
80113-- | Defines the 'getSemanticTokensRule' function, compute semantic tokens for a Haskell source file.
81114--
@@ -98,9 +131,6 @@ getSemanticTokensRule recorder =
98131 let hsFinder = idSemantic getTyThingMap (hieKindFunMasksKind hieKind) refMap
99132 return $ computeRangeHsSemanticTokenTypeList hsFinder virtualFile ast
100133
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 )
104134
105135-- taken from /haskell-language-server/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs
106136
@@ -113,3 +143,22 @@ handleError recorder action' = do
113143 logWith recorder Warning msg
114144 pure $ toIdeResult (Left [] )
115145 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