From 377cfc2014b6190f6247840b7a883b014d8659a3 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 11 Nov 2025 14:55:09 +0800 Subject: [PATCH 1/8] perf: throttling telemetry event --- src/copilot/contextProvider.ts | 88 +++++++++++++++++++++++----------- src/copilot/copilotHelper.ts | 51 +++++++++++--------- src/copilot/utils.ts | 53 +++++++++++++++----- 3 files changed, 130 insertions(+), 62 deletions(-) diff --git a/src/copilot/contextProvider.ts b/src/copilot/contextProvider.ts index 4857b8e6..a7b2b591 100644 --- a/src/copilot/contextProvider.ts +++ b/src/copilot/contextProvider.ts @@ -18,7 +18,8 @@ import { ContextResolverFunction, CopilotApi, ContextProviderRegistrationError, - ContextProviderResolverError + ContextProviderResolverError, + sendContextResolutionTelemetry } from './utils'; export async function registerCopilotContextProviders( @@ -70,70 +71,99 @@ function createJavaContextResolver(): ContextResolverFunction { }; } -/** - * Send telemetry data for Java context resolution - */ -function sendContextTelemetry(request: ResolveRequest, start: number, items: SupportedContextItem[], status: string, error?: string) { - const duration = Math.round(performance.now() - start); - const tokenCount = JavaContextProviderUtils.calculateTokenCount(items); - const telemetryData: any = { - "action": "resolveJavaContext", - "completionId": request.completionId, - "duration": duration, - "itemCount": items.length, - "tokenCount": tokenCount, - "status": status - }; - if (error) { - telemetryData.error = error; - } - sendInfo("", telemetryData); -} - async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode.CancellationToken): Promise { const items: SupportedContextItem[] = []; const start = performance.now(); + + let dependenciesResult: CopilotHelper.IResolveResult | undefined; + let importsResult: CopilotHelper.IResolveResult | undefined; + try { // Check for cancellation before starting JavaContextProviderUtils.checkCancellation(copilotCancel); + // Resolve project dependencies and convert to context items - const projectDependencyItems = await CopilotHelper.resolveAndConvertProjectDependencies( + dependenciesResult = await CopilotHelper.resolveAndConvertProjectDependencies( vscode.window.activeTextEditor, copilotCancel, JavaContextProviderUtils.checkCancellation ); JavaContextProviderUtils.checkCancellation(copilotCancel); - items.push(...projectDependencyItems); + items.push(...dependenciesResult.items); JavaContextProviderUtils.checkCancellation(copilotCancel); // Resolve local imports and convert to context items - const localImportItems = await CopilotHelper.resolveAndConvertLocalImports( + importsResult = await CopilotHelper.resolveAndConvertLocalImports( vscode.window.activeTextEditor, copilotCancel, JavaContextProviderUtils.checkCancellation ); JavaContextProviderUtils.checkCancellation(copilotCancel); - items.push(...localImportItems); + items.push(...importsResult.items); } catch (error: any) { if (error instanceof CopilotCancellationError) { - sendContextTelemetry(request, start, items, "cancelled_by_copilot"); + sendContextResolutionTelemetry( + request, + start, + items, + "cancelled_by_copilot", + sendInfo, + undefined, + dependenciesResult?.emptyReason, + importsResult?.emptyReason, + dependenciesResult?.itemCount, + importsResult?.itemCount + ); throw error; } if (error instanceof vscode.CancellationError || error.message === CancellationError.CANCELED) { - sendContextTelemetry(request, start, items, "cancelled_internally"); + sendContextResolutionTelemetry( + request, + start, + items, + "cancelled_internally", + sendInfo, + undefined, + dependenciesResult?.emptyReason, + importsResult?.emptyReason, + dependenciesResult?.itemCount, + importsResult?.itemCount + ); throw new InternalCancellationError(); } // Send telemetry for general errors (but continue with partial results) - sendContextTelemetry(request, start, items, "error_partial_results", error.message || "unknown_error"); + sendContextResolutionTelemetry( + request, + start, + items, + "error_partial_results", + sendInfo, + error.message || "unknown_error", + dependenciesResult?.emptyReason, + importsResult?.emptyReason, + dependenciesResult?.itemCount, + importsResult?.itemCount + ); // Return partial results and log completion for error case return items; } // Send telemetry data once at the end for success case - sendContextTelemetry(request, start, items, "succeeded"); + sendContextResolutionTelemetry( + request, + start, + items, + "succeeded", + sendInfo, + undefined, + dependenciesResult?.emptyReason, + importsResult?.emptyReason, + dependenciesResult?.itemCount, + importsResult?.itemCount + ); return items; } diff --git a/src/copilot/copilotHelper.ts b/src/copilot/copilotHelper.ts index 2a0b4cfd..bba0688c 100644 --- a/src/copilot/copilotHelper.ts +++ b/src/copilot/copilotHelper.ts @@ -2,8 +2,8 @@ // Licensed under the MIT license. import { commands, Uri, CancellationToken } from "vscode"; -import { sendError, sendInfo } from "vscode-extension-telemetry-wrapper"; -import { GetImportClassContentError, GetProjectDependenciesError, sendContextOperationTelemetry, JavaContextProviderUtils } from "./utils"; +import { sendError } from "vscode-extension-telemetry-wrapper"; +import { GetImportClassContentError, GetProjectDependenciesError, JavaContextProviderUtils } from "./utils"; import { Commands } from '../commands'; /** @@ -259,27 +259,35 @@ export namespace CopilotHelper { } } + /** + * Result interface for dependency resolution with diagnostic information + */ + export interface IResolveResult { + items: any[]; + emptyReason?: string; + itemCount: number; + } + /** * Resolves project dependencies and converts them to context items with cancellation support - * @param workspaceFolders The workspace folders, or undefined if none + * @param activeEditor The active text editor, or undefined if none * @param copilotCancel Cancellation token from Copilot * @param checkCancellation Function to check for cancellation - * @returns Array of context items for project dependencies, or empty array if no workspace folders + * @returns Result object containing context items and diagnostic information */ export async function resolveAndConvertProjectDependencies( activeEditor: { document: { uri: Uri; languageId: string } } | undefined, copilotCancel: CancellationToken, checkCancellation: (token: CancellationToken) => void - ): Promise<{ name: string; value: string; importance: number }[]> { + ): Promise { const items: any[] = []; + // Check if workspace folders exist if (!activeEditor) { - sendContextOperationTelemetry("resolveLocalImports", "ContextEmpty", sendInfo, EmptyReason.NoActiveEditor); - return items; + return { items: [], emptyReason: EmptyReason.NoActiveEditor, itemCount: 0 }; } if (activeEditor.document.languageId !== 'java') { - sendContextOperationTelemetry("resolveLocalImports", "ContextEmpty", sendInfo, EmptyReason.NotJavaFile); - return items; + return { items: [], emptyReason: EmptyReason.NotJavaFile, itemCount: 0 }; } const documentUri = activeEditor.document.uri; @@ -289,9 +297,9 @@ export namespace CopilotHelper { // Check for cancellation after dependency resolution checkCancellation(copilotCancel); - // Send telemetry if result is empty + // Return empty result with reason if no dependencies found if (projectDependenciesResult.isEmpty && projectDependenciesResult.emptyReason) { - sendContextOperationTelemetry("resolveProjectDependencies", "ContextEmpty", sendInfo, projectDependenciesResult.emptyReason); + return { items: [], emptyReason: projectDependenciesResult.emptyReason, itemCount: 0 }; } // Check for cancellation after telemetry @@ -306,7 +314,7 @@ export namespace CopilotHelper { items.push(...contextItems); } - return items; + return { items, itemCount: items.length }; } /** @@ -314,23 +322,21 @@ export namespace CopilotHelper { * @param activeEditor The active text editor, or undefined if none * @param copilotCancel Cancellation token from Copilot * @param checkCancellation Function to check for cancellation - * @param createContextItems Function to create context items from imports - * @returns Array of context items for local imports, or empty array if no valid editor + * @returns Result object containing context items and diagnostic information */ export async function resolveAndConvertLocalImports( activeEditor: { document: { uri: Uri; languageId: string } } | undefined, copilotCancel: CancellationToken, checkCancellation: (token: CancellationToken) => void - ): Promise { + ): Promise { const items: any[] = []; + // Check if there's an active editor with a Java document if (!activeEditor) { - sendContextOperationTelemetry("resolveLocalImports", "ContextEmpty", sendInfo, EmptyReason.NoActiveEditor); - return items; + return { items: [], emptyReason: EmptyReason.NoActiveEditor, itemCount: 0 }; } if (activeEditor.document.languageId !== 'java') { - sendContextOperationTelemetry("resolveLocalImports", "ContextEmpty", sendInfo, EmptyReason.NotJavaFile); - return items; + return { items: [], emptyReason: EmptyReason.NotJavaFile, itemCount: 0 }; } const documentUri = activeEditor.document.uri; @@ -343,10 +349,11 @@ export namespace CopilotHelper { // Check for cancellation after resolution checkCancellation(copilotCancel); - // Send telemetry if result is empty + // Return empty result with reason if no imports found if (importClassResult.isEmpty && importClassResult.emptyReason) { - sendContextOperationTelemetry("resolveLocalImports", "ContextEmpty", sendInfo, importClassResult.emptyReason); + return { items: [], emptyReason: importClassResult.emptyReason, itemCount: 0 }; } + // Check for cancellation before processing results checkCancellation(copilotCancel); if (importClassResult.classInfoList && importClassResult.classInfoList.length > 0) { @@ -357,6 +364,6 @@ export namespace CopilotHelper { items.push(...contextItems); } - return items; + return { items, itemCount: items.length }; } } diff --git a/src/copilot/utils.ts b/src/copilot/utils.ts index 69717aa2..3eefd053 100644 --- a/src/copilot/utils.ts +++ b/src/copilot/utils.ts @@ -210,24 +210,55 @@ export class ContextProviderResolverError extends Error { } /** - * Send telemetry data for context operations (like resolveProjectDependencies, resolveLocalImports) - * @param action The action being performed - * @param status The status of the action (e.g., "ContextEmpty", "succeeded") - * @param reason Optional reason for empty context + * Send consolidated telemetry data for Java context resolution + * This is the centralized function for sending context resolution telemetry + * + * @param request The resolve request from Copilot + * @param start Performance timestamp when resolution started + * @param items The resolved context items + * @param status Status of the resolution ("succeeded", "cancelled_by_copilot", "cancelled_internally", "error_partial_results") * @param sendInfo The sendInfo function from vscode-extension-telemetry-wrapper + * @param error Optional error message + * @param dependenciesEmptyReason Optional reason why dependencies were empty + * @param importsEmptyReason Optional reason why imports were empty + * @param dependenciesCount Number of dependency items resolved + * @param importsCount Number of import items resolved */ -export function sendContextOperationTelemetry( - action: string, +export function sendContextResolutionTelemetry( + request: ResolveRequest, + start: number, + items: SupportedContextItem[], status: string, sendInfo: (eventName: string, properties?: any) => void, - reason?: string + error?: string, + dependenciesEmptyReason?: string, + importsEmptyReason?: string, + dependenciesCount?: number, + importsCount?: number ): void { + const duration = Math.round(performance.now() - start); + const tokenCount = JavaContextProviderUtils.calculateTokenCount(items); const telemetryData: any = { - "action": action, - "status": status + "action": "resolveJavaContext", + "completionId": request.completionId, + "duration": duration, + "itemCount": items.length, + "tokenCount": tokenCount, + "status": status, + "dependenciesCount": dependenciesCount ?? 0, + "importsCount": importsCount ?? 0 }; - if (reason) { - telemetryData.ContextEmptyReason = reason; + + // Add empty reasons if present + if (dependenciesEmptyReason) { + telemetryData.dependenciesEmptyReason = dependenciesEmptyReason; } + if (importsEmptyReason) { + telemetryData.importsEmptyReason = importsEmptyReason; + } + if (error) { + telemetryData.error = error; + } + sendInfo("", telemetryData); } \ No newline at end of file From ae6c8f23d0d67f9030f791fd13c7fc30018e59d4 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 11 Nov 2025 15:04:09 +0800 Subject: [PATCH 2/8] fix: update --- src/copilot/contextProvider.ts | 4 ---- src/copilot/utils.ts | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/copilot/contextProvider.ts b/src/copilot/contextProvider.ts index a7b2b591..5a454772 100644 --- a/src/copilot/contextProvider.ts +++ b/src/copilot/contextProvider.ts @@ -108,7 +108,6 @@ async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode start, items, "cancelled_by_copilot", - sendInfo, undefined, dependenciesResult?.emptyReason, importsResult?.emptyReason, @@ -123,7 +122,6 @@ async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode start, items, "cancelled_internally", - sendInfo, undefined, dependenciesResult?.emptyReason, importsResult?.emptyReason, @@ -139,7 +137,6 @@ async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode start, items, "error_partial_results", - sendInfo, error.message || "unknown_error", dependenciesResult?.emptyReason, importsResult?.emptyReason, @@ -157,7 +154,6 @@ async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode start, items, "succeeded", - sendInfo, undefined, dependenciesResult?.emptyReason, importsResult?.emptyReason, diff --git a/src/copilot/utils.ts b/src/copilot/utils.ts index 3eefd053..fcc6eae5 100644 --- a/src/copilot/utils.ts +++ b/src/copilot/utils.ts @@ -7,6 +7,7 @@ import { SupportedContextItem, type ContextProvider, } from '@github/copilot-language-server'; +import { sendInfo } from "vscode-extension-telemetry-wrapper"; /** * Error classes for Copilot context provider cancellation handling */ @@ -229,7 +230,6 @@ export function sendContextResolutionTelemetry( start: number, items: SupportedContextItem[], status: string, - sendInfo: (eventName: string, properties?: any) => void, error?: string, dependenciesEmptyReason?: string, importsEmptyReason?: string, From 452b11082d75734169d050f20a00665f3b8555b9 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 11 Nov 2025 15:05:03 +0800 Subject: [PATCH 3/8] fix: update --- src/copilot/copilotHelper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/copilot/copilotHelper.ts b/src/copilot/copilotHelper.ts index bba0688c..48ef7f61 100644 --- a/src/copilot/copilotHelper.ts +++ b/src/copilot/copilotHelper.ts @@ -282,7 +282,7 @@ export namespace CopilotHelper { ): Promise { const items: any[] = []; - // Check if workspace folders exist + // Check if active editor exists if (!activeEditor) { return { items: [], emptyReason: EmptyReason.NoActiveEditor, itemCount: 0 }; } @@ -302,7 +302,7 @@ export namespace CopilotHelper { return { items: [], emptyReason: projectDependenciesResult.emptyReason, itemCount: 0 }; } - // Check for cancellation after telemetry + // Check for cancellation after dependency resolution checkCancellation(copilotCancel); // Convert project dependencies to context items From e8d81e7b2ce29b5da1a892d123ebac81f157c594 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 11 Nov 2025 15:12:19 +0800 Subject: [PATCH 4/8] fix: update --- src/copilot/copilotHelper.ts | 121 ++++++++++++++++------------------- 1 file changed, 55 insertions(+), 66 deletions(-) diff --git a/src/copilot/copilotHelper.ts b/src/copilot/copilotHelper.ts index 48ef7f61..c8c2c394 100644 --- a/src/copilot/copilotHelper.ts +++ b/src/copilot/copilotHelper.ts @@ -85,46 +85,41 @@ export namespace CopilotHelper { Commands.JAVA_PROJECT_GET_IMPORT_CLASS_CONTENT, normalizedUri ) as Promise; + + // Build promises array for race condition + // Note: Client-side timeout is NECESSARY even if backend has timeout because: + // 1. Network delays may prevent backend response from arriving + // 2. Process hangs won't trigger backend timeout + // 3. Command dispatch failures need to be caught + const promises: Promise[] = [ + commandPromise, + new Promise((_, reject) => { + setTimeout(() => { + reject(new Error(ErrorMessage.OperationTimedOut)); + }, 80); // 80ms client-side timeout (independent of backend timeout) + }) + ]; + + // Add cancellation promise if token provided if (cancellationToken) { - const result = await Promise.race([ - commandPromise, + promises.push( new Promise((_, reject) => { cancellationToken.onCancellationRequested(() => { reject(new Error(ErrorMessage.OperationCancelled)); }); - }), - new Promise((_, reject) => { - setTimeout(() => { - reject(new Error(ErrorMessage.OperationTimedOut)); - }, 80); // 80ms timeout - }) - ]); - if (!result) { - return { - classInfoList: [], - emptyReason: EmptyReason.CommandNullResult, - isEmpty: true - }; - } - return result; - } else { - const result = await Promise.race([ - commandPromise, - new Promise((_, reject) => { - setTimeout(() => { - reject(new Error(ErrorMessage.OperationTimedOut)); - }, 80); // 80ms timeout }) - ]); - if (!result) { - return { - classInfoList: [], - emptyReason: EmptyReason.CommandNullResult, - isEmpty: true - }; - } - return result; + ); + } + + const result = await Promise.race(promises); + if (!result) { + return { + classInfoList: [], + emptyReason: EmptyReason.CommandNullResult, + isEmpty: true + }; } + return result; } catch (error: any) { if (error.message === ErrorMessage.OperationCancelled) { return { @@ -194,46 +189,40 @@ export namespace CopilotHelper { normalizedUri ) as Promise; + // Build promises array for race condition + // Note: Client-side timeout is NECESSARY even if backend has timeout because: + // 1. Network delays may prevent backend response from arriving + // 2. Process hangs won't trigger backend timeout + // 3. Command dispatch failures need to be caught + const promises: Promise[] = [ + commandPromise, + new Promise((_, reject) => { + setTimeout(() => { + reject(new Error(ErrorMessage.OperationTimedOut)); + }, 40); // 40ms client-side timeout (independent of backend timeout) + }) + ]; + + // Add cancellation promise if token provided if (cancellationToken) { - const result = await Promise.race([ - commandPromise, + promises.push( new Promise((_, reject) => { cancellationToken.onCancellationRequested(() => { reject(new Error(ErrorMessage.OperationCancelled)); }); - }), - new Promise((_, reject) => { - setTimeout(() => { - reject(new Error(ErrorMessage.OperationTimedOut)); - }, 40); // 40ms timeout - }) - ]); - if (!result) { - return { - dependencyInfoList: [], - emptyReason: EmptyReason.CommandNullResult, - isEmpty: true - }; - } - return result; - } else { - const result = await Promise.race([ - commandPromise, - new Promise((_, reject) => { - setTimeout(() => { - reject(new Error(ErrorMessage.OperationTimedOut)); - }, 40); // 40ms timeout }) - ]); - if (!result) { - return { - dependencyInfoList: [], - emptyReason: EmptyReason.CommandNullResult, - isEmpty: true - }; - } - return result; + ); + } + + const result = await Promise.race(promises); + if (!result) { + return { + dependencyInfoList: [], + emptyReason: EmptyReason.CommandNullResult, + isEmpty: true + }; } + return result; } catch (error: any) { if (error.message === ErrorMessage.OperationCancelled) { return { From 052de6295b427e2e0192be3ae09037028859d601 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 11 Nov 2025 15:17:40 +0800 Subject: [PATCH 5/8] fix: remove senderror --- src/copilot/copilotHelper.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/copilot/copilotHelper.ts b/src/copilot/copilotHelper.ts index c8c2c394..07d928d4 100644 --- a/src/copilot/copilotHelper.ts +++ b/src/copilot/copilotHelper.ts @@ -2,8 +2,7 @@ // Licensed under the MIT license. import { commands, Uri, CancellationToken } from "vscode"; -import { sendError } from "vscode-extension-telemetry-wrapper"; -import { GetImportClassContentError, GetProjectDependenciesError, JavaContextProviderUtils } from "./utils"; +import { JavaContextProviderUtils } from "./utils"; import { Commands } from '../commands'; /** @@ -136,7 +135,6 @@ export namespace CopilotHelper { }; } const errorMessage = 'TsException_' + ((error as Error).message || "unknown"); - sendError(new GetImportClassContentError(errorMessage)); return { classInfoList: [], emptyReason: errorMessage, @@ -239,7 +237,6 @@ export namespace CopilotHelper { }; } const errorMessage = 'TsException_' + ((error as Error).message || "unknown"); - sendError(new GetProjectDependenciesError(errorMessage)); return { dependencyInfoList: [], emptyReason: errorMessage, From 3b50acc66ae65a538d76be1226d471c25e23571b Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 11 Nov 2025 15:18:52 +0800 Subject: [PATCH 6/8] perf: remove useless code --- src/copilot/copilotHelper.ts | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/copilot/copilotHelper.ts b/src/copilot/copilotHelper.ts index 07d928d4..1cee3dc0 100644 --- a/src/copilot/copilotHelper.ts +++ b/src/copilot/copilotHelper.ts @@ -51,17 +51,6 @@ export interface IProjectDependenciesResult { * Helper class for Copilot integration to analyze Java project dependencies */ export namespace CopilotHelper { - /** - * Resolves all local project types imported by the given file (backward compatibility version) - * @param fileUri The URI of the Java file to analyze - * @param cancellationToken Optional cancellation token to abort the operation - * @returns Array of import class information - */ - export async function resolveLocalImports(fileUri: Uri, cancellationToken?: CancellationToken): Promise { - const result = await resolveLocalImportsWithReason(fileUri, cancellationToken); - return result.classInfoList; - } - /** * Resolves all local project types imported by the given file with detailed error reporting * @param fileUri The URI of the Java file to analyze @@ -143,24 +132,6 @@ export namespace CopilotHelper { } } - /** - * Resolves project dependencies for the given project URI (backward compatibility version) - * @param projectUri The URI of the Java project to analyze - * @param cancellationToken Optional cancellation token to abort the operation - * @returns Object containing project dependencies as key-value pairs - */ - export async function resolveProjectDependencies(projectUri: Uri, cancellationToken?: CancellationToken): Promise { - const result = await resolveProjectDependenciesWithReason(projectUri, cancellationToken); - - // Convert to legacy format - const dependencies: IProjectDependency = {}; - for (const dep of result.dependencyInfoList) { - dependencies[dep.key] = dep.value; - } - - return dependencies; - } - /** * Resolves project dependencies with detailed error reporting * @param projectUri The URI of the Java project to analyze From 8c284281c68bea39140952bc654e642948b06afa Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 11 Nov 2025 15:43:21 +0800 Subject: [PATCH 7/8] perf: remove useless code --- src/copilot/contextProvider.ts | 30 +++++++++++++++--------------- src/copilot/copilotHelper.ts | 16 ++++++++-------- src/copilot/utils.ts | 4 ++-- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/copilot/contextProvider.ts b/src/copilot/contextProvider.ts index 5a454772..6640ee13 100644 --- a/src/copilot/contextProvider.ts +++ b/src/copilot/contextProvider.ts @@ -74,14 +74,14 @@ function createJavaContextResolver(): ContextResolverFunction { async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode.CancellationToken): Promise { const items: SupportedContextItem[] = []; const start = performance.now(); - + let dependenciesResult: CopilotHelper.IResolveResult | undefined; let importsResult: CopilotHelper.IResolveResult | undefined; - + try { // Check for cancellation before starting JavaContextProviderUtils.checkCancellation(copilotCancel); - + // Resolve project dependencies and convert to context items dependenciesResult = await CopilotHelper.resolveAndConvertProjectDependencies( vscode.window.activeTextEditor, @@ -104,9 +104,9 @@ async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode } catch (error: any) { if (error instanceof CopilotCancellationError) { sendContextResolutionTelemetry( - request, - start, - items, + request, + start, + items, "cancelled_by_copilot", undefined, dependenciesResult?.emptyReason, @@ -118,9 +118,9 @@ async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode } if (error instanceof vscode.CancellationError || error.message === CancellationError.CANCELED) { sendContextResolutionTelemetry( - request, - start, - items, + request, + start, + items, "cancelled_internally", undefined, dependenciesResult?.emptyReason, @@ -133,9 +133,9 @@ async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode // Send telemetry for general errors (but continue with partial results) sendContextResolutionTelemetry( - request, - start, - items, + request, + start, + items, "error_partial_results", error.message || "unknown_error", dependenciesResult?.emptyReason, @@ -150,9 +150,9 @@ async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode // Send telemetry data once at the end for success case sendContextResolutionTelemetry( - request, - start, - items, + request, + start, + items, "succeeded", undefined, dependenciesResult?.emptyReason, diff --git a/src/copilot/copilotHelper.ts b/src/copilot/copilotHelper.ts index 1cee3dc0..67bf3196 100644 --- a/src/copilot/copilotHelper.ts +++ b/src/copilot/copilotHelper.ts @@ -73,7 +73,7 @@ export namespace CopilotHelper { Commands.JAVA_PROJECT_GET_IMPORT_CLASS_CONTENT, normalizedUri ) as Promise; - + // Build promises array for race condition // Note: Client-side timeout is NECESSARY even if backend has timeout because: // 1. Network delays may prevent backend response from arriving @@ -87,7 +87,7 @@ export namespace CopilotHelper { }, 80); // 80ms client-side timeout (independent of backend timeout) }) ]; - + // Add cancellation promise if token provided if (cancellationToken) { promises.push( @@ -98,7 +98,7 @@ export namespace CopilotHelper { }) ); } - + const result = await Promise.race(promises); if (!result) { return { @@ -171,7 +171,7 @@ export namespace CopilotHelper { }, 40); // 40ms client-side timeout (independent of backend timeout) }) ]; - + // Add cancellation promise if token provided if (cancellationToken) { promises.push( @@ -182,7 +182,7 @@ export namespace CopilotHelper { }) ); } - + const result = await Promise.race(promises); if (!result) { return { @@ -238,7 +238,7 @@ export namespace CopilotHelper { checkCancellation: (token: CancellationToken) => void ): Promise { const items: any[] = []; - + // Check if active editor exists if (!activeEditor) { return { items: [], emptyReason: EmptyReason.NoActiveEditor, itemCount: 0 }; @@ -287,7 +287,7 @@ export namespace CopilotHelper { checkCancellation: (token: CancellationToken) => void ): Promise { const items: any[] = []; - + // Check if there's an active editor with a Java document if (!activeEditor) { return { items: [], emptyReason: EmptyReason.NoActiveEditor, itemCount: 0 }; @@ -310,7 +310,7 @@ export namespace CopilotHelper { if (importClassResult.isEmpty && importClassResult.emptyReason) { return { items: [], emptyReason: importClassResult.emptyReason, itemCount: 0 }; } - + // Check for cancellation before processing results checkCancellation(copilotCancel); if (importClassResult.classInfoList && importClassResult.classInfoList.length > 0) { diff --git a/src/copilot/utils.ts b/src/copilot/utils.ts index fcc6eae5..ac1ea341 100644 --- a/src/copilot/utils.ts +++ b/src/copilot/utils.ts @@ -248,7 +248,7 @@ export function sendContextResolutionTelemetry( "dependenciesCount": dependenciesCount ?? 0, "importsCount": importsCount ?? 0 }; - + // Add empty reasons if present if (dependenciesEmptyReason) { telemetryData.dependenciesEmptyReason = dependenciesEmptyReason; @@ -259,6 +259,6 @@ export function sendContextResolutionTelemetry( if (error) { telemetryData.error = error; } - + sendInfo("", telemetryData); } \ No newline at end of file From 5952bf553ff8058ced863f1ec750cdf57afb660d Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 11 Nov 2025 15:48:19 +0800 Subject: [PATCH 8/8] perf: remove useless code --- src/copilot/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/copilot/utils.ts b/src/copilot/utils.ts index ac1ea341..4a2b424a 100644 --- a/src/copilot/utils.ts +++ b/src/copilot/utils.ts @@ -213,7 +213,7 @@ export class ContextProviderResolverError extends Error { /** * Send consolidated telemetry data for Java context resolution * This is the centralized function for sending context resolution telemetry - * + * * @param request The resolve request from Copilot * @param start Performance timestamp when resolution started * @param items The resolved context items