Skip to content

Commit 5e8a8f0

Browse files
alcarneydanixeee
andauthored
Fix how semantic tokens server capabilities are computed (#213)
* Fix server capabilities for semantic tokens. A server can support up to three separate semantic tokens requests. Rather than have `@server.feature()` methods provide the full `SemanticTokensOptions` definition, this commit adjusts the `LSP_METHODS_MAP` to accept just the `SemanticTokensLengend`. Then in the `ServerCapabilitiesBuilder`, the `full` and `range` fields of `SemanticTokensOptions` are computed according to which features are present. Since `SemanticTokensOptions` only supports a single legend, if multiple legends are found, only the first one will be used. * Add semantic tokens example This updates the example `json-extension` to include an example `textDocument/semanticTokens/full` implementation that highlights all keys in a JSON document. * Update changelog Co-authored-by: Daniel Elero <[email protected]>
1 parent ea11bc2 commit 5e8a8f0

File tree

6 files changed

+355
-22
lines changed

6 files changed

+355
-22
lines changed

CHANGELOG.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,16 @@ and this project adheres to [Semantic Versioning][semver].
1111

1212
### Changed
1313

14-
### Fixed
15-
16-
## [0.11.3] - 09/30/2021
17-
18-
### Added
19-
20-
### Changed
14+
- Update json-example to include an example semantic tokens method ([204])
2115

2216
### Fixed
2317

2418
- Fix example extension client not detecting debug mode appropriately ([#193])
19+
- Fix how the `semantic_tokens_provider` field of `ServerCapabilities` is computed ([213])
2520

2621
[#193]: https://github.com/openlawlibrary/pygls/issues/193
22+
[#204]: https://github.com/openlawlibrary/pygls/issues/204
23+
[#213]: https://github.com/openlawlibrary/pygls/pulls/213
2724

2825
## [0.11.2] - 07/23/2021
2926

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Contributors (alphabetical)
22

33
- [@augb](https://github.com/augb)
4+
- [Alex Carney](https://github.com/alcarney)
45
- [Brett Cannon](https://github.com/brettcannon/)
56
- [Daniel Elero](https://github.com/danixeee)
67
- [Daniel Miller](https://github.com/millerdev)

examples/json-extension/server/server.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,23 @@
1616
############################################################################
1717
import asyncio
1818
import json
19+
import re
1920
import time
2021
import uuid
2122
from json import JSONDecodeError
2223
from typing import Optional
2324

2425
from pygls.lsp.methods import (COMPLETION, TEXT_DOCUMENT_DID_CHANGE,
25-
TEXT_DOCUMENT_DID_CLOSE, TEXT_DOCUMENT_DID_OPEN)
26+
TEXT_DOCUMENT_DID_CLOSE, TEXT_DOCUMENT_DID_OPEN,
27+
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL)
2628
from pygls.lsp.types import (CompletionItem, CompletionList, CompletionOptions,
2729
CompletionParams, ConfigurationItem,
2830
ConfigurationParams, Diagnostic,
2931
DidChangeTextDocumentParams,
3032
DidCloseTextDocumentParams,
3133
DidOpenTextDocumentParams, MessageType, Position,
3234
Range, Registration, RegistrationParams,
35+
SemanticTokens, SemanticTokensLegend, SemanticTokensParams,
3336
Unregistration, UnregistrationParams)
3437
from pygls.lsp.types.basic_structures import (WorkDoneProgressBegin,
3538
WorkDoneProgressEnd,
@@ -151,6 +154,47 @@ async def did_open(ls, params: DidOpenTextDocumentParams):
151154
_validate(ls, params)
152155

153156

157+
@json_server.feature(
158+
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
159+
SemanticTokensLegend(
160+
token_types = ["operator"],
161+
token_modifiers = []
162+
)
163+
)
164+
def semantic_tokens(ls: JsonLanguageServer, params: SemanticTokensParams):
165+
"""See https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens
166+
for details on how semantic tokens are encoded."""
167+
168+
TOKENS = re.compile('".*"(?=:)')
169+
170+
uri = params.text_document.uri
171+
doc = ls.workspace.get_document(uri)
172+
173+
last_line = 0
174+
last_start = 0
175+
176+
data = []
177+
178+
for lineno, line in enumerate(doc.lines):
179+
last_start = 0
180+
181+
for match in TOKENS.finditer(line):
182+
start, end = match.span()
183+
data += [
184+
(lineno - last_line),
185+
(start - last_start),
186+
(end - start),
187+
0,
188+
0
189+
]
190+
191+
last_line = lineno
192+
last_start = start
193+
194+
return SemanticTokens(data=data)
195+
196+
197+
154198
@json_server.command(JsonLanguageServer.CMD_PROGRESS)
155199
async def progress(ls: JsonLanguageServer, *args):
156200
"""Create and start the progress on the client."""

pygls/capabilities.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,22 @@
2222
TEXT_DOCUMENT_CALL_HIERARCHY_PREPARE, TEXT_DOCUMENT_DID_CLOSE,
2323
TEXT_DOCUMENT_DID_OPEN, TEXT_DOCUMENT_DID_SAVE,
2424
TEXT_DOCUMENT_LINKED_EDITING_RANGE, TEXT_DOCUMENT_MONIKER,
25-
TEXT_DOCUMENT_SEMANTIC_TOKENS, TEXT_DOCUMENT_WILL_SAVE,
25+
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
26+
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA,
27+
TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE, TEXT_DOCUMENT_WILL_SAVE,
2628
TEXT_DOCUMENT_WILL_SAVE_WAIT_UNTIL, TYPE_DEFINITION,
2729
WORKSPACE_DID_CREATE_FILES, WORKSPACE_DID_DELETE_FILES,
2830
WORKSPACE_DID_RENAME_FILES, WORKSPACE_SYMBOL,
2931
WORKSPACE_WILL_CREATE_FILES, WORKSPACE_WILL_DELETE_FILES,
3032
WORKSPACE_WILL_RENAME_FILES)
3133
from pygls.lsp.types import (CodeLensOptions, CompletionOptions, DocumentLinkOptions,
3234
ExecuteCommandOptions, ImplementationOptions, SaveOptions,
35+
SemanticTokensOptions, SemanticTokensRegistrationOptions,
36+
SemanticTokensRequestsFull,
3337
ServerCapabilities, SignatureHelpOptions,
3438
TextDocumentSyncOptionsServerCapabilities, TypeDefinitionOptions,
3539
WorkspaceFileOperationsServerCapabilities,
3640
WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities)
37-
from pygls.lsp.types.language_features.semantic_tokens import (SemanticTokensLegend,
38-
SemanticTokensOptions)
3941

4042

4143
class ServerCapabilitiesBuilder:
@@ -229,15 +231,40 @@ def _with_call_hierarchy(self):
229231
return self
230232

231233
def _with_semantic_tokens(self):
232-
value = self._provider_options(TEXT_DOCUMENT_SEMANTIC_TOKENS,
233-
default=SemanticTokensOptions(
234-
legend=SemanticTokensLegend(
235-
token_types=[],
236-
token_modifiers=[],
237-
),
238-
))
239-
if value is not None:
234+
235+
providers = [
236+
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
237+
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA,
238+
TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE
239+
]
240+
241+
for provider in providers:
242+
value = self._provider_options(provider, None)
243+
if value:
244+
break
245+
246+
if value is None:
247+
return self
248+
249+
if isinstance(value, SemanticTokensRegistrationOptions):
240250
self.server_cap.semantic_tokens_provider = value
251+
return self
252+
253+
full_support = (
254+
SemanticTokensRequestsFull(delta=True)
255+
if TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA in self.features
256+
else TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL in self.features
257+
)
258+
259+
options = SemanticTokensOptions(
260+
legend=value,
261+
full=full_support or None,
262+
range=TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE in self.features or None
263+
)
264+
265+
if options.full or options.range:
266+
self.server_cap.semantic_tokens_provider = options
267+
241268
return self
242269

243270
def _with_linked_editing_range(self):

pygls/lsp/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,17 +108,17 @@
108108
Optional[List[Moniker]],
109109
),
110110
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL: (
111-
Union[SemanticTokensOptions, SemanticTokensRegistrationOptions],
111+
Union[SemanticTokensLegend, SemanticTokensRegistrationOptions],
112112
SemanticTokensParams,
113113
Union[SemanticTokensPartialResult, Optional[SemanticTokens]],
114114
),
115115
TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL_DELTA: (
116-
Union[SemanticTokensOptions, SemanticTokensRegistrationOptions],
116+
Union[SemanticTokensLegend, SemanticTokensRegistrationOptions],
117117
SemanticTokensDeltaParams,
118118
Union[SemanticTokensDeltaPartialResult, Optional[Union[SemanticTokens, SemanticTokensDelta]]],
119119
),
120120
TEXT_DOCUMENT_SEMANTIC_TOKENS_RANGE: (
121-
Union[SemanticTokensOptions, SemanticTokensRegistrationOptions],
121+
Union[SemanticTokensLegend, SemanticTokensRegistrationOptions],
122122
SemanticTokensRangeParams,
123123
Union[SemanticTokensPartialResult, Optional[SemanticTokens]],
124124

0 commit comments

Comments
 (0)