diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index ce52bf0f58dc7..7faf2dcf42f46 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -6,7 +6,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction2, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; @@ -42,6 +42,28 @@ export function isCodeBlockActionContext(thing: unknown): thing is IChatCodeBloc return typeof thing === 'object' && thing !== null && 'code' in thing && 'element' in thing; } +abstract class ChatCodeBlockAction extends Action2 { + run(accessor: ServicesAccessor, ...args: any[]) { + let context = args[0]; + if (!isCodeBlockActionContext(context)) { + const codeEditorService = accessor.get(ICodeEditorService); + const editor = codeEditorService.getFocusedCodeEditor() || codeEditorService.getActiveCodeEditor(); + if (!editor) { + return; + } + + context = getContextFromEditor(editor, accessor); + if (!isCodeBlockActionContext(context)) { + return; + } + } + + return this.runWithContext(accessor, context); + } + + abstract runWithContext(accessor: ServicesAccessor, context: IChatCodeBlockActionContext): any; +} + export function registerChatCodeBlockActions() { registerAction2(class CopyCodeBlockAction extends Action2 { constructor() { @@ -135,7 +157,7 @@ export function registerChatCodeBlockActions() { return false; }); - registerAction2(class InsertCodeBlockAction extends EditorAction2 { + registerAction2(class InsertCodeBlockAction extends ChatCodeBlockAction { constructor() { super({ id: 'workbench.action.chat.insertCodeBlock', @@ -153,15 +175,7 @@ export function registerChatCodeBlockActions() { }); } - override async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { - let context = args[0]; - if (!isCodeBlockActionContext(context)) { - context = getContextFromEditor(editor, accessor); - if (!isCodeBlockActionContext(context)) { - return; - } - } - + override async runWithContext(accessor: ServicesAccessor, context: IChatCodeBlockActionContext) { const editorService = accessor.get(IEditorService); const textFileService = accessor.get(ITextFileService); @@ -220,12 +234,15 @@ export function registerChatCodeBlockActions() { private async handleTextEditor(accessor: ServicesAccessor, codeEditor: ICodeEditor, activeModel: ITextModel, context: IChatCodeBlockActionContext) { this.notifyUserAction(accessor, context); const bulkEditService = accessor.get(IBulkEditService); + const codeEditorService = accessor.get(ICodeEditorService); const activeSelection = codeEditor.getSelection() ?? new Range(activeModel.getLineCount(), 1, activeModel.getLineCount(), 1); await bulkEditService.apply([new ResourceTextEdit(activeModel.uri, { range: activeSelection, text: context.code, })]); + + codeEditorService.listCodeEditors().find(editor => editor.getModel()?.uri.toString() === activeModel.uri.toString())?.focus(); } private notifyUserAction(accessor: ServicesAccessor, context: IChatCodeBlockActionContext) { @@ -243,7 +260,7 @@ export function registerChatCodeBlockActions() { }); - registerAction2(class InsertIntoNewFileAction extends EditorAction2 { + registerAction2(class InsertIntoNewFileAction extends ChatCodeBlockAction { constructor() { super({ id: 'workbench.action.chat.insertIntoNewFile', @@ -262,15 +279,7 @@ export function registerChatCodeBlockActions() { }); } - override async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { - let context = args[0]; - if (!isCodeBlockActionContext(context)) { - context = getContextFromEditor(editor, accessor); - if (!isCodeBlockActionContext(context)) { - return; - } - } - + override async runWithContext(accessor: ServicesAccessor, context: IChatCodeBlockActionContext) { const editorService = accessor.get(IEditorService); const chatService = accessor.get(IChatService); editorService.openEditor({ contents: context.code, languageId: context.languageId, resource: undefined }); @@ -288,7 +297,7 @@ export function registerChatCodeBlockActions() { } }); - registerAction2(class RunInTerminalAction extends EditorAction2 { + registerAction2(class RunInTerminalAction extends ChatCodeBlockAction { constructor() { super({ id: 'workbench.action.chat.runInTerminal', @@ -314,15 +323,7 @@ export function registerChatCodeBlockActions() { }); } - override async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { - let context = args[0]; - if (!isCodeBlockActionContext(context)) { - context = getContextFromEditor(editor, accessor); - if (!isCodeBlockActionContext(context)) { - return; - } - } - + override async runWithContext(accessor: ServicesAccessor, context: IChatCodeBlockActionContext) { const chatService = accessor.get(IChatService); const terminalService = accessor.get(ITerminalService); const editorService = accessor.get(IEditorService); diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index 827e722a1fd0a..d28651f82deb1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -11,6 +11,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ILogService } from 'vs/platform/log/common/log'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -58,6 +59,7 @@ export class ChatViewPane extends ViewPane implements IChatViewPane { @ITelemetryService telemetryService: ITelemetryService, @IStorageService private readonly storageService: IStorageService, @IChatService private readonly chatService: IChatService, + @ILogService private readonly logService: ILogService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); @@ -79,26 +81,31 @@ export class ChatViewPane extends ViewPane implements IChatViewPane { } protected override renderBody(parent: HTMLElement): void { - super.renderBody(parent); - - const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])); - - this._widget = this._register(scopedInstantiationService.createInstance( - ChatWidget, - { viewId: this.id }, - { - listForeground: SIDE_BAR_FOREGROUND, - listBackground: this.getBackgroundColor(), - inputEditorBackground: this.getBackgroundColor(), - resultEditorBackground: editorBackground + try { + super.renderBody(parent); + + const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])); + + this._widget = this._register(scopedInstantiationService.createInstance( + ChatWidget, + { viewId: this.id }, + { + listForeground: SIDE_BAR_FOREGROUND, + listBackground: this.getBackgroundColor(), + inputEditorBackground: this.getBackgroundColor(), + resultEditorBackground: editorBackground + })); + this._register(this.onDidChangeBodyVisibility(visible => { + this._widget.setVisible(visible); })); - this._register(this.onDidChangeBodyVisibility(visible => { - this._widget.setVisible(visible); - })); - this._widget.render(parent); + this._widget.render(parent); - const initialModel = this.viewState.sessionId ? this.chatService.getOrRestoreSession(this.viewState.sessionId) : undefined; - this.updateModel(initialModel); + const initialModel = this.viewState.sessionId ? this.chatService.getOrRestoreSession(this.viewState.sessionId) : undefined; + this.updateModel(initialModel); + } catch (e) { + this.logService.error(e); + throw e; + } } acceptInput(query?: string): void { diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index cdf2275866820..e74309e561d71 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -260,6 +260,7 @@ export class ChatWidget extends Disposable implements IChatWidget { [this.renderer], { identityProvider: { getId: (e: InteractiveTreeItem) => e.id }, + horizontalScrolling: false, supportDynamicHeights: true, hideTwistiesOfChildlessElements: true, accessibilityProvider: new ChatAccessibilityProvider(),