Skip to content

Commit 87fc3f4

Browse files
committed
WIP - custom semantic tokens
1 parent c1fb1ab commit 87fc3f4

5 files changed

+95
-1
lines changed

package.json

+34
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,40 @@
8585
"path": "./syntaxes/terraform.tmGrammar.json"
8686
}
8787
],
88+
"semanticTokenTypes": [
89+
{"id": "hcl-AttrName", "superType": "property"},
90+
{"id": "hcl-BlockType", "superType": "type"},
91+
{"id": "hcl-BlockLabel", "superType": "enumMember"},
92+
93+
{"id": "hcl-Bool", "superType": "keyword"},
94+
{"id": "hcl-String", "superType": "string"},
95+
{"id": "hcl-Number", "superType": "number"},
96+
{"id": "hcl-ObjectKey", "superType": "parameter"},
97+
{"id": "hcl-MapKey", "superType": "parameter"},
98+
{"id": "hcl-Keyword", "superType": "variable"},
99+
{"id": "hcl-TraversalStep", "superType": "variable"},
100+
{"id": "hcl-TypeCapsule"},
101+
{"id": "hcl-TypePrimitive"}
102+
],
103+
"semanticTokenModifiers": [
104+
{"id": "hcl-dependent"},
105+
{"id": "terraform-block-type-data"},
106+
{"id": "terraform-block-label-data-type"},
107+
{"id": "terraform-block-label-data-name"},
108+
{"id": "terraform-block-type-locals"},
109+
{"id": "terraform-block-type-module"},
110+
{"id": "terraform-block-label-module-name"},
111+
{"id": "terraform-block-type-output"},
112+
{"id": "terraform-block-label-output-name"},
113+
{"id": "terraform-block-type-provider"},
114+
{"id": "terraform-block-label-provider-name"},
115+
{"id": "terraform-block-type-resource"},
116+
{"id": "terraform-block-label-resource-type"},
117+
{"id": "terraform-block-label-resource-name"},
118+
{"id": "terraform-block-type-variable"},
119+
{"id": "terraform-block-label-variable-name"},
120+
{"id": "terraform-block-type-terraform"}
121+
],
88122
"snippets": [
89123
{
90124
"language": "terraform",

src/clientHandler.ts

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { ServerPath } from './serverPath';
1414
import { ShowReferencesFeature } from './showReferences';
1515
import { TelemetryFeature } from './telemetry';
1616
import { config } from './vscodeUtils';
17+
import { CustomSemanticTokens } from './semanticTokens';
1718

1819
export interface TerraformLanguageClient {
1920
commandPrefix: string;
@@ -29,6 +30,9 @@ export class ClientHandler {
2930
private tfClient: TerraformLanguageClient | undefined;
3031
private commands: string[] = [];
3132

33+
public extSemanticTokenTypes: string[] = [];
34+
public extSemanticTokenModifiers: string[] = [];
35+
3236
constructor(
3337
private lsPath: ServerPath,
3438
private outputChannel: vscode.OutputChannel,
@@ -86,6 +90,10 @@ export class ClientHandler {
8690
const id = `terraform`;
8791
const client = new LanguageClient(id, serverOptions, clientOptions);
8892

93+
client.registerFeature(
94+
new CustomSemanticTokens(client, this.extSemanticTokenTypes, this.extSemanticTokenModifiers),
95+
);
96+
8997
const codeLensReferenceCount = config('terraform').get<boolean>('codelens.referenceCount');
9098
if (codeLensReferenceCount) {
9199
client.registerFeature(new ShowReferencesFeature(client));

src/extension.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as vscode from 'vscode';
22
import TelemetryReporter from '@vscode/extension-telemetry';
3-
import { ExecuteCommandParams, ExecuteCommandRequest } from 'vscode-languageclient';
3+
import { CodeLensResolveRequest, ExecuteCommandParams, ExecuteCommandRequest } from 'vscode-languageclient';
44
import { LanguageClient } from 'vscode-languageclient/node';
55
import { Utils } from 'vscode-uri';
66
import { ClientHandler, TerraformLanguageClient } from './clientHandler';
@@ -25,6 +25,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
2525

2626
const lsPath = new ServerPath(context);
2727
clientHandler = new ClientHandler(lsPath, outputChannel, reporter);
28+
clientHandler.extSemanticTokenTypes = tokenTypesFromExtManifest(context.extension);
29+
clientHandler.extSemanticTokenModifiers = tokenModifiersFromExtManifest(context.extension);
2830

2931
// get rid of pre-2.0.0 settings
3032
if (config('terraform').has('languageServer.enabled')) {
@@ -293,6 +295,25 @@ async function terraformCommand(command: string, languageServerExec = true): Pro
293295
}
294296
}
295297

298+
interface ObjectWithId {
299+
id: string;
300+
}
301+
302+
function tokenTypesFromExtManifest(ext: vscode.Extension<unknown>): string[] {
303+
if (!ext.packageJSON.contributes.semanticTokenTypes) {
304+
return [];
305+
}
306+
return ext.packageJSON.contributes.semanticTokenTypes.map((token: ObjectWithId) => token.id);
307+
}
308+
309+
function tokenModifiersFromExtManifest(ext: vscode.Extension<unknown>): string[] {
310+
if (!ext.packageJSON.contributes.semanticTokenModifiers) {
311+
return [];
312+
}
313+
314+
return ext.packageJSON.contributes.semanticTokenModifiers.map((modifier: ObjectWithId) => modifier.id);
315+
}
316+
296317
function enabled(): boolean {
297318
return config('terraform').get('languageServer.external', false);
298319
}

src/semanticTokens.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { BaseLanguageClient, ClientCapabilities, ServerCapabilities, StaticFeature } from 'vscode-languageclient';
2+
3+
export class CustomSemanticTokens implements StaticFeature {
4+
constructor(
5+
private _client: BaseLanguageClient,
6+
private extTokenTypes: string[],
7+
private extTokenModifiers: string[],
8+
) {}
9+
10+
public fillClientCapabilities(capabilities: ClientCapabilities): void {
11+
if (!capabilities.textDocument || !capabilities.textDocument.semanticTokens) {
12+
return;
13+
}
14+
15+
const tokenTypes = capabilities.textDocument.semanticTokens.tokenTypes;
16+
capabilities.textDocument.semanticTokens.tokenTypes = tokenTypes.concat(this.extTokenTypes);
17+
18+
const tokenModifiers = capabilities.textDocument.semanticTokens.tokenModifiers;
19+
capabilities.textDocument.semanticTokens.tokenModifiers = tokenModifiers.concat(this.extTokenModifiers);
20+
}
21+
22+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
23+
public initialize(capabilities: ServerCapabilities): void {
24+
return;
25+
}
26+
27+
public dispose(): void {
28+
return;
29+
}
30+
}

src/vscodeUtils.ts

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export function isTerraformFile(document?: vscode.TextDocument): boolean {
4242
return false;
4343
}
4444

45+
// TODO: check for supported language IDs here instead
4546
if (document.fileName.endsWith('tf')) {
4647
// For the purposes of this extension, anything with the tf file
4748
// extension is a Terraform file

0 commit comments

Comments
 (0)