Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts
3296 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
6
import { CancellationToken } from '../../../../base/common/cancellation.js';
7
import { Emitter, Event } from '../../../../base/common/event.js';
8
import { Disposable, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
9
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
10
import { IProgress } from '../../../../platform/progress/common/progress.js';
11
import { IChatMessage } from './languageModels.js';
12
import { IChatFollowup, IChatProgress, IChatResponseProgressFileTreeData } from './chatService.js';
13
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
14
import { ChatAgentLocation, ChatModeKind } from './constants.js';
15
16
//#region slash service, commands etc
17
18
export interface IChatSlashData {
19
command: string;
20
detail: string;
21
sortText?: string;
22
/**
23
* Whether the command should execute as soon
24
* as it is entered. Defaults to `false`.
25
*/
26
executeImmediately?: boolean;
27
28
/**
29
* Whether the command should be added as a request/response
30
* turn to the chat history. Defaults to `false`.
31
*
32
* For instance, the `/save` command opens an untitled document
33
* to the side hence does not contain any chatbot responses.
34
*/
35
silent?: boolean;
36
37
locations: ChatAgentLocation[];
38
modes?: ChatModeKind[];
39
}
40
41
export interface IChatSlashFragment {
42
content: string | { treeData: IChatResponseProgressFileTreeData };
43
}
44
export type IChatSlashCallback = { (prompt: string, progress: IProgress<IChatProgress>, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> };
45
46
export const IChatSlashCommandService = createDecorator<IChatSlashCommandService>('chatSlashCommandService');
47
48
/**
49
* This currently only exists to drive /clear and /help
50
*/
51
export interface IChatSlashCommandService {
52
_serviceBrand: undefined;
53
readonly onDidChangeCommands: Event<void>;
54
registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable;
55
executeCommand(id: string, prompt: string, progress: IProgress<IChatProgress>, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>;
56
getCommands(location: ChatAgentLocation, mode: ChatModeKind): Array<IChatSlashData>;
57
hasCommand(id: string): boolean;
58
}
59
60
type Tuple = { data: IChatSlashData; command?: IChatSlashCallback };
61
62
export class ChatSlashCommandService extends Disposable implements IChatSlashCommandService {
63
64
declare _serviceBrand: undefined;
65
66
private readonly _commands = new Map<string, Tuple>();
67
68
private readonly _onDidChangeCommands = this._register(new Emitter<void>());
69
readonly onDidChangeCommands: Event<void> = this._onDidChangeCommands.event;
70
71
constructor(@IExtensionService private readonly _extensionService: IExtensionService) {
72
super();
73
}
74
75
override dispose(): void {
76
super.dispose();
77
this._commands.clear();
78
}
79
80
registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable {
81
if (this._commands.has(data.command)) {
82
throw new Error(`Already registered a command with id ${data.command}}`);
83
}
84
85
this._commands.set(data.command, { data, command });
86
this._onDidChangeCommands.fire();
87
88
return toDisposable(() => {
89
if (this._commands.delete(data.command)) {
90
this._onDidChangeCommands.fire();
91
}
92
});
93
}
94
95
getCommands(location: ChatAgentLocation, mode: ChatModeKind): Array<IChatSlashData> {
96
return Array
97
.from(this._commands.values(), v => v.data)
98
.filter(c => c.locations.includes(location) && (!c.modes || c.modes.includes(mode)));
99
}
100
101
hasCommand(id: string): boolean {
102
return this._commands.has(id);
103
}
104
105
async executeCommand(id: string, prompt: string, progress: IProgress<IChatProgress>, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> {
106
const data = this._commands.get(id);
107
if (!data) {
108
throw new Error('No command with id ${id} NOT registered');
109
}
110
if (!data.command) {
111
await this._extensionService.activateByEvent(`onSlash:${id}`);
112
}
113
if (!data.command) {
114
throw new Error(`No command with id ${id} NOT resolved`);
115
}
116
117
return await data.command(prompt, progress, history, location, token);
118
}
119
}
120
121