Skip to content

Commit 0946e4c

Browse files
authored
Add workspace context (#8170)
Part of microsoft/vscode#271104
1 parent 8e8d2f4 commit 0946e4c

File tree

3 files changed

+117
-15
lines changed

3 files changed

+117
-15
lines changed

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

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,61 @@ declare module 'vscode' {
1010

1111
export namespace chat {
1212

13-
// TODO@alexr00 API:
14-
// selector is confusing
15-
export function registerChatContextProvider(selector: DocumentSelector, id: string, provider: ChatContextProvider): Disposable;
13+
/**
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.
18+
*
19+
* To ensure your extension is activated when chat context is requested, make sure to include the `onChatContextProvider:<id>` activation event in your `package.json`.
20+
*
21+
* @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.
22+
* @param id Unique identifier for the provider.
23+
* @param provider The chat context provider.
24+
*/
25+
export function registerChatContextProvider(selector: DocumentSelector | undefined, id: string, provider: ChatContextProvider): Disposable;
1626

1727
}
1828

1929
export interface ChatContextItem {
30+
/**
31+
* Icon for the context item.
32+
*/
2033
icon: ThemeIcon;
34+
/**
35+
* Human readable label for the context item.
36+
*/
2137
label: string;
38+
/**
39+
* An optional description of the context item, e.g. to describe the item to the language model.
40+
*/
2241
modelDescription?: string;
42+
/**
43+
* The value of the context item. Can be omitted when returned from one of the `provide` methods if the provider supports `resolveChatContext`.
44+
*/
2345
value?: string;
2446
}
2547

2648
export interface ChatContextProvider<T extends ChatContextItem = ChatContextItem> {
2749

50+
/**
51+
* An optional event that should be fired when the workspace chat context has changed.
52+
*/
53+
onDidChangeWorkspaceChatContext?: Event<void>;
54+
55+
/**
56+
* Provide a list of chat context items to be included as workspace context for all chat sessions.
57+
*
58+
* @param token A cancellation token.
59+
*/
60+
provideWorkspaceChatContext?(token: CancellationToken): ProviderResult<T[]>;
61+
2862
/**
2963
* 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.
3064
* Chat context items can be provided without a `value`, as the `value` can be resolved later using `resolveChatContext`.
3165
* `resolveChatContext` is only called for items that do not have a `value`.
3266
*
33-
* @param options
34-
* @param token
67+
* @param token A cancellation token.
3568
*/
3669
provideChatContextExplicit?(token: CancellationToken): ProviderResult<T[]>;
3770

@@ -40,17 +73,16 @@ declare module 'vscode' {
4073
* Chat context items can be provided without a `value`, as the `value` can be resolved later using `resolveChatContext`.
4174
* `resolveChatContext` is only called for items that do not have a `value`.
4275
*
43-
* @param resource
44-
* @param options
45-
* @param token
76+
* @param options Options include the resource for which to provide context.
77+
* @param token A cancellation token.
4678
*/
4779
provideChatContextForResource?(options: { resource: Uri }, token: CancellationToken): ProviderResult<T | undefined>;
4880

4981
/**
5082
* 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.
5183
*
52-
* @param context
53-
* @param token
84+
* @param context The context item to resolve.
85+
* @param token A cancellation token.
5486
*/
5587
resolveChatContext(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
5688
}

src/extension.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,10 @@ async function init(
261261
context.subscriptions.push(issuesFeatures);
262262
await issuesFeatures.initialize();
263263

264-
vscode.chat.registerChatContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', new PullRequestContextProvider(prsTreeModel, reposManager));
264+
const pullRequestContextProvider = new PullRequestContextProvider(prsTreeModel, reposManager, git);
265+
vscode.chat.registerChatContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', pullRequestContextProvider);
265266
vscode.chat.registerChatContextProvider({ scheme: 'webview-panel', pattern: '**/webview-IssueOverview**' }, 'githubissue', new IssueContextProvider(issueStateManager, reposManager));
267+
pullRequestContextProvider.initialize();
266268

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

src/lm/pullRequestContextProvider.ts

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,84 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as vscode from 'vscode';
7+
import { GitApiImpl } from '../api/api1';
8+
import { Disposable } from '../common/lifecycle';
9+
import { onceEvent } from '../common/utils';
710
import { PullRequestModel } from '../github/pullRequestModel';
811
import { PullRequestOverviewPanel } from '../github/pullRequestOverview';
912
import { RepositoriesManager } from '../github/repositoriesManager';
1013
import { PrsTreeModel } from '../view/prsTreeModel';
1114

1215
interface PRChatContextItem extends vscode.ChatContextItem {
13-
pr: PullRequestModel;
16+
pr?: PullRequestModel;
1417
}
1518

16-
export class PullRequestContextProvider implements vscode.ChatContextProvider {
19+
export class PullRequestContextProvider extends Disposable implements vscode.ChatContextProvider {
20+
private readonly _onDidChangeWorkspaceChatContext = new vscode.EventEmitter<void>();
21+
readonly onDidChangeWorkspaceChatContext = this._onDidChangeWorkspaceChatContext.event;
22+
1723
constructor(private readonly _prsTreeModel: PrsTreeModel,
18-
private readonly _reposManager: RepositoriesManager
19-
) { }
24+
private readonly _reposManager: RepositoriesManager,
25+
private readonly _git: GitApiImpl
26+
) {
27+
super();
28+
}
29+
30+
/**
31+
* Do this setup in the initialize method so that it can be called after the provider is registered.
32+
*/
33+
async initialize() {
34+
if (this._git.state === 'uninitialized') {
35+
await new Promise<void>(resolve => {
36+
this._register(onceEvent(this._git.onDidChangeState)(() => resolve()));
37+
});
38+
}
39+
this._reposManager.folderManagers.forEach(folderManager => {
40+
this._register(folderManager.onDidChangeActivePullRequest(() => {
41+
this._onDidChangeWorkspaceChatContext.fire();
42+
}));
43+
});
44+
this._register(this._reposManager.onDidChangeFolderRepositories(e => {
45+
if (!e.added) {
46+
return;
47+
}
48+
this._register(e.added.onDidChangeActivePullRequest(() => {
49+
this._onDidChangeWorkspaceChatContext.fire();
50+
}));
51+
this._onDidChangeWorkspaceChatContext.fire();
52+
}));
53+
this._register(this._reposManager.onDidChangeAnyGitHubRepository(() => {
54+
this._onDidChangeWorkspaceChatContext.fire();
55+
}));
56+
this._onDidChangeWorkspaceChatContext.fire();
57+
}
58+
59+
async provideWorkspaceChatContext(_token: vscode.CancellationToken): Promise<vscode.ChatContextItem[]> {
60+
const modelDescription = this._reposManager.folderManagers.length > 1 ? 'Information about one of the current repositories. You can use this information when you need to calculate diffs or compare changes with the default branch' : 'Information about the current repository. You can use this information when you need to calculate diffs or compare changes with the default branch';
61+
const contexts: vscode.ChatContextItem[] = [];
62+
for (const folderManager of this._reposManager.folderManagers) {
63+
if (folderManager.gitHubRepositories.length === 0) {
64+
continue;
65+
}
66+
const defaults = await folderManager.getPullRequestDefaults();
67+
68+
let value = `Repository name: ${defaults.repo}
69+
Owner: ${defaults.owner}
70+
Current branch: ${folderManager.repository.state.HEAD?.name ?? 'unknown'}
71+
Default branch: ${defaults.base}`;
72+
if (folderManager.activePullRequest) {
73+
value = `${value}
74+
Active pull request (may not be the same as open pull request): #${folderManager.activePullRequest.number} ${folderManager.activePullRequest.html_url}`;
75+
}
76+
contexts.push({
77+
icon: new vscode.ThemeIcon('github-alt'),
78+
label: `${defaults.owner}/${defaults.repo}`,
79+
modelDescription,
80+
value
81+
});
82+
}
83+
return contexts;
84+
}
2085

2186
async provideChatContextForResource(_options: { resource: vscode.Uri }, _token: vscode.CancellationToken): Promise<PRChatContextItem | undefined> {
2287
const item = PullRequestOverviewPanel.currentPanel?.getCurrentItem();
@@ -26,6 +91,9 @@ export class PullRequestContextProvider implements vscode.ChatContextProvider {
2691
}
2792

2893
async resolveChatContext(context: PRChatContextItem, _token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
94+
if (!context.pr) {
95+
return context;
96+
}
2997
context.value = await this._resolvedPrValue(context.pr);
3098
context.modelDescription = 'All the information about the GitHub pull request the user is viewing, including comments, review threads, and changes.';
3199
return context;

0 commit comments

Comments
 (0)