diff --git a/Extension/src/LanguageServer/extension.ts b/Extension/src/LanguageServer/extension.ts index 470ca51fe..67077b2ba 100644 --- a/Extension/src/LanguageServer/extension.ts +++ b/Extension/src/LanguageServer/extension.ts @@ -33,6 +33,13 @@ import { CppSettings } from './settings'; import { LanguageStatusUI, getUI } from './ui'; import { makeLspRange, rangeEquals, showInstallCompilerWalkthrough } from './utils'; +interface CopilotApi { + registerRelatedFilesProvider( + providerId: { extensionId: string; languageId: string }, + callback: (uri: vscode.Uri) => Promise<{ entries: vscode.Uri[]; traits?: { name: string; value: string }[] }> + ): void; +} + nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })(); const localize: nls.LocalizeFunc = nls.loadMessageBundle(); export const CppSourceStr: string = "C/C++"; @@ -183,7 +190,8 @@ export async function activate(): Promise { void clients.ActiveClient.ready.then(() => intervalTimer = global.setInterval(onInterval, 2500)); - registerCommands(true); + const isRelatedFilesApiEnabled = await telemetry.isExperimentEnabled("CppToolsRelatedFilesApi"); + registerCommands(true, isRelatedFilesApiEnabled); vscode.tasks.onDidStartTask(() => getActiveClient().PauseCodeAnalysis()); @@ -254,6 +262,23 @@ export async function activate(): Promise { const tool = vscode.lm.registerTool('cpptools-lmtool-configuration', new CppConfigurationLanguageModelTool()); disposables.push(tool); } + + if (isRelatedFilesApiEnabled) { + const api = await getCopilotApi(); + if (util.extensionContext && api) { + try { + for (const languageId of ['c', 'cpp', 'cuda-cpp']) { + api.registerRelatedFilesProvider( + { extensionId: util.extensionContext.extension.id, languageId }, + async (_uri: vscode.Uri) => + ({ entries: (await clients.ActiveClient.getIncludes(1))?.includedFiles.map(file => vscode.Uri.file(file)) ?? [] }) + ); + } + } catch { + console.log("Failed to register Copilot related files provider."); + } + } + } } export function updateLanguageConfigurations(): void { @@ -350,7 +375,7 @@ function onInterval(): void { /** * registered commands */ -export function registerCommands(enabled: boolean): void { +export function registerCommands(enabled: boolean, isRelatedFilesApiEnabled: boolean = false): void { commandDisposables.forEach(d => d.dispose()); commandDisposables.length = 0; commandDisposables.push(vscode.commands.registerCommand('C_Cpp.SwitchHeaderSource', enabled ? onSwitchHeaderSource : onDisabledCommand)); @@ -408,7 +433,10 @@ export function registerCommands(enabled: boolean): void { commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ExtractToFreeFunction', enabled ? () => onExtractToFunction(true, false) : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ExtractToMemberFunction', enabled ? () => onExtractToFunction(false, true) : onDisabledCommand)); commandDisposables.push(vscode.commands.registerCommand('C_Cpp.ExpandSelection', enabled ? (r: Range) => onExpandSelection(r) : onDisabledCommand)); - commandDisposables.push(vscode.commands.registerCommand('C_Cpp.getIncludes', enabled ? (maxDepth: number) => getIncludes(maxDepth) : () => Promise.resolve())); + + if (!isRelatedFilesApiEnabled) { + commandDisposables.push(vscode.commands.registerCommand('C_Cpp.getIncludes', enabled ? (maxDepth: number) => getIncludes(maxDepth) : () => Promise.resolve())); + } } function onDisabledCommand() { @@ -1378,3 +1406,20 @@ export async function getIncludes(maxDepth: number): Promise { const includes = await clients.ActiveClient.getIncludes(maxDepth); return includes; } + +async function getCopilotApi(): Promise { + const copilotExtension = vscode.extensions.getExtension('github.copilot'); + if (!copilotExtension) { + return undefined; + } + + if (!copilotExtension.isActive) { + try { + return await copilotExtension.activate(); + } catch { + return undefined; + } + } else { + return copilotExtension.exports; + } +}