Skip to content

add Method_TextDocumentSemanticTokensFullDelta #4073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
43796d0
add Method_TextDocumentSemanticTokensFullDelta
soulomoon Feb 13, 2024
3f29007
fix cache and add test
soulomoon Feb 13, 2024
ff93475
cleanpu
soulomoon Feb 13, 2024
decd9e1
add tset
soulomoon Feb 13, 2024
e1fd84e
add delete tokens test
soulomoon Feb 13, 2024
41499b4
clean up
soulomoon Feb 13, 2024
9b373f3
clean up
soulomoon Feb 13, 2024
4f7546a
add delete tokens test
soulomoon Feb 13, 2024
8af3b99
cleanup
soulomoon Feb 13, 2024
181b10a
fix test
soulomoon Feb 13, 2024
9e7af72
cleanup
soulomoon Feb 13, 2024
cef51ed
remove persistentGetSemanticTokensRule
soulomoon Feb 14, 2024
fa908a0
fix export
soulomoon Feb 14, 2024
e3ed49b
cleanup
soulomoon Feb 14, 2024
82b1eaf
Update plugins/hls-semantic-tokens-plugin/test/SemanticTokensTest.hs
soulomoon Feb 14, 2024
e7a17e7
cleanup
soulomoon Feb 14, 2024
cc69775
add doc
soulomoon Feb 14, 2024
49fd150
cleanup
soulomoon Feb 14, 2024
dcdbb27
Update plugins/hls-semantic-tokens-plugin/src/Ide/Plugin/SemanticToke…
soulomoon Feb 14, 2024
7e7398a
fix
soulomoon Feb 14, 2024
e6ff123
Merge branch 'master' into 4072-textdocumentsemantictokensfulldelta
soulomoon Feb 14, 2024
9ef7796
Merge branch 'master' into 4072-textdocumentsemantictokensfulldelta
soulomoon Feb 19, 2024
b287639
neat
soulomoon Feb 19, 2024
154013b
add doc
soulomoon Feb 19, 2024
42f6284
Merge branch 'master' into 4072-textdocumentsemantictokensfulldelta
soulomoon Feb 21, 2024
699dab7
add doc about semanticTokensCache location
soulomoon Feb 21, 2024
a0b6ac7
add Note [Semantic Tokens Cache Location]
soulomoon Feb 21, 2024
4180c43
fix doc
soulomoon Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions ghcide/src/Development/IDE/Core/Shake.hs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ import Language.LSP.Diagnostics
import qualified Language.LSP.Protocol.Lens as L
import Language.LSP.Protocol.Message
import Language.LSP.Protocol.Types
import Language.LSP.Protocol.Types (SemanticTokens)
import qualified Language.LSP.Protocol.Types as LSP
import qualified Language.LSP.Server as LSP
import Language.LSP.VFS hiding (start)
Expand Down Expand Up @@ -243,6 +244,13 @@ data HieDbWriter
-- with (currently) retry functionality
type IndexQueue = TQueue (((HieDb -> IO ()) -> IO ()) -> IO ())

-- Note [Semantic Tokens Cache Location]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- storing semantic tokens cache for each file in shakeExtras might
-- not be ideal, since it most used in LSP request handlers
-- instead of rules. We should consider moving it to a more
-- appropriate place in the future if we find one, store it for now.

-- information we stash inside the shakeExtra field
data ShakeExtras = ShakeExtras
{ --eventer :: LSP.FromServerMessage -> IO ()
Expand All @@ -259,6 +267,14 @@ data ShakeExtras = ShakeExtras
,publishedDiagnostics :: STM.Map NormalizedUri [Diagnostic]
-- ^ This represents the set of diagnostics that we have published.
-- Due to debouncing not every change might get published.

,semanticTokensCache:: STM.Map NormalizedFilePath SemanticTokens
-- ^ Cache of last response of semantic tokens for each file,
-- so we can compute deltas for semantic tokens(SMethod_TextDocumentSemanticTokensFullDelta).
-- putting semantic tokens cache and id in shakeExtras might not be ideal
-- see Note [Semantic Tokens Cache Location]
,semanticTokensId :: TVar Int
-- ^ semanticTokensId is used to generate unique ids for each lsp response of semantic tokens.
,positionMapping :: STM.Map NormalizedUri (EnumMap Int32 (PositionDelta, PositionMapping))
-- ^ Map from a text document version to a PositionMapping that describes how to map
-- positions in a version of that document to positions in the latest version
Expand Down Expand Up @@ -616,12 +632,14 @@ shakeOpen recorder lspEnv defaultConfig idePlugins logger debouncer
diagnostics <- STM.newIO
hiddenDiagnostics <- STM.newIO
publishedDiagnostics <- STM.newIO
semanticTokensCache <- STM.newIO
positionMapping <- STM.newIO
knownTargetsVar <- newTVarIO $ hashed HMap.empty
let restartShakeSession = shakeRestart recorder ideState
persistentKeys <- newTVarIO mempty
indexPending <- newTVarIO HMap.empty
indexCompleted <- newTVarIO 0
semanticTokensId <- newTVarIO 0
indexProgressToken <- newVar Nothing
let hiedbWriter = HieDbWriter{..}
exportsMap <- newTVarIO mempty
Expand Down
5 changes: 4 additions & 1 deletion haskell-language-server.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -1574,14 +1574,16 @@ library hls-semantic-tokens-plugin
, hls-graph == 2.6.0.0
, template-haskell
, data-default
, stm
, stm-containers

default-extensions: DataKinds

test-suite hls-semantic-tokens-plugin-tests
import: defaults, pedantic, test-defaults, warnings
type: exitcode-stdio-1.0
hs-source-dirs: plugins/hls-semantic-tokens-plugin/test
main-is: Main.hs
main-is: SemanticTokensTest.hs

build-depends:
, aeson
Expand All @@ -1601,6 +1603,7 @@ test-suite hls-semantic-tokens-plugin-tests
, ghcide == 2.6.0.0
, hls-plugin-api == 2.6.0.0
, data-default
, row-types

-----------------------------
-- HLS
Expand Down
2 changes: 2 additions & 0 deletions hls-plugin-api/src/Ide/Plugin/ConfigUtils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pluginsToDefaultConfig IdePlugins {..} =
SMethod_TextDocumentCompletion -> ["completionOn" A..= plcCompletionOn]
SMethod_TextDocumentPrepareCallHierarchy -> ["callHierarchyOn" A..= plcCallHierarchyOn]
SMethod_TextDocumentSemanticTokensFull -> ["semanticTokensOn" A..= plcSemanticTokensOn]
SMethod_TextDocumentSemanticTokensFullDelta -> ["semanticTokensOn" A..= plcSemanticTokensOn]
_ -> []

-- | Generates json schema used in haskell vscode extension
Expand Down Expand Up @@ -125,6 +126,7 @@ pluginsToVSCodeExtensionSchema IdePlugins {..} = A.object $ mconcat $ singlePlug
SMethod_TextDocumentCompletion -> [toKey' "completionOn" A..= schemaEntry "completions" plcCompletionOn]
SMethod_TextDocumentPrepareCallHierarchy -> [toKey' "callHierarchyOn" A..= schemaEntry "call hierarchy" plcCallHierarchyOn]
SMethod_TextDocumentSemanticTokensFull -> [toKey' "semanticTokensOn" A..= schemaEntry "semantic tokens" plcSemanticTokensOn]
SMethod_TextDocumentSemanticTokensFullDelta -> [toKey' "semanticTokensOn" A..= schemaEntry "semantic tokens" plcSemanticTokensOn]
_ -> []
schemaEntry desc defaultVal =
A.object
Expand Down
6 changes: 6 additions & 0 deletions hls-plugin-api/src/Ide/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,9 @@ instance PluginMethod Request Method_TextDocumentRangeFormatting where
instance PluginMethod Request Method_TextDocumentSemanticTokensFull where
handlesRequest = pluginEnabledWithFeature plcSemanticTokensOn

instance PluginMethod Request Method_TextDocumentSemanticTokensFullDelta where
handlesRequest = pluginEnabledWithFeature plcSemanticTokensOn

instance PluginMethod Request Method_TextDocumentPrepareCallHierarchy where
handlesRequest = pluginEnabledWithFeature plcCallHierarchyOn

Expand Down Expand Up @@ -751,6 +754,9 @@ instance PluginRequestMethod (Method_CustomMethod m) where
instance PluginRequestMethod Method_TextDocumentSemanticTokensFull where
combineResponses _ _ _ _ (x :| _) = x

instance PluginRequestMethod Method_TextDocumentSemanticTokensFullDelta where
combineResponses _ _ _ _ (x :| _) = x

takeLefts :: [a |? b] -> [a]
takeLefts = mapMaybe (\x -> [res | (InL res) <- Just x])

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}


module Ide.Plugin.SemanticTokens (descriptor) where

Expand All @@ -12,8 +12,10 @@ import Language.LSP.Protocol.Message
descriptor :: Recorder (WithPriority SemanticLog) -> PluginId -> PluginDescriptor IdeState
descriptor recorder plId =
(defaultPluginDescriptor plId "Provides semantic tokens")
{ Ide.Types.pluginHandlers = mkPluginHandler SMethod_TextDocumentSemanticTokensFull (Internal.semanticTokensFull recorder),
Ide.Types.pluginRules = Internal.getSemanticTokensRule recorder <> Internal.persistentGetSemanticTokensRule,
{ Ide.Types.pluginHandlers =
mkPluginHandler SMethod_TextDocumentSemanticTokensFull (Internal.semanticTokensFull recorder)
<> mkPluginHandler SMethod_TextDocumentSemanticTokensFullDelta (Internal.semanticTokensFullDelta recorder),
Ide.Types.pluginRules = Internal.getSemanticTokensRule recorder,
pluginConfigDescriptor =
defaultConfigDescriptor
{ configInitialGenericConfig = (configInitialGenericConfig defaultConfigDescriptor) {plcGlobalOn = False}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@

-- |
-- This module provides the core functionality of the plugin.
module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull, getSemanticTokensRule, persistentGetSemanticTokensRule, semanticConfigProperties) where
module Ide.Plugin.SemanticTokens.Internal (semanticTokensFull, getSemanticTokensRule, semanticConfigProperties, semanticTokensFullDelta) where

import Control.Concurrent.STM (stateTVar)
import Control.Concurrent.STM.Stats (atomically)
import Control.Lens ((^.))
import Control.Monad.Except (ExceptT, liftEither,
withExceptT)
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Except (runExceptT)
import qualified Data.Map.Strict as M
import Data.Text (Text)
import qualified Data.Text as T
import Development.IDE (Action,
GetDocMap (GetDocMap),
GetHieAst (GetHieAst),
Expand All @@ -31,10 +36,10 @@ import Development.IDE (Action,
hieKind, use_)
import Development.IDE.Core.PluginUtils (runActionE,
useWithStaleE)
import Development.IDE.Core.PositionMapping (idDelta)
import Development.IDE.Core.Rules (toIdeResult)
import Development.IDE.Core.RuleTypes (DocAndTyThingMap (..))
import Development.IDE.Core.Shake (addPersistentRule,
import Development.IDE.Core.Shake (ShakeExtras (..),
getShakeExtras,
getVirtualFile,
useWithStale_)
import Development.IDE.GHC.Compat hiding (Warning)
Expand All @@ -51,11 +56,13 @@ import Ide.Plugin.SemanticTokens.Tokenize (computeRangeHsSemanti
import Ide.Plugin.SemanticTokens.Types
import Ide.Types
import qualified Language.LSP.Protocol.Lens as L
import Language.LSP.Protocol.Message (Method (Method_TextDocumentSemanticTokensFull))
import Language.LSP.Protocol.Message (MessageResult,
Method (Method_TextDocumentSemanticTokensFull, Method_TextDocumentSemanticTokensFullDelta))
import Language.LSP.Protocol.Types (NormalizedFilePath,
SemanticTokens,
type (|?) (InL))
type (|?) (InL, InR))
import Prelude hiding (span)
import qualified StmContainers.Map as STM


$mkSemanticConfigFunctions
Expand All @@ -68,14 +75,40 @@ computeSemanticTokens :: Recorder (WithPriority SemanticLog) -> PluginId -> IdeS
computeSemanticTokens recorder pid _ nfp = do
config <- lift $ useSemanticConfigAction pid
logWith recorder Debug (LogConfig config)
semanticId <- lift getAndIncreaseSemanticTokensId
(RangeHsSemanticTokenTypes {rangeSemanticList}, mapping) <- useWithStaleE GetSemanticTokens nfp
withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens config mapping rangeSemanticList
withExceptT PluginInternalError $ liftEither $ rangeSemanticsSemanticTokens semanticId config mapping rangeSemanticList

semanticTokensFull :: Recorder (WithPriority SemanticLog) -> PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFull
semanticTokensFull recorder state pid param = do
semanticTokensFull recorder state pid param = runActionE "SemanticTokens.semanticTokensFull" state computeSemanticTokensFull
where
computeSemanticTokensFull :: ExceptT PluginError Action (MessageResult Method_TextDocumentSemanticTokensFull)
computeSemanticTokensFull = do
nfp <- getNormalizedFilePathE (param ^. L.textDocument . L.uri)
items <- computeSemanticTokens recorder pid state nfp
lift $ setSemanticTokens nfp items
return $ InL items


semanticTokensFullDelta :: Recorder (WithPriority SemanticLog) -> PluginMethodHandler IdeState 'Method_TextDocumentSemanticTokensFullDelta
semanticTokensFullDelta recorder state pid param = do
nfp <- getNormalizedFilePathE (param ^. L.textDocument . L.uri)
items <- runActionE "SemanticTokens.semanticTokensFull" state $ computeSemanticTokens recorder pid state nfp
return $ InL items
let previousVersionFromParam = param ^. L.previousResultId
runActionE "SemanticTokens.semanticTokensFullDelta" state $ computeSemanticTokensFullDelta recorder previousVersionFromParam pid state nfp
where
computeSemanticTokensFullDelta :: Recorder (WithPriority SemanticLog) -> Text -> PluginId -> IdeState -> NormalizedFilePath -> ExceptT PluginError Action (MessageResult Method_TextDocumentSemanticTokensFullDelta)
computeSemanticTokensFullDelta recorder previousVersionFromParam pid state nfp = do
semanticTokens <- computeSemanticTokens recorder pid state nfp
previousSemanticTokensMaybe <- lift $ getPreviousSemanticTokens nfp
lift $ setSemanticTokens nfp semanticTokens
case previousSemanticTokensMaybe of
Nothing -> return $ InL semanticTokens
Just previousSemanticTokens ->
if Just previousVersionFromParam == previousSemanticTokens^.L.resultId
then return $ InR $ InL $ makeSemanticTokensDeltaWithId (semanticTokens^.L.resultId) previousSemanticTokens semanticTokens
else do
logWith recorder Warning (LogSemanticTokensDeltaMisMatch previousVersionFromParam (previousSemanticTokens^.L.resultId))
return $ InL semanticTokens

-- | Defines the 'getSemanticTokensRule' function, compute semantic tokens for a Haskell source file.
--
Expand All @@ -98,9 +131,6 @@ getSemanticTokensRule recorder =
let hsFinder = idSemantic getTyThingMap (hieKindFunMasksKind hieKind) refMap
return $ computeRangeHsSemanticTokenTypeList hsFinder virtualFile ast

-- | Persistent rule to ensure that semantic tokens doesn't block on startup
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why drop this?

Copy link
Collaborator Author

@soulomoon soulomoon Feb 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For semantic tokens,
lastValueIO return [](case with persistent key) or Nothing(case without persistent key).
Between faking a stale value [] and mark the semantic tokens rules' value actually Failed in shake state.
The former would hide the failure in both user of the rule and in the shake caching result(I don't think it is wise, we want to know if we fail in this case).

Also we are not using IdeAction with useWithStaleFastMT(Which look directly into the caching and demand instance response) for semantic tokens as in other IdeAction that requires instance response(e.g. hover, this fix startup of hover when the rule first compute without any stale value in the cach).

The persistent rule is rather useless for us and might shadow some actual failures.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so does this mean we have to wait quite a while to get responses when we start up?

Copy link
Collaborator Author

@soulomoon soulomoon Feb 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, as we have been. Persistent rule won't help us here(Since it is not ideAction, the persistent rule serves only as a fall back when the actual computation of the rule failed and not already having a cache) .

persistentGetSemanticTokensRule :: Rules ()
persistentGetSemanticTokensRule = addPersistentRule GetSemanticTokens $ \_ -> pure $ Just (RangeHsSemanticTokenTypes mempty, idDelta, Nothing)

-- taken from /haskell-language-server/plugins/hls-code-range-plugin/src/Ide/Plugin/CodeRange/Rules.hs

Expand All @@ -113,3 +143,22 @@ handleError recorder action' = do
logWith recorder Warning msg
pure $ toIdeResult (Left [])
Right value -> pure $ toIdeResult (Right value)

-----------------------
-- helper functions
-----------------------

-- keep track of the semantic tokens response id
-- so that we can compute the delta between two versions
getAndIncreaseSemanticTokensId :: Action SemanticTokenId
getAndIncreaseSemanticTokensId = do
ShakeExtras{semanticTokensId} <- getShakeExtras
liftIO $ atomically $ do
i <- stateTVar semanticTokensId (\val -> (val, val+1))
return $ T.pack $ show i

getPreviousSemanticTokens :: NormalizedFilePath -> Action (Maybe SemanticTokens)
getPreviousSemanticTokens uri = getShakeExtras >>= liftIO . atomically . STM.lookup uri . semanticTokensCache

setSemanticTokens :: NormalizedFilePath -> SemanticTokens -> Action ()
setSemanticTokens uri tokens = getShakeExtras >>= liftIO . atomically . STM.insert tokens uri . semanticTokensCache
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}


-- |
-- This module provides mappings to convert token type information in the Haskell IDE plugin. It includes functions for:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}

-- |
-- The query module is used to query the semantic tokens from the AST
module Ide.Plugin.SemanticTokens.Query where
Expand All @@ -18,13 +15,16 @@ import Ide.Plugin.SemanticTokens.Mappings
import Ide.Plugin.SemanticTokens.Types (HieFunMaskKind,
HsSemanticTokenType (TModule),
RangeSemanticTokenTypeList,
SemanticTokenId,
SemanticTokensConfig)
import Language.LSP.Protocol.Types (Position (Position),
Range (Range),
SemanticTokenAbsolute (SemanticTokenAbsolute),
SemanticTokens,
SemanticTokens (SemanticTokens),
SemanticTokensDelta (SemanticTokensDelta),
defaultSemanticTokensLegend,
makeSemanticTokens)
makeSemanticTokens,
makeSemanticTokensDelta)
import Prelude hiding (length, span)

---------------------------------------------------------
Expand All @@ -47,8 +47,7 @@ idSemantic tyThingMap hieKind rm (Right n) =
---------------------------------------------------------

nameSemanticFromHie :: forall a. HieFunMaskKind a -> RefMap a -> Name -> Maybe HsSemanticTokenType
nameSemanticFromHie hieKind rm n = do
idSemanticFromRefMap rm (Right n)
nameSemanticFromHie hieKind rm n = idSemanticFromRefMap rm (Right n)
where
idSemanticFromRefMap :: RefMap a -> Identifier -> Maybe HsSemanticTokenType
idSemanticFromRefMap rm' name' = do
Expand All @@ -67,10 +66,9 @@ nameSemanticFromHie hieKind rm n = do

-------------------------------------------------

rangeSemanticsSemanticTokens :: SemanticTokensConfig -> PositionMapping -> RangeSemanticTokenTypeList -> Either Text SemanticTokens
rangeSemanticsSemanticTokens stc mapping =
makeSemanticTokens defaultSemanticTokensLegend
. mapMaybe (\(ran, tk) -> toAbsSemanticToken <$> toCurrentRange mapping ran <*> return tk)
rangeSemanticsSemanticTokens :: SemanticTokenId -> SemanticTokensConfig -> PositionMapping -> RangeSemanticTokenTypeList -> Either Text SemanticTokens
rangeSemanticsSemanticTokens sid stc mapping =
makeSemanticTokensWithId (Just sid) . mapMaybe (\(ran, tk) -> toAbsSemanticToken <$> toCurrentRange mapping ran <*> return tk)
where
toAbsSemanticToken :: Range -> HsSemanticTokenType -> SemanticTokenAbsolute
toAbsSemanticToken (Range (Position startLine startColumn) (Position _endLine endColumn)) tokenType =
Expand All @@ -81,3 +79,14 @@ rangeSemanticsSemanticTokens stc mapping =
(fromIntegral len)
(toLspTokenType stc tokenType)
[]

makeSemanticTokensWithId :: Maybe SemanticTokenId -> [SemanticTokenAbsolute] -> Either Text SemanticTokens
makeSemanticTokensWithId sid tokens = do
(SemanticTokens _ tokens) <- makeSemanticTokens defaultSemanticTokensLegend tokens
return $ SemanticTokens sid tokens

makeSemanticTokensDeltaWithId :: Maybe SemanticTokenId -> SemanticTokens -> SemanticTokens -> SemanticTokensDelta
makeSemanticTokensDeltaWithId sid previousTokens currentTokens =
let (SemanticTokensDelta _ stEdits) = makeSemanticTokensDelta previousTokens currentTokens
in SemanticTokensDelta sid stEdits

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Development.IDE.Graph.Classes (Hashable)
import GHC.Generics (Generic)
import Language.LSP.Protocol.Types
-- import template haskell
import Data.Text (Text)
import Language.Haskell.TH.Syntax (Lift)


Expand Down Expand Up @@ -140,6 +141,7 @@ data SemanticLog
| LogConfig SemanticTokensConfig
| LogMsg String
| LogNoVF
| LogSemanticTokensDeltaMisMatch Text (Maybe Text)
deriving (Show)

instance Pretty SemanticLog where
Expand All @@ -149,4 +151,9 @@ instance Pretty SemanticLog where
LogNoVF -> "no VirtualSourceFile exist for file"
LogConfig config -> "SemanticTokensConfig_: " <> pretty (show config)
LogMsg msg -> "SemanticLog Debug Message: " <> pretty msg
LogSemanticTokensDeltaMisMatch previousIdFromRequest previousIdFromCache
-> "SemanticTokensDeltaMisMatch: previousIdFromRequest: " <> pretty previousIdFromRequest
<> " previousIdFromCache: " <> pretty previousIdFromCache


type SemanticTokenId = Text
Loading