From 609e733c5ae0f4291692947337f5f2e4088fbaa5 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Sat, 16 May 2026 14:02:26 -0700 Subject: [PATCH 1/4] Stop automatically cancelling the previous Find All References based request when a new one comes in. --- .../src/LanguageServer/Providers/callHierarchyProvider.ts | 2 -- .../src/LanguageServer/Providers/findAllReferencesProvider.ts | 3 +-- Extension/src/LanguageServer/Providers/renameProvider.ts | 3 +-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts index 61b7b110b..78d12df32 100644 --- a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts +++ b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts @@ -223,7 +223,6 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { public async prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { await this.client.ready; - workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest); workspaceReferences.clearViews(); const range: vscode.Range | undefined = document.getWordRangeAtPosition(position); @@ -262,7 +261,6 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { public async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { await this.client.ready; - workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest); const CallHierarchyCallsToEvent: string = "CallHierarchyCallsTo"; if (item === undefined) { diff --git a/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts b/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts index 664cf80c6..f80457dbf 100644 --- a/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts +++ b/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { Position, RequestType, ResponseError } from 'vscode-languageclient'; import { DefaultClient, workspaceReferences } from '../client'; import { RequestCancelled, ServerCancelled } from '../protocolFilter'; -import { CancellationSender, ReferenceInfo, ReferenceType, ReferencesParams, ReferencesResult } from '../references'; +import { ReferenceInfo, ReferenceType, ReferencesParams, ReferencesResult } from '../references'; const FindAllReferencesRequest: RequestType = new RequestType('cpptools/findAllReferences'); @@ -64,7 +64,6 @@ export class FindAllReferencesProvider implements vscode.ReferenceProvider { public async provideReferences(document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise { await this.client.ready; - workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest); // Listen to a cancellation for this request. When this request is cancelled, // use a local cancellation source to explicitly cancel a token. diff --git a/Extension/src/LanguageServer/Providers/renameProvider.ts b/Extension/src/LanguageServer/Providers/renameProvider.ts index ea93ba1c2..8effd622e 100644 --- a/Extension/src/LanguageServer/Providers/renameProvider.ts +++ b/Extension/src/LanguageServer/Providers/renameProvider.ts @@ -8,7 +8,7 @@ import * as nls from 'vscode-nls'; import * as util from '../../common'; import { DefaultClient, workspaceReferences } from '../client'; import { RequestCancelled, ServerCancelled } from '../protocolFilter'; -import { CancellationSender, ReferenceType, ReferencesParams, ReferencesResult, getReferenceItemIconPath, getReferenceTagString } from '../references'; +import { ReferenceType, ReferencesParams, ReferencesResult, getReferenceItemIconPath, getReferenceTagString } from '../references'; import { CppSettings } from '../settings'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); @@ -26,7 +26,6 @@ export class RenameProvider implements vscode.RenameProvider { public async provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string, _token: vscode.CancellationToken): Promise { await this.client.ready; - workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest); const settings: CppSettings = new CppSettings(); if (settings.renameRequiresIdentifier && !util.isValidIdentifier(newName)) { From 09da1a7b03a98d57090b08e2c7c3d283319442f1 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Sat, 16 May 2026 17:55:48 -0700 Subject: [PATCH 2/4] Alternative fix. --- .../Providers/callHierarchyProvider.ts | 170 +++++++++--------- .../Providers/findAllReferencesProvider.ts | 70 ++++---- .../Providers/renameProvider.ts | 108 ++++++----- Extension/src/LanguageServer/client.ts | 4 +- Extension/src/LanguageServer/references.ts | 41 ++++- 5 files changed, 222 insertions(+), 171 deletions(-) diff --git a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts index 78d12df32..6b1f4589b 100644 --- a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts +++ b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts @@ -220,91 +220,99 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { this.client = client; } - public async prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - await this.client.ready; - - workspaceReferences.clearViews(); - - const range: vscode.Range | undefined = document.getWordRangeAtPosition(position); - if (range === undefined) { - return undefined; - } - - // Listen to a cancellation for this request. When this request is cancelled, - // use a local cancellation source to explicitly cancel a token. - const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(); - const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => { - cancelSource.cancel(); + public prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { + return this.client.enqueue(async () => { + if (token.isCancellationRequested) { + throw new vscode.CancellationError(); + } + + workspaceReferences.clearViews(); + + const range: vscode.Range | undefined = document.getWordRangeAtPosition(position); + if (range === undefined) { + return undefined; + } + + // Listen to a cancellation for this request. When this request is cancelled, + // use a local cancellation source to explicitly cancel a token. + const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(); + const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => { + cancelSource.cancel(); + }); + const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { + cancelSource.cancel(); + }); + + let result: vscode.CallHierarchyItem[] | undefined; + try { + result = await sendPrepareCallHierarchyRequest(this.client, document.uri, position, cancelSource.token); + } finally { + cancellationTokenListener.dispose(); + requestCanceledListener.dispose(); + } + + if (cancelSource.token.isCancellationRequested) { + throw new vscode.CancellationError(); + } + if (!result || result.length === 0) { + return undefined; + } + + this.isEntryRootNodeTelemetry = true; + return result[0]; }); - const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { - cancelSource.cancel(); - }); - - let result: vscode.CallHierarchyItem[] | undefined; - try { - result = await sendPrepareCallHierarchyRequest(this.client, document.uri, position, cancelSource.token); - } finally { - cancellationTokenListener.dispose(); - requestCanceledListener.dispose(); - } - - if (cancelSource.token.isCancellationRequested) { - throw new vscode.CancellationError(); - } - if (!result || result.length === 0) { - return undefined; - } - - this.isEntryRootNodeTelemetry = true; - return result[0]; } - public async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { - await this.client.ready; - - const CallHierarchyCallsToEvent: string = "CallHierarchyCallsTo"; - if (item === undefined) { - this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Failed); - return undefined; - } - - // Listen to a cancellation for this request. When this request is cancelled, - // use a local cancellation source to explicitly cancel a token. - let requestCanceled: CancellationSender | undefined; - const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(); - const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => { - requestCanceled = CancellationSender.ProviderToken; - cancelSource.cancel(); - }); - const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(sender => { - requestCanceled = sender; - cancelSource.cancel(); + public provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { + return this.client.enqueue(async () => { + if (token.isCancellationRequested) { + throw new vscode.CancellationError(); + } + + const CallHierarchyCallsToEvent: string = "CallHierarchyCallsTo"; + if (item === undefined) { + this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Failed); + return undefined; + } + + // Listen to a cancellation for this request. When this request is cancelled, + // use a local cancellation source to explicitly cancel a token. + let requestCanceled: CancellationSender | undefined; + const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(); + const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => { + requestCanceled = CancellationSender.ProviderToken; + cancelSource.cancel(); + }); + const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(sender => { + requestCanceled = sender; + cancelSource.cancel(); + }); + + // Send the request to the language server. + let result: vscode.CallHierarchyIncomingCall[] | undefined; + let progressBarDuration: number | undefined; + try { + result = await sendCallHierarchyCallsToRequest(this.client, item, cancelSource.token); + } finally { + // Reset anything that can be cleared before processing the result. + progressBarDuration = workspaceReferences.getCallHierarchyProgressBarDuration(); + workspaceReferences.resetProgressBar(); + workspaceReferences.resetReferences(); + cancellationTokenListener.dispose(); + requestCanceledListener.dispose(); + } + + // Process the result. + if (cancelSource.token.isCancellationRequested || result === undefined || requestCanceled !== undefined) { + const requestStatus: CallHierarchyRequestStatus = requestCanceled === CancellationSender.User ? + CallHierarchyRequestStatus.CanceledByUser : CallHierarchyRequestStatus.Canceled; + this.logTelemetry(CallHierarchyCallsToEvent, requestStatus, progressBarDuration); + throw new vscode.CancellationError(); + } + + this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Succeeded, progressBarDuration); + return result.length !== 0 ? result : undefined; }); - - // Send the request to the language server. - let result: vscode.CallHierarchyIncomingCall[] | undefined; - let progressBarDuration: number | undefined; - try { - result = await sendCallHierarchyCallsToRequest(this.client, item, cancelSource.token); - } finally { - // Reset anything that can be cleared before processing the result. - progressBarDuration = workspaceReferences.getCallHierarchyProgressBarDuration(); - workspaceReferences.resetProgressBar(); - workspaceReferences.resetReferences(); - cancellationTokenListener.dispose(); - requestCanceledListener.dispose(); - } - - // Process the result. - if (cancelSource.token.isCancellationRequested || result === undefined || requestCanceled !== undefined) { - const requestStatus: CallHierarchyRequestStatus = requestCanceled === CancellationSender.User ? - CallHierarchyRequestStatus.CanceledByUser : CallHierarchyRequestStatus.Canceled; - this.logTelemetry(CallHierarchyCallsToEvent, requestStatus, progressBarDuration); - throw new vscode.CancellationError(); - } - - this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Succeeded, progressBarDuration); - return result.length !== 0 ? result : undefined; } public async provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { diff --git a/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts b/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts index f80457dbf..5db3f6a4e 100644 --- a/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts +++ b/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts @@ -62,42 +62,46 @@ export class FindAllReferencesProvider implements vscode.ReferenceProvider { this.client = client; } - public async provideReferences(document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise { - await this.client.ready; + public provideReferences(document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise { + return this.client.enqueue(async () => { + if (token.isCancellationRequested) { + throw new vscode.CancellationError(); + } - // Listen to a cancellation for this request. When this request is cancelled, - // use a local cancellation source to explicitly cancel a token. - const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(); - const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => { cancelSource.cancel(); }); - const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); }); + // Listen to a cancellation for this request. When this request is cancelled, + // use a local cancellation source to explicitly cancel a token. + const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(); + const cancellationTokenListener: vscode.Disposable = token.onCancellationRequested(() => { cancelSource.cancel(); }); + const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); }); - // Send the request to the language server. - let result: FindAllReferencesResult | undefined; - try { - result = await sendFindAllReferencesRequest(this.client, document.uri, position, cancelSource.token); - } finally { - // Reset anything that can be cleared before processing the result. - workspaceReferences.resetProgressBar(); - cancellationTokenListener.dispose(); - requestCanceledListener.dispose(); - } + // Send the request to the language server. + let result: FindAllReferencesResult | undefined; + try { + result = await sendFindAllReferencesRequest(this.client, document.uri, position, cancelSource.token); + } finally { + // Reset anything that can be cleared before processing the result. + workspaceReferences.resetProgressBar(); + cancellationTokenListener.dispose(); + requestCanceledListener.dispose(); + } - // Process the result. - if (cancelSource.token.isCancellationRequested || !result) { - // Return undefined instead of vscode.CancellationError to avoid the following error message from VS Code: - // "Cannot destructure property 'range' of 'e.location' as it is undefined." - // TODO: per issue https://github.com/microsoft/vscode/issues/169698 - // vscode.CancellationError is expected, so when VS Code fixes the error use vscode.CancellationError again. - workspaceReferences.resetReferences(); - return undefined; - } else if (result.referencesResult.referenceInfos.length > 0) { - // Display other reference types in panel or channel view. - // Note: ReferencesManager.resetReferences is called in ReferencesManager.showResultsInPanelView - workspaceReferences.showResultsInPanelView(result.referencesResult); - } else { - workspaceReferences.resetReferences(); - } + // Process the result. + if (cancelSource.token.isCancellationRequested || !result) { + // Return undefined instead of vscode.CancellationError to avoid the following error message from VS Code: + // "Cannot destructure property 'range' of 'e.location' as it is undefined." + // TODO: per issue https://github.com/microsoft/vscode/issues/169698 + // vscode.CancellationError is expected, so when VS Code fixes the error use vscode.CancellationError again. + workspaceReferences.resetReferences(); + return undefined; + } else if (result.referencesResult.referenceInfos.length > 0) { + // Display other reference types in panel or channel view. + // Note: ReferencesManager.resetReferences is called in ReferencesManager.showResultsInPanelView + workspaceReferences.showResultsInPanelView(result.referencesResult); + } else { + workspaceReferences.resetReferences(); + } - return result.locations; + return result.locations; + }); } } diff --git a/Extension/src/LanguageServer/Providers/renameProvider.ts b/Extension/src/LanguageServer/Providers/renameProvider.ts index 8effd622e..44ae94096 100644 --- a/Extension/src/LanguageServer/Providers/renameProvider.ts +++ b/Extension/src/LanguageServer/Providers/renameProvider.ts @@ -24,64 +24,76 @@ export class RenameProvider implements vscode.RenameProvider { this.client = client; } - public async provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string, _token: vscode.CancellationToken): Promise { - await this.client.ready; - + public provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string, _token: vscode.CancellationToken): Promise { const settings: CppSettings = new CppSettings(); if (settings.renameRequiresIdentifier && !util.isValidIdentifier(newName)) { void vscode.window.showErrorMessage(localize("invalid.identifier.for.rename", "Invalid identifier provided for the Rename Symbol operation.")); - return undefined; + return Promise.resolve(undefined); } - // Listen to a cancellation for this request. When this request is cancelled, - // use a local cancellation source to explicitly cancel a token. - // Don't listen to the token from the provider, as it will cancel when the cursor is moved to a different position. - const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(); - const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); }); + const renameRequestId: number = workspaceReferences.createRenameRequest(); - // Send the request to the language server. - workspaceReferences.startRename(); - const workspaceEditResult: vscode.WorkspaceEdit = new vscode.WorkspaceEdit(); - const params: ReferencesParams = { - newName: newName, - position: Position.create(position.line, position.character), - textDocument: { uri: document.uri.toString() } - }; - let response: ReferencesResult; - try { - response = await this.client.languageClient.sendRequest(RenameRequest, params, cancelSource.token); - } catch (e: any) { - if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return this.client.enqueue(async () => { + if (!workspaceReferences.hasPendingRenameRequest(renameRequestId)) { throw new vscode.CancellationError(); } - throw e; - } - finally { - // Reset anything that can be cleared before processing the result. - workspaceReferences.resetProgressBar(); - workspaceReferences.resetReferences(); - requestCanceledListener.dispose(); - } - // Process the result. - if (cancelSource.token.isCancellationRequested || response.isCanceled) { - throw new vscode.CancellationError(); - } else if (response.referenceInfos.length === 0) { - void vscode.window.showErrorMessage(localize("unable.to.locate.selected.symbol", "A definition for the selected symbol could not be located.")); - } else { - for (const reference of response.referenceInfos) { - const uri: vscode.Uri = vscode.Uri.file(reference.file); - const range: vscode.Range = new vscode.Range(reference.position.line, reference.position.character, - reference.position.line, reference.position.character + response.text.length); - const metadata: vscode.WorkspaceEditEntryMetadata = { - needsConfirmation: reference.type !== ReferenceType.Confirmed, - label: getReferenceTagString(reference.type, false, true), - iconPath: getReferenceItemIconPath(reference.type, false) - }; - workspaceEditResult.replace(uri, range, newName, metadata); + // Listen to a cancellation for this request. When this request is cancelled, + // use a local cancellation source to explicitly cancel a token. + // Don't listen to the token from the provider, as it will cancel when the cursor is moved to a different position. + const cancelSource: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(); + const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); }); + const renameCanceledListener: vscode.Disposable = workspaceReferences.onRenameCancellationRequested((sender) => { + if (sender === CancellationSender.User) { + cancelSource.cancel(); + } + }); + + // Send the request to the language server. + const workspaceEditResult: vscode.WorkspaceEdit = new vscode.WorkspaceEdit(); + const params: ReferencesParams = { + newName: newName, + position: Position.create(position.line, position.character), + textDocument: { uri: document.uri.toString() } + }; + let response: ReferencesResult; + try { + response = await this.client.languageClient.sendRequest(RenameRequest, params, cancelSource.token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + throw new vscode.CancellationError(); + } + throw e; + } + finally { + // Reset anything that can be cleared before processing the result. + workspaceReferences.finishRenameRequest(renameRequestId); + workspaceReferences.resetProgressBar(); + workspaceReferences.resetReferences(); + requestCanceledListener.dispose(); + renameCanceledListener.dispose(); + } + + // Process the result. + if (cancelSource.token.isCancellationRequested || response.isCanceled) { + throw new vscode.CancellationError(); + } else if (response.referenceInfos.length === 0) { + void vscode.window.showErrorMessage(localize("unable.to.locate.selected.symbol", "A definition for the selected symbol could not be located.")); + } else { + for (const reference of response.referenceInfos) { + const uri: vscode.Uri = vscode.Uri.file(reference.file); + const range: vscode.Range = new vscode.Range(reference.position.line, reference.position.character, + reference.position.line, reference.position.character + response.text.length); + const metadata: vscode.WorkspaceEditEntryMetadata = { + needsConfirmation: reference.type !== ReferenceType.Confirmed, + label: getReferenceTagString(reference.type, false, true), + iconPath: getReferenceItemIconPath(reference.type, false) + }; + workspaceEditResult.replace(uri, range, newName, metadata); + } } - } - return workspaceEditResult; + return workspaceEditResult; + }); } } diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index b6c00ab44..9129373ce 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -2015,10 +2015,10 @@ export class DefaultClient implements Client { public onDidChangeTextDocument(textDocumentChangeEvent: vscode.TextDocumentChangeEvent): void { if (util.isCpp(textDocumentChangeEvent.document)) { - // If any file has changed, we need to abort the current rename operation + // If any file has changed, abort any pending rename operations. if (workspaceReferences !== undefined // Occurs when a document changes before cpptools starts. && workspaceReferences.renamePending) { - workspaceReferences.cancelCurrentReferenceRequest(refs.CancellationSender.User); + workspaceReferences.cancelPendingRenameRequests(refs.CancellationSender.User); } const oldVersion: number | undefined = openFileVersions.get(textDocumentChangeEvent.document.uri.toString()); diff --git a/Extension/src/LanguageServer/references.ts b/Extension/src/LanguageServer/references.ts index e8e3567b7..a10d6698a 100644 --- a/Extension/src/LanguageServer/references.ts +++ b/Extension/src/LanguageServer/references.ts @@ -196,8 +196,10 @@ export class ReferencesManager { private findAllRefsView?: FindAllRefsView; private viewsInitialized: boolean = false; - public renamePending: boolean = false; + private nextRenameRequestId: number = 0; + private pendingRenameRequests: Set = new Set(); private referenceRequestCanceled = new vscode.EventEmitter(); + private renameRequestCanceled = new vscode.EventEmitter(); private referencesCurrentProgress?: ReportReferencesProgressNotification; private referencesPrevProgressIncrement: number = 0; @@ -225,7 +227,7 @@ export class ReferencesManager { constructor(client: DefaultClient) { this.client = client; - this.disposables.push(vscode.Disposable.from(this.referenceRequestCanceled)); + this.disposables.push(vscode.Disposable.from(this.referenceRequestCanceled, this.renameRequestCanceled)); } initializeViews(): void { @@ -239,11 +241,41 @@ export class ReferencesManager { return this.referenceRequestCanceled.event; } + public get onRenameCancellationRequested(): vscode.Event { + return this.renameRequestCanceled.event; + } + + public get renamePending(): boolean { + return this.pendingRenameRequests.size !== 0; + } + public cancelCurrentReferenceRequest(sender: CancellationSender): void { // Notify the current listener to cancel its request. this.referenceRequestCanceled.fire(sender); } + public createRenameRequest(): number { + const requestId: number = ++this.nextRenameRequestId; + this.pendingRenameRequests.add(requestId); + return requestId; + } + + public hasPendingRenameRequest(requestId: number): boolean { + return this.pendingRenameRequests.has(requestId); + } + + public cancelPendingRenameRequests(sender: CancellationSender): void { + if (this.pendingRenameRequests.size === 0) { + return; + } + this.pendingRenameRequests.clear(); + this.renameRequestCanceled.fire(sender); + } + + public finishRenameRequest(requestId: number): void { + this.pendingRenameRequests.delete(requestId); + } + public dispose(): void { this.disposables.forEach((d) => d.dispose()); this.disposables = []; @@ -450,12 +482,7 @@ export class ReferencesManager { this.referencesProgressBarStartTime = 0; } - public startRename(): void { - this.renamePending = true; - } - public resetReferences(): void { - this.renamePending = false; this.initializeViews(); this.client.setReferencesCommandMode(ReferencesCommandMode.None); } From 1e2bf9172b34726048bbc7436beb537ebee0083a Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Sat, 16 May 2026 18:02:09 -0700 Subject: [PATCH 3/4] Fix build error. --- Extension/src/LanguageServer/Providers/renameProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extension/src/LanguageServer/Providers/renameProvider.ts b/Extension/src/LanguageServer/Providers/renameProvider.ts index 44ae94096..215895a81 100644 --- a/Extension/src/LanguageServer/Providers/renameProvider.ts +++ b/Extension/src/LanguageServer/Providers/renameProvider.ts @@ -8,7 +8,7 @@ import * as nls from 'vscode-nls'; import * as util from '../../common'; import { DefaultClient, workspaceReferences } from '../client'; import { RequestCancelled, ServerCancelled } from '../protocolFilter'; -import { ReferenceType, ReferencesParams, ReferencesResult, getReferenceItemIconPath, getReferenceTagString } from '../references'; +import { CancellationSender, ReferenceType, ReferencesParams, ReferencesResult, getReferenceItemIconPath, getReferenceTagString } from '../references'; import { CppSettings } from '../settings'; nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); From 525348e7cf118f554327d45fe0e8fb0fffa90345 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Sat, 16 May 2026 18:17:12 -0700 Subject: [PATCH 4/4] Fix. --- Extension/src/LanguageServer/Providers/renameProvider.ts | 7 +------ Extension/src/LanguageServer/references.ts | 4 ---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Extension/src/LanguageServer/Providers/renameProvider.ts b/Extension/src/LanguageServer/Providers/renameProvider.ts index 215895a81..9018b09d2 100644 --- a/Extension/src/LanguageServer/Providers/renameProvider.ts +++ b/Extension/src/LanguageServer/Providers/renameProvider.ts @@ -31,13 +31,7 @@ export class RenameProvider implements vscode.RenameProvider { return Promise.resolve(undefined); } - const renameRequestId: number = workspaceReferences.createRenameRequest(); - return this.client.enqueue(async () => { - if (!workspaceReferences.hasPendingRenameRequest(renameRequestId)) { - throw new vscode.CancellationError(); - } - // Listen to a cancellation for this request. When this request is cancelled, // use a local cancellation source to explicitly cancel a token. // Don't listen to the token from the provider, as it will cancel when the cursor is moved to a different position. @@ -48,6 +42,7 @@ export class RenameProvider implements vscode.RenameProvider { cancelSource.cancel(); } }); + const renameRequestId: number = workspaceReferences.createRenameRequest(); // Send the request to the language server. const workspaceEditResult: vscode.WorkspaceEdit = new vscode.WorkspaceEdit(); diff --git a/Extension/src/LanguageServer/references.ts b/Extension/src/LanguageServer/references.ts index a10d6698a..533c6f0ec 100644 --- a/Extension/src/LanguageServer/references.ts +++ b/Extension/src/LanguageServer/references.ts @@ -260,10 +260,6 @@ export class ReferencesManager { return requestId; } - public hasPendingRenameRequest(requestId: number): boolean { - return this.pendingRenameRequests.has(requestId); - } - public cancelPendingRenameRequests(sender: CancellationSender): void { if (this.pendingRenameRequests.size === 0) { return;