diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 2c9ddc6db..9dafdcbc5 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,6 +1,6 @@ # C/C++ for Visual Studio Code Changelog -## Version 1.24.4: March 25, 2025 +## Version 1.24.4: March 27, 2025 ### Enhancements * Add a new `recursiveIncludes` property to `c_cpp_properties.json`. [PR #13374](https://github.com/microsoft/vscode-cpptools/pull/13374) * Turn Copilot hover on by default. [PR #13385](https://github.com/microsoft/vscode-cpptools/pull/13385) @@ -8,7 +8,7 @@ ### Bug Fixes * Fix one potential cause of the `get_mangled_function_name` IntelliSense process crash. [#13358](https://github.com/Microsoft/vscode-cpptools/issues/13358) -* Fix Copilot-related logging appearing when it shouldn't. [PR #13388](https://github.com/microsoft/vscode-cpptools/pull/13388) +* Fix Copilot-related logging appearing when it shouldn't. [PR #13388](https://github.com/microsoft/vscode-cpptools/pull/13388), [PR #13417](https://github.com/microsoft/vscode-cpptools/pull/13417) * Fix relative compiler paths being expanded in `compile_commands.json`. [#13405](https://github.com/microsoft/vscode-cpptools/issues/13405) * Fix all caps clang-format logging on Windows. [#13406](https://github.com/microsoft/vscode-cpptools/issues/13406) * Fix an IntelliSense process crash in `handle_function`. diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index 024e862b6..807e76201 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -22,7 +22,7 @@ import { SemanticToken, SemanticTokensProvider } from './Providers/semanticToken import { WorkspaceSymbolProvider } from './Providers/workspaceSymbolProvider'; // End provider imports -import { SupportedContextItem } from '@github/copilot-language-server'; +import { CodeSnippet, Trait } from '@github/copilot-language-server'; import { ok } from 'assert'; import * as fs from 'fs'; import * as os from 'os'; @@ -569,13 +569,12 @@ interface FilesEncodingChanged { export interface CopilotCompletionContextResult { requestId: number; - areCodeSnippetsMissing: boolean; - snippets: SupportedContextItem[]; - translationUnitUri: string; + areSnippetsMissing: boolean; + snippets: CodeSnippet[]; + traits: Trait[]; + sourceFileUri: string; caretOffset: number; featureFlag: CopilotCompletionContextFeatures; - codeSnippetsCount: number; - traitsCount: number; } export interface CopilotCompletionContextParams { diff --git a/Extension/src/LanguageServer/copilotCompletionContextProvider.ts b/Extension/src/LanguageServer/copilotCompletionContextProvider.ts index 51396a9b1..17ec88028 100644 --- a/Extension/src/LanguageServer/copilotCompletionContextProvider.ts +++ b/Extension/src/LanguageServer/copilotCompletionContextProvider.ts @@ -13,7 +13,6 @@ import { CopilotCompletionContextResult } from './client'; import { CopilotCompletionContextTelemetry } from './copilotCompletionContextTelemetry'; import { getCopilotApi } from './copilotProviders'; import { clients } from './extension'; -import { ProjectContext } from './lmTool'; import { CppSettings } from './settings'; class DefaultValueFallback extends Error { @@ -82,6 +81,9 @@ export class CopilotCompletionContextProvider implements ContextResolver(promise: Promise, defaultValue: T | undefined, timeout: number, copilotToken: vscode.CancellationToken): Promise<[T | undefined, CopilotCompletionKind]> { const defaultValuePromise = new Promise((_resolve, reject) => setTimeout(() => { @@ -123,7 +125,7 @@ export class CopilotCompletionContextProvider implements ContextResolver { const documentUri = context.documentContext.uri; const caretOffset = context.documentContext.offset; @@ -133,55 +135,42 @@ export class CopilotCompletionContextProvider implements ContextResolver => { - const elapsedTimeMs = performance.now(); - const projectContext = await client.getChatContext(docUri, internalToken); - telemetry.addCppStandardVersionMetadata(projectContext.standardVersion, - CopilotCompletionContextProvider.getRoundedDuration(elapsedTimeMs)); - return projectContext; - })(); - const copilotCompletionContext: CopilotCompletionContextResult = await client.getCompletionContext(docUri, caretOffset, snippetsFeatureFlag, internalToken); - copilotCompletionContext.codeSnippetsCount = copilotCompletionContext.snippets.length; telemetry.addRequestId(copilotCompletionContext.requestId); - logMessage += ` (id:${copilotCompletionContext.requestId})`; - // Collect project traits and if any add them to the completion context. - const projectContext = await projectContextPromise; - if (projectContext?.standardVersion) { - copilotCompletionContext.snippets.push({ name: "C++ language standard version", value: `This project uses the ${projectContext.standardVersion} language standard version.` }); - copilotCompletionContext.traitsCount = 1; + logMessage += `(id:${copilotCompletionContext.requestId}) (getClientFor elapsed:${getClientForDuration}ms)`; + if (!copilotCompletionContext.areSnippetsMissing) { + const resultMismatch = copilotCompletionContext.sourceFileUri !== docUri.toString(); + if (resultMismatch) { logMessage += ` (mismatch TU vs result)`; } } - - let resultMismatch = false; - if (!copilotCompletionContext.areCodeSnippetsMissing) { - resultMismatch = copilotCompletionContext.translationUnitUri !== docUri.toString(); - const cacheEntryId = randomUUID().toString(); - this.completionContextCache.set(copilotCompletionContext.translationUnitUri, [cacheEntryId, copilotCompletionContext]); - const duration = CopilotCompletionContextProvider.getRoundedDuration(startTime); - telemetry.addCacheComputedData(duration, cacheEntryId); - if (resultMismatch) { logMessage += `, mismatch TU vs result`; } - logMessage += `, cached ${copilotCompletionContext.snippets.length} snippets in ${duration}ms`; - logMessage += `, response.featureFlag:${copilotCompletionContext.featureFlag},\ - ${copilotCompletionContext.translationUnitUri}:${copilotCompletionContext.caretOffset},\ - snippetsCount:${copilotCompletionContext.codeSnippetsCount}, traitsCount:${copilotCompletionContext.traitsCount}`; - } else { - logMessage += ` (snippets are missing) `; + const cacheEntryId = randomUUID().toString(); + this.completionContextCache.set(copilotCompletionContext.sourceFileUri, [cacheEntryId, copilotCompletionContext]); + const duration = CopilotCompletionContextProvider.getRoundedDuration(startTime); + telemetry.addCacheComputedData(duration, cacheEntryId); + logMessage += ` cached in ${duration}ms ${copilotCompletionContext.traits.length} trait(s)`; + if (copilotCompletionContext.areSnippetsMissing) { logMessage += ` (missing code-snippets) `; } + else { + logMessage += ` and ${copilotCompletionContext.snippets.length} snippet(s)`; + logMessage += `, response.featureFlag:${copilotCompletionContext.featureFlag}, \ +response.uri:${copilotCompletionContext.sourceFileUri || ""}:${copilotCompletionContext.caretOffset} `; } - telemetry.addResponseMetadata(copilotCompletionContext.areCodeSnippetsMissing, copilotCompletionContext.snippets.length, copilotCompletionContext.codeSnippetsCount, - copilotCompletionContext.traitsCount, copilotCompletionContext.caretOffset, copilotCompletionContext.featureFlag); + + telemetry.addResponseMetadata(copilotCompletionContext.areSnippetsMissing, copilotCompletionContext.snippets.length, + copilotCompletionContext.traits.length, copilotCompletionContext.caretOffset, copilotCompletionContext.featureFlag); telemetry.addComputeContextElapsed(CopilotCompletionContextProvider.getRoundedDuration(getCompletionContextStartTime)); - return resultMismatch ? undefined : copilotCompletionContext; + return copilotCompletionContext; } catch (e: any) { if (e instanceof vscode.CancellationError || e.message === CancellationError.Canceled) { telemetry.addInternalCanceled(CopilotCompletionContextProvider.getRoundedDuration(startTime)); - logMessage += `, (internal cancellation)`; + logMessage += ` (internal cancellation) `; throw InternalCancellationError; } @@ -190,14 +179,11 @@ export class CopilotCompletionContextProvider implements ContextResolver { const resolveStartTime = performance.now(); - const out: Logger = getOutputChannelLogger(); - let logMessage = `Copilot: resolve(${context.documentContext.uri}:${context.documentContext.offset}): `; + let logMessage = `Copilot: resolve(${context.documentContext.uri}:${context.documentContext.offset}):`; const timeBudgetFactor = await this.fetchTimeBudgetFactor(context); const maxCaretDistance = await this.fetchMaxDistanceToCaret(context); const telemetry = new CopilotCompletionContextTelemetry(); let copilotCompletionContext: CopilotCompletionContextResult | undefined; let copilotCompletionContextKind: CopilotCompletionKind = CopilotCompletionKind.Unknown; let featureFlag: CopilotCompletionContextFeatures | undefined; + const docUri = context.documentContext.uri; + const docOffset = context.documentContext.offset; try { featureFlag = await this.getEnabledFeatureFlag(context); telemetry.addRequestMetadata(context.documentContext.uri, context.documentContext.offset, @@ -282,11 +269,10 @@ export class CopilotCompletionContextProvider implements ContextResolver"; - logMessage += ` for ${uri} provided ${copilotCompletionContext.codeSnippetsCount} code-snippets (${copilotCompletionContextKind.toString()},\ -${copilotCompletionContext?.areCodeSnippetsMissing ? " missing code-snippets" : ""}) and ${copilotCompletionContext.traitsCount} traits, elapsed time:${duration}ms`; + logMessage += ` for ${docUri}:${docOffset} provided ${copilotCompletionContext.snippets.length} code-snippet(s) (${copilotCompletionContextKind.toString()}\ +${copilotCompletionContext?.areSnippetsMissing ? ", missing code-snippets" : ""}) and ${copilotCompletionContext.traits.length} trait(s), elapsed time:${duration}ms`; } - telemetry.addResponseMetadata(copilotCompletionContext?.areCodeSnippetsMissing ?? true, - copilotCompletionContext?.snippets?.length, - copilotCompletionContext?.codeSnippetsCount, copilotCompletionContext?.traitsCount, + telemetry.addResponseMetadata(copilotCompletionContext?.areSnippetsMissing ?? true, + copilotCompletionContext?.snippets.length, copilotCompletionContext?.traits.length, copilotCompletionContext?.caretOffset, copilotCompletionContext?.featureFlag); telemetry.addResolvedElapsed(duration); telemetry.addCacheSize(this.completionContextCache.size); telemetry.send(); - out.appendLineAtLevel(6, logMessage); + this.logger.appendLineAtLevel(7, logMessage); } } diff --git a/Extension/src/LanguageServer/copilotCompletionContextTelemetry.ts b/Extension/src/LanguageServer/copilotCompletionContextTelemetry.ts index 8c3049c48..26ecc7537 100644 --- a/Extension/src/LanguageServer/copilotCompletionContextTelemetry.ts +++ b/Extension/src/LanguageServer/copilotCompletionContextTelemetry.ts @@ -72,12 +72,15 @@ export class CopilotCompletionContextTelemetry { this.addMetric('computeContextElapsedMs', duration); } - public addResponseMetadata(areCodeSnippetsMissing: boolean, snippetCount?: number, codeSnippetsCount?: number, traitsCount?: number, caretOffset?: number, + public addGetClientForElapsed(duration: number): void { + this.addMetric('getClientForElapsedMs', duration); + } + + public addResponseMetadata(areSnippetsMissing: boolean, codeSnippetsCount?: number, traitsCount?: number, caretOffset?: number, featureFlag?: CopilotCompletionContextFeatures): void { - this.addProperty('response.areCodeSnippetsMissing', areCodeSnippetsMissing.toString()); + this.addProperty('response.areCodeSnippetsMissing', areSnippetsMissing.toString()); // Args can be undefined, in which case the value is set to a // special value (e.g. -1) to indicate data is not set. - this.addMetric('response.snippetsCount', snippetCount ?? -1); this.addMetric('response.caretOffset', caretOffset ?? -1); this.addProperty('response.featureFlag', featureFlag?.toString() ?? ''); this.addMetric('response.codeSnippetsCount', codeSnippetsCount ?? -1);