Path: blob/main/src/vs/workbench/contrib/chat/common/chatSlashCommands.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*--------------------------------------------------------------------------------------------*/45import { CancellationToken } from '../../../../base/common/cancellation.js';6import { Emitter, Event } from '../../../../base/common/event.js';7import { Disposable, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';8import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';9import { IProgress } from '../../../../platform/progress/common/progress.js';10import { IChatMessage } from './languageModels.js';11import { IChatFollowup, IChatProgress, IChatResponseProgressFileTreeData } from './chatService.js';12import { IExtensionService } from '../../../services/extensions/common/extensions.js';13import { ChatAgentLocation, ChatModeKind } from './constants.js';1415//#region slash service, commands etc1617export interface IChatSlashData {18command: string;19detail: string;20sortText?: string;21/**22* Whether the command should execute as soon23* as it is entered. Defaults to `false`.24*/25executeImmediately?: boolean;2627/**28* Whether the command should be added as a request/response29* turn to the chat history. Defaults to `false`.30*31* For instance, the `/save` command opens an untitled document32* to the side hence does not contain any chatbot responses.33*/34silent?: boolean;3536locations: ChatAgentLocation[];37modes?: ChatModeKind[];38}3940export interface IChatSlashFragment {41content: string | { treeData: IChatResponseProgressFileTreeData };42}43export type IChatSlashCallback = { (prompt: string, progress: IProgress<IChatProgress>, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> };4445export const IChatSlashCommandService = createDecorator<IChatSlashCommandService>('chatSlashCommandService');4647/**48* This currently only exists to drive /clear and /help49*/50export interface IChatSlashCommandService {51_serviceBrand: undefined;52readonly onDidChangeCommands: Event<void>;53registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable;54executeCommand(id: string, prompt: string, progress: IProgress<IChatProgress>, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>;55getCommands(location: ChatAgentLocation, mode: ChatModeKind): Array<IChatSlashData>;56hasCommand(id: string): boolean;57}5859type Tuple = { data: IChatSlashData; command?: IChatSlashCallback };6061export class ChatSlashCommandService extends Disposable implements IChatSlashCommandService {6263declare _serviceBrand: undefined;6465private readonly _commands = new Map<string, Tuple>();6667private readonly _onDidChangeCommands = this._register(new Emitter<void>());68readonly onDidChangeCommands: Event<void> = this._onDidChangeCommands.event;6970constructor(@IExtensionService private readonly _extensionService: IExtensionService) {71super();72}7374override dispose(): void {75super.dispose();76this._commands.clear();77}7879registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable {80if (this._commands.has(data.command)) {81throw new Error(`Already registered a command with id ${data.command}}`);82}8384this._commands.set(data.command, { data, command });85this._onDidChangeCommands.fire();8687return toDisposable(() => {88if (this._commands.delete(data.command)) {89this._onDidChangeCommands.fire();90}91});92}9394getCommands(location: ChatAgentLocation, mode: ChatModeKind): Array<IChatSlashData> {95return Array96.from(this._commands.values(), v => v.data)97.filter(c => c.locations.includes(location) && (!c.modes || c.modes.includes(mode)));98}99100hasCommand(id: string): boolean {101return this._commands.has(id);102}103104async executeCommand(id: string, prompt: string, progress: IProgress<IChatProgress>, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> {105const data = this._commands.get(id);106if (!data) {107throw new Error('No command with id ${id} NOT registered');108}109if (!data.command) {110await this._extensionService.activateByEvent(`onSlash:${id}`);111}112if (!data.command) {113throw new Error(`No command with id ${id} NOT resolved`);114}115116return await data.command(prompt, progress, history, location, token);117}118}119120121