diff --git a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts index 6c6d39f07..550ff0cb6 100644 --- a/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts +++ b/Extension/src/LanguageServer/Providers/callHierarchyProvider.ts @@ -88,6 +88,157 @@ const CallHierarchyCallsToRequest: RequestType = new RequestType('cpptools/callHierarchyCallsFrom'); +interface CallHierarchyRequestResult { + result?: T; + cancelled: boolean; +} + +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, document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise> { + const range: vscode.Range | undefined = document.getWordRangeAtPosition(position); + if (range === undefined) { + return { cancelled: false }; + } + + await client.ready; + + const params: CallHierarchyParams = { + textDocument: { uri: document.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 { cancelled: true }; + } + throw e; + } + + if (token.isCancellationRequested) { + return { cancelled: true }; + } + + return { + cancelled: false, + result: response.item ? makeVscodeCallHierarchyItem(client, response.item) : undefined + }; +} + +export async function sendCallHierarchyIncomingCallsRequest(client: DefaultClient, item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise> { + await client.ready; + + 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 { cancelled: true }; + } + throw e; + } + + if (token.isCancellationRequested) { + return { cancelled: true }; + } + + return { + cancelled: false, + result: response.calls.length !== 0 ? createIncomingCalls(client, response.calls) : undefined + }; +} + +export async function sendCallHierarchyOutgoingCallsRequest(client: DefaultClient, item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise> { + await client.ready; + + 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 { cancelled: true }; + } + throw e; + } + + if (token.isCancellationRequested) { + return { cancelled: true }; + } + + return { + cancelled: false, + result: response.calls.length !== 0 ? createOutgoingCalls(client, response.calls) : undefined + }; +} + 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; @@ -98,16 +249,9 @@ 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); - 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(); @@ -118,37 +262,26 @@ 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: CallHierarchyRequestResult; 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, position, cancelSource.token); + } finally { cancellationTokenListener.dispose(); requestCanceledListener.dispose(); } - if (cancelSource.token.isCancellationRequested) { + if (cancelSource.token.isCancellationRequested || result.cancelled) { throw new vscode.CancellationError(); } - if (response.item === undefined) { + if (result.result === undefined) { return undefined; } this.isEntryRootNodeTelemetry = true; - return this.makeVscodeCallHierarchyItem(response.item); + return result.result; } public async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { - await this.client.ready; workspaceReferences.cancelCurrentReferenceRequest(CancellationSender.NewRequest); const CallHierarchyCallsToEvent: string = "CallHierarchyCallsTo"; @@ -171,40 +304,30 @@ 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 result: CallHierarchyRequestResult; 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 sendCallHierarchyIncomingCallsRequest(this.client, item, cancelSource.token); + } finally { + 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.cancelled || 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); } + const incomingCalls: vscode.CallHierarchyIncomingCall[] | undefined = result.result; this.logTelemetry(CallHierarchyCallsToEvent, CallHierarchyRequestStatus.Succeeded, progressBarDuration); - return result; + return incomingCalls; } public async provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { @@ -214,89 +337,15 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider { return undefined; } - 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: CallHierarchyRequestResult = + await sendCallHierarchyOutgoingCallsRequest(this.client, item, token); + if (token.isCancellationRequested || result.cancelled) { 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.result; } private logTelemetry(eventName: string, requestStatus: CallHierarchyRequestStatus, progressBarDuration?: number): void { diff --git a/Extension/src/LanguageServer/Providers/documentSymbolProvider.ts b/Extension/src/LanguageServer/Providers/documentSymbolProvider.ts index 4d4c3019d..07778524f 100644 --- a/Extension/src/LanguageServer/Providers/documentSymbolProvider.ts +++ b/Extension/src/LanguageServer/Providers/documentSymbolProvider.ts @@ -10,72 +10,83 @@ import { getLocalizedString, getLocalizedSymbolScope } from '../localization'; import { RequestCancelled, ServerCancelled } from '../protocolFilter'; import { makeVscodeRange } from '../utils'; -export class DocumentSymbolProvider implements vscode.DocumentSymbolProvider { - private getChildrenSymbols(symbols: LocalizeDocumentSymbol[]): vscode.DocumentSymbol[] { - const documentSymbols: vscode.DocumentSymbol[] = []; - if (symbols) { - symbols.forEach((symbol) => { - let detail: string = getLocalizedString(symbol.detail); - if (symbol.scope === SymbolScope.Private) { - if (detail.length === 0) { - detail = "private"; - } else { - detail = getLocalizedSymbolScope("private", detail); - } - } else if (symbol.scope === SymbolScope.Protected) { - if (detail.length === 0) { - detail = "protected"; - } else { - detail = getLocalizedSymbolScope("protected", detail); - } +function getChildrenSymbols(symbols: LocalizeDocumentSymbol[]): vscode.DocumentSymbol[] { + const documentSymbols: vscode.DocumentSymbol[] = []; + if (symbols) { + symbols.forEach((symbol) => { + let detail: string = getLocalizedString(symbol.detail); + if (symbol.scope === SymbolScope.Private) { + if (detail.length === 0) { + detail = "private"; + } else { + detail = getLocalizedSymbolScope("private", detail); } - - // Move the scope in the name to the detail. + } else if (symbol.scope === SymbolScope.Protected) { if (detail.length === 0) { - let offset_paren: number = symbol.name.indexOf("("); - if (offset_paren < 0) { - offset_paren = symbol.name.length; - } - const offset_scope: number = symbol.name.lastIndexOf("::", offset_paren - 2); - if (offset_scope > 0) { - detail = symbol.name.substring(0, offset_scope); - symbol.name = symbol.name.substring(offset_scope + 2); - } + detail = "protected"; + } else { + detail = getLocalizedSymbolScope("protected", detail); } + } - let r: vscode.Range = makeVscodeRange(symbol.range); - const sr: vscode.Range = makeVscodeRange(symbol.selectionRange); - if (!r.contains(sr)) { - r = sr; + // Move the scope in the name to the detail. + if (detail.length === 0) { + let offset_paren: number = symbol.name.indexOf("("); + if (offset_paren < 0) { + offset_paren = symbol.name.length; + } + const offset_scope: number = symbol.name.lastIndexOf("::", offset_paren - 2); + if (offset_scope > 0) { + detail = symbol.name.substring(0, offset_scope); + symbol.name = symbol.name.substring(offset_scope + 2); } - const vscodeSymbol: vscode.DocumentSymbol = new vscode.DocumentSymbol(symbol.name, detail, symbol.kind, r, sr); - vscodeSymbol.children = this.getChildrenSymbols(symbol.children); - documentSymbols.push(vscodeSymbol); - }); + } + + let r: vscode.Range = makeVscodeRange(symbol.range); + const sr: vscode.Range = makeVscodeRange(symbol.selectionRange); + if (!r.contains(sr)) { + r = sr; + } + const vscodeSymbol: vscode.DocumentSymbol = new vscode.DocumentSymbol(symbol.name, detail, symbol.kind, r, sr); + vscodeSymbol.children = getChildrenSymbols(symbol.children); + documentSymbols.push(vscodeSymbol); + }); + } + return documentSymbols; +} + +export async function sendDocumentSymbolRequest(client: DefaultClient, uri: vscode.Uri, token: vscode.CancellationToken): Promise { + await client.ready; + + const params: GetDocumentSymbolRequestParams = { + uri: uri.toString() + }; + + let response: GetDocumentSymbolResult; + try { + response = await client.languageClient.sendRequest(GetDocumentSymbolRequest, params, token); + } catch (e: any) { + if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { + return undefined; } - return documentSymbols; + throw e; } + + if (token.isCancellationRequested) { + return undefined; + } + + return getChildrenSymbols(response.symbols); +} + +export class DocumentSymbolProvider implements vscode.DocumentSymbolProvider { public async provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { const client: Client = clients.getClientFor(document.uri); if (client instanceof DefaultClient) { - const defaultClient: DefaultClient = client; - await client.ready; - const params: GetDocumentSymbolRequestParams = { - uri: document.uri.toString() - }; - let response: GetDocumentSymbolResult; - try { - response = await defaultClient.languageClient.sendRequest(GetDocumentSymbolRequest, params, token); - } catch (e: any) { - if (e instanceof ResponseError && (e.code === RequestCancelled || e.code === ServerCancelled)) { - throw new vscode.CancellationError(); - } - throw e; - } - if (token.isCancellationRequested) { + const resultSymbols: vscode.DocumentSymbol[] | undefined = await sendDocumentSymbolRequest(client, document.uri, token); + if (resultSymbols === undefined) { throw new vscode.CancellationError(); } - const resultSymbols: vscode.DocumentSymbol[] = this.getChildrenSymbols(response.symbols); return resultSymbols; } return []; 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..47fe77183 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 { sendCallHierarchyIncomingCallsRequest, sendCallHierarchyOutgoingCallsRequest, sendPrepareCallHierarchyRequest } from './Providers/callHierarchyProvider'; +import { sendDocumentSymbolRequest } from './Providers/documentSymbolProvider'; +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 +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.DocumentSymbols', enabled ? onDocumentSymbols : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.PrepareCallHierarchy', enabled ? onPrepareCallHierarchy : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.CallHierarchyIncomingCalls', enabled ? onCallHierarchyIncomingCalls : onDisabledCommand)); + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.CallHierarchyOutgoingCalls', enabled ? onCallHierarchyOutgoingCalls : 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 onDocumentSymbols(uri: vscode.Uri, token?: vscode.CancellationToken): Promise { + if (!uri) { + throw new Error("C_Cpp.DocumentSymbols requires a uri."); + } + + const client: Client = clients.getClientFor(uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + return sendDocumentSymbolRequest(client, uri, 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; + } + + const document: vscode.TextDocument = await vscode.workspace.openTextDocument(uri); + const result = await sendPrepareCallHierarchyRequest(client, document, position, token ?? CancellationToken.None); + return result.result; +} + +async function onCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token?: vscode.CancellationToken): Promise { + if (!item) { + throw new Error("C_Cpp.CallHierarchyIncomingCalls requires a call hierarchy item."); + } + + const client: Client = clients.getClientFor(item.uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + const result = await sendCallHierarchyIncomingCallsRequest(client, item, token ?? CancellationToken.None); + return result.result; +} + +async function onCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token?: vscode.CancellationToken): Promise { + if (!item) { + throw new Error("C_Cpp.CallHierarchyOutgoingCalls requires a call hierarchy item."); + } + + const client: Client = clients.getClientFor(item.uri); + if (!(client instanceof DefaultClient)) { + return undefined; + } + + const result = await sendCallHierarchyOutgoingCallsRequest(client, item, token ?? CancellationToken.None); + return result.result; +} + function onToggleRefGroupView(): void { // Set context to switch icons const client: Client = getActiveClient();