From b0f5f4682650a70a726c2bdb81e018279a0dffda Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Mon, 25 Aug 2025 11:30:14 +0200 Subject: [PATCH 1/2] Register context provider API with copilot chat as well. --- .../copilotCompletionContextProvider.ts | 57 +++++++++++++++---- .../src/LanguageServer/copilotProviders.ts | 24 +++++++- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/Extension/src/LanguageServer/copilotCompletionContextProvider.ts b/Extension/src/LanguageServer/copilotCompletionContextProvider.ts index cdf75308f..5aabf84d2 100644 --- a/Extension/src/LanguageServer/copilotCompletionContextProvider.ts +++ b/Extension/src/LanguageServer/copilotCompletionContextProvider.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All Rights Reserved. * See 'LICENSE' in the project root for license information. * ------------------------------------------------------------------------------------------ */ -import { ContextResolver, ResolveRequest, SupportedContextItem } from '@github/copilot-language-server'; +import { ContextResolver, ResolveRequest, SupportedContextItem, type ContextProvider } from '@github/copilot-language-server'; import { randomUUID } from 'crypto'; import * as vscode from 'vscode'; import { DocumentSelector } from 'vscode-languageserver-protocol'; @@ -11,7 +11,7 @@ import { getOutputChannelLogger, Logger } from '../logger'; import * as telemetry from '../telemetry'; import { CopilotCompletionContextResult } from './client'; import { CopilotCompletionContextTelemetry } from './copilotCompletionContextTelemetry'; -import { getCopilotApi } from './copilotProviders'; +import { getCopilotApi, getCopilotChatApi, type CopilotChatApi } from './copilotProviders'; import { clients } from './extension'; import { CppSettings } from './settings'; @@ -83,7 +83,7 @@ export class CopilotCompletionContextProvider implements ContextResolver): Promise<{ hasGetContextProviderAPI: boolean; hasAPI: boolean }> { + const hasGetContextProviderAPI = "getContextProviderAPI" in copilotAPI; + if (hasGetContextProviderAPI) { + const contextAPI = await copilotAPI.getContextProviderAPI("v1"); + if (contextAPI) { + this.contextProviderDisposables = this.contextProviderDisposables ?? []; + this.contextProviderDisposables.push(contextAPI.registerContextProvider(contextProvider)); + } + return { hasGetContextProviderAPI, hasAPI: contextAPI !== undefined }; + } else { + return { hasGetContextProviderAPI: false, hasAPI: false }; + } + } } diff --git a/Extension/src/LanguageServer/copilotProviders.ts b/Extension/src/LanguageServer/copilotProviders.ts index e0551edcb..f9c825c61 100644 --- a/Extension/src/LanguageServer/copilotProviders.ts +++ b/Extension/src/LanguageServer/copilotProviders.ts @@ -24,7 +24,11 @@ export interface CopilotTrait { promptTextOverride?: string; } -export interface CopilotApi { +export interface CopilotChatApi { + getContextProviderAPI(version: string): Promise; +} + +export interface CopilotApi extends CopilotChatApi { registerRelatedFilesProvider( providerId: { extensionId: string; languageId: string }, callback: ( @@ -33,7 +37,6 @@ export interface CopilotApi { cancellationToken: vscode.CancellationToken ) => Promise<{ entries: vscode.Uri[]; traits?: CopilotTrait[] } | undefined> ): Disposable; - getContextProviderAPI(version: string): Promise; } export async function registerRelatedFilesProvider(): Promise { @@ -145,3 +148,20 @@ export async function getCopilotApi(): Promise { return copilotExtension.exports; } } + +export async function getCopilotChatApi(): Promise { + const copilotExtension = vscode.extensions.getExtension('github.copilot-chat'); + if (!copilotExtension) { + return undefined; + } + + if (!copilotExtension.isActive) { + try { + return await copilotExtension.activate(); + } catch { + return undefined; + } + } else { + return copilotExtension.exports; + } +} From 0ef5409d3c7da03a8d5c9bfc87504c16fe91fcd1 Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Tue, 26 Aug 2025 08:53:02 +0200 Subject: [PATCH 2/2] Minor code cleanup --- .../copilotCompletionContextProvider.ts | 8 +++--- .../src/LanguageServer/copilotProviders.ts | 25 +++++++++++++------ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Extension/src/LanguageServer/copilotCompletionContextProvider.ts b/Extension/src/LanguageServer/copilotCompletionContextProvider.ts index 5aabf84d2..a456ab73c 100644 --- a/Extension/src/LanguageServer/copilotCompletionContextProvider.ts +++ b/Extension/src/LanguageServer/copilotCompletionContextProvider.ts @@ -11,7 +11,7 @@ import { getOutputChannelLogger, Logger } from '../logger'; import * as telemetry from '../telemetry'; import { CopilotCompletionContextResult } from './client'; import { CopilotCompletionContextTelemetry } from './copilotCompletionContextTelemetry'; -import { getCopilotApi, getCopilotChatApi, type CopilotChatApi } from './copilotProviders'; +import { getCopilotChatApi, getCopilotClientApi, type CopilotContextProviderAPI } from './copilotProviders'; import { clients } from './extension'; import { CppSettings } from './settings'; @@ -449,7 +449,7 @@ ${copilotCompletionContext?.areSnippetsMissing ? "(missing code snippets)" : ""} const properties: Record = {}; const registerCopilotContextProvider = 'registerCopilotContextProvider'; try { - const copilotApi = await getCopilotApi(); + const copilotApi = await getCopilotClientApi(); const copilotChatApi = await getCopilotChatApi(); if (!copilotApi && !copilotChatApi) { throw new CopilotContextProviderException("getCopilotApi() returned null, Copilot is missing or inactive."); } const contextProvider = { @@ -486,8 +486,8 @@ ${copilotCompletionContext?.areSnippetsMissing ? "(missing code snippets)" : ""} } } - private async installContextProvider(copilotAPI: CopilotChatApi, contextProvider: ContextProvider): Promise<{ hasGetContextProviderAPI: boolean; hasAPI: boolean }> { - const hasGetContextProviderAPI = "getContextProviderAPI" in copilotAPI; + private async installContextProvider(copilotAPI: CopilotContextProviderAPI, contextProvider: ContextProvider): Promise<{ hasGetContextProviderAPI: boolean; hasAPI: boolean }> { + const hasGetContextProviderAPI = typeof copilotAPI.getContextProviderAPI === 'function'; if (hasGetContextProviderAPI) { const contextAPI = await copilotAPI.getContextProviderAPI("v1"); if (contextAPI) { diff --git a/Extension/src/LanguageServer/copilotProviders.ts b/Extension/src/LanguageServer/copilotProviders.ts index f9c825c61..31cf21f3e 100644 --- a/Extension/src/LanguageServer/copilotProviders.ts +++ b/Extension/src/LanguageServer/copilotProviders.ts @@ -24,11 +24,11 @@ export interface CopilotTrait { promptTextOverride?: string; } -export interface CopilotChatApi { +export interface CopilotContextProviderAPI { getContextProviderAPI(version: string): Promise; } -export interface CopilotApi extends CopilotChatApi { +export interface CopilotApi extends CopilotContextProviderAPI { registerRelatedFilesProvider( providerId: { extensionId: string; languageId: string }, callback: ( @@ -40,7 +40,7 @@ export interface CopilotApi extends CopilotChatApi { } export async function registerRelatedFilesProvider(): Promise { - const api = await getCopilotApi(); + const api = await getCopilotClientApi(); if (util.extensionContext && api) { try { for (const languageId of ['c', 'cpp', 'cuda-cpp']) { @@ -132,7 +132,7 @@ async function getIncludes(uri: vscode.Uri, maxDepth: number): Promise { +export async function getCopilotClientApi(): Promise { const copilotExtension = vscode.extensions.getExtension('github.copilot'); if (!copilotExtension) { return undefined; @@ -149,19 +149,30 @@ export async function getCopilotApi(): Promise { } } -export async function getCopilotChatApi(): Promise { +export async function getCopilotChatApi(): Promise { + type CopilotChatApi = { getAPI?(version: number): CopilotContextProviderAPI | undefined }; const copilotExtension = vscode.extensions.getExtension('github.copilot-chat'); if (!copilotExtension) { return undefined; } + let exports: CopilotChatApi | undefined; if (!copilotExtension.isActive) { try { - return await copilotExtension.activate(); + exports = await copilotExtension.activate(); } catch { return undefined; } } else { - return copilotExtension.exports; + exports = copilotExtension.exports; + } + if (!exports || typeof exports.getAPI !== 'function') { + return undefined; } + const result = exports.getAPI(1); + return result; +} + +interface Disposable { + dispose(): void; }