Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 129 additions & 14 deletions src/@types/vscode.proposed.chatContextProvider.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,47 @@ declare module 'vscode' {
export namespace chat {

/**
* Register a chat context provider. Chat context can be provided:
* - For a resource. Make sure to pass a selector that matches the resource you want to provide context for.
* Providers registered without a selector will not be called for resource-based context.
* - Explicitly. These context items are shown as options when the user explicitly attaches context.
* Register a chat workspace context provider. Workspace context is automatically included in all chat requests.
*
* To ensure your extension is activated when chat context is requested, make sure to include the following activations events:
* - If your extension implements `provideWorkspaceChatContext` or `provideChatContextForResource`, find an activation event which is a good signal to activate.
* Ex: `onLanguage:<languageId>`, `onWebviewPanel:<viewType>`, etc.`
* - If your extension implements `provideChatContextExplicit`, your extension will be automatically activated when the user requests explicit context.
*
* @param id Unique identifier for the provider.
* @param provider The chat workspace context provider.
*/
export function registerChatWorkspaceContextProvider(id: string, provider: ChatWorkspaceContextProvider): Disposable;

/**
* 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.
*
* Explicit context providers should also be statically contributed in package.json using the `chatContext` contribution point.
*
* To ensure your extension is activated when chat context is requested, make sure to include the `onChatContextProvider:<id>` activation event in your `package.json`.
*
* @param id Unique identifier for the provider.
* @param provider The chat explicit context provider.
*/
export function registerChatExplicitContextProvider(id: string, provider: ChatExplicitContextProvider): Disposable;

/**
* Register a chat resource context provider. Resource context is provided for a specific resource.
* Make sure to pass a selector that matches the resource you want to provide context for.
*
* To ensure your extension is activated when chat context is requested, make sure to include the `onChatContextProvider:<id>` activation event in your `package.json`.
*
* @param selector Document selector to filter which resources the provider is called for.
* @param id Unique identifier for the provider.
* @param provider The chat resource context provider.
*/
export function registerChatResourceContextProvider(selector: DocumentSelector, id: string, provider: ChatResourceContextProvider): Disposable;

/**
* Register a chat context provider.
*
* @deprecated Use {@link registerChatWorkspaceContextProvider}, {@link registerChatExplicitContextProvider}, or {@link registerChatResourceContextProvider} instead.
*
* @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.
* @param id Unique identifier for the provider.
* @param provider The chat context provider.
Expand All @@ -32,12 +63,21 @@ declare module 'vscode' {
export interface ChatContextItem {
/**
* Icon for the context item.
* - If `icon` is not defined, no icon is shown.
* - If `icon` is defined and is a file or folder icon, the icon is derived from {@link resourceUri} if `resourceUri` is defined.
* - Otherwise, `icon` is used.
*/
icon: ThemeIcon;
icon?: ThemeIcon;
/**
* Human readable label for the context item.
* If not set, the label is derived from {@link resourceUri}.
*/
label?: string;
/**
* A resource URI for the context item.
* Used to derive the {@link label} and {@link icon} if they are not set.
*/
label: string;
resourceUri?: Uri;
/**
* An optional description of the context item, e.g. to describe the item to the language model.
*/
Expand All @@ -53,27 +93,34 @@ declare module 'vscode' {
/**
* An optional command that is executed when the context item is clicked.
* The original context item will be passed as the first argument to the command.
* The original context item will be passed as the first argument to the command.
*/
command?: Command;
}

export interface ChatContextProvider<T extends ChatContextItem = ChatContextItem> {
export interface ChatWorkspaceContextProvider<T extends ChatContextItem = ChatContextItem> {

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

/**
* TODO @API: should this be a separate provider interface?
*
* Provide a list of chat context items to be included as workspace context for all chat requests.
* This should be used very sparingly to avoid providing useless context and to avoid using up the context window.
* A good example use case is to provide information about which branch the user is working on in a source control context.
*
* @param token A cancellation token.
*/
provideWorkspaceChatContext?(token: CancellationToken): ProviderResult<T[]>;
provideWorkspaceChatContext(token: CancellationToken): ProviderResult<T[]>;

/**
* @deprecated
*/
provideChatContext?(token: CancellationToken): ProviderResult<T[]>;
}

export interface ChatExplicitContextProvider<T extends ChatContextItem = ChatContextItem> {

/**
* 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.
Expand All @@ -82,27 +129,95 @@ declare module 'vscode' {
*
* @param token A cancellation token.
*/
provideChatContextExplicit?(token: CancellationToken): ProviderResult<T[]>;
provideExplicitChatContext(token: CancellationToken): ProviderResult<T[]>;

/**
* @deprecated
*/
provideChatContext?(token: CancellationToken): ProviderResult<T[]>;

/**
* If a chat context item is provided without a `value`, this method is called to resolve the `value` for the item.
*
* @param context The context item to resolve.
* @param token A cancellation token.
*/
resolveExplicitChatContext(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;

/**
* @deprecated
*/
resolveChatContext?(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
}

export interface ChatResourceContextProvider<T extends ChatContextItem = ChatContextItem> {

/**
* 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`).
* Chat context items can be provided without a `value`, as the `value` can be resolved later using `resolveChatContext`.
* `resolveChatContext` is only called for items that do not have a `value`.
*
* Called when the resource is a webview or a text editor.
* Called when the resource is a webview or a text editor.
*
* @param options Options include the resource for which to provide context.
* @param token A cancellation token.
*/
provideChatContextForResource?(options: { resource: Uri }, token: CancellationToken): ProviderResult<T | undefined>;
provideResourceChatContext(options: { resource: Uri }, token: CancellationToken): ProviderResult<T | undefined>;

/**
* 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.
* @deprecated
*/
provideChatContext?(options: { resource: Uri }, token: CancellationToken): ProviderResult<T | undefined>;

/**
* If a chat context item is provided without a `value`, this method is called to resolve the `value` for the item.
*
* @param context The context item to resolve.
* @param token A cancellation token.
*/
resolveChatContext(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
resolveResourceChatContext(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;

/**
* @deprecated
*/
resolveChatContext?(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
}

/**
* @deprecated Use {@link ChatWorkspaceContextProvider}, {@link ChatExplicitContextProvider}, or {@link ChatResourceContextProvider} instead.
*/
export interface ChatContextProvider<T extends ChatContextItem = ChatContextItem> {

/**
* An optional event that should be fired when the workspace chat context has changed.
* @deprecated Use {@link ChatWorkspaceContextProvider.onDidChangeWorkspaceChatContext} instead.
*/
onDidChangeWorkspaceChatContext?: Event<void>;

/**
* Provide a list of chat context items to be included as workspace context for all chat requests.
* @deprecated Use {@link ChatWorkspaceContextProvider.provideWorkspaceChatContext} instead.
*/
provideWorkspaceChatContext?(token: CancellationToken): ProviderResult<T[]>;

/**
* Provide a list of chat context items that a user can choose from.
* @deprecated Use {@link ChatExplicitContextProvider.provideExplicitChatContext} instead.
*/
provideChatContextExplicit?(token: CancellationToken): ProviderResult<T[]>;

/**
* Given a particular resource, provide a chat context item for it.
* @deprecated Use {@link ChatResourceContextProvider.provideResourceChatContext} instead.
*/
provideChatContextForResource?(options: { resource: Uri }, token: CancellationToken): ProviderResult<T | undefined>;

/**
* If a chat context item is provided without a `value`, this method is called to resolve the `value` for the item.
* @deprecated Use the `resolveChatContext` method on the specific provider type instead.
*/
resolveChatContext?(context: T, token: CancellationToken): ProviderResult<ChatContextItem>;
}

}
17 changes: 12 additions & 5 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { GitLensIntegration } from './integrations/gitlens/gitlensImpl';
import { IssueFeatureRegistrar } from './issues/issueFeatureRegistrar';
import { StateManager } from './issues/stateManager';
import { IssueContextProvider } from './lm/issueContextProvider';
import { PullRequestContextProvider } from './lm/pullRequestContextProvider';
import { PullRequestContextProvider, WorkspaceContextProvider } from './lm/pullRequestContextProvider';
import { registerTools } from './lm/tools/tools';
import { migrate } from './migrations';
import { NotificationsFeatureRegister } from './notifications/notificationsFeatureRegistar';
Expand Down Expand Up @@ -276,10 +276,17 @@ async function init(
context.subscriptions.push(issuesFeatures);
await issuesFeatures.initialize();

const pullRequestContextProvider = new PullRequestContextProvider(prsTreeModel, reposManager, git, context);
vscode.chat.registerChatContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', pullRequestContextProvider);
vscode.chat.registerChatContextProvider({ scheme: 'webview-panel', pattern: '**/webview-IssueOverview**' }, 'githubissue', new IssueContextProvider(issueStateManager, reposManager, context));
pullRequestContextProvider.initialize();
const workspaceContextProvider = new WorkspaceContextProvider(reposManager, git);
context.subscriptions.push(workspaceContextProvider);
context.subscriptions.push(vscode.chat.registerChatWorkspaceContextProvider('githubpr', workspaceContextProvider));
workspaceContextProvider.initialize();
const pullRequestContextProvider = new PullRequestContextProvider(prsTreeModel, reposManager, context);
context.subscriptions.push(pullRequestContextProvider);
context.subscriptions.push(vscode.chat.registerChatExplicitContextProvider('githubpr', pullRequestContextProvider));
context.subscriptions.push(vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-PullRequestOverview**' }, 'githubpr', pullRequestContextProvider));
const issueContextProvider = new IssueContextProvider(issueStateManager, reposManager, context);
context.subscriptions.push(vscode.chat.registerChatExplicitContextProvider('githubissue', issueContextProvider));
context.subscriptions.push(vscode.chat.registerChatResourceContextProvider({ scheme: 'webview-panel', pattern: '**/webview-IssueOverview**' }, 'githubissue', issueContextProvider));

const notificationsFeatures = new NotificationsFeatureRegister(credentialStore, reposManager, telemetry, notificationsManager);
context.subscriptions.push(notificationsFeatures);
Expand Down
18 changes: 13 additions & 5 deletions src/lm/issueContextProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,35 @@ export namespace IssueChatContextItem {
}
}

export class IssueContextProvider implements vscode.ChatContextProvider {
export class IssueContextProvider implements vscode.ChatExplicitContextProvider<IssueChatContextItem>, vscode.ChatResourceContextProvider<IssueChatContextItem> {
constructor(private readonly _stateManager: StateManager,
private readonly _reposManager: RepositoriesManager,
private readonly _context: vscode.ExtensionContext
) { }

async provideChatContextForResource(_options: { resource: vscode.Uri }, _token: vscode.CancellationToken): Promise<IssueChatContextItem | undefined> {
const item = IssueOverviewPanel.getActivePanel()?.getCurrentItem();
async provideResourceChatContext(_options: { resource: vscode.Uri; }, _token: vscode.CancellationToken): Promise<IssueChatContextItem | undefined> {
const item = IssueOverviewPanel.getActivePanel()?.getCurrentItem();;
Comment thread
alexr00 marked this conversation as resolved.
if (item) {
return this._issueToUnresolvedContext(item);
}
}

async resolveChatContext(context: IssueChatContextItem, _token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
resolveExplicitChatContext(context: IssueChatContextItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.ChatContextItem> {
return this._resolveChatContext(context, token);
}

resolveResourceChatContext(context: IssueChatContextItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.ChatContextItem> {
return this._resolveChatContext(context, token);
}

private async _resolveChatContext(context: IssueChatContextItem, _token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
context.value = await this._resolvedIssueValue(context.issue);
context.modelDescription = 'All the information about the GitHub issue the user is viewing, including comments.';
context.tooltip = await issueMarkdown(context.issue, this._context, this._reposManager);
return context;
}

async provideChatContextExplicit(_token: vscode.CancellationToken): Promise<IssueChatContextItem[] | undefined> {
async provideExplicitChatContext(_token: vscode.CancellationToken): Promise<IssueChatContextItem[]> {
const contextItems: IssueChatContextItem[] = [];
const seenIssues: Set<string> = new Set();
for (const folderManager of this._reposManager.folderManagers) {
Expand Down
44 changes: 30 additions & 14 deletions src/lm/pullRequestContextProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ export namespace PRChatContextItem {
}
}

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

constructor(private readonly _prsTreeModel: PrsTreeModel,
constructor(
private readonly _reposManager: RepositoriesManager,
private readonly _git: GitApiImpl,
private readonly _context: vscode.ExtensionContext
private readonly _git: GitApiImpl
) {
super();
}
Expand Down Expand Up @@ -90,15 +89,39 @@ Active pull request (may not be the same as open pull request): ${folderManager.
}
return contexts;
Comment thread
alexr00 marked this conversation as resolved.
}
}

export class PullRequestContextProvider extends Disposable implements vscode.ChatExplicitContextProvider<PRChatContextItem>, vscode.ChatResourceContextProvider<PRChatContextItem> {
constructor(private readonly _prsTreeModel: PrsTreeModel,
private readonly _reposManager: RepositoriesManager,
private readonly _context: vscode.ExtensionContext
) {
super();
}

async provideExplicitChatContext(_token: vscode.CancellationToken): Promise<PRChatContextItem[]> {
const prs = await this._prsTreeModel.getAllPullRequests(this._reposManager.folderManagers[0], false);
return prs.items.map(pr => {
Comment thread
alexr00 marked this conversation as resolved.
return this._prToUnresolvedContext(pr);
});
}

async provideChatContextForResource(_options: { resource: vscode.Uri }, _token: vscode.CancellationToken): Promise<PRChatContextItem | undefined> {
async provideResourceChatContext(_options: { resource: vscode.Uri; }, _token: vscode.CancellationToken): Promise<PRChatContextItem | undefined> {
const item = PullRequestOverviewPanel.getActivePanel()?.getCurrentItem();
if (item) {
return this._prToUnresolvedContext(item);
}
}

async resolveChatContext(context: PRChatContextItem, _token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
async resolveExplicitChatContext(context: PRChatContextItem, token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
return this._resolveChatContext(context, token);
}

async resolveResourceChatContext(context: PRChatContextItem, token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
return this._resolveChatContext(context, token);
}

private async _resolveChatContext(context: PRChatContextItem, _token: vscode.CancellationToken): Promise<vscode.ChatContextItem> {
if (!context.pr) {
return context;
}
Expand All @@ -108,13 +131,6 @@ Active pull request (may not be the same as open pull request): ${folderManager.
return context;
}

async provideChatContextExplicit(_token: vscode.CancellationToken): Promise<PRChatContextItem[] | undefined> {
const prs = await this._prsTreeModel.getAllPullRequests(this._reposManager.folderManagers[0], false);
return prs.items.map(pr => {
return this._prToUnresolvedContext(pr);
});
}

private _prToUnresolvedContext(pr: PullRequestModel): PRChatContextItem {
return {
icon: new vscode.ThemeIcon('git-pull-request'),
Expand Down
Loading