1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use lsp_types::ClientCapabilities;
use super::client_id::ClientId;
#[derive(Debug)]
pub struct LspFeatures {
client_capabilities: ClientCapabilities,
/// **Which editor this is**, from `initialize.clientInfo` — the question a *custom* semantic token type turns on,
/// because whether a client can draw one is not in its capability list. See
/// [`LspFeatures::client_id`].
client_id: ClientId,
}
#[allow(unused)]
impl LspFeatures {
pub fn new(client_capabilities: ClientCapabilities, client_id: ClientId) -> Self {
Self {
client_capabilities,
client_id,
}
}
/// **Which editor asked.** A client's capability list names the *standard* semantic token types it knows, so a
/// custom one (`formatSpecifier`) is never in it — even for a client that draws it through its own theme
/// mapping. This is the fact that decides whether a custom type is still worth sending; see
/// [`crate::handlers::semantic_token`] and `draw.rs`'s note on the fallbacks.
pub fn client_id(&self) -> ClientId {
self.client_id
}
pub fn supports_multiline_tokens(&self) -> bool {
self.client_capabilities
.text_document
.as_ref()
.and_then(|text_document| text_document.semantic_tokens.as_ref())
.and_then(|semantic_tokens| semantic_tokens.multiline_token_support)
.unwrap_or_default()
}
/// **The token types the client says it can draw** — `None` when it does not do semantic tokens at all.
///
/// The server's legend is fixed (the numbers in an answer are indices into it), so this is a *filter* rather
/// than a legend: a kind the client cannot draw is left out of the answer instead of being sent as a number it
/// maps to nothing. An empty list is treated as "said nothing" by the caller, because a client that asks for
/// tokens without naming any type is one this server cannot filter for.
pub fn semantic_token_types(&self) -> Option<Vec<lsp_types::SemanticTokenType>> {
self.client_capabilities
.text_document
.as_ref()
.and_then(|text_document| text_document.semantic_tokens.as_ref())
.map(|semantic_tokens| semantic_tokens.token_types.clone())
}
pub fn supports_config_request(&self) -> bool {
self.client_capabilities
.workspace
.as_ref()
.and_then(|workspace| workspace.configuration)
.unwrap_or_default()
}
pub fn supports_work_done_progress(&self) -> bool {
self.client_capabilities
.window
.as_ref()
.and_then(|window| window.work_done_progress)
.unwrap_or_default()
}
/// **Can the client interpolate a snippet body?**
///
/// The one capability a completion has to branch on, because the cost of guessing wrong is asymmetric: a client
/// that supports snippets and is not sent any loses a convenience, while a client that does not and is sent one
/// writes `${1:condition}` into the file as literal text.
///
/// `snippetSupport` is the protocol's flag for exactly this question, and its absence is a **no**: the field's
/// own note says a client that does not set it is assumed to take plain text only. That default matters here
/// more than usual, because the failure is not a missing feature — it is placeholder syntax written into the
/// user's file, which they then have to delete.
pub fn supports_snippets(&self) -> bool {
self.client_capabilities
.text_document
.as_ref()
.and_then(|text_document| text_document.completion.as_ref())
.and_then(|completion| completion.completion_item.as_ref())
.and_then(|item| item.snippet_support)
.unwrap_or(false)
}
pub fn supports_pull_diagnostic(&self) -> bool {
if let Some(text_document) = &self.client_capabilities.text_document {
return text_document.diagnostic.is_some();
}
false
}
pub fn supports_workspace_diagnostic(&self) -> bool {
self.supports_pull_diagnostic()
}
/// May the server ask the client to ask again for the inlay hints it is showing?
///
/// The capability that makes a hint answer **repairable**. A request that arrives while the index is still
/// reading is answered with *fewer* hints than the file deserves — a callee in a file nobody has read yet
/// resolves to nothing, and a hint naming the wrong parameter would be worse than none — so the answer changes
/// without the document changing, which is the one case a client cannot notice by itself. See
/// [`Client::refresh_inlay_hints`], where the measurement behind this lives.
pub fn supports_refresh_inlay_hint(&self) -> bool {
self.client_capabilities
.workspace
.as_ref()
.and_then(|workspace| workspace.inlay_hint.as_ref())
.and_then(|hint| hint.refresh_support)
.unwrap_or_default()
}
/// May the server ask the client to ask again for the **semantic tokens** it is showing?
///
/// The same repair as [`LspFeatures::supports_refresh_inlay_hint`], for the other answer that changes without
/// the document changing: a name whose declaration lives in a header nobody has read yet is drawn with the
/// client's own grammar colour rather than the analysis's, and the client has no reason to ask again. A server
/// that answers a token request while the file's include closure is still unread therefore produces a file
/// coloured **once, wrongly**, until the next edit — which is what "`std::endl` is not a function until I type
/// something" was.
pub fn supports_refresh_semantic_tokens(&self) -> bool {
self.client_capabilities
.workspace
.as_ref()
.and_then(|workspace| workspace.semantic_tokens.as_ref())
.and_then(|tokens| tokens.refresh_support)
.unwrap_or_default()
}
pub fn supports_dynamic_watched_files_registration(&self) -> bool {
self.client_capabilities
.workspace
.as_ref()
.and_then(|ws| ws.did_change_watched_files.as_ref())
.and_then(|watch| watch.dynamic_registration)
.unwrap_or_default()
}
}