Skip to content

Commit 1040187

Browse files
committed
feat: support context provider api
1 parent 609dfd2 commit 1040187

File tree

7 files changed

+942
-1
lines changed

7 files changed

+942
-1
lines changed

package-lock.json

Lines changed: 173 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,7 @@
11101110
"webpack-cli": "^4.10.0"
11111111
},
11121112
"dependencies": {
1113+
"@github/copilot-language-server": "^1.388.0",
11131114
"await-lock": "^2.2.2",
11141115
"fmtr": "^1.1.4",
11151116
"fs-extra": "^10.1.0",

src/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ export namespace Commands {
136136

137137
export const JAVA_PROJECT_GET_DEPENDENCIES = "java.project.getDependencies";
138138

139+
export const JAVA_PROJECT_GET_IMPORT_CLASS_CONTENT = "java.project.getImportClassContent";
140+
139141
export const JAVA_UPGRADE_WITH_COPILOT = "_java.upgradeWithCopilot";
140142

141143
/**

src/copilot/contextProvider.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
import {
6+
ResolveRequest,
7+
SupportedContextItem,
8+
type ContextProvider,
9+
} from '@github/copilot-language-server';
10+
import * as vscode from 'vscode';
11+
import { CopilotHelper } from './copilotHelper';
12+
import { sendError, sendInfo } from "vscode-extension-telemetry-wrapper";
13+
import {
14+
JavaContextProviderUtils,
15+
CancellationError,
16+
InternalCancellationError,
17+
CopilotCancellationError,
18+
ContextResolverFunction,
19+
CopilotApi,
20+
ContextProviderRegistrationError,
21+
ContextProviderResolverError
22+
} from './utils';
23+
24+
export async function registerCopilotContextProviders(
25+
context: vscode.ExtensionContext
26+
) {
27+
try {
28+
const apis = await JavaContextProviderUtils.getCopilotApis();
29+
if (!apis.clientApi || !apis.chatApi) {
30+
return;
31+
}
32+
33+
// Register the Java completion context provider
34+
const provider: ContextProvider<SupportedContextItem> = {
35+
id: 'vscjava.vscode-java-dependency', // use extension id as provider id for now
36+
selector: [{ language: "java" }],
37+
resolver: { resolve: createJavaContextResolver() }
38+
};
39+
40+
const installCount = await JavaContextProviderUtils.installContextProviderOnApis(apis, provider, context, installContextProvider);
41+
42+
if (installCount === 0) {
43+
return;
44+
}
45+
46+
sendInfo("", {
47+
"action": "registerCopilotContextProvider",
48+
"extension": 'vscjava.vscode-java-dependency',
49+
"status": "succeeded",
50+
"installCount": installCount
51+
});
52+
console.log(`Registered Copilot context provider for Java on ${installCount} APIs.`);
53+
}
54+
catch (error) {
55+
sendError(new ContextProviderRegistrationError('Failed to register Copilot context provider: ' + ((error as Error).message || "unknown_error")));
56+
}
57+
}
58+
59+
/**
60+
* Create the Java context resolver function
61+
*/
62+
function createJavaContextResolver(): ContextResolverFunction {
63+
return async (request: ResolveRequest, copilotCancel: vscode.CancellationToken): Promise<SupportedContextItem[]> => {
64+
try {
65+
// Check for immediate cancellation
66+
JavaContextProviderUtils.checkCancellation(copilotCancel);
67+
68+
return await resolveJavaContext(request, copilotCancel);
69+
} catch (error: any) {
70+
sendError(new ContextProviderResolverError('Java Context Resolution Failed: ' + ((error as Error).message || "unknown_error")));
71+
// This should never be reached due to handleError throwing, but TypeScript requires it
72+
return [];
73+
}
74+
};
75+
}
76+
77+
/**
78+
* Send telemetry data for Java context resolution
79+
*/
80+
function sendContextTelemetry(request: ResolveRequest, start: number, items: SupportedContextItem[], status: string, error?: string) {
81+
const duration = Math.round(performance.now() - start);
82+
const tokenCount = JavaContextProviderUtils.calculateTokenCount(items);
83+
const telemetryData: any = {
84+
"action": "resolveJavaContext",
85+
"completionId": request.completionId,
86+
"duration": duration,
87+
"itemCount": items.length,
88+
"tokenCount": tokenCount,
89+
"status": status
90+
};
91+
92+
if (error) {
93+
telemetryData.error = error;
94+
}
95+
96+
sendInfo("", telemetryData);
97+
}
98+
99+
async function resolveJavaContext(request: ResolveRequest, copilotCancel: vscode.CancellationToken): Promise<SupportedContextItem[]> {
100+
const items: SupportedContextItem[] = [];
101+
const start = performance.now();
102+
103+
try {
104+
// Check for cancellation before starting
105+
JavaContextProviderUtils.checkCancellation(copilotCancel);
106+
107+
// Resolve project dependencies and convert to context items
108+
const projectDependencyItems = await CopilotHelper.resolveAndConvertProjectDependencies(
109+
vscode.workspace.workspaceFolders,
110+
copilotCancel,
111+
JavaContextProviderUtils.checkCancellation
112+
);
113+
JavaContextProviderUtils.checkCancellation(copilotCancel);
114+
console.dir(projectDependencyItems);
115+
items.push(...projectDependencyItems);
116+
117+
JavaContextProviderUtils.checkCancellation(copilotCancel);
118+
119+
// Resolve local imports and convert to context items
120+
const localImportItems = await CopilotHelper.resolveAndConvertLocalImports(
121+
vscode.window.activeTextEditor,
122+
copilotCancel,
123+
JavaContextProviderUtils.checkCancellation
124+
);
125+
JavaContextProviderUtils.checkCancellation(copilotCancel);
126+
console.dir(localImportItems);
127+
items.push(...localImportItems);
128+
} catch (error: any) {
129+
if (error instanceof CopilotCancellationError) {
130+
sendContextTelemetry(request, start, items, "cancelled_by_copilot");
131+
throw error;
132+
}
133+
if (error instanceof vscode.CancellationError || error.message === CancellationError.Canceled) {
134+
sendContextTelemetry(request, start, items, "cancelled_internally");
135+
throw new InternalCancellationError();
136+
}
137+
138+
// Send telemetry for general errors (but continue with partial results)
139+
sendContextTelemetry(request, start, items, "error_partial_results", error.message || "unknown_error");
140+
141+
// Return partial results and log completion for error case
142+
return items;
143+
}
144+
145+
// Send telemetry data once at the end for success case
146+
sendContextTelemetry(request, start, items, "succeeded");
147+
148+
return items;
149+
}
150+
151+
export async function installContextProvider(
152+
copilotAPI: CopilotApi,
153+
contextProvider: ContextProvider<SupportedContextItem>
154+
): Promise<vscode.Disposable | undefined> {
155+
const hasGetContextProviderAPI = typeof copilotAPI.getContextProviderAPI === 'function';
156+
if (hasGetContextProviderAPI) {
157+
const contextAPI = await copilotAPI.getContextProviderAPI('v1');
158+
if (contextAPI) {
159+
return contextAPI.registerContextProvider(contextProvider);
160+
}
161+
}
162+
return undefined;
163+
}

0 commit comments

Comments
 (0)