mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Implement shouldRepopulate and followupPlaceholder for agents (#196317)
This commit is contained in:
parent
09a4a2f7ab
commit
c833c4d7b5
|
@ -259,7 +259,7 @@ class ExtHostChatAgent {
|
|||
return [];
|
||||
}
|
||||
this._lastSlashCommands = result;
|
||||
return result.map(c => ({ name: c.name, description: c.description }));
|
||||
return result.map(c => ({ name: c.name, description: c.description, followupPlaceholder: c.followupPlaceholder, shouldRepopulate: c.shouldRepopulate }));
|
||||
}
|
||||
|
||||
async provideFollowups(result: vscode.ChatAgentResult2, token: CancellationToken): Promise<IChatFollowup[]> {
|
||||
|
|
|
@ -27,7 +27,7 @@ import { IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/brows
|
|||
import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart';
|
||||
import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import { SelectAndInsertFileAction, dynamicReferenceDecorationType } from 'vs/workbench/contrib/chat/browser/contrib/chatDynamicReferences';
|
||||
import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { chatSlashCommandBackground, chatSlashCommandForeground } from 'vs/workbench/contrib/chat/common/chatColors';
|
||||
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestVariablePart, chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser';
|
||||
|
@ -41,6 +41,10 @@ const placeholderDecorationType = 'chat-session-detail';
|
|||
const slashCommandTextDecorationType = 'chat-session-text';
|
||||
const variableTextDecorationType = 'chat-variable-text';
|
||||
|
||||
function agentAndCommandToKey(agent: string, subcommand: string): string {
|
||||
return `${agent}__${subcommand}`;
|
||||
}
|
||||
|
||||
class InputEditorDecorations extends Disposable {
|
||||
|
||||
public readonly id = 'inputEditorDecorations';
|
||||
|
@ -71,8 +75,12 @@ class InputEditorDecorations extends Disposable {
|
|||
this.updateInputEditorDecorations();
|
||||
}));
|
||||
this._register(this.chatService.onDidSubmitSlashCommand((e) => {
|
||||
if (e.sessionId === this.widget.viewModel?.sessionId && !this.previouslyUsedSlashCommands.has(e.slashCommand)) {
|
||||
this.previouslyUsedSlashCommands.add(e.slashCommand);
|
||||
if (e.sessionId === this.widget.viewModel?.sessionId) {
|
||||
if ('agent' in e) {
|
||||
this.previouslyUsedSlashCommands.add(agentAndCommandToKey(e.agent.id, e.slashCommand.name));
|
||||
} else {
|
||||
this.previouslyUsedSlashCommands.add(e.slashCommand);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -180,6 +188,29 @@ class InputEditorDecorations extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
const onlyAgentCommandAndWhitespace = agentPart && agentSubcommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart);
|
||||
if (onlyAgentCommandAndWhitespace) {
|
||||
// Agent reference and subcommand with no other text - show the placeholder
|
||||
const isFollowupSlashCommand = this.previouslyUsedSlashCommands.has(agentAndCommandToKey(agentPart.agent.id, agentSubcommandPart.command.name));
|
||||
const shouldRenderFollowupPlaceholder = isFollowupSlashCommand && agentSubcommandPart.command.followupPlaceholder;
|
||||
if (agentSubcommandPart?.command.description) {
|
||||
placeholderDecoration = [{
|
||||
range: {
|
||||
startLineNumber: 1,
|
||||
endLineNumber: 1,
|
||||
startColumn: inputValue.length,
|
||||
endColumn: 1000
|
||||
},
|
||||
renderOptions: {
|
||||
after: {
|
||||
contentText: shouldRenderFollowupPlaceholder ? agentSubcommandPart.command.followupPlaceholder : agentSubcommandPart.command.description,
|
||||
color: this.getPlaceholderColor(),
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
const onlySlashCommandAndWhitespace = slashCommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestSlashCommandPart);
|
||||
if (onlySlashCommandAndWhitespace) {
|
||||
// Command reference with no other text - show the placeholder
|
||||
|
@ -237,14 +268,27 @@ class InputEditorSlashCommandMode extends Disposable {
|
|||
@IChatService private readonly chatService: IChatService
|
||||
) {
|
||||
super();
|
||||
this._register(this.chatService.onDidSubmitSlashCommand(({ slashCommand, sessionId }) => this.repopulateSlashCommand(slashCommand, sessionId)));
|
||||
this._register(this.chatService.onDidSubmitSlashCommand(e => {
|
||||
if (this.widget.viewModel?.sessionId !== e.sessionId) {
|
||||
return;
|
||||
}
|
||||
if ('agent' in e) {
|
||||
this.repopulateAgentCommand(e.agent, e.slashCommand);
|
||||
} else {
|
||||
this.repopulateSlashCommand(e.slashCommand);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private async repopulateSlashCommand(slashCommand: string, sessionId: string) {
|
||||
if (this.widget.viewModel?.sessionId !== sessionId) {
|
||||
return;
|
||||
private async repopulateAgentCommand(agent: IChatAgentData, slashCommand: IChatAgentCommand) {
|
||||
if (slashCommand.shouldRepopulate) {
|
||||
const value = `${chatAgentLeader}${agent.id} ${chatSubcommandLeader}${slashCommand.name} `;
|
||||
this.widget.inputEditor.setValue(value);
|
||||
this.widget.inputEditor.setPosition({ lineNumber: 1, column: value.length + 1 });
|
||||
}
|
||||
}
|
||||
|
||||
private async repopulateSlashCommand(slashCommand: string) {
|
||||
const slashCommands = await this.widget.getSlashCommands();
|
||||
|
||||
if (this.widget.inputEditor.getValue().trim().length !== 0) {
|
||||
|
@ -255,7 +299,6 @@ class InputEditorSlashCommandMode extends Disposable {
|
|||
const value = `/${slashCommand} `;
|
||||
this.widget.inputEditor.setValue(value);
|
||||
this.widget.inputEditor.setPosition({ lineNumber: 1, column: value.length + 1 });
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,23 @@ export interface IChatAgentFragment {
|
|||
export interface IChatAgentCommand {
|
||||
name: string;
|
||||
description: string;
|
||||
/**
|
||||
* Whether the command should execute as soon
|
||||
* as it is entered. Defaults to `false`.
|
||||
*/
|
||||
executeImmediately?: boolean;
|
||||
/**
|
||||
* Whether executing the command puts the
|
||||
* chat into a persistent mode, where the
|
||||
* slash command is prepended to the chat input.
|
||||
*/
|
||||
shouldRepopulate?: boolean;
|
||||
/**
|
||||
* Placeholder text to render in the chat input
|
||||
* when the slash command has been repopulated.
|
||||
* Has no effect if `shouldRepopulate` is `false`.
|
||||
*/
|
||||
followupPlaceholder?: string;
|
||||
}
|
||||
|
||||
export interface IChatAgentMetadata {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri';
|
|||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
import { ProviderResult, Location } from 'vs/editor/common/languages';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IChatAgentCommand } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatAgentCommand, IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatModel, ChatModel, ISerializableChatData } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
|
@ -271,7 +271,7 @@ export interface IChatService {
|
|||
_serviceBrand: undefined;
|
||||
transferredSessionData: IChatTransferredSessionData | undefined;
|
||||
|
||||
onDidSubmitSlashCommand: Event<{ slashCommand: string; sessionId: string }>;
|
||||
onDidSubmitSlashCommand: Event<{ slashCommand: string; sessionId: string } | { agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>;
|
||||
onDidRegisterProvider: Event<{ providerId: string }>;
|
||||
registerProvider(provider: IChatProvider): IDisposable;
|
||||
hasSessions(providerId: string): boolean;
|
||||
|
|
|
@ -21,7 +21,7 @@ import { Progress } from 'vs/platform/progress/common/progress';
|
|||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { IChatAgentCommand, IChatAgentData, IChatAgentRequest, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
|
||||
import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { ChatModel, ChatModelInitState, ChatRequestModel, ChatWelcomeMessageModel, IChatModel, ISerializableChatData, ISerializableChatsData, isCompleteInteractiveProgressTreeData } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart } from 'vs/workbench/contrib/chat/common/chatParserTypes';
|
||||
|
@ -143,7 +143,7 @@ export class ChatService extends Disposable implements IChatService {
|
|||
private readonly _onDidPerformUserAction = this._register(new Emitter<IChatUserActionEvent>());
|
||||
public readonly onDidPerformUserAction: Event<IChatUserActionEvent> = this._onDidPerformUserAction.event;
|
||||
|
||||
private readonly _onDidSubmitSlashCommand = this._register(new Emitter<{ slashCommand: string; sessionId: string }>());
|
||||
private readonly _onDidSubmitSlashCommand = this._register(new Emitter<{ slashCommand: string; sessionId: string } | { agent: IChatAgentData; slashCommand: IChatAgentCommand; sessionId: string }>());
|
||||
public readonly onDidSubmitSlashCommand = this._onDidSubmitSlashCommand.event;
|
||||
|
||||
private readonly _onDidDisposeSession = this._register(new Emitter<{ sessionId: string; providerId: string; reason: 'initializationFailed' | 'cleared' }>());
|
||||
|
@ -501,6 +501,8 @@ export class ChatService extends Disposable implements IChatService {
|
|||
try {
|
||||
if (usedSlashCommand?.command) {
|
||||
this._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId });
|
||||
} else if (agentPart && agentSlashCommandPart?.command) {
|
||||
this._onDidSubmitSlashCommand.fire({ agent: agentPart.agent, slashCommand: agentSlashCommandPart.command, sessionId: model.sessionId });
|
||||
}
|
||||
|
||||
let rawResponse: IChatResponse | null | undefined;
|
||||
|
|
14
src/vscode-dts/vscode.proposed.chatAgents2.d.ts
vendored
14
src/vscode-dts/vscode.proposed.chatAgents2.d.ts
vendored
|
@ -33,7 +33,6 @@ declare module 'vscode' {
|
|||
}
|
||||
|
||||
export interface ChatAgentSlashCommand {
|
||||
|
||||
/**
|
||||
* A short name by which this command is referred to in the UI, e.g. `fix` or
|
||||
* `explain` for commands that fix an issue or explain code.
|
||||
|
@ -44,6 +43,19 @@ declare module 'vscode' {
|
|||
* Human-readable description explaining what this command does.
|
||||
*/
|
||||
readonly description: string;
|
||||
|
||||
/**
|
||||
* Whether executing the command puts the
|
||||
* chat into a persistent mode, where the
|
||||
* slash command is prepended to the chat input.
|
||||
*/
|
||||
readonly shouldRepopulate?: boolean;
|
||||
/**
|
||||
* Placeholder text to render in the chat input
|
||||
* when the slash command has been repopulated.
|
||||
* Has no effect if `shouldRepopulate` is `false`.
|
||||
*/
|
||||
readonly followupPlaceholder?: string;
|
||||
}
|
||||
|
||||
export interface ChatAgentSlashCommandProvider {
|
||||
|
|
Loading…
Reference in a new issue