diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index f14f4efb2..d26655b0e 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -1389,7 +1389,7 @@ export class DefaultClient implements Client { this.semanticTokensProviderDisposable = vscode.languages.registerDocumentSemanticTokensProvider(util.documentSelector, this.semanticTokensProvider, semanticTokensLegend); } - this.copilotCompletionProvider = await CopilotCompletionContextProvider.Create(); + this.copilotCompletionProvider = CopilotCompletionContextProvider.Create(); this.disposables.push(this.copilotCompletionProvider); // Listen for messages from the language server. diff --git a/Extension/src/LanguageServer/copilotCompletionContextProvider.ts b/Extension/src/LanguageServer/copilotCompletionContextProvider.ts index a456ab73c..1e6d32658 100644 --- a/Extension/src/LanguageServer/copilotCompletionContextProvider.ts +++ b/Extension/src/LanguageServer/copilotCompletionContextProvider.ts @@ -304,9 +304,9 @@ export class CopilotCompletionContextProvider implements ContextResolver { - const properties: Record = {}; + public registerCopilotContextProvider(): void { const registerCopilotContextProvider = 'registerCopilotContextProvider'; - try { - const copilotApi = await getCopilotClientApi(); - const copilotChatApi = await getCopilotChatApi(); - if (!copilotApi && !copilotChatApi) { throw new CopilotContextProviderException("getCopilotApi() returned null, Copilot is missing or inactive."); } - const contextProvider = { - id: CopilotCompletionContextProvider.providerId, - selector: CopilotCompletionContextProvider.defaultCppDocumentSelector, - resolver: this - }; - type InstallSummary = { hasGetContextProviderAPI: boolean; hasAPI: boolean }; - const installSummary: { client?: InstallSummary; chat?: InstallSummary } = {}; - if (copilotApi) { - installSummary.client = await this.installContextProvider(copilotApi, contextProvider); + const contextProvider = { + id: CopilotCompletionContextProvider.providerId, + selector: CopilotCompletionContextProvider.defaultCppDocumentSelector, + resolver: this + }; + type RegistrationResult = { message: string } | boolean; + const clientPromise: Promise = getCopilotClientApi().then(async (api) => { + if (!api) { + throw new CopilotContextProviderException("getCopilotApi() returned null, Copilot client is missing or inactive."); + } + const disposable = await this.installContextProvider(api, contextProvider); + if (disposable) { + this.contextProviderDisposables = this.contextProviderDisposables ?? []; + this.contextProviderDisposables.push(disposable); + return true; + } else { + throw new CopilotContextProviderException("getContextProviderAPI() is not available in Copilot client."); } - if (copilotChatApi) { - installSummary.chat = await this.installContextProvider(copilotChatApi, contextProvider); + }).catch((e) => { + console.debug("Failed to register the Copilot Context Provider with Copilot client."); + let message = "Failed to register the Copilot Context Provider with Copilot client"; + if (e instanceof CopilotContextProviderException) { + message += `: ${e.message} `; } - if (installSummary.client?.hasAPI || installSummary.chat?.hasAPI) { - properties["cppCodeSnippetsProviderRegistered"] = "true"; + return { message }; + }); + const chatPromise: Promise = getCopilotChatApi().then(async (api) => { + if (!api) { + throw new CopilotContextProviderException("getCopilotChatApi() returned null, Copilot Chat is missing or inactive."); + } + const disposable = await this.installContextProvider(api, contextProvider); + if (disposable) { + this.contextProviderDisposables = this.contextProviderDisposables ?? []; + this.contextProviderDisposables.push(disposable); + return true; } else { - if (installSummary.client?.hasGetContextProviderAPI === false && - installSummary.chat?.hasGetContextProviderAPI === false) { - throw new CopilotContextProviderException("getContextProviderAPI() is not available."); - } else { - throw new CopilotContextProviderException("getContextProviderAPI(v1) returned null."); - } + throw new CopilotContextProviderException("getContextProviderAPI() is not available in Copilot Chat."); } - } catch (e) { - console.debug("Failed to register the Copilot Context Provider."); - properties["error"] = "Failed to register the Copilot Context Provider"; + }).catch((e) => { + console.debug("Failed to register the Copilot Context Provider with Copilot Chat."); + let message = "Failed to register the Copilot Context Provider with Copilot Chat"; if (e instanceof CopilotContextProviderException) { - properties["error"] += `: ${e.message} `; + message += `: ${e.message} `; } - } finally { + return { message }; + }); + // The client usually doesn't block. So test it first. + clientPromise.then((clientResult) => { + const properties: Record = {}; + if (isBoolean(clientResult) && clientResult) { + properties["cppCodeSnippetsProviderRegistered"] = "true"; + telemetry.logCopilotEvent(registerCopilotContextProvider, { ...properties }); + return; + } + return chatPromise.then((chatResult) => { + const properties: Record = {}; + if (isBoolean(chatResult) && chatResult) { + properties["cppCodeSnippetsProviderRegistered"] = "true"; + telemetry.logCopilotEvent(registerCopilotContextProvider, { ...properties }); + return; + } else if (!isBoolean(clientResult) && isString(clientResult.message)) { + properties["error"] = clientResult.message; + } else if (!isBoolean(chatResult) && isString(chatResult.message)) { + properties["error"] = chatResult.message; + } else { + properties["error"] = "Failed to register the Copilot Context Provider for unknown reason."; + } + telemetry.logCopilotEvent(registerCopilotContextProvider, { ...properties }); + }); + }).catch((e) => { + const properties: Record = {}; + properties["error"] = `Failed to register the Copilot Context Provider with exception: ${e}`; telemetry.logCopilotEvent(registerCopilotContextProvider, { ...properties }); - } + }); } - private async installContextProvider(copilotAPI: CopilotContextProviderAPI, contextProvider: ContextProvider): Promise<{ hasGetContextProviderAPI: boolean; hasAPI: boolean }> { + private async installContextProvider(copilotAPI: CopilotContextProviderAPI, contextProvider: ContextProvider): Promise { const hasGetContextProviderAPI = typeof copilotAPI.getContextProviderAPI === 'function'; if (hasGetContextProviderAPI) { const contextAPI = await copilotAPI.getContextProviderAPI("v1"); if (contextAPI) { - this.contextProviderDisposables = this.contextProviderDisposables ?? []; - this.contextProviderDisposables.push(contextAPI.registerContextProvider(contextProvider)); + return contextAPI.registerContextProvider(contextProvider); } - return { hasGetContextProviderAPI, hasAPI: contextAPI !== undefined }; + return undefined; } else { - return { hasGetContextProviderAPI: false, hasAPI: false }; + return undefined; } } } diff --git a/Extension/src/LanguageServer/copilotProviders.ts b/Extension/src/LanguageServer/copilotProviders.ts index c402afec8..31cf21f3e 100644 --- a/Extension/src/LanguageServer/copilotProviders.ts +++ b/Extension/src/LanguageServer/copilotProviders.ts @@ -159,14 +159,7 @@ export async function getCopilotChatApi(): Promise(resolve => { - setTimeout(() => { - resolve(undefined); - }, 3000); - }) - ]); + exports = await copilotExtension.activate(); } catch { return undefined; }