diff --git a/src/@types/vscode.proposed.chatContextProvider.d.ts b/src/@types/vscode.proposed.chatContextProvider.d.ts index 47a9284099..cf0f1744a4 100644 --- a/src/@types/vscode.proposed.chatContextProvider.d.ts +++ b/src/@types/vscode.proposed.chatContextProvider.d.ts @@ -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:`, `onWebviewPanel:`, 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:` 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:` 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. @@ -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. */ @@ -53,11 +93,12 @@ 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 { + export interface ChatWorkspaceContextProvider { /** * An optional event that should be fired when the workspace chat context has changed. @@ -65,15 +106,21 @@ declare module 'vscode' { onDidChangeWorkspaceChatContext?: Event; /** - * 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; + provideWorkspaceChatContext(token: CancellationToken): ProviderResult; + + /** + * @deprecated + */ + provideChatContext?(token: CancellationToken): ProviderResult; + } + + export interface ChatExplicitContextProvider { /** * 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,7 +129,28 @@ declare module 'vscode' { * * @param token A cancellation token. */ - provideChatContextExplicit?(token: CancellationToken): ProviderResult; + provideExplicitChatContext(token: CancellationToken): ProviderResult; + + /** + * @deprecated + */ + provideChatContext?(token: CancellationToken): ProviderResult; + + /** + * 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; + + /** + * @deprecated + */ + resolveChatContext?(context: T, token: CancellationToken): ProviderResult; + } + + export interface ChatResourceContextProvider { /** * 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`). @@ -90,19 +158,66 @@ declare module 'vscode' { * `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; + provideResourceChatContext(options: { resource: Uri }, token: CancellationToken): ProviderResult; /** - * 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; + + /** + * 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; + resolveResourceChatContext(context: T, token: CancellationToken): ProviderResult; + + /** + * @deprecated + */ + resolveChatContext?(context: T, token: CancellationToken): ProviderResult; + } + + /** + * @deprecated Use {@link ChatWorkspaceContextProvider}, {@link ChatExplicitContextProvider}, or {@link ChatResourceContextProvider} instead. + */ + export interface ChatContextProvider { + + /** + * An optional event that should be fired when the workspace chat context has changed. + * @deprecated Use {@link ChatWorkspaceContextProvider.onDidChangeWorkspaceChatContext} instead. + */ + onDidChangeWorkspaceChatContext?: Event; + + /** + * 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; + + /** + * Provide a list of chat context items that a user can choose from. + * @deprecated Use {@link ChatExplicitContextProvider.provideExplicitChatContext} instead. + */ + provideChatContextExplicit?(token: CancellationToken): ProviderResult; + + /** + * Given a particular resource, provide a chat context item for it. + * @deprecated Use {@link ChatResourceContextProvider.provideResourceChatContext} instead. + */ + provideChatContextForResource?(options: { resource: Uri }, token: CancellationToken): ProviderResult; + + /** + * 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; } } diff --git a/src/extension.ts b/src/extension.ts index 6395cba6e2..fb785d7224 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -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'; @@ -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); diff --git a/src/lm/issueContextProvider.ts b/src/lm/issueContextProvider.ts index 31af386c28..f7ee33ab40 100644 --- a/src/lm/issueContextProvider.ts +++ b/src/lm/issueContextProvider.ts @@ -22,27 +22,35 @@ export namespace IssueChatContextItem { } } -export class IssueContextProvider implements vscode.ChatContextProvider { +export class IssueContextProvider implements vscode.ChatExplicitContextProvider, vscode.ChatResourceContextProvider { constructor(private readonly _stateManager: StateManager, private readonly _reposManager: RepositoriesManager, private readonly _context: vscode.ExtensionContext ) { } - async provideChatContextForResource(_options: { resource: vscode.Uri }, _token: vscode.CancellationToken): Promise { - const item = IssueOverviewPanel.getActivePanel()?.getCurrentItem(); + async provideResourceChatContext(_options: { resource: vscode.Uri; }, _token: vscode.CancellationToken): Promise { + const item = IssueOverviewPanel.getActivePanel()?.getCurrentItem();; if (item) { return this._issueToUnresolvedContext(item); } } - async resolveChatContext(context: IssueChatContextItem, _token: vscode.CancellationToken): Promise { + resolveExplicitChatContext(context: IssueChatContextItem, token: vscode.CancellationToken): vscode.ProviderResult { + return this._resolveChatContext(context, token); + } + + resolveResourceChatContext(context: IssueChatContextItem, token: vscode.CancellationToken): vscode.ProviderResult { + return this._resolveChatContext(context, token); + } + + private async _resolveChatContext(context: IssueChatContextItem, _token: vscode.CancellationToken): Promise { 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 { + async provideExplicitChatContext(_token: vscode.CancellationToken): Promise { const contextItems: IssueChatContextItem[] = []; const seenIssues: Set = new Set(); for (const folderManager of this._reposManager.folderManagers) { diff --git a/src/lm/pullRequestContextProvider.ts b/src/lm/pullRequestContextProvider.ts index d9cb881d96..42e7659bb4 100644 --- a/src/lm/pullRequestContextProvider.ts +++ b/src/lm/pullRequestContextProvider.ts @@ -23,14 +23,13 @@ export namespace PRChatContextItem { } } -export class PullRequestContextProvider extends Disposable implements vscode.ChatContextProvider { - private readonly _onDidChangeWorkspaceChatContext = new vscode.EventEmitter(); +export class WorkspaceContextProvider extends Disposable implements vscode.ChatWorkspaceContextProvider { + private readonly _onDidChangeWorkspaceChatContext = this._register(new vscode.EventEmitter()); 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(); } @@ -90,15 +89,39 @@ Active pull request (may not be the same as open pull request): ${folderManager. } return contexts; } +} + +export class PullRequestContextProvider extends Disposable implements vscode.ChatExplicitContextProvider, vscode.ChatResourceContextProvider { + constructor(private readonly _prsTreeModel: PrsTreeModel, + private readonly _reposManager: RepositoriesManager, + private readonly _context: vscode.ExtensionContext + ) { + super(); + } + + async provideExplicitChatContext(_token: vscode.CancellationToken): Promise { + const prs = await this._prsTreeModel.getAllPullRequests(this._reposManager.folderManagers[0], false); + return prs.items.map(pr => { + return this._prToUnresolvedContext(pr); + }); + } - async provideChatContextForResource(_options: { resource: vscode.Uri }, _token: vscode.CancellationToken): Promise { + async provideResourceChatContext(_options: { resource: vscode.Uri; }, _token: vscode.CancellationToken): Promise { const item = PullRequestOverviewPanel.getActivePanel()?.getCurrentItem(); if (item) { return this._prToUnresolvedContext(item); } } - async resolveChatContext(context: PRChatContextItem, _token: vscode.CancellationToken): Promise { + async resolveExplicitChatContext(context: PRChatContextItem, token: vscode.CancellationToken): Promise { + return this._resolveChatContext(context, token); + } + + async resolveResourceChatContext(context: PRChatContextItem, token: vscode.CancellationToken): Promise { + return this._resolveChatContext(context, token); + } + + private async _resolveChatContext(context: PRChatContextItem, _token: vscode.CancellationToken): Promise { if (!context.pr) { return context; } @@ -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 { - 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'),