diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index 5ede4ef49fc..b97a1a8b73d 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -32,9 +32,11 @@ export interface ITypeScriptServer { kill(): void; - executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, executionTarget?: ExecutionTarget }): undefined; - executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Promise>; - executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Promise> | undefined; + /** + * @return A list of all execute requests. If there are multiple entries, the first item is the primary + * request while the rest are secondary ones. + */ + executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Array> | undefined>; dispose(): void; } @@ -202,9 +204,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe } } - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, executionTarget?: ExecutionTarget }): undefined; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Promise>; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Promise> | undefined { + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Array> | undefined> { const request = this._requestQueue.createRequest(command, args); const requestInfo: RequestItem = { request, @@ -244,7 +244,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe this._requestQueue.enqueue(requestInfo); this.sendNextRequests(); - return result; + return [result]; } private sendNextRequests(): void { @@ -328,9 +328,13 @@ class RequestRouter { private readonly delegate: TsServerDelegate, ) { } - public execute(command: keyof TypeScriptRequests, args: any, executeInfo: ExecuteInfo): Promise> | undefined { + public execute( + command: keyof TypeScriptRequests, + args: any, + executeInfo: ExecuteInfo, + ): Array> | undefined> { if (RequestRouter.sharedCommands.has(command) && typeof executeInfo.executionTarget === 'undefined') { - // Dispatch shared commands to all servers but only return from first one + // Dispatch shared commands to all servers but use first one as the primary response const requestStates: RequestState.State[] = this.servers.map(() => RequestState.Unresolved); @@ -350,15 +354,13 @@ class RequestRouter { token = source.token; } - let firstRequest: Promise> | undefined; + const allRequests: Array> | undefined> = []; for (let serverIndex = 0; serverIndex < this.servers.length; ++serverIndex) { const server = this.servers[serverIndex].server; - const request = server.executeImpl(command, args, { ...executeInfo, token }) as Promise> | undefined; - if (serverIndex === 0) { - firstRequest = request; - } + const request = server.executeImpl(command, args, { ...executeInfo, token })[0]; + allRequests.push(request); if (request) { request .then(result => { @@ -380,7 +382,7 @@ class RequestRouter { } } - return firstRequest; + return allRequests; } for (const { canRun, server } of this.servers) { @@ -460,9 +462,7 @@ export class GetErrRoutingTsServer extends Disposable implements ITypeScriptServ this.mainServer.kill(); } - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, executionTarget?: ExecutionTarget }): undefined; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Promise>; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Promise> | undefined { + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Array> | undefined> { return this.router.execute(command, args, executeInfo); } } @@ -602,9 +602,7 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ this.semanticServer.kill(); } - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, executionTarget?: ExecutionTarget }): undefined; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Promise>; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Promise> | undefined { + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExecutionTarget }): Array> | undefined> { return this.router.execute(command, args, executeInfo); } } diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 2bdf9d855b7..c72375bd348 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -727,7 +727,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } public execute(command: keyof TypeScriptRequests, args: any, token: vscode.CancellationToken, config?: ExecConfig): Promise> { - let execution: Promise>; + let executions: Array> | undefined>; if (config?.cancelOnResourceChange) { const runningServerState = this.service(); @@ -741,17 +741,18 @@ export default class TypeScriptServiceClient extends Disposable implements IType }; runningServerState.toCancelOnResourceChange.add(inFlight); - execution = this.executeImpl(command, args, { + executions = this.executeImpl(command, args, { isAsync: false, token: source.token, expectsResult: true, ...config, - }).finally(() => { + }); + executions[0]!.finally(() => { runningServerState.toCancelOnResourceChange.delete(inFlight); source.dispose(); }); } else { - execution = this.executeImpl(command, args, { + executions = this.executeImpl(command, args, { isAsync: false, token, expectsResult: true, @@ -760,25 +761,25 @@ export default class TypeScriptServiceClient extends Disposable implements IType } if (config?.nonRecoverable) { - execution.catch(err => this.fatalError(command, err)); + executions[0]!.catch(err => this.fatalError(command, err)); } - return execution; - } - - public executeWithoutWaitingForResponse(command: keyof TypeScriptRequests, args: any): void { - const promise = this.executeImpl(command, args, { - isAsync: false, - token: undefined, - expectsResult: command === 'updateOpen' - }); - if (command === 'updateOpen') { // If update open has completed, consider that the project has loaded - promise.then(() => { + Promise.all(executions).then(() => { this.loadingIndicator.reset(); }); } + + return executions[0]!; + } + + public executeWithoutWaitingForResponse(command: keyof TypeScriptRequests, args: any): void { + this.executeImpl(command, args, { + isAsync: false, + token: undefined, + expectsResult: false + }); } public executeAsync(command: keyof TypeScriptRequests, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise> { @@ -786,12 +787,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType isAsync: true, token, expectsResult: true - }); + })[0]!; } - private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: false, lowPriority?: boolean, requireSemantic?: boolean }): undefined; - private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, requireSemantic?: boolean }): Promise>; - private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, requireSemantic?: boolean }): Promise> | undefined { + private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, requireSemantic?: boolean }): Array> | undefined> { this.bufferSyncSupport.beforeCommand(command); const runningServerState = this.service(); return runningServerState.server.executeImpl(command, args, executeInfo);