From 1beeb2e113ddf014478124e1aae1c341cc55ee2e Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 10 Jul 2023 15:01:54 -0700 Subject: [PATCH] Repopulate slash command in chat input editor (#187207) * Repopulate slash command in chat input editor --- .../browser/contrib/chatInputEditorContrib.ts | 32 ++++++++++++++++++- .../contrib/chat/common/chatService.ts | 3 ++ .../contrib/chat/common/chatServiceImpl.ts | 9 ++++++ .../vscode.proposed.interactive.d.ts | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 4dff6bbd72a..d869e5bed4f 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -21,6 +21,7 @@ import { IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/brows import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart'; +import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; const decorationDescription = 'chat'; const slashCommandPlaceholderDecorationType = 'chat-session-detail'; @@ -131,7 +132,36 @@ class InputEditorDecorations extends Disposable { } } -ChatWidget.CONTRIBS.push(InputEditorDecorations); +class InputEditorSlashCommandFollowups extends Disposable { + constructor( + private readonly widget: IChatWidget, + @IChatService private readonly chatService: IChatService + ) { + super(); + this._register(this.chatService.onDidCompleteSlashCommand(({ slashCommand, sessionId }) => this.repopulateSlashCommand(slashCommand, sessionId))); + } + + private async repopulateSlashCommand(slashCommand: string, sessionId: string) { + if (this.widget.viewModel?.sessionId !== sessionId) { + return; + } + + const slashCommands = await this.widget.getSlashCommands(); + + if (this.widget.inputEditor.getValue().trim().length !== 0) { + return; + } + + if (slashCommands?.find(c => c.command === slashCommand)?.shouldRepopulate) { + const value = `/${slashCommand} `; + this.widget.inputEditor.setValue(value); + this.widget.inputEditor.setPosition({ lineNumber: 1, column: value.length + 1 }); + + } + } +} + +ChatWidget.CONTRIBS.push(InputEditorDecorations, InputEditorSlashCommandFollowups); class SlashCommandCompletions extends Disposable { constructor( diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 477f7118062..8acfa91959c 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -67,6 +67,7 @@ export interface ISlashCommandProvider { export interface ISlashCommand { command: string; + shouldRepopulate?: boolean; provider?: ISlashCommandProvider; sortText?: string; detail?: string; @@ -176,6 +177,8 @@ export const IChatService = createDecorator('IChatService'); export interface IChatService { _serviceBrand: undefined; transferredSessionId: string | undefined; + + onDidCompleteSlashCommand: Event<{ slashCommand: string; sessionId: string }>; registerProvider(provider: IChatProvider): IDisposable; registerSlashCommandProvider(provider: ISlashCommandProvider): IDisposable; getProviderInfos(): IChatProviderInfo[]; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index c15379f06c6..3c20b21d168 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -135,6 +135,9 @@ export class ChatService extends Disposable implements IChatService { private readonly _onDidPerformUserAction = this._register(new Emitter()); public readonly onDidPerformUserAction: Event = this._onDidPerformUserAction.event; + private readonly _onDidCompleteSlashCommand = this._register(new Emitter<{ slashCommand: string; sessionId: string }>()); + public readonly onDidCompleteSlashCommand = this._onDidCompleteSlashCommand.event; + constructor( @IStorageService private readonly storageService: IStorageService, @ILogService private readonly logService: ILogService, @@ -472,9 +475,15 @@ export class ChatService extends Disposable implements IChatService { Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => { model.setFollowups(request, withNullAsUndefined(followups)); model.completeResponse(request); + if (usedSlashCommand?.command) { + this._onDidCompleteSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); + } }); } else { model.completeResponse(request); + if (usedSlashCommand?.command) { + this._onDidCompleteSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); + } } } }); diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index 916e425189c..79d6db91573 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -128,6 +128,7 @@ declare module 'vscode' { export interface InteractiveSessionSlashCommand { command: string; + shouldRepopulate?: boolean; kind: CompletionItemKind; detail?: string; }