Reserve focus for cell chat widget. (#207000)

This commit is contained in:
Peng Lyu 2024-03-06 16:13:47 -08:00 committed by GitHub
parent 59ec734843
commit c8024cf91f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 73 additions and 19 deletions

View file

@ -40,7 +40,7 @@ import { asProgressiveEdit, performAsyncTextEdit } from 'vs/workbench/contrib/in
import { CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, EditMode, IInlineChatProgressItem, IInlineChatRequest, InlineChatResponseFeedbackKind, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { insertCell, runDeleteAction } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations';
import { CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST, CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION, CTX_NOTEBOOK_CHAT_USER_DID_EDIT, MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_FEEDBACK, MENU_CELL_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext';
import { INotebookEditor, INotebookEditorContribution, INotebookViewZone, ScrollToRevealBehavior } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { INotebookEditor, INotebookEditorContribution, INotebookViewZone } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
@ -96,21 +96,35 @@ class NotebookChatWidget extends Disposable implements INotebookViewZone {
this._layoutWidget(inlineChatWidget, widgetContainer);
}
hasFocus() {
return this.inlineChatWidget.hasFocus();
}
focus() {
this.updateNotebookEditorFocusNSelections();
this.inlineChatWidget.focus();
}
updateNotebookEditorFocusNSelections() {
this._notebookEditor.focusContainer(true);
this._notebookEditor.setFocus({ start: this.afterModelPosition, end: this.afterModelPosition });
this._notebookEditor.setSelections([{
start: this.afterModelPosition,
end: this.afterModelPosition
}]);
}
getEditingCell() {
return this._editingCell;
}
async getOrCreateEditingCell(): Promise<{ cell: CellViewModel; editor: IActiveCodeEditor } | undefined> {
if (this._editingCell) {
await this._notebookEditor.focusNotebookCell(this._editingCell, 'editor');
if (this._notebookEditor.activeCodeEditor?.hasModel()) {
const codeEditor = this._notebookEditor.codeEditors.find(ce => ce[0] === this._editingCell)?.[1];
if (codeEditor?.hasModel()) {
return {
cell: this._editingCell,
editor: this._notebookEditor.activeCodeEditor
editor: codeEditor
};
} else {
return undefined;
@ -121,17 +135,25 @@ class NotebookChatWidget extends Disposable implements INotebookViewZone {
return undefined;
}
const widgetHasFocus = this.inlineChatWidget.hasFocus();
this._editingCell = insertCell(this._languageService, this._notebookEditor, this.afterModelPosition, CellKind.Code, 'above');
if (!this._editingCell) {
return undefined;
}
await this._notebookEditor.focusNotebookCell(this._editingCell, 'editor', { revealBehavior: ScrollToRevealBehavior.firstLine });
if (this._notebookEditor.activeCodeEditor?.hasModel()) {
await this._notebookEditor.revealFirstLineIfOutsideViewport(this._editingCell);
if (widgetHasFocus) {
this.focus();
}
const codeEditor = this._notebookEditor.codeEditors.find(ce => ce[0] === this._editingCell)?.[1];
if (codeEditor?.hasModel()) {
return {
cell: this._editingCell,
editor: this._notebookEditor.activeCodeEditor
editor: codeEditor
};
}
@ -390,12 +412,7 @@ export class NotebookChatController extends Disposable implements INotebookEdito
return;
}
this._notebookEditor.focusContainer(true);
this._notebookEditor.setFocus({ start: this._widget.afterModelPosition, end: this._widget.afterModelPosition });
this._notebookEditor.setSelections([{
start: this._widget.afterModelPosition,
end: this._widget.afterModelPosition
}]);
this._widget.updateNotebookEditorFocusNSelections();
}
async acceptInput() {
@ -739,6 +756,10 @@ export class NotebookChatController extends Disposable implements INotebookEdito
await this._notebookEditor.focusNotebookCell(cell, 'editor');
}
hasFocus() {
return this._widget?.hasFocus() ?? false;
}
focus() {
this._focusWidget();
}
@ -769,6 +790,10 @@ export class NotebookChatController extends Disposable implements INotebookEdito
this._activeRequestCts?.cancel();
}
getEditingCell() {
return this._widget?.getEditingCell();
}
discard() {
this._strategy?.cancel();
this._activeRequestCts?.cancel();

View file

@ -20,6 +20,8 @@ import { IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
import { CTX_INLINE_CHAT_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { insertCell } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations';
import { CTX_NOTEBOOK_CELL_CHAT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext';
import { NotebookChatController } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController';
import { CELL_TITLE_CELL_GROUP_ID, CellToolbarOrder, INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext, INotebookCommandContext, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, NotebookAction, NotebookCellAction, NotebookMultiCellAction, cellExecutionArgs, executeNotebookCondition, getContextFromActiveEditor, getContextFromUri, parseMultiCellExecutionArgs } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, IFocusNotebookCellOptions, ScrollToRevealBehavior } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons';
@ -198,7 +200,10 @@ registerAction2(class ExecuteCell extends NotebookMultiCellAction {
precondition: executeThisCellCondition,
title: localize('notebookActions.execute', "Execute Cell"),
keybinding: {
when: NOTEBOOK_CELL_LIST_FOCUSED,
when: ContextKeyExpr.or(
NOTEBOOK_CELL_LIST_FOCUSED,
ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED)
),
primary: KeyMod.WinCtrl | KeyCode.Enter,
win: {
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter
@ -229,6 +234,21 @@ registerAction2(class ExecuteCell extends NotebookMultiCellAction {
await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });
}
const chatController = NotebookChatController.get(context.notebookEditor);
const editingCell = chatController?.getEditingCell();
if (chatController?.hasFocus() && editingCell) {
const group = editorGroupsService.activeGroup;
if (group) {
if (group.activeEditor) {
group.pinEditor(group.activeEditor);
}
}
await context.notebookEditor.executeNotebookCells([editingCell]);
return;
}
await runCell(editorGroupsService, context);
}
});

View file

@ -629,6 +629,11 @@ export interface INotebookEditor {
*/
revealInCenterIfOutsideViewport(cell: ICellViewModel): Promise<void>;
/**
* Reveal the first line of the cell into the view if the cell is outside of the viewport.
*/
revealFirstLineIfOutsideViewport(cell: ICellViewModel): Promise<void>;
/**
* Reveal a line in notebook cell into viewport with minimal scrolling.
*/

View file

@ -2122,8 +2122,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
await this._list.revealCell(cell, CellRevealType.CenterIfOutsideViewport);
}
revealFirstLineIfOutsideViewport(cell: ICellViewModel) {
this._list.revealCell(cell, CellRevealType.FirstLineIfOutsideViewport);
async revealFirstLineIfOutsideViewport(cell: ICellViewModel) {
await this._list.revealCell(cell, CellRevealType.FirstLineIfOutsideViewport);
}
async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise<void> {
@ -2446,7 +2446,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
this._cursorNavMode.set(true);
await this.revealInView(cell);
} else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) {
this.revealFirstLineIfOutsideViewport(cell);
await this.revealFirstLineIfOutsideViewport(cell);
} else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) {
await this.revealInView(cell);
} else {

View file

@ -925,8 +925,12 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
break;
}
// wait for the editor to be created only if the cell is in editing mode (meaning it has an editor and will focus the editor)
if (cell.getEditState() === CellEditState.Editing && !cell.editorAttached) {
if ((
// wait for the editor to be created if the cell is in editing mode
cell.getEditState() === CellEditState.Editing
// wait for the editor to be created if we are revealing the first line of the cell
|| revealType === CellRevealType.FirstLineIfOutsideViewport
) && !cell.editorAttached) {
return getEditorAttachedPromise(cell);
}