diff --git a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts index 6c6d39f07..61b7b110b 100644 --- a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts +++ b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts @@ -88,6 +88,129 @@ const CallHierarchyCallsToRequest: RequestType = new RequestType('cpptools/callHierarchyCallsFrom'); +function makeVscodeCallHierarchyItem(client: DefaultClient, item: CallHierarchyItem): vscode.CallHierarchyItem { + const containerDetail: string = (item.detail !== "") ? `${item.detail} - ` : ""; + const itemUri: vscode.Uri = vscode.Uri.file(item.file); + + // Get file detail + const isInWorkspace: boolean = client.RootUri !== undefined && + itemUri.fsPath.startsWith(client.RootUri.fsPath); + const dirPath: string = isInWorkspace ? + path.relative(client.RootPath, path.dirname(item.file)) : path.dirname(item.file); + const fileDetail: string = dirPath.length === 0 ? + `${path.basename(item.file)}` : `${path.basename(item.file)} (${dirPath})`; + + return new vscode.CallHierarchyItem( + item.kind, + item.name, + containerDetail + fileDetail, + itemUri, + makeVscodeRange(item.range), + makeVscodeRange(item.selectionRange)); +} + +function createIncomingCalls(client: DefaultClient, calls: CallHierarchyCallsItem[]): vscode.CallHierarchyIncomingCall[] { + const result: vscode.CallHierarchyIncomingCall[] = []; + + for (const call of calls) { + const item: vscode.CallHierarchyItem = makeVscodeCallHierarchyItem(client, call.item); + const ranges: vscode.Range[] = []; + call.fromRanges.forEach(r => { + ranges.push(makeVscodeRange(r)); + }); + + const incomingCall: vscode.CallHierarchyIncomingCall = + new vscode.CallHierarchyIncomingCall(item, ranges); + result.push(incomingCall); + } + + return result; +} + +function createOutgoingCalls(client: DefaultClient, calls: CallHierarchyCallsItem[]): vscode.CallHierarchyOutgoingCall[] { + const result: vscode.CallHierarchyOutgoingCall[] = []; + + for (const call of calls) { + const item: vscode.CallHierarchyItem = makeVscodeCallHierarchyItem(client, call.item); + const ranges: vscode.Range[] = []; + call.fromRanges.forEach(r => { + ranges.push(makeVscodeRange(r)); + }); + + const outgoingCall: vscode.CallHierarchyOutgoingCall = + new vscode.CallHierarchyOutgoingCall(item, ranges); + result.push(outgoingCall); + } + + return result; +} + +export async function sendPrepareCallHierarchyRequest(client: DefaultClient, uri: vscode.Uri, position: vscode.Position, token: vscode.CancellationToken): Promise { + const params: CallHierarchyParams = { + textDocument: { uri: uri.toString() }, + position: Position.create(position.line, position.character) + }; + let response: CallHierarchyItemResult; + try { + response = await client.languageClient.sendRequest(CallHierarchyItemRequest, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested) { + return undefined; + } + + return response.item === undefined ? [] : [makeVscodeCallHierarchyItem(client, response.item)]; +} + +export async function sendCallHierarchyCallsToRequest(client: DefaultClient, item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { + const params: CallHierarchyParams = { + textDocument: { uri: item.uri.toString() }, + position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character) + }; + let response: CallHierarchyCallsItemResult; + try { + response = await client.languageClient.sendRequest(CallHierarchyCallsToRequest, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested) { + return undefined; + } + + return response.calls.length !== 0 ? createIncomingCalls(client, response.calls) : []; +} + +export async function sendCallHierarchyCallsFromRequest(client: DefaultClient, item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { + const params: CallHierarchyParams = { + textDocument: { uri: item.uri.toString() }, + position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character) + }; + let response: CallHierarchyCallsItemResult; + try { + response = await client.languageClient.sendRequest(CallHierarchyCallsFromRequest, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested) { + return undefined; + } + + return response.calls.length !== 0 ? createOutgoingCalls(client, response.calls) : []; +} + export class CallHierarchyProvider implements vscode.CallHierarchyProvider { // Indicates whether a request is from an entry root node (e.g. top function in the call tree). private isEntryRootNodeTelemetry: boolean = false; @@ -118,20 +241,10 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { cancelSource.cancel(); }); - const params: CallHierarchyParams = { - textDocument: { uri: document.uri.toString() }, - position: Position.create(position.line, position.character) - }; - let response: CallHierarchyItemResult; + let result: vscode.CallHierarchyItem[] | undefined; try { - response = await this.client.languageClient.sendRequest(CallHierarchyItemRequest, params, cancelSource.token); - } catch (e: any) { - if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { - return undefined; - } - throw e; - } - finally { + result = await sendPrepareCallHierarchyRequest(this.client, document.uri, position, cancelSource.token); + } finally { cancellationTokenListener.dispose(); requestCanceledListener.dispose(); } @@ -139,12 +252,12 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { if (cancelSource.token.isCancellationRequested) { throw new vscode.CancellationError(); } - if (response.item === undefined) { + if (!result || result.length === 0) { return undefined; } this.isEntryRootNodeTelemetry = true; - return this.makeVscodeCallHierarchyItem(response.item); + return result[0]; } public async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { @@ -172,39 +285,28 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { // Send the request to the language server. let result: vscode.CallHierarchyIncomingCall[] | undefined; - const params: CallHierarchyParams = { - textDocument: { uri: item.uri.toString() }, - position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character) - }; - let response: CallHierarchyCallsItemResult | undefined; - let cancelled: boolean = false; + let progressBarDuration: number | undefined; try { - response = await this.client.languageClient.sendRequest(CallHierarchyCallsToRequest, params, cancelSource.token); - } catch (e: any) { - cancelled = e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled); - if (!cancelled) { - throw e; - } + 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(); } - // Reset anything that can be cleared before processing the result. - const progressBarDuration: number | undefined = workspaceReferences.getCallHierarchyProgressBarDuration(); - workspaceReferences.resetProgressBar(); - workspaceReferences.resetReferences(); - cancellationTokenListener.dispose(); - requestCanceledListener.dispose(); // Process the result. - if (cancelSource.token.isCancellationRequested || cancelled || requestCanceled !== undefined) { + 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(); - } else if (response && response.calls.length !== 0) { - result = this.createIncomingCalls(response.calls); } this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Succeeded, progressBarDuration); - return result; + return result.length !== 0 ? result : undefined; } public async provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { @@ -216,87 +318,15 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { await this.client.ready; - let result: vscode.CallHierarchyOutgoingCall[] | undefined; - const params: CallHierarchyParams = { - textDocument: { uri: item.uri.toString() }, - position: Position.create(item.selectionRange.start.line, item.selectionRange.start.character) - }; - let response: CallHierarchyCallsItemResult | undefined; - let cancelled: boolean = false; - try { - response = await this.client.languageClient.sendRequest(CallHierarchyCallsFromRequest, params, token); - } catch (e: any) { - cancelled = e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled); - if (!cancelled) { - throw e; - } - } - if (token.isCancellationRequested || cancelled) { + const result: vscode.CallHierarchyOutgoingCall[] | undefined = + await sendCallHierarchyCallsFromRequest(this.client, item, token); + if (token.isCancellationRequested || result === undefined) { this.logTelemetry(CallHierarchyCallsFromEvent, CallHierarchyRequestStatus.Canceled); throw new vscode.CancellationError(); - } else if (response && response.calls.length !== 0) { - result = this.createOutgoingCalls(response.calls); } this.logTelemetry(CallHierarchyCallsFromEvent, CallHierarchyRequestStatus.Succeeded); - return result; - } - - private makeVscodeCallHierarchyItem(item: CallHierarchyItem): vscode.CallHierarchyItem { - const containerDetail: string = (item.detail !== "") ? `${item.detail} - ` : ""; - const itemUri: vscode.Uri = vscode.Uri.file(item.file); - - // Get file detail - const isInWorkspace: boolean = this.client.RootUri !== undefined && - itemUri.fsPath.startsWith(this.client.RootUri?.fsPath); - const dirPath: string = isInWorkspace ? - path.relative(this.client.RootPath, path.dirname(item.file)) : path.dirname(item.file); - const fileDetail: string = dirPath.length === 0 ? - `${path.basename(item.file)}` : `${path.basename(item.file)} (${dirPath})`; - - return new vscode.CallHierarchyItem( - item.kind, - item.name, - containerDetail + fileDetail, - itemUri, - makeVscodeRange(item.range), - makeVscodeRange(item.selectionRange)); - } - - private createIncomingCalls(calls: CallHierarchyCallsItem[]): vscode.CallHierarchyIncomingCall[] { - const result: vscode.CallHierarchyIncomingCall[] = []; - - for (const call of calls) { - const item: vscode.CallHierarchyItem = this.makeVscodeCallHierarchyItem(call.item); - const ranges: vscode.Range[] = []; - call.fromRanges.forEach(r => { - ranges.push(makeVscodeRange(r)); - }); - - const incomingCall: vscode.CallHierarchyIncomingCall = - new vscode.CallHierarchyIncomingCall(item, ranges); - result.push(incomingCall); - } - - return result; - } - - private createOutgoingCalls(calls: CallHierarchyCallsItem[]): vscode.CallHierarchyOutgoingCall[] { - const result: vscode.CallHierarchyOutgoingCall[] = []; - - for (const call of calls) { - const item: vscode.CallHierarchyItem = this.makeVscodeCallHierarchyItem(call.item); - const ranges: vscode.Range[] = []; - call.fromRanges.forEach(r => { - ranges.push(makeVscodeRange(r)); - }); - - const outgoingCall: vscode.CallHierarchyOutgoingCall = - new vscode.CallHierarchyOutgoingCall(item, ranges); - result.push(outgoingCall); - } - - return result; + return result.length !== 0 ? result : undefined; } private logTelemetry(eventName: string, requestStatus: CallHierarchyRequestStatus, progressBarDuration?: number): void { 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/Providers/goToDefinitionProvider.ts b/Extension/src/LanguageServer/Providers/goToDefinitionProvider.ts new file mode 100644 index 000000000..80fb17c34 --- /dev/null +++ b/Extension/src/LanguageServer/Providers/goToDefinitionProvider.ts @@ -0,0 +1,57 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +import * as vscode from 'vscode'; +import { Definition, DefinitionLink, DefinitionRequest, Position, ResponseError, TextDocumentPositionParams } from 'vscode-languageclient'; +import { DefaultClient } from '../client'; +import { RequestCancelled, ServerCancelled } from '../protocolFilter'; + +function convertDefinitionsToLocations(definitionsResult: vscode.Definition | vscode.DefinitionLink[] | undefined): vscode.Location[] { + if (!definitionsResult) { + return []; + } + + if (!Array.isArray(definitionsResult)) { + return [definitionsResult]; + } + + const result: vscode.Location[] = []; + for (const definition of definitionsResult) { + if (definition instanceof vscode.Location) { + result.push(definition); + } else { + result.push(new vscode.Location(definition.targetUri, definition.targetSelectionRange ?? definition.targetRange)); + } + } + + return result; +} + +export async function sendGoToDefinitionRequest(client: DefaultClient, uri: vscode.Uri, position: vscode.Position, token: vscode.CancellationToken): Promise { + const params: TextDocumentPositionParams = { + position: Position.create(position.line, position.character), + textDocument: { uri: uri.toString() } + }; + let response: Definition | DefinitionLink[] | null; + try { + response = await client.languageClient.sendRequest(DefinitionRequest.type, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; + } + throw e; + } + + if (token.isCancellationRequested) { + return undefined; + } + + const result: vscode.Definition | vscode.DefinitionLink[] | undefined = + await client.languageClient.protocol2CodeConverter.asDefinitionResult(response, token); + if (token.isCancellationRequested) { + return undefined; + } + + return convertDefinitionsToLocations(result); +} diff --git a/Extension/src/LanguageServer/extension.ts b/Extension/src/LanguageServer/extension.ts index f4d3417b2..fd8267b42 100644 --- a/Extension/src/LanguageServer/extension.ts +++ b/Extension/src/LanguageServer/extension.ts @@ -23,6 +23,9 @@ import { getCrashCallStacksChannel } from '../logger'; import { PlatformInformation } from '../platform'; import * as telemetry from '../telemetry'; import { CopilotHoverProvider } from './Providers/CopilotHoverProvider'; +import { sendCallHierarchyCallsFromRequest, sendCallHierarchyCallsToRequest, sendPrepareCallHierarchyRequest } from './Providers/callHierarchyProvider'; +import { sendFindAllReferencesRequest } from './Providers/findAllReferencesProvider'; +import { sendGoToDefinitionRequest } from './Providers/goToDefinitionProvider'; import { Client, DefaultClient, DoxygenCodeActionCommandArguments, openFileVersions } from './client'; import { ClientCollection } from './clientCollection'; import { CodeActionDiagnosticInfo, CodeAnalysisDiagnosticIdentifiersAndUri, codeAnalysisAllFixes, codeAnalysisCodeToFixes, codeAnalysisFileToCodeActions } from './codeAnalysis'; @@ -397,6 +400,11 @@ 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.GoToDefinition', enabled ? onGoToDefinition : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.PrepareCallHierarchy', enabled ? onPrepareCallHierarchy : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.CallHierarchyCallsTo', enabled ? onCallHierarchyCallsTo : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.CallHierarchyCallsFrom', enabled ? onCallHierarchyCallsFrom : 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 +816,77 @@ 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; +} + +async function onGoToDefinition(uri: vscode.Uri, position: vscode.Position, token?: vscode.CancellationToken): Promise { + if (!uri || !position) { + throw new Error("C_Cpp.GoToDefinition requires both a uri and position."); + } + + const client: Client = clients.getClientFor(uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + return sendGoToDefinitionRequest(client, uri, position, token ?? CancellationToken.None); +} + +async function onPrepareCallHierarchy(uri: vscode.Uri, position: vscode.Position, token?: vscode.CancellationToken): Promise { + if (!uri || !position) { + throw new Error("C_Cpp.PrepareCallHierarchy requires both a uri and position."); + } + + const client: Client = clients.getClientFor(uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + return sendPrepareCallHierarchyRequest(client, uri, position, token ?? CancellationToken.None); +} + +async function onCallHierarchyCallsTo(item: vscode.CallHierarchyItem, token?: vscode.CancellationToken): Promise { + if (!item) { + throw new Error("C_Cpp.CallHierarchyCallsTo requires an item."); + } + + const client: Client = clients.getClientFor(item.uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + return sendCallHierarchyCallsToRequest(client, item, token ?? CancellationToken.None); +} + +async function onCallHierarchyCallsFrom(item: vscode.CallHierarchyItem, token?: vscode.CancellationToken): Promise { + if (!item) { + throw new Error("C_Cpp.CallHierarchyCallsFrom requires an item."); + } + + const client: Client = clients.getClientFor(item.uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + await client.ready; + return sendCallHierarchyCallsFromRequest(client, item, token ?? CancellationToken.None); +} + function onToggleRefGroupView(): void { // Set context to switch icons const client: Client = getActiveClient();