Skip to content

Commit b73dbca

Browse files
authored
Chat context API changes (#8507)
* Chat context API changes * Copilot PR feedback
1 parent ba6227c commit b73dbca

4 files changed

Lines changed: 184 additions & 38 deletions

File tree

src/@types/vscode.proposed.chatContextProvider.d.ts

Lines changed: 129 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,47 @@ declare module 'vscode' {
1111
export namespace chat {
1212

1313
/**
14-
* Register a chat context provider. Chat context can be provided:
15-
* - For a resource. Make sure to pass a selector that matches the resource you want to provide context for.
16-
* Providers registered without a selector will not be called for resource-based context.
17-
* - Explicitly. These context items are shown as options when the user explicitly attaches context.
14+
* Register a chat workspace context provider. Workspace context is automatically included in all chat requests.
1815
*
1916
* To ensure your extension is activated when chat context is requested, make sure to include the following activations events:
2017
* - If your extension implements `provideWorkspaceChatContext` or `provideChatContextForResource`, find an activation event which is a good signal to activate.
2118
* Ex: `onLanguage:<languageId>`, `onWebviewPanel:<viewType>`, etc.`
2219
* - If your extension implements `provideChatContextExplicit`, your extension will be automatically activated when the user requests explicit context.
2320
*
21+
* @param id Unique identifier for the provider.
22+
* @param provider The chat workspace context provider.
23+
*/
24+
export function registerChatWorkspaceContextProvider(id: string, provider: ChatWorkspaceContextProvider): Disposable;
25+
26+
/**
27+
* Register a chat explicit context provider. Explicit context items are shown as options when the user explicitly attaches context use the "Attache Context" action in the chat input box.
28+
*
29+
* Explicit context providers should also be statically contributed in package.json using the `chatContext` contribution point.
30+
*
31+
* To ensure your extension is activated when chat context is requested, make sure to include the `onChatContextProvider:<id>` activation event in your `package.json`.
32+
*
33+
* @param id Unique identifier for the provider.
34+
* @param provider The chat explicit context provider.
35+
*/
36+
export function registerChatExplicitContextProvider(id: string, provider: ChatExplicitContextProvider): Disposable;
37+
38+
/**
39+
* Register a chat resource context provider. Resource context is provided for a specific resource.
40+
* Make sure to pass a selector that matches the resource you want to provide context for.
41+
*
42+
* To ensure your extension is activated when chat context is requested, make sure to include the `onChatContextProvider:<id>` activation event in your `package.json`.
43+
*
44+
* @param selector Document selector to filter which resources the provider is called for.
45+
* @param id Unique identifier for the provider.
46+
* @param provider The chat resource context provider.
47+
*/
48+
export function registerChatResourceContextProvider(selector: DocumentSelector, id: string, provider: ChatResourceContextProvider): Disposable;
49+
50+
/**
51+
* Register a chat context provider.
52+
*
53+
* @deprecated Use {@link registerChatWorkspaceContextProvider}, {@link registerChatExplicitContextProvider}, or {@link registerChatResourceContextProvider} instead.
54+
*
2455
* @param selector Optional document selector to filter which resources the provider is called for. If omitted, the provider will only be called for explicit context requests.
2556
* @param id Unique identifier for the provider.
2657
* @param provider The chat context provider.
@@ -32,12 +63,21 @@ declare module 'vscode' {
3263
export interface ChatContextItem {
3364
/**
3465
* Icon for the context item.
66+
* - If `icon` is not defined, no icon is shown.
67+
* - If `icon` is defined and is a file or folder icon, the icon is derived from {@link resourceUri} if `resourceUri` is defined.
68+
* - Otherwise, `icon` is used.
3569
*/
36-
icon: ThemeIcon;
70+
icon?: ThemeIcon;
3771
/**
3872
* Human readable label for the context item.
73+
* If not set, the label is derived from {@link resourceUri}.
74+
*/
75+
label?: string;
76+
/**
77+
* A resource URI for the context item.
78+
* Used to derive the {@link label} and {@link icon} if they are not set.
3979
*/
40-
label: string;
80+
resourceUri?: Uri;
4181
/**
4282
* An optional description of the context item, e.g. to describe the item to the language model.
4383
*/
@@ -53,27 +93,34 @@ declare module 'vscode' {
5393
/**
5494
* An optional command that is executed when the context item is clicked.
5595
* The original context item will be passed as the first argument to the command.
96+
* The original context item will be passed as the first argument to the command.
5697
*/
5798
command?: Command;
5899
}
59100

60-
export interface ChatContextProvider<T extends ChatContextItem = ChatContextItem> {
101+
export interface ChatWorkspaceContextProvider<T extends ChatContextItem = ChatContextItem> {
61102

62103
/**
63104
* An optional event that should be fired when the workspace chat context has changed.
64105
*/
65106
onDidChangeWorkspaceChatContext?: Event<void>;
66107

67108
/**
68-
* TODO @API: should this be a separate provider interface?
69-
*
70109
* Provide a list of chat context items to be included as workspace context for all chat requests.
71110
* This should be used very sparingly to avoid providing useless context and to avoid using up the context window.
72111
* A good example use case is to provide information about which branch the user is working on in a source control context.
73112
*
74113
* @param token A cancellation token.
75114
*/
76-
provideWorkspaceChatContext?(token: CancellationToken): ProviderResult<T[]>;
115+
provideWorkspaceChatContext(token: CancellationToken): ProviderResult<T[]>;
116+
117+
/**
118+
* @deprecated
119+
*/
120+
provideChatContext?(token: CancellationToken): ProviderResult<T[]>;
121+
}
122+
123+
export interface ChatExplicitContextProvider<T extends ChatContextItem = ChatContextItem> {
77124

78125
/**
79126
* Provide a list of chat context items that a user can choose from. These context items are shown as options when the user explicitly attaches context.
@@ -82,27 +129,95 @@ declare module 'vscode' {
82129
*
83130
* @param token A cancellation token.
84131
*/
85-
provideChatContextExplicit?(token: CancellationToken): ProviderResult<T[]>;
132+
provideExplicitChatContext(token: CancellationToken): ProviderResult<T[]>;
133+
134+
/**
135+
* @deprecated
136+
*/
137+
provideChatContext?(token: CancellationToken): ProviderResult<T[]>;
138+
139+
/**
140+
* If a chat context item is provided without a `value`, this method is called to resolve the `value` for the item.
141+
*
142+
* @param context The context item to resolve.
143+
* @param token A cancellation token.
144+
*/
145+
resolveExplicitChatContext(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
146+
147+
/**
148+
* @deprecated
149+
*/
150+
resolveChatContext?(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
151+
}
152+
153+
export interface ChatResourceContextProvider<T extends ChatContextItem = ChatContextItem> {
86154

87155
/**
88156
* Given a particular resource, provide a chat context item for it. This is used for implicit context (see the settings `chat.implicitContext.enabled` and `chat.implicitContext.suggestedContext`).
89157
* Chat context items can be provided without a `value`, as the `value` can be resolved later using `resolveChatContext`.
90158
* `resolveChatContext` is only called for items that do not have a `value`.
91159
*
92160
* Called when the resource is a webview or a text editor.
161+
* Called when the resource is a webview or a text editor.
93162
*
94163
* @param options Options include the resource for which to provide context.
95164
* @param token A cancellation token.
96165
*/
97-
provideChatContextForResource?(options: { resource: Uri }, token: CancellationToken): ProviderResult<T | undefined>;
166+
provideResourceChatContext(options: { resource: Uri }, token: CancellationToken): ProviderResult<T | undefined>;
98167

99168
/**
100-
* If a chat context item is provided without a `value`, from either of the `provide` methods, this method is called to resolve the `value` for the item.
169+
* @deprecated
170+
*/
171+
provideChatContext?(options: { resource: Uri }, token: CancellationToken): ProviderResult<T | undefined>;
172+
173+
/**
174+
* If a chat context item is provided without a `value`, this method is called to resolve the `value` for the item.
101175
*
102176
* @param context The context item to resolve.
103177
* @param token A cancellation token.
104178
*/
105-
resolveChatContext(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
179+
resolveResourceChatContext(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
180+
181+
/**
182+
* @deprecated
183+
*/
184+
resolveChatContext?(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
185+
}
186+
187+
/**
188+
* @deprecated Use {@link ChatWorkspaceContextProvider}, {@link ChatExplicitContextProvider}, or {@link ChatResourceContextProvider} instead.
189+
*/
190+
export interface ChatContextProvider<T extends ChatContextItem = ChatContextItem> {
191+
192+
/**
193+
* An optional event that should be fired when the workspace chat context has changed.
194+
* @deprecated Use {@link ChatWorkspaceContextProvider.onDidChangeWorkspaceChatContext} instead.
195+
*/
196+
onDidChangeWorkspaceChatContext?: Event<void>;
197+
198+
/**
199+
* Provide a list of chat context items to be included as workspace context for all chat requests.
200+
* @deprecated Use {@link ChatWorkspaceContextProvider.provideWorkspaceChatContext} instead.
201+
*/
202+
provideWorkspaceChatContext?(token: CancellationToken): ProviderResult<T[]>;
203+
204+
/**
205+
* Provide a list of chat context items that a user can choose from.
206+
* @deprecated Use {@link ChatExplicitContextProvider.provideExplicitChatContext} instead.
207+
*/
208+
provideChatContextExplicit?(token: CancellationToken): ProviderResult<T[]>;
209+
210+
/**
211+
* Given a particular resource, provide a chat context item for it.
212+
* @deprecated Use {@link ChatResourceContextProvider.provideResourceChatContext} instead.
213+
*/
214+
provideChatContextForResource?(options: { resource: Uri }, token: CancellationToken): ProviderResult<T | undefined>;
215+
216+
/**
217+
* If a chat context item is provided without a `value`, this method is called to resolve the `value` for the item.
218+
* @deprecated Use the `resolveChatContext` method on the specific provider type instead.
219+
*/
220+
resolveChatContext?(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
106221
}
107222

108223
}

src/extension.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { GitLensIntegration } from './integrations/gitlens/gitlensImpl';
3333
import { IssueFeatureRegistrar } from './issues/issueFeatureRegistrar';
3434
import { StateManager } from './issues/stateManager';
3535
import { IssueContextProvider } from './lm/issueContextProvider';
36-
import { PullRequestContextProvider } from './lm/pullRequestContextProvider';
36+
import { PullRequestContextProvider, WorkspaceContextProvider } from './lm/pullRequestContextProvider';
3737
import { registerTools } from './lm/tools/tools';
3838
import { migrate } from './migrations';
3939
import { NotificationsFeatureRegister } from './notifications/notificationsFeatureRegistar';
@@ -276,10 +276,17 @@ async function init(
276276
context.subscriptions.push(issuesFeatures);
277277
await issuesFeatures.initialize();
278278

279-
const pullRequestContextProvider = new PullRequestContextProvider(prsTreeModel, reposManager, git, context);
280-
vscode.chat.registerChatContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', pullRequestContextProvider);
281-
vscode.chat.registerChatContextProvider({ scheme: 'webview-panel', pattern: '**/webview-IssueOverview**' }, 'githubissue', new IssueContextProvider(issueStateManager, reposManager, context));
282-
pullRequestContextProvider.initialize();
279+
const workspaceContextProvider = new WorkspaceContextProvider(reposManager, git);
280+
context.subscriptions.push(workspaceContextProvider);
281+
context.subscriptions.push(vscode.chat.registerChatWorkspaceContextProvider('githubpr', workspaceContextProvider));
282+
workspaceContextProvider.initialize();
283+
const pullRequestContextProvider = new PullRequestContextProvider(prsTreeModel, reposManager, context);
284+
context.subscriptions.push(pullRequestContextProvider);
285+
context.subscriptions.push(vscode.chat.registerChatExplicitContextProvider('githubpr', pullRequestContextProvider));
286+
context.subscriptions.push(vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', pullRequestContextProvider));
287+
const issueContextProvider = new IssueContextProvider(issueStateManager, reposManager, context);
288+
context.subscriptions.push(vscode.chat.registerChatExplicitContextProvider('githubissue', issueContextProvider));
289+
context.subscriptions.push(vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-IssueOverview**' }, 'githubissue', issueContextProvider));
283290

284291
const notificationsFeatures = new NotificationsFeatureRegister(credentialStore, reposManager, telemetry, notificationsManager);
285292
context.subscriptions.push(notificationsFeatures);

src/lm/issueContextProvider.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,35 @@ export namespace IssueChatContextItem {
2222
}
2323
}
2424

25-
export class IssueContextProvider implements vscode.ChatContextProvider {
25+
export class IssueContextProvider implements vscode.ChatExplicitContextProvider<IssueChatContextItem>, vscode.ChatResourceContextProvider<IssueChatContextItem> {
2626
constructor(private readonly _stateManager: StateManager,
2727
private readonly _reposManager: RepositoriesManager,
2828
private readonly _context: vscode.ExtensionContext
2929
) { }
3030

31-
async provideChatContextForResource(_options: { resource: vscode.Uri }, _token: vscode.CancellationToken): Promise<IssueChatContextItem | undefined> {
32-
const item = IssueOverviewPanel.getActivePanel()?.getCurrentItem();
31+
async provideResourceChatContext(_options: { resource: vscode.Uri; }, _token: vscode.CancellationToken): Promise<IssueChatContextItem | undefined> {
32+
const item = IssueOverviewPanel.getActivePanel()?.getCurrentItem();;
3333
if (item) {
3434
return this._issueToUnresolvedContext(item);
3535
}
3636
}
3737

38-
async resolveChatContext(context: IssueChatContextItem, _token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
38+
resolveExplicitChatContext(context: IssueChatContextItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.ChatContextItem> {
39+
return this._resolveChatContext(context, token);
40+
}
41+
42+
resolveResourceChatContext(context: IssueChatContextItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.ChatContextItem> {
43+
return this._resolveChatContext(context, token);
44+
}
45+
46+
private async _resolveChatContext(context: IssueChatContextItem, _token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
3947
context.value = await this._resolvedIssueValue(context.issue);
4048
context.modelDescription = 'All the information about the GitHub issue the user is viewing, including comments.';
4149
context.tooltip = await issueMarkdown(context.issue, this._context, this._reposManager);
4250
return context;
4351
}
4452

45-
async provideChatContextExplicit(_token: vscode.CancellationToken): Promise<IssueChatContextItem[] | undefined> {
53+
async provideExplicitChatContext(_token: vscode.CancellationToken): Promise<IssueChatContextItem[]> {
4654
const contextItems: IssueChatContextItem[] = [];
4755
const seenIssues: Set<string> = new Set();
4856
for (const folderManager of this._reposManager.folderManagers) {

src/lm/pullRequestContextProvider.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ export namespace PRChatContextItem {
2323
}
2424
}
2525

26-
export class PullRequestContextProvider extends Disposable implements vscode.ChatContextProvider {
27-
private readonly _onDidChangeWorkspaceChatContext = new vscode.EventEmitter<void>();
26+
export class WorkspaceContextProvider extends Disposable implements vscode.ChatWorkspaceContextProvider {
27+
private readonly _onDidChangeWorkspaceChatContext = this._register(new vscode.EventEmitter<void>());
2828
readonly onDidChangeWorkspaceChatContext = this._onDidChangeWorkspaceChatContext.event;
2929

30-
constructor(private readonly _prsTreeModel: PrsTreeModel,
30+
constructor(
3131
private readonly _reposManager: RepositoriesManager,
32-
private readonly _git: GitApiImpl,
33-
private readonly _context: vscode.ExtensionContext
32+
private readonly _git: GitApiImpl
3433
) {
3534
super();
3635
}
@@ -90,15 +89,39 @@ Active pull request (may not be the same as open pull request): ${folderManager.
9089
}
9190
return contexts;
9291
}
92+
}
93+
94+
export class PullRequestContextProvider extends Disposable implements vscode.ChatExplicitContextProvider<PRChatContextItem>, vscode.ChatResourceContextProvider<PRChatContextItem> {
95+
constructor(private readonly _prsTreeModel: PrsTreeModel,
96+
private readonly _reposManager: RepositoriesManager,
97+
private readonly _context: vscode.ExtensionContext
98+
) {
99+
super();
100+
}
101+
102+
async provideExplicitChatContext(_token: vscode.CancellationToken): Promise<PRChatContextItem[]> {
103+
const prs = await this._prsTreeModel.getAllPullRequests(this._reposManager.folderManagers[0], false);
104+
return prs.items.map(pr => {
105+
return this._prToUnresolvedContext(pr);
106+
});
107+
}
93108

94-
async provideChatContextForResource(_options: { resource: vscode.Uri }, _token: vscode.CancellationToken): Promise<PRChatContextItem | undefined> {
109+
async provideResourceChatContext(_options: { resource: vscode.Uri; }, _token: vscode.CancellationToken): Promise<PRChatContextItem | undefined> {
95110
const item = PullRequestOverviewPanel.getActivePanel()?.getCurrentItem();
96111
if (item) {
97112
return this._prToUnresolvedContext(item);
98113
}
99114
}
100115

101-
async resolveChatContext(context: PRChatContextItem, _token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
116+
async resolveExplicitChatContext(context: PRChatContextItem, token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
117+
return this._resolveChatContext(context, token);
118+
}
119+
120+
async resolveResourceChatContext(context: PRChatContextItem, token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
121+
return this._resolveChatContext(context, token);
122+
}
123+
124+
private async _resolveChatContext(context: PRChatContextItem, _token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
102125
if (!context.pr) {
103126
return context;
104127
}
@@ -108,13 +131,6 @@ Active pull request (may not be the same as open pull request): ${folderManager.
108131
return context;
109132
}
110133

111-
async provideChatContextExplicit(_token: vscode.CancellationToken): Promise<PRChatContextItem[] | undefined> {
112-
const prs = await this._prsTreeModel.getAllPullRequests(this._reposManager.folderManagers[0], false);
113-
return prs.items.map(pr => {
114-
return this._prToUnresolvedContext(pr);
115-
});
116-
}
117-
118134
private _prToUnresolvedContext(pr: PullRequestModel): PRChatContextItem {
119135
return {
120136
icon: new vscode.ThemeIcon('git-pull-request'),

0 commit comments

Comments
 (0)