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