Skip to content

Commit a670bb5

Browse files
authored
(perf) cancellation support (#1028)
Enabling some cancellation checks on frequently updated language features while typing. Only asynchronously executed features can be canceled in this cancellation mechanism. The reason for the vscode-language-server bumps is because of microsoft/vscode-languageserver-node#722. There's some improvement that makes the cancellation more easily happen.
1 parent acfc729 commit a670bb5

20 files changed

+422
-82
lines changed

packages/language-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"vscode-css-languageservice": "5.0.0",
6464
"vscode-emmet-helper": "2.1.2",
6565
"vscode-html-languageservice": "4.0.0",
66-
"vscode-languageserver": "7.0.0",
66+
"vscode-languageserver": "7.1.0-next.4",
6767
"vscode-languageserver-types": "3.16.0",
6868
"vscode-uri": "2.1.2"
6969
}

packages/language-server/src/plugins/PluginHost.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { flatten } from 'lodash';
22
import {
3+
CancellationToken,
34
CodeAction,
45
CodeActionContext,
56
Color,
@@ -86,7 +87,8 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
8687
async getCompletions(
8788
textDocument: TextDocumentIdentifier,
8889
position: Position,
89-
completionContext?: CompletionContext
90+
completionContext?: CompletionContext,
91+
cancellationToken?: CancellationToken
9092
): Promise<CompletionList> {
9193
const document = this.getDocument(textDocument.uri);
9294
if (!document) {
@@ -96,7 +98,7 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
9698
const completions = (
9799
await this.execute<CompletionList>(
98100
'getCompletions',
99-
[document, position, completionContext],
101+
[document, position, completionContext, cancellationToken],
100102
ExecuteMode.Collect
101103
)
102104
).filter((completion) => completion != null);
@@ -127,7 +129,8 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
127129

128130
async resolveCompletion(
129131
textDocument: TextDocumentIdentifier,
130-
completionItem: AppCompletionItem
132+
completionItem: AppCompletionItem,
133+
cancellationToken: CancellationToken
131134
): Promise<CompletionItem> {
132135
const document = this.getDocument(textDocument.uri);
133136

@@ -137,7 +140,7 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
137140

138141
const result = await this.execute<CompletionItem>(
139142
'resolveCompletion',
140-
[document, completionItem],
143+
[document, completionItem, cancellationToken],
141144
ExecuteMode.FirstNonNull
142145
);
143146

@@ -212,7 +215,10 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
212215
);
213216
}
214217

215-
async getDocumentSymbols(textDocument: TextDocumentIdentifier): Promise<SymbolInformation[]> {
218+
async getDocumentSymbols(
219+
textDocument: TextDocumentIdentifier,
220+
cancellationToken: CancellationToken
221+
): Promise<SymbolInformation[]> {
216222
const document = this.getDocument(textDocument.uri);
217223
if (!document) {
218224
throw new Error('Cannot call methods on an unopened document');
@@ -221,7 +227,7 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
221227
return flatten(
222228
await this.execute<SymbolInformation[]>(
223229
'getDocumentSymbols',
224-
[document],
230+
[document, cancellationToken],
225231
ExecuteMode.Collect
226232
)
227233
);
@@ -256,7 +262,8 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
256262
async getCodeActions(
257263
textDocument: TextDocumentIdentifier,
258264
range: Range,
259-
context: CodeActionContext
265+
context: CodeActionContext,
266+
cancellationToken: CancellationToken
260267
): Promise<CodeAction[]> {
261268
const document = this.getDocument(textDocument.uri);
262269
if (!document) {
@@ -266,7 +273,7 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
266273
return flatten(
267274
await this.execute<CodeAction[]>(
268275
'getCodeActions',
269-
[document, range, context],
276+
[document, range, context, cancellationToken],
270277
ExecuteMode.Collect
271278
)
272279
);
@@ -350,7 +357,8 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
350357
async getSignatureHelp(
351358
textDocument: TextDocumentIdentifier,
352359
position: Position,
353-
context: SignatureHelpContext | undefined
360+
context: SignatureHelpContext | undefined,
361+
cancellationToken: CancellationToken
354362
): Promise<SignatureHelp | null> {
355363
const document = this.getDocument(textDocument.uri);
356364
if (!document) {
@@ -359,7 +367,7 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
359367

360368
return await this.execute<any>(
361369
'getSignatureHelp',
362-
[document, position, context],
370+
[document, position, context, cancellationToken],
363371
ExecuteMode.FirstNonNull
364372
);
365373
}
@@ -403,15 +411,19 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
403411
}
404412
}
405413

406-
async getSemanticTokens(textDocument: TextDocumentIdentifier, range?: Range) {
414+
async getSemanticTokens(
415+
textDocument: TextDocumentIdentifier,
416+
range?: Range,
417+
cancellationToken?: CancellationToken
418+
) {
407419
const document = this.getDocument(textDocument.uri);
408420
if (!document) {
409421
throw new Error('Cannot call methods on an unopened document');
410422
}
411423

412424
return await this.execute<SemanticTokens>(
413425
'getSemanticTokens',
414-
[document, range],
426+
[document, range, cancellationToken],
415427
ExecuteMode.FirstNonNull
416428
);
417429
}

packages/language-server/src/plugins/interfaces.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
CancellationToken,
23
CompletionContext,
34
FileChangeType,
45
LinkedEditingRanges,
@@ -53,12 +54,14 @@ export interface CompletionsProvider<T extends TextDocumentIdentifier = any> {
5354
getCompletions(
5455
document: Document,
5556
position: Position,
56-
completionContext?: CompletionContext
57+
completionContext?: CompletionContext,
58+
cancellationToken?: CancellationToken
5759
): Resolvable<AppCompletionList<T> | null>;
5860

5961
resolveCompletion?(
6062
document: Document,
61-
completionItem: AppCompletionItem<T>
63+
completionItem: AppCompletionItem<T>,
64+
cancellationToken?: CancellationToken
6265
): Resolvable<AppCompletionItem<T>>;
6366
}
6467

@@ -83,7 +86,10 @@ export interface ColorPresentationsProvider {
8386
}
8487

8588
export interface DocumentSymbolsProvider {
86-
getDocumentSymbols(document: Document): Resolvable<SymbolInformation[]>;
89+
getDocumentSymbols(
90+
document: Document,
91+
cancellationToken?: CancellationToken
92+
): Resolvable<SymbolInformation[]>;
8793
}
8894

8995
export interface DefinitionsProvider {
@@ -101,7 +107,8 @@ export interface CodeActionsProvider {
101107
getCodeActions(
102108
document: Document,
103109
range: Range,
104-
context: CodeActionContext
110+
context: CodeActionContext,
111+
cancellationToken?: CancellationToken
105112
): Resolvable<CodeAction[]>;
106113
executeCommand?(
107114
document: Document,
@@ -140,7 +147,8 @@ export interface SignatureHelpProvider {
140147
getSignatureHelp(
141148
document: Document,
142149
position: Position,
143-
context: SignatureHelpContext | undefined
150+
context: SignatureHelpContext | undefined,
151+
cancellationToken?: CancellationToken
144152
): Resolvable<SignatureHelp | null>;
145153
}
146154

packages/language-server/src/plugins/svelte/SveltePlugin.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import {
99
Range,
1010
TextEdit,
1111
WorkspaceEdit,
12-
SelectionRange
12+
SelectionRange,
13+
CancellationToken,
14+
CompletionContext
1315
} from 'vscode-languageserver';
1416
import { Document } from '../../lib/documents';
1517
import { LSConfigManager, LSSvelteConfig } from '../../ls-config';
@@ -138,12 +140,22 @@ export class SveltePlugin
138140
}
139141
}
140142

141-
async getCompletions(document: Document, position: Position): Promise<CompletionList | null> {
143+
async getCompletions(
144+
document: Document,
145+
position: Position,
146+
_?: CompletionContext,
147+
cancellationToken?: CancellationToken
148+
): Promise<CompletionList | null> {
142149
if (!this.featureEnabled('completions')) {
143150
return null;
144151
}
145152

146-
return getCompletions(await this.getSvelteDoc(document), position);
153+
const svelteDoc = await this.getSvelteDoc(document);
154+
if (cancellationToken?.isCancellationRequested) {
155+
return null;
156+
}
157+
158+
return getCompletions(svelteDoc, position);
147159
}
148160

149161
async doHover(document: Document, position: Position): Promise<Hover | null> {
@@ -157,13 +169,19 @@ export class SveltePlugin
157169
async getCodeActions(
158170
document: Document,
159171
range: Range,
160-
context: CodeActionContext
172+
context: CodeActionContext,
173+
cancellationToken?: CancellationToken
161174
): Promise<CodeAction[]> {
162175
if (!this.featureEnabled('codeActions')) {
163176
return [];
164177
}
165178

166179
const svelteDoc = await this.getSvelteDoc(document);
180+
181+
if (cancellationToken?.isCancellationRequested) {
182+
return [];
183+
}
184+
167185
try {
168186
return getCodeActions(svelteDoc, range, context);
169187
} catch (error) {

packages/language-server/src/plugins/typescript/TypeScriptPlugin.ts

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ts, { NavigationTree } from 'typescript';
22
import {
3+
CancellationToken,
34
CodeAction,
45
CodeActionContext,
56
CompletionContext,
@@ -112,12 +113,15 @@ export class TypeScriptPlugin
112113
this.semanticTokensProvider = new SemanticTokensProviderImpl(this.lsAndTsDocResolver);
113114
}
114115

115-
async getDiagnostics(document: Document): Promise<Diagnostic[]> {
116+
async getDiagnostics(
117+
document: Document,
118+
cancellationToken?: CancellationToken
119+
): Promise<Diagnostic[]> {
116120
if (!this.featureEnabled('diagnostics')) {
117121
return [];
118122
}
119123

120-
return this.diagnosticsProvider.getDiagnostics(document);
124+
return this.diagnosticsProvider.getDiagnostics(document, cancellationToken);
121125
}
122126

123127
async doHover(document: Document, position: Position): Promise<Hover | null> {
@@ -128,13 +132,21 @@ export class TypeScriptPlugin
128132
return this.hoverProvider.doHover(document, position);
129133
}
130134

131-
async getDocumentSymbols(document: Document): Promise<SymbolInformation[]> {
135+
async getDocumentSymbols(
136+
document: Document,
137+
cancellationToken?: CancellationToken
138+
): Promise<SymbolInformation[]> {
132139
if (!this.featureEnabled('documentSymbols')) {
133140
return [];
134141
}
135142

136143
const { lang, tsDoc } = await this.getLSAndTSDoc(document);
137144
const fragment = await tsDoc.getFragment();
145+
146+
if (cancellationToken?.isCancellationRequested) {
147+
return [];
148+
}
149+
138150
const navTree = lang.getNavigationTree(tsDoc.filePath);
139151

140152
const symbols: SymbolInformation[] = [];
@@ -209,7 +221,8 @@ export class TypeScriptPlugin
209221
async getCompletions(
210222
document: Document,
211223
position: Position,
212-
completionContext?: CompletionContext
224+
completionContext?: CompletionContext,
225+
cancellationToken?: CancellationToken
213226
): Promise<AppCompletionList<CompletionEntryWithIdentifer> | null> {
214227
if (!this.featureEnabled('completions')) {
215228
return null;
@@ -224,7 +237,8 @@ export class TypeScriptPlugin
224237
const completions = await this.completionProvider.getCompletions(
225238
document,
226239
position,
227-
completionContext
240+
completionContext,
241+
cancellationToken
228242
);
229243

230244
if (completions && tsDirectiveCommentCompletions) {
@@ -239,9 +253,14 @@ export class TypeScriptPlugin
239253

240254
async resolveCompletion(
241255
document: Document,
242-
completionItem: AppCompletionItem<CompletionEntryWithIdentifer>
256+
completionItem: AppCompletionItem<CompletionEntryWithIdentifer>,
257+
cancellationToken?: CancellationToken
243258
): Promise<AppCompletionItem<CompletionEntryWithIdentifer>> {
244-
return this.completionProvider.resolveCompletion(document, completionItem);
259+
return this.completionProvider.resolveCompletion(
260+
document,
261+
completionItem,
262+
cancellationToken
263+
);
245264
}
246265

247266
async getDefinitions(document: Document, position: Position): Promise<DefinitionLink[]> {
@@ -304,13 +323,14 @@ export class TypeScriptPlugin
304323
async getCodeActions(
305324
document: Document,
306325
range: Range,
307-
context: CodeActionContext
326+
context: CodeActionContext,
327+
cancellationToken?: CancellationToken
308328
): Promise<CodeAction[]> {
309329
if (!this.featureEnabled('codeActions')) {
310330
return [];
311331
}
312332

313-
return this.codeActionsProvider.getCodeActions(document, range, context);
333+
return this.codeActionsProvider.getCodeActions(document, range, context, cancellationToken);
314334
}
315335

316336
async executeCommand(
@@ -405,23 +425,37 @@ export class TypeScriptPlugin
405425
async getSignatureHelp(
406426
document: Document,
407427
position: Position,
408-
context: SignatureHelpContext | undefined
428+
context: SignatureHelpContext | undefined,
429+
cancellationToken?: CancellationToken
409430
): Promise<SignatureHelp | null> {
410431
if (!this.featureEnabled('signatureHelp')) {
411432
return null;
412433
}
413434

414-
return this.signatureHelpProvider.getSignatureHelp(document, position, context);
435+
return this.signatureHelpProvider.getSignatureHelp(
436+
document,
437+
position,
438+
context,
439+
cancellationToken
440+
);
415441
}
416442

417-
async getSemanticTokens(textDocument: Document, range?: Range): Promise<SemanticTokens | null> {
443+
async getSemanticTokens(
444+
textDocument: Document,
445+
range?: Range,
446+
cancellationToken?: CancellationToken
447+
): Promise<SemanticTokens | null> {
418448
if (!this.featureEnabled('semanticTokens')) {
419449
return {
420450
data: []
421451
};
422452
}
423453

424-
return this.semanticTokensProvider.getSemanticTokens(textDocument, range);
454+
return this.semanticTokensProvider.getSemanticTokens(
455+
textDocument,
456+
range,
457+
cancellationToken
458+
);
425459
}
426460

427461
private async getLSAndTSDoc(document: Document) {

0 commit comments

Comments
 (0)