Path: blob/main/src/vs/workbench/api/common/extHostChatContext.ts
4778 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import type * as vscode from 'vscode';6import { CancellationToken } from '../../../base/common/cancellation.js';7import { URI, UriComponents } from '../../../base/common/uri.js';8import { ExtHostChatContextShape, MainContext, MainThreadChatContextShape } from './extHost.protocol.js';9import { DocumentSelector } from './extHostTypeConverters.js';10import { IExtHostRpcService } from './extHostRpcService.js';11import { IChatContextItem } from '../../contrib/chat/common/contextContrib/chatContext.js';12import { Disposable, DisposableStore } from '../../../base/common/lifecycle.js';1314export class ExtHostChatContext extends Disposable implements ExtHostChatContextShape {15declare _serviceBrand: undefined;1617private _proxy: MainThreadChatContextShape;18private _handlePool: number = 0;19private _providers: Map<number, { provider: vscode.ChatContextProvider; disposables: DisposableStore }> = new Map();20private _itemPool: number = 0;21private _items: Map<number, Map<number, vscode.ChatContextItem>> = new Map(); // handle -> itemHandle -> item2223constructor(@IExtHostRpcService extHostRpc: IExtHostRpcService,24) {25super();26this._proxy = extHostRpc.getProxy(MainContext.MainThreadChatContext);27}2829async $provideChatContext(handle: number, token: CancellationToken): Promise<IChatContextItem[]> {30this._items.delete(handle); // clear previous items31const provider = this._getProvider(handle);32if (!provider.provideChatContextExplicit) {33throw new Error('provideChatContext not implemented');34}35const result = (await provider.provideChatContextExplicit!(token)) ?? [];36const items: IChatContextItem[] = [];37for (const item of result) {38const itemHandle = this._addTrackedItem(handle, item);39items.push({40handle: itemHandle,41icon: item.icon,42label: item.label,43modelDescription: item.modelDescription,44value: item.value45});46}47return items;48}4950private _addTrackedItem(handle: number, item: vscode.ChatContextItem): number {51const itemHandle = this._itemPool++;52if (!this._items.has(handle)) {53this._items.set(handle, new Map());54}55this._items.get(handle)!.set(itemHandle, item);56return itemHandle;57}5859async $provideChatContextForResource(handle: number, options: { resource: UriComponents; withValue: boolean }, token: CancellationToken): Promise<IChatContextItem | undefined> {60const provider = this._getProvider(handle);6162if (!provider.provideChatContextForResource) {63throw new Error('provideChatContextForResource not implemented');64}6566const result = await provider.provideChatContextForResource({ resource: URI.revive(options.resource) }, token);67if (!result) {68return undefined;69}70const itemHandle = this._addTrackedItem(handle, result);7172const item: IChatContextItem | undefined = {73handle: itemHandle,74icon: result.icon,75label: result.label,76modelDescription: result.modelDescription,77value: options.withValue ? result.value : undefined78};79if (options.withValue && !item.value && provider.resolveChatContext) {80const resolved = await provider.resolveChatContext(result, token);81item.value = resolved?.value;82}8384return item;85}8687private async _doResolve(provider: vscode.ChatContextProvider, context: IChatContextItem, extItem: vscode.ChatContextItem, token: CancellationToken): Promise<IChatContextItem> {88const extResult = await provider.resolveChatContext(extItem, token);89const result = extResult ?? context;90return {91handle: context.handle,92icon: result.icon,93label: result.label,94modelDescription: result.modelDescription,95value: result.value96};97}9899async $resolveChatContext(handle: number, context: IChatContextItem, token: CancellationToken): Promise<IChatContextItem> {100const provider = this._getProvider(handle);101102if (!provider.resolveChatContext) {103throw new Error('resolveChatContext not implemented');104}105const extItem = this._items.get(handle)?.get(context.handle);106if (!extItem) {107throw new Error('Chat context item not found');108}109return this._doResolve(provider, context, extItem, token);110}111112registerChatContextProvider(selector: vscode.DocumentSelector | undefined, id: string, provider: vscode.ChatContextProvider): vscode.Disposable {113const handle = this._handlePool++;114const disposables = new DisposableStore();115this._listenForWorkspaceContextChanges(handle, provider, disposables);116this._providers.set(handle, { provider, disposables });117this._proxy.$registerChatContextProvider(handle, `${id}`, selector ? DocumentSelector.from(selector) : undefined, {}, { supportsResource: !!provider.provideChatContextForResource, supportsResolve: !!provider.resolveChatContext });118119return {120dispose: () => {121this._providers.delete(handle);122this._proxy.$unregisterChatContextProvider(handle);123disposables.dispose();124}125};126}127128private _listenForWorkspaceContextChanges(handle: number, provider: vscode.ChatContextProvider, disposables: DisposableStore): void {129if (!provider.onDidChangeWorkspaceChatContext || !provider.provideWorkspaceChatContext) {130return;131}132disposables.add(provider.onDidChangeWorkspaceChatContext(async () => {133const workspaceContexts = await provider.provideWorkspaceChatContext!(CancellationToken.None);134const resolvedContexts: IChatContextItem[] = [];135for (const item of workspaceContexts ?? []) {136const contextItem: IChatContextItem = {137icon: item.icon,138label: item.label,139modelDescription: item.modelDescription,140value: item.value,141handle: this._itemPool++142};143const resolved = await this._doResolve(provider, contextItem, item, CancellationToken.None);144resolvedContexts.push(resolved);145}146147this._proxy.$updateWorkspaceContextItems(handle, resolvedContexts);148}));149}150151private _getProvider(handle: number): vscode.ChatContextProvider {152if (!this._providers.has(handle)) {153throw new Error('Chat context provider not found');154}155return this._providers.get(handle)!.provider;156}157158public override dispose(): void {159super.dispose();160for (const { disposables } of this._providers.values()) {161disposables.dispose();162}163}164}165166167