diff --git a/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts b/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts index ffdb1cf9e..664cf80c6 100644 --- a/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts +++ b/Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts @@ -11,6 +11,50 @@ import { CancellationSender, ReferenceInfo, ReferenceType, ReferencesParams, Ref const FindAllReferencesRequest: RequestType = new RequestType('cpptools/findAllReferences'); +export interface FindAllReferencesResult { + referencesResult: ReferencesResult; + locations: vscode.Location[]; +} + +function convertConfirmedReferencesToLocations(referencesResult: ReferencesResult): vscode.Location[] { + const locationsResult: vscode.Location[] = []; + referencesResult.referenceInfos.forEach((referenceInfo: ReferenceInfo) => { + if (referenceInfo.type === ReferenceType.Confirmed) { + const uri: vscode.Uri = vscode.Uri.file(referenceInfo.file); + const range: vscode.Range = new vscode.Range(referenceInfo.position.line, referenceInfo.position.character, + referenceInfo.position.line, referenceInfo.position.character + referencesResult.text.length); + locationsResult.push(new vscode.Location(uri, range)); + } + }); + return locationsResult; +} + +export async function sendFindAllReferencesRequest(client: DefaultClient, uri: vscode.Uri, position: vscode.Position, token: vscode.CancellationToken): Promise { + const params: ReferencesParams = { + newName: "", + position: Position.create(position.line, position.character), + textDocument: { uri: uri.toString() } + }; + let response: ReferencesResult; + try { + response = await client.languageClient.sendRequest(FindAllReferencesRequest, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested || response.isCanceled) { + return undefined; + } + + return { + referencesResult: response, + locations: convertConfirmedReferencesToLocations(response) + }; +} + export class FindAllReferencesProvider implements vscode.ReferenceProvider { private client: DefaultClient; @@ -29,23 +73,10 @@ export class FindAllReferencesProvider implements vscode.ReferenceProvider { const requestCanceledListener: vscode.Disposable = workspaceReferences.onCancellationRequested(_sender => { cancelSource.cancel(); }); // Send the request to the language server. - const locationsResult: vscode.Location[] = []; - const params: ReferencesParams = { - newName: "", - position: Position.create(position.line, position.character), - textDocument: { uri: document.uri.toString() } - }; - let response: ReferencesResult | undefined; - let cancelled: boolean = false; + let result: FindAllReferencesResult | undefined; try { - response = await this.client.languageClient.sendRequest(FindAllReferencesRequest, params, cancelSource.token); - } catch (e: any) { - cancelled = e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled); - if (!cancelled) { - throw e; - } - } - finally { + 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(); @@ -53,30 +84,21 @@ export class FindAllReferencesProvider implements vscode.ReferenceProvider { } // Process the result. - if (cancelSource.token.isCancellationRequested || cancelled || (response && response.isCanceled)) { + 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 (response && response.referenceInfos.length > 0) { - response.referenceInfos.forEach((referenceInfo: ReferenceInfo) => { - if (referenceInfo.type === ReferenceType.Confirmed) { - const uri: vscode.Uri = vscode.Uri.file(referenceInfo.file); - const range: vscode.Range = new vscode.Range(referenceInfo.position.line, referenceInfo.position.character, - referenceInfo.position.line, referenceInfo.position.character + response.text.length); - locationsResult.push(new vscode.Location(uri, range)); - } - }); - + } 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(response); + workspaceReferences.showResultsInPanelView(result.referencesResult); } else { workspaceReferences.resetReferences(); } - return locationsResult; + return result.locations; } } diff --git a/Extension/src/LanguageServer/extension.ts b/Extension/src/LanguageServer/extension.ts index 2c072eff3..4c0ce4739 100644 --- a/Extension/src/LanguageServer/extension.ts +++ b/Extension/src/LanguageServer/extension.ts @@ -23,6 +23,7 @@ import { getCrashCallStacksChannel } from '../logger'; import { PlatformInformation } from '../platform'; import * as telemetry from '../telemetry'; import { CopilotHoverProvider } from './Providers/CopilotHoverProvider'; +import { sendFindAllReferencesRequest } from './Providers/findAllReferencesProvider'; import { Client, DefaultClient, DoxygenCodeActionCommandArguments, openFileVersions } from './client'; import { ClientCollection } from './clientCollection'; import { CodeActionDiagnosticInfo, CodeAnalysisDiagnosticIdentifiersAndUri, codeAnalysisAllFixes, codeAnalysisCodeToFixes, codeAnalysisFileToCodeActions } from './codeAnalysis'; @@ -397,6 +398,7 @@ export async function registerCommands(enabled: boolean): Promise { commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ShowActiveCodeAnalysisCommands', enabled ? onShowActiveCodeAnalysisCommands : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ShowIdleCodeAnalysisCommands', enabled ? onShowIdleCodeAnalysisCommands : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ShowReferencesProgress', enabled ? onShowReferencesProgress : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.FindAllReferences', enabled ? onFindAllReferences : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.TakeSurvey', enabled ? onTakeSurvey : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.LogDiagnostics', enabled ? onLogDiagnostics : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.RescanWorkspace', enabled ? onRescanWorkspace : onDisabledCommand)); @@ -808,6 +810,21 @@ function onShowReferencesProgress(): void { void clients.ActiveClient.handleReferencesIcon().catch(logAndReturn.undefined); } +async function onFindAllReferences(uri: vscode.Uri, position: vscode.Position, token?: vscode.CancellationToken): Promise { + if (!uri || !position) { + throw new Error("C_Cpp.FindAllReferences requires both a uri and position."); + } + + const client: Client = clients.getClientFor(uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + const result = await sendFindAllReferencesRequest(client, uri, position, token ?? CancellationToken.None); + return result?.locations; +} + function onToggleRefGroupView(): void { // Set context to switch icons const client: Client = getActiveClient();