Path: blob/main/src/vs/workbench/contrib/chat/browser/chatContextPickService.ts
3296 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*--------------------------------------------------------------------------------------------*/4import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';5import { IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';6import { derived, IObservable, ObservablePromise } from '../../../../base/common/observable.js';7import { compare } from '../../../../base/common/strings.js';8import { ThemeIcon } from '../../../../base/common/themables.js';9import { isObject } from '../../../../base/common/types.js';10import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';11import { IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js';12import { IChatRequestVariableEntry } from '../common/chatVariableEntries.js';13import { IChatWidget } from './chat.js';141516export interface IChatContextPickerPickItem {17label: string;18iconClass?: string;19description?: string;20detail?: string;21disabled?: boolean;22asAttachment(): IChatRequestVariableEntry | Promise<IChatRequestVariableEntry>;23}2425export function isChatContextPickerPickItem(item: unknown): item is IChatContextPickerPickItem {26return isObject(item) && typeof (item as IChatContextPickerPickItem).asAttachment === 'function';27}2829interface IChatContextItem {30readonly label: string;31readonly icon: ThemeIcon;32readonly commandId?: string;33readonly ordinal?: number;34isEnabled?(widget: IChatWidget): Promise<boolean> | boolean;35}3637export interface IChatContextValueItem extends IChatContextItem {38readonly type: 'valuePick';3940asAttachment(widget: IChatWidget): Promise<IChatRequestVariableEntry | IChatRequestVariableEntry[] | undefined>;41}4243export type ChatContextPick = IChatContextPickerPickItem | IQuickPickSeparator;4445export interface IChatContextPicker {46readonly placeholder: string;47/**48* Picks that should either be:49* - A promise that resolves to the picked items50* - A function that maps input query into items to display.51*/52readonly picks: Promise<ChatContextPick[]> | ((query: IObservable<string>, token: CancellationToken) => IObservable<{ busy: boolean; picks: ChatContextPick[] }>);5354readonly configure?: {55label: string;56commandId: string;57};58}5960export interface IChatContextPickerItem extends IChatContextItem {61readonly type: 'pickerPick';6263asPicker(widget: IChatWidget): IChatContextPicker;64}6566/**67* Helper for use in {@IChatContextPickerItem} that wraps a simple query->promise68* function into the requisite observable.69*/70export function picksWithPromiseFn(fn: (query: string, token: CancellationToken) => Promise<ChatContextPick[]>): (query: IObservable<string>, token: CancellationToken) => IObservable<{ busy: boolean; picks: ChatContextPick[] }> {71return (query, token) => {72const promise = derived(reader => {73const queryValue = query.read(reader);74const cts = new CancellationTokenSource(token);75reader.store.add(toDisposable(() => cts.dispose(true)));76return new ObservablePromise(fn(queryValue, cts.token));77});7879return promise.map((value, reader) => {80const result = value.promiseResult.read(reader);81return { picks: result?.data || [], busy: result === undefined };82});83};84}8586export interface IChatContextPickService {87_serviceBrand: undefined;8889items: Iterable<IChatContextValueItem | IChatContextPickerItem>;9091/**92* Register a value or picker to the "Add Context" flow. A value directly resolved to a93* chat attachment and a picker first shows a list of items to pick from and then94* resolves the selected item to a chat attachment.95*/96registerChatContextItem(item: IChatContextValueItem | IChatContextPickerItem): IDisposable;97}9899export const IChatContextPickService = createDecorator<IChatContextPickService>('IContextPickService');100101export class ChatContextPickService implements IChatContextPickService {102103declare _serviceBrand: undefined;104105private readonly _picks: IChatContextValueItem[] = [];106107readonly items: Iterable<IChatContextValueItem> = this._picks;108109registerChatContextItem(pick: IChatContextValueItem): IDisposable {110this._picks.push(pick);111112this._picks.sort((a, b) => {113const valueA = a.ordinal ?? 0;114const valueB = b.ordinal ?? 0;115if (valueA === valueB) {116return compare(a.label, b.label);117} else if (valueA < valueB) {118return 1;119} else {120return -1;121}122});123124return toDisposable(() => {125const index = this._picks.indexOf(pick);126if (index >= 0) {127this._picks.splice(index, 1);128}129});130}131}132133134