Skip to content

Commit

Permalink
Add the initial support for semantic highlighting
Browse files Browse the repository at this point in the history
  • Loading branch information
yegappan committed Dec 3, 2023
1 parent dd32aec commit 9bfc47d
Show file tree
Hide file tree
Showing 8 changed files with 415 additions and 31 deletions.
59 changes: 30 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,45 +110,46 @@ Some of the LSP plugin features can be enabled or disabled by using the LspOptio
Here is an example of configuration with default values:
```viml
call LspOptionsSet(#{
\ aleSupport: false,
\ autoComplete: true,
\ autoHighlight: false,
\ autoHighlightDiags: true,
\ autoPopulateDiags: false,
\ aleSupport: v:false,
\ autoComplete: v:true,
\ autoHighlight: v:false,
\ autoHighlightDiags: v:true,
\ autoPopulateDiags: v:false,
\ completionMatcher: 'case',
\ completionMatcherValue: 1,
\ diagSignErrorText: 'E>',
\ diagSignHintText: 'H>',
\ diagSignInfoText: 'I>',
\ diagSignWarningText: 'W>',
\ echoSignature: false,
\ hideDisabledCodeActions: false,
\ highlightDiagInline: true,
\ hoverInPreview: false,
\ ignoreMissingServer: false,
\ keepFocusInDiags: true,
\ keepFocusInReferences: true,
\ completionTextEdit: true,
\ echoSignature: v:false,
\ hideDisabledCodeActions: v:false,
\ highlightDiagInline: v:true,
\ hoverInPreview: v:false,
\ ignoreMissingServer: v:false,
\ keepFocusInDiags: v:true,
\ keepFocusInReferences: v:true,
\ completionTextEdit: v:true,
\ diagVirtualTextAlign: 'above',
\ noNewlineInCompletion: false,
\ noNewlineInCompletion: v:false,
\ omniComplete: null,
\ outlineOnRight: false,
\ outlineOnRight: v:false,
\ outlineWinSize: 20,
\ showDiagInBalloon: true,
\ showDiagInPopup: true,
\ showDiagOnStatusLine: false,
\ showDiagWithSign: true,
\ showDiagWithVirtualText: false,
\ showInlayHints: false,
\ showSignature: true,
\ snippetSupport: false,
\ ultisnipsSupport: false,
\ useBufferCompletion: false,
\ usePopupInCodeAction: false,
\ useQuickfixForLocations: false,
\ vsnipSupport: false,
\ semanticHighlight: v:true,
\ showDiagInBalloon: v:true,
\ showDiagInPopup: v:true,
\ showDiagOnStatusLine: v:false,
\ showDiagWithSign: v:true,
\ showDiagWithVirtualText: v:false,
\ showInlayHints: v:false,
\ showSignature: v:true,
\ snippetSupport: v:false,
\ ultisnipsSupport: v:false,
\ useBufferCompletion: v:false,
\ usePopupInCodeAction: v:false,
\ useQuickfixForLocations: v:false,
\ vsnipSupport: v:false,
\ bufferCompletionTimeout: 100,
\ customCompletionKinds: false,
\ customCompletionKinds: v:false,
\ completionKinds: {}
\ })
```
Expand Down
1 change: 1 addition & 0 deletions autoload/lsp/buffer.vim
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ var SupportedCheckFns = {
references: (lspserver) => lspserver.isReferencesProvider,
rename: (lspserver) => lspserver.isRenameProvider,
selectionRange: (lspserver) => lspserver.isSelectionRangeProvider,
semanticTokens: (lspserver) => lspserver.isSemanticTokensProvider,
signatureHelp: (lspserver) => lspserver.isSignatureHelpProvider,
typeDefinition: (lspserver) => lspserver.isTypeDefinitionProvider,
typeHierarchy: (lspserver) => lspserver.isTypeHierarchyProvider,
Expand Down
54 changes: 54 additions & 0 deletions autoload/lsp/capabilities.vim
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,35 @@ export def ProcessServerCaps(lspserver: dict<any>, caps: dict<any>)
lspserver.isCallHierarchyProvider = false
endif

# semanticTokensProvider
if lspserver.caps->has_key('semanticTokensProvider')
lspserver.isSemanticTokensProvider = true
lspserver.semanticTokensLegend =
lspserver.caps.semanticTokensProvider.legend
lspserver.semanticTokensRange =
lspserver.caps.semanticTokensProvider->get('range', false)
if lspserver.caps.semanticTokensProvider->has_key('full')
if lspserver.caps.semanticTokensProvider.full->type() == v:t_bool
lspserver.semanticTokensFull =
lspserver.caps.semanticTokensProvider.full
lspserver.semanticTokensDelta = false
else
lspserver.semanticTokensFull = true
if lspserver.caps.semanticTokensProvider.full->has_key('delta')
lspserver.semanticTokensDelta =
lspserver.caps.semanticTokensProvider.full.delta
else
lspserver.semanticTokensDelta = false
endif
endif
else
lspserver.semanticTokensfull = false
lspserver.semanticTokensdelta = false
endif
else
lspserver.isSemanticTokensProvider = false
endif

# typeHierarchyProvider
if lspserver.caps->has_key('typeHierarchyProvider')
lspserver.isTypeHierarchyProvider = true
Expand Down Expand Up @@ -404,6 +433,31 @@ export def GetClientCaps(): dict<any>
activeParameterSupport: true
}
},
semanticTokens: {
dynamicRegistration: false,
requests: {
range: false,
full: {
delta: true
}
},
tokenTypes: [
'type', 'class', 'enum', 'interface', 'struct', 'typeParameter',
'parameter', 'variable', 'property', 'enumMember', 'event',
'function', 'method', 'macro', 'keyword', 'modifier', 'comment',
'string', 'number', 'regexp', 'operator'
],
tokenModifiers: [
'declaration', 'definition', 'readonly', 'static', 'deprecated',
'abstract', 'async', 'modification', 'documentation',
'defaultLibrary'
],
formats: ['relative'],
overlappingTokenSupport: false,
multilineTokenSupport: false,
serverCancelSupport: false,
augmentsSyntaxTokens: true
},
synchronization: {
dynamicRegistration: false,
didSave: true,
Expand Down
16 changes: 16 additions & 0 deletions autoload/lsp/lsp.vim
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import './outline.vim'
import './signature.vim'
import './codeaction.vim'
import './inlayhints.vim'
import './semantichighlight.vim'

# LSP server information
var LSPServers: list<dict<any>> = []
Expand Down Expand Up @@ -48,6 +49,7 @@ def LspInitOnce()
inlayhints.InitOnce()
signature.InitOnce()
symbol.InitOnce()
semantichighlight.InitOnce()

lspInitializedOnce = true
enddef
Expand Down Expand Up @@ -424,6 +426,11 @@ def BufferInit(lspserverId: number, bnr: number): void
if !inlayHintServer->empty() && lspsrv.id == inlayHintServer.id
inlayhints.BufferInit(lspsrv, bnr)
endif

var semanticServer = buf.BufLspServerGet(bnr, 'semanticTokens')
if !semanticServer->empty() && lspsrv.id == semanticServer.id
semantichighlight.BufferInit(lspserver, bnr)
endif
endfor

if exists('#User#LspAttached')
Expand Down Expand Up @@ -503,7 +510,16 @@ export def BufferLoadedInWin(bnr: number)
if opt.lspOptions.autoHighlightDiags
diag.DiagsRefresh(bnr)
endif

completion.BufferLoadedInWin(bnr)

# Refresh the semantic highlights
if opt.lspOptions.semanticHighlight
var semanticServer = buf.BufLspServerGet(bnr, 'semanticTokens')
if !semanticServer->empty()
semanticServer.semanticHighlightUpdate(bnr)
endif
endif
enddef

# Stop all the LSP servers
Expand Down
40 changes: 40 additions & 0 deletions autoload/lsp/lspserver.vim
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import './codelens.vim'
import './callhierarchy.vim' as callhier
import './typehierarchy.vim' as typehier
import './inlayhints.vim'
import './semantichighlight.vim'

# LSP server standard output handler
def Output_cb(lspserver: dict<any>, chan: channel, msg: any): void
Expand Down Expand Up @@ -531,6 +532,44 @@ def WorkspaceConfigGet(lspserver: dict<any>, configItem: dict<any>): dict<any>
return config
enddef

# Update semantic highlighting for buffer "bnr"
# Request: textDocument/semanticTokens/full or
# textDocument/semanticTokens/full/delta
def SemanticHighlightUpdate(lspserver: dict<any>, bnr: number)
if !lspserver.isSemanticTokensProvider
return
endif

# Send the pending buffer changes to the language server
bnr->listener_flush()

var method = 'textDocument/semanticTokens/full'
var params: dict<any> = {
textDocument: {
uri: util.LspBufnrToUri(bnr)
}
}

# Should we send a semantic tokens delta request instead of a full request?
if lspserver.semanticTokensDelta
var prevResultId: string = ''
prevResultId = bnr->getbufvar('LspSemanticResultId', '')
if prevResultId != ''
# semantic tokens delta request
params.previousResultId = prevResultId
method ..= '/delta'
endif
endif

var reply = lspserver.rpc(method, params)

if reply->empty() || reply.result->empty()
return
endif

semantichighlight.UpdateTokens(lspserver, bnr, reply.result)
enddef

# Send a "workspace/didChangeConfiguration" notification to the language
# server.
def SendWorkspaceConfig(lspserver: dict<any>)
Expand Down Expand Up @@ -1925,6 +1964,7 @@ export def NewLspServer(serverParams: dict<any>): dict<any>
foldRange: function(FoldRange, [lspserver]),
executeCommand: function(ExecuteCommand, [lspserver]),
workspaceConfigGet: function(WorkspaceConfigGet, [lspserver]),
semanticHighlightUpdate: function(SemanticHighlightUpdate, [lspserver]),
getCapabilities: function(GetCapabilities, [lspserver]),
getInitializeRequest: function(GetInitializeRequest, [lspserver]),
addMessage: function(AddMessage, [lspserver]),
Expand Down
3 changes: 3 additions & 0 deletions autoload/lsp/options.vim
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ export var lspOptions: dict<any> = {
# Outline window size
outlineWinSize: 20,

# Enable semantic highlighting
semanticHighlight: false,

# Show diagnostic text in a balloon when the mouse is over the diagnostic
showDiagInBalloon: true,

Expand Down
Loading

0 comments on commit 9bfc47d

Please sign in to comment.