Skip to content

Commit 1826243

Browse files
authored
Allow slash commands to opt into auto-executing (microsoft#188097)
* Allow slash commands to opt into auto-executing * Allow inline chat slash commands to opt into auto-executing * `hasArgs` -> `executeImmediately` * Check for one line of text * Use completion commands * One more `executeImmediately` rename * Reuse existing inline and panel chat actions
1 parent e055922 commit 1826243

File tree

8 files changed

+64
-33
lines changed

8 files changed

+64
-33
lines changed

src/vs/workbench/api/common/extHostInlineChat.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape {
123123
return {
124124
id,
125125
placeholder: session.placeholder,
126-
slashCommands: session.slashCommands?.map(c => ({ command: c.command, detail: c.detail, refer: c.refer })),
126+
slashCommands: session.slashCommands?.map(c => ({ command: c.command, detail: c.detail, refer: c.refer, executeImmediately: c.executeImmediately })),
127127
wholeRange: typeConvert.Range.from(session.wholeRange),
128128
message: session.message
129129
};

src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,42 @@ function isExecuteActionContext(thing: unknown): thing is IChatExecuteActionCont
2020
return typeof thing === 'object' && thing !== null && 'widget' in thing;
2121
}
2222

23-
export function registerChatExecuteActions() {
24-
registerAction2(class SubmitAction extends Action2 {
25-
constructor() {
26-
super({
27-
id: 'workbench.action.chat.submit',
28-
title: {
29-
value: localize('interactive.submit.label', "Submit"),
30-
original: 'Submit'
31-
},
32-
f1: false,
33-
category: CHAT_CATEGORY,
34-
icon: Codicon.send,
35-
precondition: CONTEXT_CHAT_INPUT_HAS_TEXT,
36-
menu: {
37-
id: MenuId.ChatExecute,
38-
when: CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate(),
39-
group: 'navigation',
40-
}
41-
});
42-
}
23+
export class SubmitAction extends Action2 {
24+
static readonly ID = 'workbench.action.chat.submit';
4325

44-
run(accessor: ServicesAccessor, ...args: any[]) {
45-
const context = args[0];
46-
if (!isExecuteActionContext(context)) {
47-
return;
26+
constructor() {
27+
super({
28+
id: SubmitAction.ID,
29+
title: {
30+
value: localize('interactive.submit.label', "Submit"),
31+
original: 'Submit'
32+
},
33+
f1: false,
34+
category: CHAT_CATEGORY,
35+
icon: Codicon.send,
36+
precondition: CONTEXT_CHAT_INPUT_HAS_TEXT,
37+
menu: {
38+
id: MenuId.ChatExecute,
39+
when: CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate(),
40+
group: 'navigation',
4841
}
42+
});
43+
}
4944

50-
context.widget.acceptInput();
45+
run(accessor: ServicesAccessor, ...args: any[]) {
46+
const context = args[0];
47+
if (!isExecuteActionContext(context)) {
48+
return;
5149
}
52-
});
5350

54-
registerAction2(class SubmitAction extends Action2 {
51+
context.widget.acceptInput();
52+
}
53+
}
54+
55+
export function registerChatExecuteActions() {
56+
registerAction2(SubmitAction);
57+
58+
registerAction2(class CancelAction extends Action2 {
5559
constructor() {
5660
super({
5761
id: 'workbench.action.chat.cancel',

src/vs/workbench/contrib/chat/browser/chatWidget.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
238238
command: 'clear',
239239
sortText: 'z_clear',
240240
detail: localize('clear', "Clear the session"),
241+
executeImmediately: true
241242
};
242243
this.lastSlashCommands = [
243244
...(commands ?? []),

src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
2323
import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart';
2424
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
2525
import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget';
26+
import { SubmitAction } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions';
2627

2728
const decorationDescription = 'chat';
2829
const slashCommandPlaceholderDecorationType = 'chat-session-detail';
@@ -165,7 +166,7 @@ class InputEditorDecorations extends Disposable {
165166
}
166167
}
167168

168-
class InputEditorSlashCommandFollowups extends Disposable {
169+
class InputEditorSlashCommandMode extends Disposable {
169170
constructor(
170171
private readonly widget: IChatWidget,
171172
@IChatService private readonly chatService: IChatService
@@ -194,7 +195,7 @@ class InputEditorSlashCommandFollowups extends Disposable {
194195
}
195196
}
196197

197-
ChatWidget.CONTRIBS.push(InputEditorDecorations, InputEditorSlashCommandFollowups);
198+
ChatWidget.CONTRIBS.push(InputEditorDecorations, InputEditorSlashCommandMode);
198199

199200
class SlashCommandCompletions extends Disposable {
200201
constructor(
@@ -230,7 +231,8 @@ class SlashCommandCompletions extends Disposable {
230231
detail: c.detail,
231232
range: new Range(1, 1, 1, 1),
232233
sortText: c.sortText ?? c.command,
233-
kind: CompletionItemKind.Text // The icons are disabled here anyway
234+
kind: CompletionItemKind.Text, // The icons are disabled here anyway,
235+
command: c.executeImmediately ? { id: SubmitAction.ID, title: withSlash, arguments: [{ widget }] } : undefined,
234236
};
235237
})
236238
};

src/vs/workbench/contrib/chat/common/chatService.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,26 @@ export interface ISlashCommandProvider {
6767

6868
export interface ISlashCommand {
6969
command: string;
70-
shouldRepopulate?: boolean;
7170
provider?: ISlashCommandProvider;
7271
sortText?: string;
7372
detail?: string;
73+
74+
/**
75+
* Whether the command should execute as soon
76+
* as it is entered. Defaults to `false`.
77+
*/
78+
executeImmediately?: boolean;
79+
/**
80+
* Whether executing the command puts the
81+
* chat into a persistent mode, where the
82+
* slash command is prepended to the chat input.
83+
*/
84+
shouldRepopulate?: boolean;
85+
/**
86+
* Placeholder text to render in the chat input
87+
* when the slash command has been repopulated.
88+
* Has no effect if `shouldRepopulate` is `false`.
89+
*/
7490
followupPlaceholder?: string;
7591
}
7692

src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ export class InlineChatWidget {
670670
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet,
671671
kind: CompletionItemKind.Text,
672672
range: new Range(1, 1, 1, 1),
673+
command: command.executeImmediately ? { id: 'inlineChat.accept', title: withSlash } : undefined
673674
};
674675
});
675676

src/vs/workbench/contrib/inlineChat/common/inlineChat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface IInlineChatSlashCommand {
2424
command: string;
2525
detail?: string;
2626
refer?: boolean;
27+
executeImmediately?: boolean;
2728
}
2829

2930
export interface IInlineChatSession {

src/vscode-dts/vscode.proposed.interactive.d.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ declare module 'vscode' {
99
command: string;
1010
detail?: string;
1111
refer?: boolean;
12+
/**
13+
* Whether the command should execute as soon
14+
* as it is entered. Defaults to `false`.
15+
*/
16+
executeImmediately?: boolean;
1217
// kind: CompletionItemKind;
1318
}
1419

@@ -130,10 +135,11 @@ declare module 'vscode' {
130135

131136
export interface InteractiveSessionSlashCommand {
132137
command: string;
133-
shouldRepopulate?: boolean;
134138
kind: CompletionItemKind;
135139
detail?: string;
140+
shouldRepopulate?: boolean;
136141
followupPlaceholder?: string;
142+
executeImmediately?: boolean;
137143
}
138144

139145
export interface InteractiveSessionReplyFollowup {

0 commit comments

Comments
 (0)