From e822ae92ea05e56540b0535e436cb043c01ffbbb Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 6 Feb 2024 20:37:26 -0300 Subject: [PATCH] Revert "Replace chat "command followups" with command button content (#204512)" This reverts commit cda51f6ab4a708a83dd2522d2616963c60f4509a. Have to revert this because I can't get the vscode-copilot change to work. I might try again tomorrow in a back-compatible way. --- .../workbench/api/common/extHost.api.impl.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 8 +- .../api/common/extHostChatAgents2.ts | 40 ++------ .../workbench/api/common/extHostInlineChat.ts | 15 +-- .../api/common/extHostTypeConverters.ts | 35 +++---- .../contrib/chat/browser/chatFollowups.ts | 3 +- .../contrib/chat/browser/chatInputPart.ts | 6 +- .../contrib/chat/browser/chatListRenderer.ts | 82 ++++++++-------- .../contrib/chat/browser/chatWidget.ts | 4 +- .../contrib/chat/browser/media/chat.css | 30 +++--- .../contrib/chat/common/chatAgents.ts | 4 +- .../contrib/chat/common/chatModel.ts | 32 ++----- .../contrib/chat/common/chatService.ts | 26 ++--- .../contrib/chat/common/chatServiceImpl.ts | 4 +- .../contrib/chat/common/chatViewModel.ts | 22 ++--- .../inlineChat/browser/inlineChatWidget.ts | 95 ++++++++++--------- .../contrib/inlineChat/common/inlineChat.ts | 22 +---- .../vscode.proposed.chatAgents2.d.ts | 25 ++--- .../vscode.proposed.chatAgents2Additions.d.ts | 2 +- .../vscode.proposed.defaultChatAgent.d.ts | 2 +- .../vscode.proposed.interactive.d.ts | 2 +- 21 files changed, 204 insertions(+), 257 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f41ba74a6e6..7cffe74fd74 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -208,7 +208,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService)); const extHostInteractiveEditor = rpcProtocol.set(ExtHostContext.ExtHostInlineChat, new ExtHostInteractiveEditor(rpcProtocol, extHostCommands, extHostDocuments, extHostLogService)); const extHostChatProvider = rpcProtocol.set(ExtHostContext.ExtHostChatProvider, new ExtHostChatProvider(rpcProtocol, extHostLogService)); - const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostChatProvider, extHostLogService, extHostCommands)); + const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostChatProvider, extHostLogService)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol)); const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol)); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 8abc5dca98d..d24c57433a8 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -53,10 +53,10 @@ import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/cal import { IChatAgentCommand, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider'; -import { IChatDynamicRequest, IChatProgress, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection, IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatDynamicRequest, IChatFollowup, IChatProgress, IChatReplyFollowup, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue, IChatVariableData } from 'vs/workbench/contrib/chat/common/chatVariables'; import { DebugConfigurationProviderTriggerKind, MainThreadDebugVisualization, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugVisualization, IDebugVisualizationContext } from 'vs/workbench/contrib/debug/common/debug'; -import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatFollowup, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatProgressItem, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { ICellExecutionComplete, ICellExecutionStateUpdate } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; @@ -1225,7 +1225,7 @@ export interface ExtHostChatAgentsShape2 { $acceptAction(handle: number, sessionId: string, requestId: string, action: IChatUserActionEvent): void; $invokeCompletionProvider(handle: number, query: string, token: CancellationToken): Promise; $provideWelcomeMessage(handle: number, token: CancellationToken): Promise<(string | IMarkdownString)[] | undefined>; - $provideSampleQuestions(handle: number, token: CancellationToken): Promise; + $provideSampleQuestions(handle: number, token: CancellationToken): Promise; $releaseSession(sessionId: string): void; } @@ -1251,7 +1251,7 @@ export type IInlineChatResponseDto = Dto; $provideResponse(handle: number, session: IInlineChatSession, request: IInlineChatRequest, token: CancellationToken): Promise; - $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise; + $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise; $handleFeedback(handle: number, sessionId: number, responseId: number, kind: InlineChatResponseFeedbackKind): void; $releaseSession(handle: number, sessionId: number): void; } diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 30ae71c8d52..cb1333f212b 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -9,7 +9,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter } from 'vs/base/common/event'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; -import { DisposableStore } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; @@ -17,11 +16,10 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostChatAgentsShape2, IChatAgentCompletionItem, IChatAgentHistoryEntryDto, IMainContext, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider'; -import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { IChatAgentCommand, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { IChatFollowup, IChatProgress, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatFollowup, IChatProgress, IChatReplyFollowup, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import type * as vscode from 'vscode'; @@ -37,8 +35,7 @@ class ChatAgentResponseStream { private readonly _extension: IExtensionDescription, private readonly _request: IChatAgentRequest, private readonly _proxy: MainThreadChatAgentsShape2, - private readonly _logService: ILogService, - private readonly _commandsConverter: CommandsConverter, + @ILogService private readonly _logService: ILogService, ) { } close() { @@ -102,14 +99,6 @@ class ChatAgentResponseStream { _report(dto); return this; }, - button(command) { - throwIfDone(this.anchor); - _report({ - kind: 'command', - command: that._commandsConverter.toInternal(command, new DisposableStore()) // ?? - }); - return this; - }, progress(value) { throwIfDone(this.progress); const part = new extHostTypes.ChatResponseProgressPart(value); @@ -161,7 +150,6 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { mainContext: IMainContext, private readonly _extHostChatProvider: ExtHostChatProvider, private readonly _logService: ILogService, - private readonly commands: ExtHostCommands, ) { this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2); } @@ -187,7 +175,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { this._extHostChatProvider.$updateAllowlist([{ extension: agent.extension.identifier, allowed: true }]); - const stream = new ChatAgentResponseStream(agent.extension, request, this._proxy, this._logService, this.commands.converter); + const stream = new ChatAgentResponseStream(agent.extension, request, this._proxy, this._logService); try { const convertedHistory = await this.prepareHistory(agent, request, context); const task = agent.invoke( @@ -232,7 +220,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { || h.result; return { request: typeConvert.ChatAgentRequest.to(h.request), - response: coalesce(h.response.map(r => typeConvert.ChatResponseProgress.toProgressContent(r, this.commands.converter))), + response: coalesce(h.response.map(r => typeConvert.ChatResponseProgress.toProgressContent(r))), result } satisfies vscode.ChatAgentHistoryEntry; }))); @@ -301,14 +289,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { // handled by $acceptFeedback return; } - - if (action.action.kind === 'command') { - const action2: vscode.ChatAgentCommandAction = { kind: 'command', commandButton: typeConvert.ChatResponseProgress.toProgressContent(action.action.commandButton, this.commands.converter) as vscode.ChatAgentCommandButton }; - agent.acceptAction(Object.freeze({ action: action2, result })); - return; - } else { - agent.acceptAction(Object.freeze({ action: action.action, result })); - } + agent.acceptAction(Object.freeze({ action: action.action, result })); } async $invokeCompletionProvider(handle: number, query: string, token: CancellationToken): Promise { @@ -330,7 +311,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 { return await agent.provideWelcomeMessage(token); } - async $provideSampleQuestions(handle: number, token: CancellationToken): Promise { + async $provideSampleQuestions(handle: number, token: CancellationToken): Promise { const agent = this._agents.get(handle); if (!agent) { return; @@ -408,10 +389,7 @@ class ExtHostChatAgent { if (!followups) { return []; } - return followups - // Filter out "command followups" from older providers - .filter(f => !(f && 'commandId' in f)) - .map(f => typeConvert.ChatFollowup.from(f)); + return followups.map(f => typeConvert.ChatFollowup.from(f)); } async provideWelcomeMessage(token: CancellationToken): Promise<(string | IMarkdownString)[] | undefined> { @@ -431,7 +409,7 @@ class ExtHostChatAgent { }); } - async provideSampleQuestions(token: CancellationToken): Promise { + async provideSampleQuestions(token: CancellationToken): Promise { if (!this._welcomeMessageProvider || !this._welcomeMessageProvider.provideSampleQuestions) { return []; } @@ -440,7 +418,7 @@ class ExtHostChatAgent { return []; } - return content?.map(f => typeConvert.ChatFollowup.from(f)); + return content?.map(f => typeConvert.ChatReplyFollowup.from(f)); } get apiAgent(): vscode.ChatAgent2 { diff --git a/src/vs/workbench/api/common/extHostInlineChat.ts b/src/vs/workbench/api/common/extHostInlineChat.ts index 9e8478b98f3..47ec20ccea2 100644 --- a/src/vs/workbench/api/common/extHostInlineChat.ts +++ b/src/vs/workbench/api/common/extHostInlineChat.ts @@ -3,22 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { raceCancellation } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { toDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IPosition } from 'vs/editor/common/core/position'; -import { IRange } from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; +import { IInlineChatSession, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostInlineChatShape, IInlineChatResponseDto, IMainContext, MainContext, MainThreadInlineChatShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; -import { IInlineChatFollowup, IInlineChatRequest, IInlineChatSession, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import type * as vscode from 'vscode'; +import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { IRange } from 'vs/editor/common/core/range'; +import { IPosition } from 'vs/editor/common/core/position'; +import { raceCancellation } from 'vs/base/common/async'; +import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; class ProviderWrapper { @@ -227,14 +228,14 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { } } - async $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise { + async $provideFollowups(handle: number, sessionId: number, responseId: number, token: CancellationToken): Promise { const entry = this._inputProvider.get(handle); const sessionData = this._inputSessions.get(sessionId); const response = sessionData?.responses[responseId]; if (entry && response && entry.provider.provideFollowups) { const task = Promise.resolve(entry.provider.provideFollowups(sessionData.session, response, token)); const followups = await raceCancellation(task, token); - return followups?.map(typeConvert.ChatInlineFollowup.from); + return followups?.map(typeConvert.ChatFollowup.from); } return undefined; } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 9ce83ae8888..4345dee16d7 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -36,9 +36,9 @@ import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from 'vs/workbench/common/edit import { IViewBadge } from 'vs/workbench/common/views'; import { IChatAgentRequest } from 'vs/workbench/contrib/chat/common/chatAgents'; import * as chatProvider from 'vs/workbench/contrib/chat/common/chatProvider'; -import { IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgressMessage, IChatTreeData } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgressMessage, IChatReplyFollowup, IChatResponseCommandFollowup, IChatTreeData } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; -import { IInlineChatCommandFollowup, IInlineChatFollowup, IInlineChatReplyFollowup, InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import * as search from 'vs/workbench/contrib/search/common/search'; @@ -2190,8 +2190,8 @@ export namespace DataTransfer { } } -export namespace ChatFollowup { - export function from(followup: vscode.ChatAgentFollowup): IChatFollowup { +export namespace ChatReplyFollowup { + export function from(followup: vscode.ChatAgentReplyFollowup | vscode.InteractiveEditorReplyFollowup): IChatReplyFollowup { return { kind: 'reply', message: followup.message, @@ -2201,25 +2201,21 @@ export namespace ChatFollowup { } } -export namespace ChatInlineFollowup { - export function from(followup: vscode.InteractiveEditorFollowup): IInlineChatFollowup { - if ('commandId' in followup) { - return { +export namespace ChatFollowup { + export function from(followup: string | vscode.ChatAgentFollowup): IChatFollowup { + if (typeof followup === 'string') { + return { title: followup, message: followup, kind: 'reply' }; + } else if ('commandId' in followup) { + return { kind: 'command', title: followup.title ?? '', commandId: followup.commandId ?? '', when: followup.when ?? '', args: followup.args - } satisfies IInlineChatCommandFollowup; + }; } else { - return { - kind: 'reply', - message: followup.message, - title: followup.title, - tooltip: followup.tooltip, - } satisfies IInlineChatReplyFollowup; + return ChatReplyFollowup.from(followup); } - } } @@ -2517,7 +2513,7 @@ export namespace ChatResponseProgress { } } - export function toProgressContent(progress: extHostProtocol.IChatContentProgressDto, commandsConverter: Command.ICommandsConverter): vscode.ChatAgentContentProgress | undefined { + export function toProgressContent(progress: extHostProtocol.IChatContentProgressDto): vscode.ChatAgentContentProgress | undefined { switch (progress.kind) { case 'markdownContent': // For simplicity, don't sent back the 'extended' types, so downgrade markdown to just some text @@ -2532,11 +2528,6 @@ export namespace ChatResponseProgress { }; case 'treeData': return { treeData: revive(progress.treeData) }; - case 'command': - // If the command isn't in the converter, then this session may have been restored, and the command args don't exist anymore - return { - command: commandsConverter.fromInternal(progress.command) ?? { command: progress.command.id, title: progress.command.title }, - }; default: // Unknown type, eg something in history that was removed? Ignore return undefined; diff --git a/src/vs/workbench/contrib/chat/browser/chatFollowups.ts b/src/vs/workbench/contrib/chat/browser/chatFollowups.ts index 70e92d3f5b1..6c6e0d8f6d7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatFollowups.ts +++ b/src/vs/workbench/contrib/chat/browser/chatFollowups.ts @@ -10,11 +10,10 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; -import { IInlineChatFollowup } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; const $ = dom.$; -export class ChatFollowups extends Disposable { +export class ChatFollowups extends Disposable { constructor( container: HTMLElement, followups: T[], diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 06a5e2bb2fd..ce8a6081bea 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -37,7 +37,7 @@ import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups'; import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { CONTEXT_CHAT_INPUT_CURSOR_AT_TOP, CONTEXT_CHAT_INPUT_HAS_TEXT, CONTEXT_IN_CHAT_INPUT } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { chatAgentLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatReplyFollowup } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IChatHistoryEntry, IChatWidgetHistoryService } from 'vs/workbench/contrib/chat/common/chatWidgetHistoryService'; import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; @@ -64,7 +64,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge private _onDidBlur = this._register(new Emitter()); readonly onDidBlur = this._onDidBlur.event; - private _onDidAcceptFollowup = this._register(new Emitter<{ followup: IChatFollowup; response: IChatResponseViewModel | undefined }>()); + private _onDidAcceptFollowup = this._register(new Emitter<{ followup: IChatReplyFollowup; response: IChatResponseViewModel | undefined }>()); readonly onDidAcceptFollowup = this._onDidAcceptFollowup.event; private inputEditorHeight = 0; @@ -338,7 +338,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } } - async renderFollowups(items: IChatFollowup[] | undefined, response: IChatResponseViewModel | undefined): Promise { + async renderFollowups(items: IChatReplyFollowup[] | undefined, response: IChatResponseViewModel | undefined): Promise { if (!this.options.renderFollowups) { return; } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 653c27fbaeb..6e30f1e51d4 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -60,7 +60,7 @@ import { IChatAgentMetadata } from 'vs/workbench/contrib/chat/common/chatAgents' import { CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatProgressRenderableResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; import { chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatCommandButton, IChatContentReference, IChatFollowup, IChatProgressMessage, IChatResponseProgressFileTreeData, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatContentReference, IChatProgressMessage, IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatProgressMessageRenderData, IChatRenderData, IChatResponseMarkdownRenderData, IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { createFileIconThemableTreeContainerScope } from 'vs/workbench/contrib/files/browser/views/explorerView'; @@ -112,8 +112,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer()); - readonly onDidClickFollowup: Event = this._onDidClickFollowup.event; + protected readonly _onDidClickFollowup = this._register(new Emitter()); + readonly onDidClickFollowup: Event = this._onDidClickFollowup.event; protected readonly _onDidChangeItemHeight = this._register(new Emitter()); readonly onDidChangeItemHeight: Event = this._onDidChangeItemHeight.event; @@ -136,11 +136,12 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { + this.chatService.notifyUserAction({ + providerId: element.providerId, + agentId: element.agent?.id, + sessionId: element.sessionId, + requestId: element.requestId, + action: { + kind: 'command', + command: followup, + } + }); + return this.commandService.executeCommand(followup.commandId, ...(followup.args ?? [])); + }, + templateData.contextKeyService)); + } + const newHeight = templateData.rowContainer.offsetHeight; const fireEvent = !element.currentRenderedHeight || element.currentRenderedHeight !== newHeight; element.currentRenderedHeight = newHeight; @@ -532,8 +554,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer this.commandService.executeCommand(commandButton.command.id, ...(commandButton.command.arguments ?? [])))); - return { - dispose() { - disposables.dispose(); - }, - element: container - }; - } - private renderMarkdown(markdown: IMarkdownString, element: ChatTreeItem, templateData: IChatListItemTemplate, fillInIncompleteTokens = false): IMarkdownRenderResult { const disposables = new DisposableStore(); let codeBlockIndex = 0; @@ -1001,6 +1002,17 @@ export class ChatAccessibilityProvider implements IListAccessibilityProvider followup.title).join(', ')); + } const fileTreeCount = element.response.value.filter((v) => !('value' in v))?.length ?? 0; let fileTreeCountHint = ''; switch (fileTreeCount) { @@ -1025,7 +1037,7 @@ export class ChatAccessibilityProvider implements IListAccessibilityProvider, i: number): boolean { return items.slice(i).every(isProgressMessage); } diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 79d74e0ba11..d0681b606f5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -30,7 +30,7 @@ import { ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane'; import { CONTEXT_CHAT_REQUEST_IN_PROGRESS, CONTEXT_IN_CHAT_SESSION, CONTEXT_RESPONSE_FILTERED } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; import { ChatModelInitState, IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; -import { IChatFollowup, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatReplyFollowup, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { ChatViewModel, IChatResponseViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; @@ -292,7 +292,7 @@ export class ChatWidget extends Disposable implements IChatWidget { } } - private async renderFollowups(items: IChatFollowup[] | undefined, response?: IChatResponseViewModel): Promise { + private async renderFollowups(items: IChatReplyFollowup[] | undefined, response?: IChatResponseViewModel): Promise { this.inputPart.renderFollowups(items, response); if (this.bodyDimension) { diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index d802cf60524..fdbd39a5961 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -425,6 +425,20 @@ align-items: start; } +.interactive-session-followups .monaco-button { + text-align: left; + width: initial; +} + +.interactive-session-followups .monaco-button .codicon { + margin-left: 0; + margin-top: 1px; +} + +.interactive-item-container .interactive-response-followups .monaco-button { + padding: 4px 8px; +} + .interactive-session .interactive-input-part .interactive-input-followups { margin: 0px 20px; } @@ -619,19 +633,3 @@ font-size: 14px; color: var(--vscode-debugIcon-startForeground) !important; } - -.interactive-item-container .chat-command-button { - display: flex; - margin-bottom: 16px; -} - -.interactive-item-container .chat-command-button .monaco-button { - text-align: left; - width: initial; - padding: 4px 8px; -} - -.interactive-item-container .chat-command-button .monaco-button .codicon { - margin-left: 0; - margin-top: 1px; -} diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index cd4e3b1b489..6b71960d119 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri'; import { ProviderResult } from 'vs/editor/common/languages'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel'; -import { IChatFollowup, IChatProgress, IChatResponseErrorDetails } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatFollowup, IChatProgress, IChatReplyFollowup, IChatResponseErrorDetails } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; //#region agent service, commands etc @@ -35,7 +35,7 @@ export interface IChatAgent extends IChatAgentData { lastSlashCommands?: IChatAgentCommand[]; provideSlashCommands(token: CancellationToken): Promise; provideWelcomeMessage?(token: CancellationToken): ProviderResult<(string | IMarkdownString)[] | undefined>; - provideSampleQuestions?(token: CancellationToken): ProviderResult; + provideSampleQuestions?(token: CancellationToken): ProviderResult; } export interface IChatAgentCommand { diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index cbf36af4e56..235c547b2e4 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -16,7 +16,7 @@ import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { ILogService } from 'vs/platform/log/common/log'; import { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChat, IChatAgentMarkdownContentWithVulnerability, IChatContent, IChatContentInlineReference, IChatContentReference, IChatMarkdownContent, IChatProgress, IChatProgressMessage, IChatFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatTreeData, IChatUsedContext, InteractiveSessionVoteDirection, isIUsedContext, IChatCommandButton } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChat, IChatAgentMarkdownContentWithVulnerability, IChatContent, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatProgress, IChatProgressMessage, IChatReplyFollowup, IChatResponse, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatTreeData, IChatUsedContext, InteractiveSessionVoteDirection, isIUsedContext } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; export interface IChatRequestVariableData { @@ -33,7 +33,7 @@ export interface IChatRequestModel { readonly username: string; readonly avatarIconUri?: URI; readonly session: IChatModel; - readonly message: IParsedChatRequest | IChatFollowup; + readonly message: IParsedChatRequest | IChatReplyFollowup; readonly variableData: IChatRequestVariableData; readonly response?: IChatResponseModel; } @@ -43,8 +43,7 @@ export type IChatProgressResponseContent = | IChatAgentMarkdownContentWithVulnerability | IChatTreeData | IChatContentInlineReference - | IChatProgressMessage - | IChatCommandButton; + | IChatProgressMessage; export type IChatProgressRenderableResponseContent = Exclude; @@ -69,8 +68,6 @@ export interface IChatResponseModel { readonly response: IResponse; readonly isComplete: boolean; readonly isCanceled: boolean; - /** A stale response is one that has been persisted and rehydrated, so e.g. Commands that have their arguments stored in the EH are gone. */ - readonly isStale: boolean; readonly vote: InteractiveSessionVoteDirection | undefined; readonly followups?: IChatFollowup[] | undefined; readonly errorDetails?: IChatResponseErrorDetails; @@ -162,7 +159,7 @@ export class Response implements IResponse { } this._updateRepr(quiet); - } else { + } else if (progress.kind === 'treeData' || progress.kind === 'inlineReference' || progress.kind === 'markdownVuln' || progress.kind === 'progressMessage') { this._responseParts.push(progress); this._updateRepr(quiet); } @@ -174,8 +171,6 @@ export class Response implements IResponse { return ''; } else if (part.kind === 'inlineReference') { return basename('uri' in part.inlineReference ? part.inlineReference.uri : part.inlineReference); - } else if (part.kind === 'command') { - return part.command.title; } else { return part.content.value; } @@ -260,11 +255,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel return this._progressMessages; } - private _isStale: boolean = false; - public get isStale(): boolean { - return this._isStale; - } - constructor( _response: IMarkdownString | ReadonlyArray, public readonly session: ChatModel, @@ -278,10 +268,6 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel followups?: ReadonlyArray ) { super(); - - // If we are creating a response with some existing content, consider it stale - this._isStale = Array.isArray(_response) && (_response.length !== 0 || isMarkdownString(_response) && _response.value.length !== 0); - this._followups = followups ? [...followups] : undefined; this._response = new Response(_response); this._register(this._response.onDidChangeValue(() => this._onDidChange.fire())); @@ -382,7 +368,7 @@ export interface ISerializableChatRequestData { export interface IExportableChatData { providerId: string; - welcomeMessage: (string | IChatFollowup[])[] | undefined; + welcomeMessage: (string | IChatReplyFollowup[])[] | undefined; requests: ISerializableChatRequestData[]; requesterUsername: string; responderUsername: string; @@ -660,7 +646,7 @@ export class ChatModel extends Disposable implements IChatModel { if (progress.kind === 'vulnerability') { request.response.updateContent({ kind: 'markdownVuln', content: { value: progress.content }, vulnerabilities: progress.vulnerabilities }, quiet); - } else if (progress.kind === 'content' || progress.kind === 'markdownContent' || progress.kind === 'treeData' || progress.kind === 'inlineReference' || progress.kind === 'markdownVuln' || progress.kind === 'progressMessage' || progress.kind === 'command') { + } else if (progress.kind === 'content' || progress.kind === 'markdownContent' || progress.kind === 'treeData' || progress.kind === 'inlineReference' || progress.kind === 'markdownVuln' || progress.kind === 'progressMessage') { request.response.updateContent(progress, quiet); } else if (progress.kind === 'usedContext' || progress.kind === 'reference') { request.response.applyReference(progress); @@ -786,12 +772,12 @@ export class ChatModel extends Disposable implements IChatModel { } } -export type IChatWelcomeMessageContent = IMarkdownString | IChatFollowup[]; +export type IChatWelcomeMessageContent = IMarkdownString | IChatReplyFollowup[]; export interface IChatWelcomeMessageModel { readonly id: string; readonly content: IChatWelcomeMessageContent[]; - readonly sampleQuestions: IChatFollowup[]; + readonly sampleQuestions: IChatReplyFollowup[]; readonly username: string; readonly avatarIconUri?: URI; @@ -808,7 +794,7 @@ export class ChatWelcomeMessageModel implements IChatWelcomeMessageModel { constructor( private readonly session: ChatModel, public readonly content: IChatWelcomeMessageContent[], - public readonly sampleQuestions: IChatFollowup[] + public readonly sampleQuestions: IChatReplyFollowup[] ) { this._id = 'welcome_' + ChatWelcomeMessageModel.nextId++; } diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index bf5c78b9ef2..9a9c1196e72 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -9,7 +9,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { Command, Location, ProviderResult } from 'vs/editor/common/languages'; +import { Location, ProviderResult } from 'vs/editor/common/languages'; import { FileType } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IChatAgentCommand, IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents'; @@ -142,11 +142,6 @@ export interface IChatAgentMarkdownContentWithVulnerability { kind: 'markdownVuln'; } -export interface IChatCommandButton { - command: Command; - kind: 'command'; -} - export type IChatProgress = | IChatContent | IChatMarkdownContent @@ -157,21 +152,30 @@ export type IChatProgress = | IChatContentReference | IChatContentInlineReference | IChatAgentDetection - | IChatProgressMessage - | IChatCommandButton; + | IChatProgressMessage; export interface IChatProvider { readonly id: string; prepareSession(token: CancellationToken): ProviderResult; } -export interface IChatFollowup { +export interface IChatReplyFollowup { kind: 'reply'; message: string; title?: string; tooltip?: string; } +export interface IChatResponseCommandFollowup { + kind: 'command'; + commandId: string; + args?: any[]; + title: string; // supports codicon strings + when?: string; +} + +export type IChatFollowup = IChatReplyFollowup | IChatResponseCommandFollowup; + // Name has to match the one in vscode.d.ts for some reason export enum InteractiveSessionVoteDirection { Down = 0, @@ -214,12 +218,12 @@ export interface IChatTerminalAction { export interface IChatCommandAction { kind: 'command'; - commandButton: IChatCommandButton; + command: IChatResponseCommandFollowup; } export interface IChatFollowupAction { kind: 'followUp'; - followup: IChatFollowup; + followup: IChatReplyFollowup; } export interface IChatBugReportAction { diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 5d52f07555b..c55c438f228 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -235,8 +235,8 @@ export class ChatService extends Disposable implements IChatService { newFile: !!action.action.newFile }); } else if (action.action.kind === 'command') { - const command = CommandsRegistry.getCommand(action.action.commandButton.command.id); - const commandId = command ? action.action.commandButton.command.id : 'INVALID'; + const command = CommandsRegistry.getCommand(action.action.command.commandId); + const commandId = command ? action.action.command.commandId : 'INVALID'; this.telemetryService.publicLog2('interactiveSessionCommand', { providerId: action.providerId, commandId diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index d55a8b0c07f..e3712490d9a 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -11,7 +11,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IChatAgentCommand, IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatModelInitState, IChatModel, IChatRequestModel, IChatResponseModel, IChatWelcomeMessageContent, IResponse } from 'vs/workbench/contrib/chat/common/chatModel'; import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserTypes'; -import { IChatContentReference, IChatProgressMessage, IChatFollowup, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatUsedContext, InteractiveSessionVoteDirection, IChatCommandButton } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatContentReference, IChatProgressMessage, IChatReplyFollowup, IChatResponseCommandFollowup, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatUsedContext, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; export function isRequestVM(item: unknown): item is IChatRequestViewModel { @@ -60,7 +60,7 @@ export interface IChatRequestViewModel { readonly dataId: string; readonly username: string; readonly avatarIconUri?: URI; - readonly message: IParsedChatRequest | IChatFollowup; + readonly message: IParsedChatRequest | IChatReplyFollowup; readonly messageText: string; currentRenderedHeight: number | undefined; } @@ -88,7 +88,7 @@ export interface IChatProgressMessageRenderData { isLast: boolean; } -export type IChatRenderData = IChatResponseProgressFileTreeData | IChatResponseMarkdownRenderData | IChatProgressMessageRenderData | IChatCommandButton; +export type IChatRenderData = IChatResponseProgressFileTreeData | IChatResponseMarkdownRenderData | IChatProgressMessageRenderData; export interface IChatResponseRenderData { renderedParts: IChatRenderData[]; } @@ -118,9 +118,9 @@ export interface IChatResponseViewModel { readonly progressMessages: ReadonlyArray; readonly isComplete: boolean; readonly isCanceled: boolean; - readonly isStale: boolean; readonly vote: InteractiveSessionVoteDirection | undefined; - readonly replyFollowups?: IChatFollowup[]; + readonly replyFollowups?: IChatReplyFollowup[]; + readonly commandFollowups?: IChatResponseCommandFollowup[]; readonly errorDetails?: IChatResponseErrorDetails; readonly contentUpdateTimings?: IChatLiveUpdateData; renderData?: IChatResponseRenderData; @@ -331,7 +331,11 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi } get replyFollowups() { - return this._model.followups?.filter((f): f is IChatFollowup => f.kind === 'reply'); + return this._model.followups?.filter((f): f is IChatReplyFollowup => f.kind === 'reply'); + } + + get commandFollowups() { + return this._model.followups?.filter((f): f is IChatResponseCommandFollowup => f.kind === 'command'); } get errorDetails() { @@ -346,10 +350,6 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi return this._model.requestId; } - get isStale() { - return this._model.isStale; - } - renderData: IChatResponseRenderData | undefined = undefined; agentAvatarHasBeenRendered?: boolean; currentRenderedHeight: number | undefined; @@ -436,6 +436,6 @@ export interface IChatWelcomeMessageViewModel { readonly username: string; readonly avatarIconUri?: URI; readonly content: IChatWelcomeMessageContent[]; - readonly sampleQuestions: IChatFollowup[]; + readonly sampleQuestions: IChatReplyFollowup[]; currentRenderedHeight?: number; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 15f98cccce7..8f730b52fe2 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -3,65 +3,66 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Dimension, addDisposableListener, getActiveElement, getTotalHeight, getTotalWidth, h, reset } from 'vs/base/browser/dom'; -import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; -import * as aria from 'vs/base/browser/ui/aria/aria'; -import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; -import { Emitter, Event, MicrotaskEmitter } from 'vs/base/common/event'; -import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; -import { Lazy } from 'vs/base/common/lazy'; -import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { assertType } from 'vs/base/common/types'; -import { URI } from 'vs/base/common/uri'; import 'vs/css!./inlineChat'; -import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; +import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IActiveCodeEditor, ICodeEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; -import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions'; -import { LineRange } from 'vs/editor/common/core/lineRange'; -import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; -import { LanguageSelector } from 'vs/editor/common/languageSelector'; -import { CompletionItem, CompletionItemInsertTextRule, CompletionItemKind, CompletionItemProvider, CompletionList, ProviderResult } from 'vs/editor/common/languages'; -import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; -import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { IModelService } from 'vs/editor/common/services/model'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; -import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; -import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; import { localize } from 'vs/nls'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { IWorkbenchButtonBarOptions, MenuWorkbenchButtonBar } from 'vs/platform/actions/browser/buttonbar'; -import { HiddenItemStrategy, MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; -import { MenuId } from 'vs/platform/actions/common/actions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { FileKind } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { ILogService } from 'vs/platform/log/common/log'; -import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry'; -import { ResourceLabel } from 'vs/workbench/browser/labels'; +import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; +import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_INPUT, MENU_INLINE_CHAT_WIDGET_STATUS, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, IInlineChatSlashCommand, MENU_INLINE_CHAT_WIDGET_FEEDBACK, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_INNER_CURSOR_END, CTX_INLINE_CHAT_RESPONSE_FOCUSED, ACTION_ACCEPT_CHANGES, MENU_INLINE_CHAT_WIDGET } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { Dimension, addDisposableListener, getActiveElement, getTotalHeight, getTotalWidth, h, reset } from 'vs/base/browser/dom'; +import { Emitter, Event, MicrotaskEmitter } from 'vs/base/common/event'; +import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; +import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; +import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; +import { IModelService } from 'vs/editor/common/services/model'; +import { URI } from 'vs/base/common/uri'; +import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { HiddenItemStrategy, MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; +import { Position } from 'vs/editor/common/core/position'; import { DEFAULT_FONT_FAMILY } from 'vs/workbench/browser/style'; +import { CompletionItem, CompletionItemInsertTextRule, CompletionItemKind, CompletionItemProvider, CompletionList, ProviderResult } from 'vs/editor/common/languages'; +import { ResourceLabel } from 'vs/workbench/browser/labels'; +import { FileKind } from 'vs/platform/files/common/files'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { LanguageSelector } from 'vs/editor/common/languageSelector'; +import { asRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; +import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { ExpansionState, HunkData } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import * as aria from 'vs/base/browser/ui/aria/aria'; +import { IWorkbenchButtonBarOptions, MenuWorkbenchButtonBar } from 'vs/platform/actions/browser/buttonbar'; +import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; -import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups'; -import { ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer'; +import { assertType } from 'vs/base/common/types'; +import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; +import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; -import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget'; -import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { ChatModel, ChatResponseModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { editorForeground, inputBackground, editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { Lazy } from 'vs/base/common/lazy'; import { ChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; -import { ExpansionState, HunkData } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; -import { asRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; -import { ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_END, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_VISIBLE, IInlineChatFollowup, IInlineChatSlashCommand, MENU_INLINE_CHAT_INPUT, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_FEEDBACK, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, MENU_INLINE_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { ChatModel, ChatResponseModel } from 'vs/workbench/contrib/chat/common/chatModel'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelegate } from 'vs/workbench/contrib/chat/browser/chatListRenderer'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups'; const defaultAriaLabel = localize('aria-label', "Inline Chat Input"); @@ -630,9 +631,9 @@ export class InlineChatWidget { return resultingAppender; } - updateFollowUps(items: IInlineChatFollowup[], onFollowup: (followup: IInlineChatFollowup) => void): void; + updateFollowUps(items: IChatFollowup[], onFollowup: (followup: IChatFollowup) => void): void; updateFollowUps(items: undefined): void; - updateFollowUps(items: IInlineChatFollowup[] | undefined, onFollowup?: ((followup: IInlineChatFollowup) => void)) { + updateFollowUps(items: IChatFollowup[] | undefined, onFollowup?: ((followup: IChatFollowup) => void)) { this._followUpDisposables.clear(); this._elements.followUps.classList.toggle('hidden', !items || items.length === 0); reset(this._elements.followUps); diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 8b1d17efa8e..0794a0f8a15 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; -import { Event } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IRange } from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; +import { Event } from 'vs/base/common/event'; import { ProviderResult, TextEdit, WorkspaceEdit } from 'vs/editor/common/languages'; import { ITextModel } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; @@ -20,6 +20,7 @@ import { IProgress } from 'vs/platform/progress/common/progress'; import { Registry } from 'vs/platform/registry/common/platform'; import { diffInserted, diffRemoved, editorHoverHighlight, editorWidgetBackground, editorWidgetBorder, focusBorder, inputBackground, inputPlaceholderForeground, registerColor, transparent, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { Extensions as ExtensionsMigration, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; +import { IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; import { URI } from 'vs/base/common/uri'; export interface IInlineChatSlashCommand { @@ -97,23 +98,6 @@ export const enum InlineChatResponseFeedbackKind { Bug = 4 } -export interface IInlineChatReplyFollowup { - kind: 'reply'; - message: string; - title?: string; - tooltip?: string; -} - -export interface IInlineChatCommandFollowup { - kind: 'command'; - commandId: string; - args?: any[]; - title: string; // supports codicon strings - when?: string; -} - -export type IInlineChatFollowup = IInlineChatReplyFollowup | IInlineChatCommandFollowup; - export interface IInlineChatSessionProvider { debugName: string; @@ -124,7 +108,7 @@ export interface IInlineChatSessionProvider { provideResponse(item: IInlineChatSession, request: IInlineChatRequest, progress: IProgress, token: CancellationToken): ProviderResult; - provideFollowups?(session: IInlineChatSession, response: IInlineChatResponse, token: CancellationToken): ProviderResult; + provideFollowups?(session: IInlineChatSession, response: IInlineChatResponse, token: CancellationToken): ProviderResult; handleInlineChatResponseFeedback?(session: IInlineChatSession, response: IInlineChatResponse, kind: InlineChatResponseFeedbackKind): void; } diff --git a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts index bfc83f254df..b7cda17484a 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2.d.ts @@ -150,10 +150,19 @@ declare module 'vscode' { provideSubCommands(token: CancellationToken): ProviderResult; } + // TODO@API This should become a progress type, and use vscode.Command + // TODO@API what's the when-property for? how about not returning it in the first place? + export interface ChatAgentCommandFollowup { + commandId: string; + args?: any[]; + title: string; // supports codicon strings + when?: string; + } + /** * A followup question suggested by the model. */ - export interface ChatAgentFollowup { + export interface ChatAgentReplyFollowup { /** * The message to send to the chat. */ @@ -170,6 +179,8 @@ declare module 'vscode' { title?: string; } + export type ChatAgentFollowup = ChatAgentCommandFollowup | ChatAgentReplyFollowup; + /** * Will be invoked once after each request to get suggested followup questions to show the user. The user can click the followup to send it to the chat. */ @@ -276,8 +287,6 @@ declare module 'vscode' { anchor(value: Uri | Location, title?: string): ChatAgentResponseStream; - button(command: Command): ChatAgentResponseStream; - // TODO@API this influences the rendering, it inserts new lines which is likely a bug progress(value: string): ChatAgentResponseStream; @@ -338,8 +347,7 @@ declare module 'vscode' { export type ChatAgentContentProgress = | ChatAgentContent | ChatAgentFileTree - | ChatAgentInlineContentReference - | ChatAgentCommandButton; + | ChatAgentInlineContentReference; /** * @deprecated use ChatAgentResponseStream instead @@ -386,13 +394,6 @@ declare module 'vscode' { title?: string; } - /** - * Displays a {@link Command command} as a button in the chat response. - */ - export interface ChatAgentCommandButton { - command: Command; - } - /** * A piece of the chat response's content. Will be merged with other progress pieces as needed, and rendered as markdown. */ diff --git a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts index b2795ef4b3a..c56dfc45c89 100644 --- a/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatAgents2Additions.d.ts @@ -113,7 +113,7 @@ declare module 'vscode' { export interface ChatAgentCommandAction { // eslint-disable-next-line local/vscode-dts-string-type-literals kind: 'command'; - commandButton: ChatAgentCommandButton; + command: ChatAgentCommandFollowup; } export interface ChatAgentSessionFollowupAction { diff --git a/src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts b/src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts index e40a66c044c..333783f844a 100644 --- a/src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts +++ b/src/vscode-dts/vscode.proposed.defaultChatAgent.d.ts @@ -9,7 +9,7 @@ declare module 'vscode' { export interface ChatAgentWelcomeMessageProvider { provideWelcomeMessage(token: CancellationToken): ProviderResult; - provideSampleQuestions?(token: CancellationToken): ProviderResult; + provideSampleQuestions?(token: CancellationToken): ProviderResult; } export interface ChatAgent2 { diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index 5cfa6c0741e..fd8dc51d014 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -121,7 +121,7 @@ declare module 'vscode' { inputPlaceholder?: string; } - export type InteractiveWelcomeMessageContent = string | MarkdownString | ChatAgentFollowup[]; + export type InteractiveWelcomeMessageContent = string | MarkdownString | ChatAgentReplyFollowup[]; export interface InteractiveSessionProvider { prepareSession(token: CancellationToken): ProviderResult;