Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/attachments/chatContextPickService.ts
4780 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js';
6
import { IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js';
7
import { derived, IObservable, ObservablePromise } from '../../../../../base/common/observable.js';
8
import { compare } from '../../../../../base/common/strings.js';
9
import { ThemeIcon } from '../../../../../base/common/themables.js';
10
import { isObject } from '../../../../../base/common/types.js';
11
import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js';
12
import { IQuickItem, IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js';
13
import { IChatRequestVariableEntry } from '../../common/attachments/chatVariableEntries.js';
14
import { IChatWidget } from '../chat.js';
15
16
17
export interface IChatContextPickerPickItem extends Partial<IQuickItem> {
18
label: string;
19
iconClass?: string;
20
iconClasses?: readonly string[];
21
description?: string;
22
detail?: string;
23
disabled?: boolean;
24
asAttachment(): ChatContextPickAttachment | Promise<ChatContextPickAttachment>;
25
}
26
27
export type ChatContextPickAttachment = IChatRequestVariableEntry | IChatRequestVariableEntry[] | 'noop';
28
29
export function isChatContextPickerPickItem(item: unknown): item is IChatContextPickerPickItem {
30
return isObject(item) && typeof (item as IChatContextPickerPickItem).asAttachment === 'function';
31
}
32
33
interface IChatContextItem {
34
readonly label: string;
35
readonly icon: ThemeIcon;
36
readonly commandId?: string;
37
readonly ordinal?: number;
38
isEnabled?(widget: IChatWidget): Promise<boolean> | boolean;
39
}
40
41
export interface IChatContextValueItem extends IChatContextItem {
42
readonly type: 'valuePick';
43
44
asAttachment(widget: IChatWidget): Promise<IChatRequestVariableEntry | IChatRequestVariableEntry[] | undefined>;
45
}
46
47
export type ChatContextPick = IChatContextPickerPickItem | IQuickPickSeparator;
48
49
export interface IChatContextPicker {
50
readonly placeholder: string;
51
/**
52
* Picks that should either be:
53
* - A promise that resolves to the picked items
54
* - A function that maps input query into items to display.
55
*/
56
readonly picks: Promise<ChatContextPick[]> | ((query: IObservable<string>, token: CancellationToken) => IObservable<{ busy: boolean; picks: ChatContextPick[] }>);
57
58
/** Return true to cancel the default behavior */
59
readonly goBack?: () => boolean;
60
61
readonly configure?: {
62
label: string;
63
commandId: string;
64
};
65
66
readonly dispose?: () => void;
67
}
68
69
export interface IChatContextPickerItem extends IChatContextItem {
70
readonly type: 'pickerPick';
71
72
asPicker(widget: IChatWidget): IChatContextPicker;
73
}
74
75
/**
76
* Helper for use in {@IChatContextPickerItem} that wraps a simple query->promise
77
* function into the requisite observable.
78
*/
79
export function picksWithPromiseFn(fn: (query: string, token: CancellationToken) => Promise<ChatContextPick[]>): (query: IObservable<string>, token: CancellationToken) => IObservable<{ busy: boolean; picks: ChatContextPick[] }> {
80
return (query, token) => {
81
const promise = derived(reader => {
82
const queryValue = query.read(reader);
83
const cts = new CancellationTokenSource(token);
84
reader.store.add(toDisposable(() => cts.dispose(true)));
85
return new ObservablePromise(fn(queryValue, cts.token));
86
});
87
88
return promise.map((value, reader) => {
89
const result = value.promiseResult.read(reader);
90
return { picks: result?.data || [], busy: result === undefined };
91
});
92
};
93
}
94
95
export interface IChatContextPickService {
96
_serviceBrand: undefined;
97
98
items: Iterable<IChatContextValueItem | IChatContextPickerItem>;
99
100
/**
101
* Register a value or picker to the "Add Context" flow. A value directly resolved to a
102
* chat attachment and a picker first shows a list of items to pick from and then
103
* resolves the selected item to a chat attachment.
104
*/
105
registerChatContextItem(item: IChatContextValueItem | IChatContextPickerItem): IDisposable;
106
}
107
108
export const IChatContextPickService = createDecorator<IChatContextPickService>('IContextPickService');
109
110
export class ChatContextPickService implements IChatContextPickService {
111
112
declare _serviceBrand: undefined;
113
114
private readonly _picks: IChatContextValueItem[] = [];
115
116
readonly items: Iterable<IChatContextValueItem> = this._picks;
117
118
registerChatContextItem(pick: IChatContextValueItem): IDisposable {
119
this._picks.push(pick);
120
121
this._picks.sort((a, b) => {
122
const valueA = a.ordinal ?? 0;
123
const valueB = b.ordinal ?? 0;
124
if (valueA === valueB) {
125
return compare(a.label, b.label);
126
} else if (valueA < valueB) {
127
return 1;
128
} else {
129
return -1;
130
}
131
});
132
133
return toDisposable(() => {
134
const index = this._picks.indexOf(pick);
135
if (index >= 0) {
136
this._picks.splice(index, 1);
137
}
138
});
139
}
140
}
141
142