Skip to content

Commit 1c0fd4e

Browse files
feat: add semantic tokens (#2191)
1 parent 195a97f commit 1c0fd4e

File tree

8 files changed

+113
-14
lines changed

8 files changed

+113
-14
lines changed

.aspect/rules/external_repository_action_cache/npm_translate_lock_LTE4Nzc1MDcwNjU=

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Input hashes for repository rule npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml").
33
# This file should be checked into version control along with the pnpm-lock.yaml file.
44
.npmrc=974837034
5-
pnpm-lock.yaml=1493175183
6-
yarn.lock=485928896
7-
package.json=-100959756
5+
pnpm-lock.yaml=1752070694
6+
yarn.lock=1122184668
7+
package.json=991750528
88
pnpm-workspace.yaml=1711114604

client/src/client.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,16 @@ export class AngularLanguageClient implements vscode.Disposable {
171171
}
172172
return next(document, context, token);
173173
},
174+
provideDocumentSemanticTokens: async (document, token, next) => {
175+
if (await this.isInAngularProject(document)) {
176+
return next(document, token);
177+
}
178+
},
179+
provideDocumentRangeSemanticTokens: async (document, range, token, next) => {
180+
if (await this.isInAngularProject(document)) {
181+
return next(document, range, token);
182+
}
183+
},
174184
}
175185
};
176186
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@
269269
"test:legacy-syntaxes": "yarn compile:syntaxes-test && yarn build:syntaxes && jasmine dist/syntaxes/test/driver.js"
270270
},
271271
"dependencies": {
272-
"@angular/language-service": "^20.1.0-next.0",
272+
"@angular/language-service": "20.1.0-rc.0",
273273
"typescript": "^5.8.1",
274274
"vscode-html-languageservice": "^4.2.5",
275275
"vscode-jsonrpc": "6.0.0",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"ngserver": "./bin/ngserver"
1616
},
1717
"dependencies": {
18-
"@angular/language-service": "20.1.0-next.0",
18+
"@angular/language-service": "20.1.0-rc.0",
1919
"vscode-html-languageservice": "^4.2.5",
2020
"vscode-jsonrpc": "6.0.0",
2121
"vscode-languageserver": "7.0.0",

server/src/semantic_tokens.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { NgLanguageService } from '@angular/language-service/api';
10+
import * as ts from 'typescript/lib/tsserverlibrary';
11+
import * as lsp from 'vscode-languageserver/node';
12+
13+
export function getSemanticTokens(
14+
languageService: NgLanguageService, classifications: ts.Classifications, script: ts.server.ScriptInfo): lsp.SemanticTokens {
15+
const spans = classifications.spans;
16+
const builder = new lsp.SemanticTokensBuilder();
17+
18+
for (let i = 0; i < spans.length;) {
19+
const offset = spans[i++];
20+
const length = spans[i++];
21+
const classification = spans[i++];
22+
23+
const tokenType = languageService.getTokenTypeFromClassification(classification);
24+
if (tokenType === undefined) {
25+
continue;
26+
}
27+
28+
const tokenModifiers = languageService.getTokenModifierFromClassification(classification);
29+
30+
const startPos = script.positionToLineOffset(offset);
31+
startPos.line -= 1;
32+
startPos.offset -= 1;
33+
34+
const endPos = script.positionToLineOffset(offset + length);
35+
endPos.line -= 1;
36+
endPos.offset -= 1;
37+
38+
for (let line = startPos.line; line <= endPos.line; line++) {
39+
const startCharacter = line === startPos.line ? startPos.offset : 0;
40+
const endCharacter =
41+
line === endPos.line ? endPos.offset : script.lineToTextSpan(line - 1).length;
42+
builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, tokenModifiers);
43+
}
44+
}
45+
46+
return builder.build();
47+
}

server/src/session.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {GetComponentsWithTemplateFile, GetTcbParams, GetTcbRequest, GetTcbRespon
2020
import {readNgCompletionData, tsCompletionEntryToLspCompletionItem} from './completion';
2121
import {tsDiagnosticToLspDiagnostic} from './diagnostic';
2222
import {getHTMLVirtualContent} from './embedded_support';
23+
import {getSemanticTokens} from './semantic_tokens';
2324
import {ServerHost} from './server_host';
2425
import {documentationToMarkdown} from './text_render';
2526
import {filePathToUri, getMappedDefinitionInfo, isConfiguredProject, isDebugMode, lspPositionToTsPosition, lspRangeToTsPositions, MruTracker, tsDisplayPartsToText, tsFileTextChangesToLspWorkspaceEdit, tsTextSpanToLspRange, uriToFilePath} from './utils';
@@ -217,8 +218,38 @@ export class Session {
217218
conn.onSignatureHelp(p => this.onSignatureHelp(p));
218219
conn.onCodeAction(p => this.onCodeAction(p));
219220
conn.onCodeActionResolve(async p => await this.onCodeActionResolve(p));
221+
conn.onRequest(lsp.SemanticTokensRequest.type, p => this.onSemanticTokensRequest(p));
222+
conn.onRequest(lsp.SemanticTokensRangeRequest.type, p => this.onSemanticTokensRangeRequest(p));
220223
}
221224

225+
private onSemanticTokensRequest(params: lsp.SemanticTokensParams): lsp.SemanticTokens|null {
226+
const lsInfo = this.getLSAndScriptInfo(params.textDocument);
227+
if (lsInfo === null) {
228+
return null;
229+
}
230+
const {languageService, scriptInfo} = lsInfo;
231+
const span = {start: 0, length: scriptInfo.getSnapshot().getLength()};
232+
const classifications = languageService.getEncodedSemanticClassifications(
233+
scriptInfo.fileName, span, ts.SemanticClassificationFormat.TwentyTwenty);
234+
return getSemanticTokens(languageService, classifications, scriptInfo);
235+
}
236+
237+
private onSemanticTokensRangeRequest(params: lsp.SemanticTokensRangeParams): lsp.SemanticTokens
238+
|null {
239+
const lsInfo = this.getLSAndScriptInfo(params.textDocument);
240+
if (lsInfo === null) {
241+
return null;
242+
}
243+
const {languageService, scriptInfo} = lsInfo;
244+
const start = lspPositionToTsPosition(lsInfo.scriptInfo, params.range.start);
245+
const end = lspPositionToTsPosition(lsInfo.scriptInfo, params.range.end);
246+
const span = {start, length: end - start};
247+
const classifications = languageService.getEncodedSemanticClassifications(
248+
scriptInfo.fileName, span, ts.SemanticClassificationFormat.TwentyTwenty);
249+
return getSemanticTokens(languageService, classifications, scriptInfo);
250+
}
251+
252+
222253
private onCodeAction(params: lsp.CodeActionParams): lsp.CodeAction[]|null {
223254
const filePath = uriToFilePath(params.textDocument.uri);
224255
const lsInfo = this.getLSAndScriptInfo(params.textDocument);
@@ -809,6 +840,17 @@ export class Session {
809840
// [here](https://github.com/angular/vscode-ng-language-service/issues/1828)
810841
codeActionKinds: [lsp.CodeActionKind.QuickFix],
811842
},
843+
semanticTokensProvider: {
844+
documentSelector: null,
845+
legend: {
846+
tokenTypes: [
847+
'class',
848+
],
849+
tokenModifiers: [],
850+
},
851+
full: true,
852+
range: true
853+
}
812854
},
813855
serverOptions,
814856
};

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@
173173
uuid "^8.3.2"
174174
yargs "^17.0.0"
175175

176-
"@angular/language-service@^20.1.0-next.0":
177-
version "20.1.0-next.0"
178-
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-20.1.0-next.0.tgz#d294576dee2c2be966020a8dc8ef0bc073e71487"
179-
integrity sha512-CH2Oj7ytaEXl/4W0CzCgO3gC71uzU+gHOnM8NdAnM0ccEjzOeq1ty0PaQzQbf2psKed7V0KRfpWT+0xOO38V4A==
176+
"@angular/[email protected]rc.0":
177+
version "20.1.0-rc.0"
178+
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-20.1.0-rc.0.tgz#6d4e8a45e6716410778f42ce8c0d54e3e7912d02"
179+
integrity sha512-8iXIiBGOD4fUulzhH1yEZIQhlwTsY3Kbj1N8wpS1uV1sH2DSgHNq4WR5k3fQYUx7VYbF3VyTCjdBzjh7s2Xcsg==
180180

181181
"@assemblyscript/loader@^0.10.1":
182182
version "0.10.1"

0 commit comments

Comments
 (0)