Merge pull request #207499 from microsoft/merogge/buttons

render terminal chat actions as buttons rather than in the toolbar
This commit is contained in:
Megan Rogge 2024-03-13 10:20:27 -07:00 committed by GitHub
commit 4d3c5598d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 36 additions and 32 deletions

View file

@ -88,8 +88,7 @@ export function registerChatCodeBlockActions() {
icon: Codicon.copy,
menu: {
id: MenuId.ChatCodeBlock,
group: 'navigation',
order: 1
group: 'navigation'
}
});
}
@ -189,8 +188,7 @@ export function registerChatCodeBlockActions() {
menu: {
id: MenuId.ChatCodeBlock,
group: 'navigation',
when: CONTEXT_IN_CHAT_SESSION,
order: 2
when: CONTEXT_IN_CHAT_SESSION
},
keybinding: {
when: ContextKeyExpr.or(ContextKeyExpr.and(CONTEXT_IN_CHAT_SESSION, CONTEXT_IN_CHAT_INPUT.negate()), accessibleViewInCodeBlock),

View file

@ -258,7 +258,7 @@ export class ChatViewModel extends Disposable implements IChatViewModel {
.forEach((item: ChatResponseViewModel) => item.dispose());
}
private updateCodeBlockTextModels(model: IChatRequestViewModel | IChatResponseViewModel) {
updateCodeBlockTextModels(model: IChatRequestViewModel | IChatResponseViewModel) {
const content = isRequestVM(model) ? model.messageText : model.response.asString();
const renderer = new marked.Renderer();

View file

@ -10,7 +10,7 @@ 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 } from 'vs/base/common/lifecycle';
import { DisposableStore, IReference, MutableDisposable } from 'vs/base/common/lifecycle';
import { ISettableObservable, constObservable, derived, observableValue } from 'vs/base/common/observable';
import 'vs/css!./inlineChat';
import { ICodeEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser';
@ -24,7 +24,7 @@ import { Range } from 'vs/editor/common/core/range';
import { DetailedLineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping';
import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService';
import { localize } from 'vs/nls';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IWorkbenchButtonBarOptions, MenuWorkbenchButtonBar } from 'vs/platform/actions/browser/buttonbar';
@ -46,7 +46,7 @@ import { ChatListItemRenderer, IChatListItemRendererOptions, IChatRendererDelega
import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions';
import { IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents';
import { ChatModel, ChatResponseModel } from 'vs/workbench/contrib/chat/common/chatModel';
import { ChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel';
import { ChatResponseViewModel, ChatViewModel, IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel';
import { CodeBlockModelCollection } from 'vs/workbench/contrib/chat/common/codeBlockModelCollection';
import { HunkData, HunkInformation, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession';
import { asRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils';
@ -170,6 +170,7 @@ export class InlineChatWidget {
private _chatMessage: MarkdownString | undefined;
private readonly _codeBlockModelCollection: CodeBlockModelCollection;
private _responseViewModel: IChatResponseViewModel | undefined;
constructor(
options: IInlineChatWidgetConstructionOptions,
@ -300,6 +301,14 @@ export class InlineChatWidget {
}
}
getCodeBlockInfo(codeBlockIndex: number): Promise<IReference<IResolvedTextEditorModel>> | undefined {
if (!this._responseViewModel) {
return;
}
return this._codeBlockModelCollection.get(this._responseViewModel.sessionId, this._responseViewModel, codeBlockIndex);
}
protected _doLayout(widgetDimension: Dimension, inputDimension: Dimension): void {
this._elements.progress.style.width = `${inputDimension.width}px`;
this._chatMessageContents.style.width = `${widgetDimension.width - 10}px`;
@ -374,6 +383,8 @@ export class InlineChatWidget {
updateChatMessage(message: IInlineChatMessage | undefined, isIncomplete?: boolean, isCodeBlockEditable?: boolean): IInlineChatMessageAppender | undefined {
this._chatMessageDisposables.clear();
this._codeBlockModelCollection.clear();
this._responseViewModel = undefined;
this._chatMessage = message ? new MarkdownString(message.message.value) : undefined;
const hasMessage = message?.message.value;
this._elements.chatMessage.classList.toggle('hidden', !hasMessage);
@ -382,7 +393,9 @@ export class InlineChatWidget {
if (hasMessage) {
const sessionModel = this._chatMessageDisposables.add(new ChatModel(message.providerId, undefined, this._logService, this._chatAgentService, this._instantiationService));
const responseModel = this._chatMessageDisposables.add(new ChatResponseModel(message.message, sessionModel, undefined, undefined, message.requestId, !isIncomplete, false, undefined));
const viewModel = this._chatMessageDisposables.add(new ChatResponseViewModel(responseModel, this._logService));
this._responseViewModel = this._chatMessageDisposables.add(new ChatResponseViewModel(responseModel, this._logService));
const chatViewModel = this._chatMessageDisposables.add(this._instantiationService.createInstance(ChatViewModel, sessionModel, this._codeBlockModelCollection));
chatViewModel.updateCodeBlockTextModels(this._responseViewModel);
const renderOptions: IChatListItemRendererOptions = { renderStyle: 'compact', noHeader: true, noPadding: true, editableCodeBlock: isCodeBlockEditable ?? false };
const chatRendererDelegate: IChatRendererDelegate = { getListLength() { return 1; } };
const renderer = this._chatMessageDisposables.add(this._instantiationService.createInstance(ChatListItemRenderer, this._editorOptions, renderOptions, chatRendererDelegate, this._codeBlockModelCollection, undefined));
@ -394,7 +407,7 @@ export class InlineChatWidget {
const template = renderer.renderTemplate(this._chatMessageContents);
this._chatMessageDisposables.add(template.elementDisposables);
this._chatMessageDisposables.add(template.templateDisposables);
renderer.renderChatTreeItem(viewModel, 0, template);
renderer.renderChatTreeItem(this._responseViewModel, 0, template);
this._chatMessageDisposables.add(renderer.onDidChangeItemHeight(() => this._onDidChangeHeight.fire()));
resultingAppender = isIncomplete ? {
@ -504,7 +517,6 @@ export class InlineChatWidget {
this.updateInfo('');
this._elements.accessibleViewer.classList.toggle('hidden', true);
this._onDidChangeHeight.fire();
}

View file

@ -6,7 +6,6 @@
import { Codicon } from 'vs/base/common/codicons';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { localize2 } from 'vs/nls';
import { MenuId } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { TerminalSettingId } from 'vs/platform/terminal/common/terminal';
@ -165,9 +164,10 @@ registerActiveXtermAction({
primary: KeyMod.CtrlCmd | KeyCode.Enter,
},
menu: {
// TODO: Allow action to be made primary, the action list is hardcoded within InlineChatWidget
id: MENU_TERMINAL_CHAT_WIDGET_STATUS,
group: '0_main',
order: 0,
id: MenuId.ChatCodeBlock,
group: 'navigation',
when: ContextKeyExpr.and(TerminalChatContextKeys.responseContainsCodeBlock, TerminalChatContextKeys.requestActive.negate())
},
run: (_xterm, _accessor, activeInstance) => {
@ -196,9 +196,9 @@ registerActiveXtermAction({
primary: KeyMod.Alt | KeyCode.Enter,
},
menu: {
id: MenuId.ChatCodeBlock,
group: 'navigation',
isHiddenByDefault: true,
id: MENU_TERMINAL_CHAT_WIDGET_STATUS,
group: '0_main',
order: 1,
when: ContextKeyExpr.and(TerminalChatContextKeys.responseContainsCodeBlock, TerminalChatContextKeys.requestActive.negate())
},
run: (_xterm, _accessor, activeInstance) => {

View file

@ -347,8 +347,12 @@ export class TerminalChatController extends Disposable implements ITerminalContr
return !!this._chatWidget?.value.hasFocus();
}
acceptCommand(shouldExecute: boolean): void {
this._chatWidget?.value.acceptCommand(shouldExecute);
async acceptCommand(shouldExecute: boolean): Promise<void> {
const code = await this.chatWidget?.inlineChatWidget.getCodeBlockInfo(0);
if (!code) {
return;
}
this._chatWidget?.value.acceptCommand(code.object.textEditorModel.getValue(), shouldExecute);
}
reveal(): void {

View file

@ -6,7 +6,6 @@
import { Dimension, IFocusTracker, trackFocus } from 'vs/base/browser/dom';
import { Disposable } from 'vs/base/common/lifecycle';
import 'vs/css!./media/terminalChatWidget';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { localize } from 'vs/nls';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@ -31,8 +30,7 @@ export class TerminalChatWidget extends Disposable {
terminalElement: HTMLElement,
private readonly _instance: ITerminalInstance,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService
@IContextKeyService private readonly _contextKeyService: IContextKeyService
) {
super();
@ -127,19 +125,11 @@ export class TerminalChatWidget extends Disposable {
setValue(value?: string) {
this._inlineChatWidget.value = value ?? '';
}
acceptCommand(shouldExecute: boolean): void {
const editor = this._codeEditorService.getFocusedCodeEditor() || this._codeEditorService.getActiveCodeEditor();
if (!editor) {
return;
}
const model = editor.getModel();
if (!model) {
return;
}
const code = editor.getValue();
acceptCommand(code: string, shouldExecute: boolean): void {
this._instance.runCommand(code, shouldExecute);
this.hide();
}
updateProgress(progress?: IChatProgress): void {
this._inlineChatWidget.updateProgress(progress?.kind === 'content' || progress?.kind === 'markdownContent');
}