Aggressively cancel code lens requests when the resource changes

For #84185
This commit is contained in:
Matt Bierner 2019-11-07 18:47:39 -08:00
parent 278b4f8cfa
commit a8ad448cb0
5 changed files with 63 additions and 8 deletions

View file

@ -337,6 +337,9 @@ export default class BufferSyncSupport extends Disposable {
private readonly _onDelete = this._register(new vscode.EventEmitter<vscode.Uri>());
public readonly onDelete = this._onDelete.event;
private readonly _onWillChange = this._register(new vscode.EventEmitter<vscode.Uri>());
public readonly onWillChange = this._onWillChange.event;
public listen(): void {
if (this.listening) {
return;
@ -456,6 +459,8 @@ export default class BufferSyncSupport extends Disposable {
return;
}
this._onWillChange.fire(syncedBuffer.resource);
syncedBuffer.onContentChanged(e.contentChanges);
const didTrigger = this.requestDiagnostic(syncedBuffer);

View file

@ -24,7 +24,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
const codeLens = inputCodeLens as ReferencesCodeLens;
const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start);
const response = await this.client.execute('implementation', args, token, { lowPriority: true });
const response = await this.client.execute('implementation', args, token, { lowPriority: true, cancelOnResourceChange: codeLens.document });
if (response.type !== 'response' || !response.body) {
codeLens.command = response.type === 'cancelled'
? TypeScriptBaseCodeLensProvider.cancelledCommand

View file

@ -19,7 +19,7 @@ class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvide
public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
const codeLens = inputCodeLens as ReferencesCodeLens;
const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start);
const response = await this.client.execute('references', args, token, { lowPriority: true });
const response = await this.client.execute('references', args, token, { lowPriority: true, cancelOnResourceChange: codeLens.document });
if (response.type !== 'response' || !response.body) {
codeLens.command = response.type === 'cancelled'
? TypeScriptBaseCodeLensProvider.cancelledCommand

View file

@ -78,6 +78,7 @@ export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRe
export type ExecConfig = {
readonly lowPriority?: boolean;
readonly nonRecoverable?: boolean;
readonly cancelOnResourceChange?: vscode.Uri
};
export interface ITypeScriptServiceClient {

View file

@ -35,6 +35,11 @@ export interface TsDiagnostics {
readonly diagnostics: Proto.Diagnostic[];
}
interface ToCancelOnResourceChanged {
readonly resource: vscode.Uri;
cancel(): void;
}
namespace ServerState {
export const enum Type {
None,
@ -60,6 +65,8 @@ namespace ServerState {
public tsserverVersion: string | undefined,
public langaugeServiceEnabled: boolean,
) { }
public readonly toCancelOnResourceChange = new Set<ToCancelOnResourceChanged>();
}
export class Errored {
@ -129,9 +136,14 @@ export default class TypeScriptServiceClient extends Disposable implements IType
this.diagnosticsManager = new DiagnosticsManager('typescript');
this.bufferSyncSupport.onDelete(resource => {
this.cancelInflightRequestsForResource(resource);
this.diagnosticsManager.delete(resource);
}, null, this._disposables);
this.bufferSyncSupport.onWillChange(resource => {
this.cancelInflightRequestsForResource(resource);
});
vscode.workspace.onDidChangeConfiguration(() => {
const oldConfiguration = this._configuration;
this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace();
@ -173,6 +185,18 @@ export default class TypeScriptServiceClient extends Disposable implements IType
}));
}
private cancelInflightRequestsForResource(resource: vscode.Uri): void {
if (this.serverState.type !== ServerState.Type.Running) {
return;
}
for (const request of this.serverState.toCancelOnResourceChange) {
if (request.resource.toString() === resource.toString()) {
request.cancel();
}
}
}
public get configuration() {
return this._configuration;
}
@ -609,12 +633,37 @@ export default class TypeScriptServiceClient extends Disposable implements IType
}
public execute(command: keyof TypeScriptRequests, args: any, token: vscode.CancellationToken, config?: ExecConfig): Promise<ServerResponse.Response<Proto.Response>> {
const execution = this.executeImpl(command, args, {
isAsync: false,
token,
expectsResult: true,
lowPriority: config?.lowPriority
});
let execution: Promise<ServerResponse.Response<Proto.Response>>;
if (config?.cancelOnResourceChange) {
const runningServerState = this.service();
const source = new vscode.CancellationTokenSource();
token.onCancellationRequested(() => source.cancel());
const inFlight: ToCancelOnResourceChanged = {
resource: config.cancelOnResourceChange,
cancel: () => source.cancel(),
};
runningServerState.toCancelOnResourceChange.add(inFlight);
execution = this.executeImpl(command, args, {
isAsync: false,
token: source.token,
expectsResult: true,
...config,
}).finally(() => {
runningServerState.toCancelOnResourceChange.delete(inFlight);
source.dispose();
});
} else {
execution = this.executeImpl(command, args, {
isAsync: false,
token,
expectsResult: true,
...config,
});
}
if (config?.nonRecoverable) {
execution.catch(() => this.fatalError(command));