Skip to content

Commit 052bcff

Browse files
authored
Fix an unnecessary cancel/re-request with GitHub Copilot requests (and fix some other bugs with Copilot exception handling) (#12988)
* Fix an unnecessary cancel/re-request with GitHub Copilot requests. * A couple bug fixes from the previous PR. * Fix another loc case.
1 parent 74d691f commit 052bcff

File tree

4 files changed

+37
-26
lines changed

4 files changed

+37
-26
lines changed

Extension/src/LanguageServer/client.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -804,9 +804,9 @@ export interface Client {
804804
getShowConfigureIntelliSenseButton(): boolean;
805805
setShowConfigureIntelliSenseButton(show: boolean): void;
806806
addTrustedCompiler(path: string): Promise<void>;
807-
getIncludes(maxDepth: number, token: vscode.CancellationToken): Promise<GetIncludesResult>;
807+
getIncludes(maxDepth: number): Promise<GetIncludesResult>;
808808
getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult>;
809-
getProjectContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ProjectContextResult>;
809+
getProjectContext(uri: vscode.Uri): Promise<ProjectContextResult>;
810810
}
811811

812812
export function createClient(workspaceFolder?: vscode.WorkspaceFolder): Client {
@@ -2228,25 +2228,31 @@ export class DefaultClient implements Client {
22282228
await this.languageClient.sendNotification(DidOpenNotification, params);
22292229
}
22302230

2231-
public async getIncludes(maxDepth: number, token: vscode.CancellationToken): Promise<GetIncludesResult> {
2231+
/**
2232+
* Copilot completion-related requests (e.g. getIncludes and getProjectContext) will have their cancellation tokens cancelled
2233+
* if the current request times out (showing the user completion results without context info),
2234+
* but the results can still be used for future requests (due to caching) so it's better to return results instead of cancelling.
2235+
* This is different behavior from the getChatContext, which does handle cancel requests, since the request blocks
2236+
* the UI results and always re-requests (no caching).
2237+
*/
2238+
2239+
public async getIncludes(maxDepth: number): Promise<GetIncludesResult> {
22322240
const params: GetIncludesParams = { maxDepth: maxDepth };
22332241
await this.ready;
2234-
return DefaultClient.withLspCancellationHandling(
2235-
() => this.languageClient.sendRequest(IncludesRequest, params, token), token);
2242+
return this.languageClient.sendRequest(IncludesRequest, params);
22362243
}
22372244

2238-
public async getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult> {
2245+
public async getProjectContext(uri: vscode.Uri): Promise<ProjectContextResult> {
22392246
const params: TextDocumentIdentifier = { uri: uri.toString() };
2240-
await withCancellation(this.ready, token);
2241-
return DefaultClient.withLspCancellationHandling(
2242-
() => this.languageClient.sendRequest(CppContextRequest, params, token), token);
2247+
await this.ready;
2248+
return this.languageClient.sendRequest(ProjectContextRequest, params);
22432249
}
22442250

2245-
public async getProjectContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ProjectContextResult> {
2251+
public async getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult> {
22462252
const params: TextDocumentIdentifier = { uri: uri.toString() };
22472253
await withCancellation(this.ready, token);
22482254
return DefaultClient.withLspCancellationHandling(
2249-
() => this.languageClient.sendRequest(ProjectContextRequest, params, token), token);
2255+
() => this.languageClient.sendRequest(CppContextRequest, params, token), token);
22502256
}
22512257

22522258
/**
@@ -2340,7 +2346,6 @@ export class DefaultClient implements Client {
23402346
throw e;
23412347
}
23422348
}
2343-
23442349
if (token.isCancellationRequested) {
23452350
throw new vscode.CancellationError();
23462351
}
@@ -4151,7 +4156,7 @@ class NullClient implements Client {
41514156
getShowConfigureIntelliSenseButton(): boolean { return false; }
41524157
setShowConfigureIntelliSenseButton(show: boolean): void { }
41534158
addTrustedCompiler(path: string): Promise<void> { return Promise.resolve(); }
4154-
getIncludes(maxDepth: number, token: vscode.CancellationToken): Promise<GetIncludesResult> { return Promise.resolve({} as GetIncludesResult); }
4159+
getIncludes(maxDepth: number): Promise<GetIncludesResult> { return Promise.resolve({} as GetIncludesResult); }
41554160
getChatContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ChatContextResult> { return Promise.resolve({} as ChatContextResult); }
4156-
getProjectContext(uri: vscode.Uri, token: vscode.CancellationToken): Promise<ProjectContextResult> { return Promise.resolve({} as ProjectContextResult); }
4161+
getProjectContext(uri: vscode.Uri): Promise<ProjectContextResult> { return Promise.resolve({} as ProjectContextResult); }
41574162
}

Extension/src/LanguageServer/copilotProviders.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
'use strict';
66

77
import * as vscode from 'vscode';
8-
import { localize } from 'vscode-nls';
8+
import * as nls from 'vscode-nls';
99
import * as util from '../common';
1010
import * as logger from '../logger';
1111
import * as telemetry from '../telemetry';
1212
import { GetIncludesResult } from './client';
1313
import { getActiveClient } from './extension';
1414
import { getCompilerArgumentFilterMap, getProjectContext } from './lmTool';
1515

16+
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
17+
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
18+
1619
export interface CopilotTrait {
1720
name: string;
1821
value: string;
@@ -38,14 +41,14 @@ export async function registerRelatedFilesProvider(): Promise<void> {
3841
for (const languageId of ['c', 'cpp', 'cuda-cpp']) {
3942
api.registerRelatedFilesProvider(
4043
{ extensionId: util.extensionContext.extension.id, languageId },
41-
async (uri: vscode.Uri, context: { flags: Record<string, unknown> }, token: vscode.CancellationToken) => {
44+
async (uri: vscode.Uri, context: { flags: Record<string, unknown> }) => {
4245
const start = performance.now();
4346
const telemetryProperties: Record<string, string> = {};
4447
const telemetryMetrics: Record<string, number> = {};
4548
try {
46-
const getIncludesHandler = async () => (await getIncludesWithCancellation(1, token))?.includedFiles.map(file => vscode.Uri.file(file)) ?? [];
49+
const getIncludesHandler = async () => (await getIncludes(1))?.includedFiles.map(file => vscode.Uri.file(file)) ?? [];
4750
const getTraitsHandler = async () => {
48-
const projectContext = await getProjectContext(uri, context, token);
51+
const projectContext = await getProjectContext(uri, context);
4952

5053
if (!projectContext) {
5154
return undefined;
@@ -154,9 +157,9 @@ export async function registerRelatedFilesProvider(): Promise<void> {
154157
}
155158
}
156159

157-
async function getIncludesWithCancellation(maxDepth: number, token: vscode.CancellationToken): Promise<GetIncludesResult> {
160+
async function getIncludes(maxDepth: number): Promise<GetIncludesResult> {
158161
const activeClient = getActiveClient();
159-
const includes = await activeClient.getIncludes(maxDepth, token);
162+
const includes = await activeClient.getIncludes(maxDepth);
160163
const wksFolder = activeClient.RootUri?.toString();
161164

162165
if (!wksFolder) {

Extension/src/LanguageServer/lmTool.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
'use strict';
66

77
import * as vscode from 'vscode';
8-
import { localize } from 'vscode-nls';
8+
import * as nls from 'vscode-nls';
99
import * as util from '../common';
1010
import * as logger from '../logger';
1111
import * as telemetry from '../telemetry';
1212
import { ChatContextResult, ProjectContextResult } from './client';
1313
import { getClients } from './extension';
1414
import { checkDuration } from './utils';
1515

16+
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
17+
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
18+
1619
const MSVC: string = 'MSVC';
1720
const Clang: string = 'Clang';
1821
const GCC: string = 'GCC';
@@ -127,11 +130,11 @@ function filterCompilerArguments(compiler: string, compilerArguments: string[],
127130
return result;
128131
}
129132

130-
export async function getProjectContext(uri: vscode.Uri, context: { flags: Record<string, unknown> }, token: vscode.CancellationToken): Promise<ProjectContext | undefined> {
133+
export async function getProjectContext(uri: vscode.Uri, context: { flags: Record<string, unknown> }): Promise<ProjectContext | undefined> {
131134
const telemetryProperties: Record<string, string> = {};
132135
const telemetryMetrics: Record<string, number> = {};
133136
try {
134-
const projectContext = await checkDuration<ProjectContextResult | undefined>(async () => await getClients()?.ActiveClient?.getProjectContext(uri, token) ?? undefined);
137+
const projectContext = await checkDuration<ProjectContextResult | undefined>(async () => await getClients()?.ActiveClient?.getProjectContext(uri) ?? undefined);
135138
telemetryMetrics["duration"] = projectContext.duration;
136139
if (!projectContext.result) {
137140
return undefined;
@@ -177,7 +180,7 @@ export async function getProjectContext(uri: vscode.Uri, context: { flags: Recor
177180
// Intentionally swallow any exception.
178181
}
179182
telemetryProperties["error"] = "true";
180-
return undefined;
183+
throw exception; // Throw the exception for auto-retry.
181184
} finally {
182185
telemetry.logCopilotEvent('ProjectContext', telemetryProperties, telemetryMetrics);
183186
}

Extension/test/scenarios/SingleRootProject/tests/lmTool.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ describe('CppConfigurationLanguageModelTool Tests', () => {
200200
}
201201
});
202202

203-
const result = await getProjectContext(mockTextDocumentStub.uri, context, new vscode.CancellationTokenSource().token);
203+
const result = await getProjectContext(mockTextDocumentStub.uri, context);
204204

205205
ok(result, 'result should not be undefined');
206206
ok(result.language === 'C++');
@@ -364,7 +364,7 @@ describe('CppConfigurationLanguageModelTool Tests', () => {
364364
}
365365
});
366366

367-
const result = await getProjectContext(mockTextDocumentStub.uri, { flags: {} }, new vscode.CancellationTokenSource().token);
367+
const result = await getProjectContext(mockTextDocumentStub.uri, { flags: {} });
368368

369369
ok(telemetryStub.calledOnce, 'Telemetry should be called once');
370370
ok(telemetryStub.calledWithMatch('ProjectContext', sinon.match({

0 commit comments

Comments
 (0)