Skip to content

Commit 80868ea

Browse files
authored
Refactor semanticTokenProvider.ts (#1679)
Signed-off-by: 0dinD <[email protected]>
1 parent 01cc378 commit 80868ea

File tree

2 files changed

+114
-80
lines changed

2 files changed

+114
-80
lines changed

src/semanticTokenProvider.ts

Lines changed: 100 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,111 @@
11
import * as vscode from 'vscode';
22
import { Commands } from './commands';
3-
import { getJavaConfiguration } from './utils';
4-
5-
const semanticHighlightingKey = 'java.semanticHighlighting.enabled';
6-
7-
export function registerSemanticTokensProvider(context: vscode.ExtensionContext) {
8-
if (!vscode.languages.registerDocumentSemanticTokensProvider) { // in case Theia doesn't support this API
9-
return;
10-
}
11-
12-
if (isSemanticHighlightingEnabled()) {
13-
getSemanticTokensLegend().then(legend => {
14-
const documentSelector = [
15-
{ scheme: 'file', language: 'java' },
16-
{ scheme: 'jdt', language: 'java' }
17-
];
18-
const semanticTokensProviderDisposable = vscode.languages.registerDocumentSemanticTokensProvider(documentSelector, semanticTokensProvider, legend);
19-
context.subscriptions.push(semanticTokensProviderDisposable);
20-
onceSemanticTokenEnabledChange(context, semanticTokensProviderDisposable);
21-
});
22-
} else {
23-
onceSemanticTokenEnabledChange(context, undefined);
24-
}
25-
}
3+
import { getJavaConfiguration, waitForDocumentChangesToEnd } from './utils';
4+
5+
export function registerSemanticTokensProvider(context: vscode.ExtensionContext): void {
6+
if (!vscode.languages.registerDocumentSemanticTokensProvider) { // in case Theia doesn't support this API
7+
return;
8+
}
9+
10+
context.subscriptions.push(semanticTokensProvider);
11+
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => {
12+
if (e.affectsConfiguration('java.semanticHighlighting.enabled')) {
13+
if (isSemanticHighlightingEnabled()) {
14+
semanticTokensProvider.enable();
15+
}
16+
else {
17+
semanticTokensProvider.disable();
18+
}
19+
}
20+
}));
2621

27-
class SemanticTokensProvider implements vscode.DocumentSemanticTokensProvider {
28-
async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.SemanticTokens> {
29-
const versionBeforeRequest: number = document.version;
30-
const response = <any> await vscode.commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.PROVIDE_SEMANTIC_TOKENS, document.uri.toString());
31-
const versionAfterRequest: number = document.version;
32-
33-
if (versionBeforeRequest !== versionAfterRequest) {
34-
await waitForDocumentChangesToEnd(document);
35-
throw new Error("busy");
36-
}
37-
if (token.isCancellationRequested) {
38-
return undefined;
39-
}
40-
if (!response || !response.data) {
41-
return undefined;
42-
}
43-
return new vscode.SemanticTokens(new Uint32Array(response.data), response.resultId);
44-
}
22+
if (isSemanticHighlightingEnabled()) {
23+
semanticTokensProvider.enable();
24+
}
4525
}
4626

47-
function waitForDocumentChangesToEnd(document: vscode.TextDocument): Promise<void> {
48-
let version = document.version;
49-
return new Promise((resolve) => {
50-
const iv = setInterval(() => {
51-
if (document.version === version) {
52-
clearInterval(iv);
53-
resolve();
54-
}
55-
version = document.version;
56-
}, 400);
57-
});
27+
function isSemanticHighlightingEnabled(): boolean {
28+
const config = getJavaConfiguration();
29+
const section = 'semanticHighlighting.enabled';
30+
return config.get(section);
5831
}
5932

60-
const semanticTokensProvider = new SemanticTokensProvider();
33+
class SemanticTokensProvider implements vscode.DocumentSemanticTokensProvider, vscode.Disposable {
6134

62-
async function getSemanticTokensLegend(): Promise<vscode.SemanticTokensLegend | undefined> {
63-
const response = await vscode.commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.GET_SEMANTIC_TOKENS_LEGEND) as vscode.SemanticTokensLegend;
64-
if (response && response.tokenModifiers !== undefined && response.tokenTypes !== undefined) {
65-
return new vscode.SemanticTokensLegend(response.tokenTypes, response.tokenModifiers);
66-
}
67-
return undefined;
68-
}
35+
private disposable?: vscode.Disposable;
36+
private tokensChangedEmitter?: vscode.EventEmitter<void>;
37+
private shouldClearTokens: boolean = false;
6938

70-
function onceSemanticTokenEnabledChange(context: vscode.ExtensionContext, registeredDisposable?: vscode.Disposable) {
71-
const configChangeListener = vscode.workspace.onDidChangeConfiguration(e => {
72-
configChangeListener.dispose();
73-
if (e.affectsConfiguration(semanticHighlightingKey)) {
74-
if (isSemanticHighlightingEnabled()) {
75-
// turn on
76-
registerSemanticTokensProvider(context);
77-
} else if (registeredDisposable) {
78-
// turn off
79-
registeredDisposable.dispose();
80-
}
81-
onceSemanticTokenEnabledChange(context);
82-
}
83-
});
84-
}
39+
public get onDidChangeSemanticTokens(): vscode.Event<void> {
40+
return this.tokensChangedEmitter?.event;
41+
}
42+
43+
public async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.SemanticTokens> {
44+
if (this.shouldClearTokens) {
45+
// We can return early here to skip the request, but just to be
46+
// safe we should also check this case after requesting tokens,
47+
// in case the provider was disbled during the request itself.
48+
this.disable(true);
49+
return undefined;
50+
}
51+
52+
const versionBeforeRequest: number = document.version;
53+
const response = <any> await vscode.commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.PROVIDE_SEMANTIC_TOKENS, document.uri.toString());
54+
const versionAfterRequest: number = document.version;
55+
56+
if (this.shouldClearTokens) {
57+
this.disable(true);
58+
return undefined;
59+
}
60+
if (versionBeforeRequest !== versionAfterRequest) {
61+
await waitForDocumentChangesToEnd(document);
62+
throw new Error("busy");
63+
}
64+
if (token.isCancellationRequested) {
65+
return undefined;
66+
}
67+
if (!response || !response.data) {
68+
return undefined;
69+
}
70+
return new vscode.SemanticTokens(new Uint32Array(response.data), response.resultId);
71+
}
72+
73+
public async enable(): Promise<void> {
74+
this.shouldClearTokens = false;
75+
this.tokensChangedEmitter = new vscode.EventEmitter();
76+
this.disposable = vscode.languages.registerDocumentSemanticTokensProvider(
77+
[
78+
{ language: 'java', scheme: 'file' },
79+
{ language: 'java', scheme: 'jdt' }
80+
],
81+
this,
82+
await this.getSemanticTokensLegend()
83+
);
84+
}
85+
86+
public disable(tokensAreCleared?: boolean): void {
87+
if (tokensAreCleared) {
88+
this.disposable?.dispose();
89+
this.tokensChangedEmitter?.dispose();
90+
}
91+
else {
92+
this.shouldClearTokens = true;
93+
this.tokensChangedEmitter?.fire();
94+
}
95+
}
96+
97+
public dispose(): void {
98+
this.disable();
99+
}
100+
101+
private async getSemanticTokensLegend(): Promise<vscode.SemanticTokensLegend | undefined> {
102+
const response = await vscode.commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.GET_SEMANTIC_TOKENS_LEGEND) as vscode.SemanticTokensLegend;
103+
if (response && response.tokenModifiers !== undefined && response.tokenTypes !== undefined) {
104+
return new vscode.SemanticTokensLegend(response.tokenTypes, response.tokenModifiers);
105+
}
106+
return undefined;
107+
}
85108

86-
function isSemanticHighlightingEnabled(): boolean {
87-
const config = getJavaConfiguration();
88-
const section = 'semanticHighlighting.enabled';
89-
return config.get(section);
90109
}
110+
111+
const semanticTokensProvider = new SemanticTokensProvider();

src/utils.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import * as fs from 'fs';
44
import * as path from 'path';
5-
import { workspace, WorkspaceConfiguration } from 'vscode';
5+
import { workspace, WorkspaceConfiguration, TextDocument } from 'vscode';
66

77
export function getJavaConfiguration(): WorkspaceConfiguration {
88
return workspace.getConfiguration('java');
@@ -112,3 +112,16 @@ function parseToStringGlob(patterns: string[]): string {
112112

113113
return `{${patterns.join(",")}}`;
114114
}
115+
116+
export async function waitForDocumentChangesToEnd(document: TextDocument): Promise<void> {
117+
let version = document.version;
118+
return new Promise((resolve) => {
119+
const iv = setInterval(() => {
120+
if (document.version === version) {
121+
clearInterval(iv);
122+
resolve();
123+
}
124+
version = document.version;
125+
}, 400);
126+
});
127+
}

0 commit comments

Comments
 (0)