Skip to content

Commit 3f86943

Browse files
authored
Implement textDocument/semanticTokens/full (#1194)
* Implement textDocument/semanticTokens/full * Update documentation * Call textDocument/semanticTokens/full on open, change and save (+ lint fixes) * Remove default mappings
1 parent b930764 commit 3f86943

File tree

8 files changed

+433
-571
lines changed

8 files changed

+433
-571
lines changed

autoload/LanguageClient.vim

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,22 @@ function! LanguageClient#Notify(method, params) abort
907907
\ }))
908908
endfunction
909909

910+
function! LanguageClient#textDocument_semanticTokensFull(...) abort
911+
if s:ShouldUseFloatWindow() && s:MoveIntoHoverPreview('__LCNHover__')
912+
return
913+
endif
914+
let l:Callback = get(a:000, 1, v:null)
915+
let l:params = {
916+
\ 'filename': LSP#filename(),
917+
\ 'text': LSP#text(),
918+
\ 'line': LSP#line(),
919+
\ 'character': LSP#character(),
920+
\ 'handle': s:IsFalse(l:Callback),
921+
\ }
922+
call extend(l:params, get(a:000, 0, {}))
923+
return LanguageClient#Call('textDocument/semanticTokens/full', l:params, l:Callback)
924+
endfunction
925+
910926
function! LanguageClient#textDocument_hover(...) abort
911927
if s:ShouldUseFloatWindow() && s:MoveIntoHoverPreview('__LCNHover__')
912928
return
@@ -1692,20 +1708,6 @@ function! s:print_semantic_scopes(response) abort
16921708
echo l:msg
16931709
endfunction
16941710

1695-
function! LanguageClient#showSemanticHighlightSymbols(...) abort
1696-
let l:params = get(a:000, 0, {})
1697-
let l:Callback = get(a:000, 1, v:null)
1698-
1699-
return LanguageClient#Call('languageClient/showSemanticHighlightSymbols', l:params, l:Callback)
1700-
endfunction
1701-
1702-
function! LanguageClient_showCursorSemanticHighlightSymbols(...) abort
1703-
let l:params = get(a:000, 0, {})
1704-
let l:Callback = get(a:000, 1, function('s:print_cursor_semantic_symbol'))
1705-
1706-
return LanguageClient#showSemanticHighlightSymbols(l:params, l:Callback)
1707-
endfunction
1708-
17091711
function! s:print_cursor_semantic_symbol(response) abort
17101712
let l:symbols = a:response.result
17111713
let l:lines = []

doc/LanguageClient.txt

Lines changed: 38 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -516,104 +516,7 @@ Valid options: 1 | 0
516516

517517
2.33 g:LanguageClient_semanticHighlightMaps *g:LanguageClient_semanticHighlightMaps*
518518

519-
String to list/map map. Defines the mapping of semantic highlighting "scopes" to
520-
highlight groups. This depends on the LSP server supporting the proposed
521-
semantic highlighting protocol, see:
522-
523-
https://github.com/microsoft/language-server-protocol/issues/18
524-
https://github.com/microsoft/vscode-languageserver-node/issues/368
525-
526-
527-
Like |g:LanguageClient_serverCommands| this is a map where the keys are
528-
filetypes. However each submap has |regexp| keys and highlight group names
529-
as values (see |highlight-groups|).
530-
>
531-
let g:LanguageClient_semanticHighlightMaps = {
532-
\ 'java': {
533-
\ '^entity.name.function.java': 'Function',
534-
\ '^entity.name.type.class.java': 'Type',
535-
\ '^[^:]*entity.name.function.java': 'Function',
536-
\ '^[^:]entity.name.type.class.java': 'Type'
537-
\ }
538-
\ }
539-
540-
The |regexp| in the keys will be used to match semantic scopes. Then any symbols
541-
that have a semantic scope that matches the key will be highlighted with the
542-
associated highlight group value. Currently there is no defined order if a
543-
semantic scope can match multiple keys, so it is recommended to make the keys
544-
more specific to only match the desired scope(s).
545-
546-
There are a fixed set of semantic scopes defined by the LSP server on startup.
547-
These can be viewed by calling |LanguageClient_showSemanticScopes| which will
548-
show all the semantic scopes and their currently mapped highlight group for
549-
the currently open buffer's filetype.
550-
>
551-
call LanguageClient_showSemanticScopes()
552-
553-
== Output from eclipse.jdt.ls ==
554-
555-
Highlight Group:
556-
None
557-
Semantic Scope:
558-
invalid.deprecated.java
559-
meta.class.java
560-
source.java
561-
562-
Highlight Group:
563-
None
564-
Semantic Scope:
565-
variable.other.autoboxing.java
566-
meta.method.body.java
567-
meta.method.java
568-
meta.class.body.java
569-
meta.class.java
570-
source.java
571-
572-
...
573-
574-
Each semantic scope is a list of strings. They are printed with increasing
575-
indent to make it easier to read. For example the first scope is:
576-
>
577-
['invalid.deprecated.java', 'meta.class.java', 'source.java']
578-
579-
It is currently isn't mapped to any highlight group as indicated by the None.
580-
581-
Often its more useful to find what semantic scope corresponds to a piece of
582-
text. This can be done by calling |LanguageClient_showCursorSemanticHighlightSymbols|
583-
while hovering over the text of interest.
584-
>
585-
call LanguageClient_showCursorSemanticHighlightSymbols()
586-
587-
When matching the semantic scopes to keys in |LanguageClient_semanticHighlightMaps|,
588-
the scopes are concatentated using |LanguageClient_semanticScopeSeparator|
589-
which is set to the string |':'| by default. For the previous example the
590-
semantic scope would have this string form using the default separator:
591-
>
592-
invalid.deprecated.java:meta.class.java:source.java
593-
594-
Here are a couple of example |regexp| keys that can/cannot match this scope:
595-
>
596-
'meta.class.java' =~ 'invalid.deprecated.java:meta.class.java:source.java'
597-
'^meta.class.java' !~ 'invalid.deprecated.java:meta.class.java:source.java'
598-
'^invalid.deprecated.java' =~ 'invalid.deprecated.java:meta.class.java:source.java'
599-
'source.java$' =~ 'invalid.deprecated.java:meta.class.java:source.java'
600-
'meta.class.java:source.java' =~ 'invalid.deprecated.java:meta.class.java:source.java'
601-
'invalid.deprecated.java:.*:source.java' =~ 'invalid.deprecated.java:meta.class.java:source.java'
602-
603-
Example configuration for eclipse.jdt.ls:
604-
>
605-
let g:LanguageClient_semanticHighlightMaps = {}
606-
let g:LanguageClient_semanticHighlightMaps['java'] = {
607-
\ '^storage.modifier.static.java:entity.name.function.java': 'JavaStaticMemberFunction',
608-
\ '^meta.definition.variable.java:meta.class.body.java:meta.class.java': 'JavaMemberVariable',
609-
\ '^entity.name.function.java': 'Function',
610-
\ '^[^:]*entity.name.function.java': 'Function',
611-
\ '^[^:]*entity.name.type.class.java': 'Type',
612-
\ }
613-
614-
highlight! JavaStaticMemberFunction ctermfg=Green cterm=none guifg=Green gui=none
615-
highlight! JavaMemberVariable ctermfg=White cterm=italic guifg=White gui=italic
616-
519+
Deprecated, see g:LanguageClient_semanticTokenMappings.
617520

618521
2.34 g:LanguageClient_applyCompletionAdditionalTextEdits *g:LanguageClient_applyCompletionAdditionalTextEdits*
619522

@@ -760,6 +663,43 @@ Show completion item documentation in a floating window right next to pmenu.
760663
Default: 1
761664
Valid options: 1 | 0
762665

666+
2.46 g:LanguageClient_semanticHighlightingEnabled *g:LanguageClient_semanticHighlightingEnabled*
667+
668+
If enabled, the client will call the LSP semantic tokens method after opening,
669+
modifying or saving a buffer to try and apply semantic highlighting to the
670+
buffer.
671+
672+
Default: 0
673+
Valid options: 1 | 0
674+
675+
2.47 g:LanguageClient_semanticTokenMappings *g:LanguageClient_semanticTokenMappings*
676+
677+
Maps LSP token types to vim highlight groups. Each item in this array is a
678+
dictionary with the fields name, modifiers and highlightGroup, where name is
679+
the name of the LSP token type, modifiers is a list of modifiers that need to
680+
be present in that token for this mapping to match and highlightGroup is the
681+
vim highlight group that should be applied to the token.
682+
683+
Example:
684+
685+
>
686+
let g:LanguageClient_semanticTokenMappings = [
687+
{ 'name': 'comment', 'modifiers': [], 'highlightGroup': 'Comment' },
688+
{ 'name': 'comment', 'modifiers': ['documentation'], 'highlightGroup': 'Question' },
689+
];
690+
<
691+
692+
The first mapping in that example maps the tokens of type `comment` to the
693+
highlight group `Comment`, and the second matches the tokens of type `comment`
694+
that have the `documentation` modifier to the `Question` highlight group, this
695+
way documentation comments and regular comments are (potentially) displayed
696+
with a different colour.
697+
698+
Default: []
699+
700+
For a full list of the possible token types (name) see the LSP documentation:
701+
https://microsoft.github.io/language-server-protocol/specifications/specification-current/
702+
763703
==============================================================================
764704
3. Commands *LanguageClientCommands*
765705

src/config/mod.rs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
mod server_command;
22

3-
pub use server_command::*;
4-
53
use crate::{
64
types::{
75
CodeLensDisplay, DiagnosticsDisplay, DiagnosticsList, DocumentHighlightDisplay,
@@ -12,6 +10,7 @@ use crate::{
1210
use anyhow::{anyhow, Result};
1311
use lsp_types::{DiagnosticSeverity, MarkupKind, MessageType, TraceOption};
1412
use serde::Deserialize;
13+
pub use server_command::*;
1514
use std::collections::HashMap;
1615
use std::{path::PathBuf, str::FromStr, time::Duration};
1716

@@ -32,6 +31,26 @@ impl LoggerConfig {
3231
}
3332
}
3433

34+
#[derive(Debug, Clone, Deserialize, PartialEq)]
35+
#[serde(rename_all = "camelCase")]
36+
pub struct SemanticTokenMapping {
37+
pub name: String,
38+
#[serde(default)]
39+
pub modifiers: Vec<String>,
40+
pub highlight_group: String,
41+
}
42+
43+
impl SemanticTokenMapping {
44+
#[allow(dead_code)]
45+
pub fn new(name: &str, modifiers: &[&str], highlight_group: &str) -> Self {
46+
Self {
47+
name: name.to_owned(),
48+
modifiers: modifiers.iter().map(|i| i.to_string()).collect(),
49+
highlight_group: highlight_group.to_owned(),
50+
}
51+
}
52+
}
53+
3554
#[derive(Debug)]
3655
pub struct Config {
3756
pub auto_start: bool,
@@ -59,22 +78,32 @@ pub struct Config {
5978
pub selection_ui_auto_open: bool,
6079
pub use_virtual_text: UseVirtualText,
6180
pub echo_project_root: bool,
62-
pub semantic_highlight_maps: HashMap<String, HashMap<String, String>>,
63-
pub semantic_scope_separator: String,
6481
pub apply_completion_text_edits: bool,
6582
pub preferred_markup_kind: Option<Vec<MarkupKind>>,
6683
pub hide_virtual_texts_on_insert: bool,
6784
pub enable_extensions: Option<HashMap<String, bool>>,
6885
pub restart_on_crash: bool,
6986
pub max_restart_retries: u8,
87+
/// semantic_token_mappings is a vec of SemanticTokenMappings, where a SemanticTokenMapping
88+
/// defines the token type by it's name, the modifiers and the highlight group to be applied to
89+
/// it.
90+
///
91+
/// If no modifiers are configured for a type it will apply for all tokens of that type.
92+
///
93+
/// For example:
94+
///
95+
/// [
96+
/// { "name": "function", "modifiers": ["async"], "highlightGroup": "Function" }
97+
/// { "name": "type", "modifiers": [], "highlightGroup": "Type" }
98+
/// ]
99+
pub semantic_token_mappings: Vec<SemanticTokenMapping>,
100+
pub semantic_highlighting_enabled: bool,
70101
}
71102

72103
impl Default for Config {
73104
fn default() -> Self {
74105
Self {
75106
server_commands: HashMap::new(),
76-
semantic_highlight_maps: HashMap::new(),
77-
semantic_scope_separator: ":".into(),
78107
auto_start: true,
79108
selection_ui: SelectionUI::LocationList,
80109
selection_ui_auto_open: true,
@@ -105,6 +134,8 @@ impl Default for Config {
105134
is_nvim: false,
106135
restart_on_crash: true,
107136
max_restart_retries: 5,
137+
semantic_token_mappings: vec![],
138+
semantic_highlighting_enabled: false,
108139
}
109140
}
110141
}
@@ -135,15 +166,15 @@ struct DeserializableConfig {
135166
selection_ui_auto_open: u8,
136167
use_virtual_text: UseVirtualText,
137168
echo_project_root: u8,
138-
semantic_highlight_maps: HashMap<String, HashMap<String, String>>,
139-
semantic_scope_separator: String,
140169
apply_completion_text_edits: u8,
141170
preferred_markup_kind: Option<Vec<MarkupKind>>,
142171
hide_virtual_texts_on_insert: u8,
143172
enable_extensions: Option<HashMap<String, bool>>,
144173
code_lens_display: Option<CodeLensDisplay>,
145174
restart_on_crash: u8,
146175
max_restart_retries: u8,
176+
semantic_token_mappings: Vec<SemanticTokenMapping>,
177+
semantic_highlighting_enabled: u8,
147178
}
148179

149180
impl Config {
@@ -172,8 +203,6 @@ impl Config {
172203
"selection_ui_auto_open": !!s:GetVar('LanguageClient_selectionUI_autoOpen', 1),
173204
"use_virtual_text": s:useVirtualText(),
174205
"echo_project_root": !!s:GetVar('LanguageClient_echoProjectRoot', 1),
175-
"semantic_highlight_maps": s:GetVar('LanguageClient_semanticHighlightMaps', {}),
176-
"semantic_scope_separator": s:GetVar('LanguageClient_semanticScopeSeparator', ':'),
177206
"apply_completion_text_edits": get(g:, 'LanguageClient_applyCompletionAdditionalTextEdits', 1),
178207
"preferred_markup_kind": get(g:, 'LanguageClient_preferredMarkupKind', v:null),
179208
"hide_virtual_texts_on_insert": s:GetVar('LanguageClient_hideVirtualTextsOnInsert', 0),
@@ -182,10 +211,11 @@ impl Config {
182211
"restart_on_crash": get(g:, 'LanguageClient_restartOnCrash', 1),
183212
"max_restart_retries": get(g:, 'LanguageClient_maxRestartRetries', 5),
184213
"server_stderr": get(g:, 'LanguageClient_serverStderr', v:null),
214+
"semantic_token_mappings": get(g:, 'LanguageClient_semanticTokenMappings', []),
215+
"semantic_highlighting_enabled": get(g:, 'LanguageClient_semanticHighlightingEnabled', 0),
185216
}"#;
186217

187218
let res: DeserializableConfig = vim.eval(req.replace("\n", ""))?;
188-
189219
let loaded_fzf = vim.eval::<_, i64>("get(g:, 'loaded_fzf')")? == 1;
190220
let selection_ui = match res.selection_ui {
191221
Some(s) => SelectionUI::from_str(&s)?,
@@ -238,14 +268,14 @@ impl Config {
238268
selection_ui_auto_open: res.selection_ui_auto_open == 1,
239269
use_virtual_text: res.use_virtual_text,
240270
echo_project_root: res.echo_project_root == 1,
241-
semantic_highlight_maps: res.semantic_highlight_maps,
242-
semantic_scope_separator: res.semantic_scope_separator,
243271
apply_completion_text_edits: res.apply_completion_text_edits == 1,
244272
preferred_markup_kind: res.preferred_markup_kind,
245273
hide_virtual_texts_on_insert: res.hide_virtual_texts_on_insert == 1,
246274
enable_extensions: res.enable_extensions,
247275
restart_on_crash: res.restart_on_crash == 1,
248276
max_restart_retries: res.max_restart_retries,
277+
semantic_token_mappings: res.semantic_token_mappings,
278+
semantic_highlighting_enabled: res.semantic_highlighting_enabled == 1,
249279
})
250280
}
251281
}

0 commit comments

Comments
 (0)