Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support cmd+arrow/enter when focus is outside of chat widget. #205260

Merged
merged 1 commit into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController';
import { CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext';
import { INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, findTargetCellEditor } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
import { CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon';
Expand Down Expand Up @@ -196,12 +197,18 @@ registerAction2(class extends NotebookAction {
super({
id: NOTEBOOK_FOCUS_TOP,
title: localize('focusFirstCell', 'Focus First Cell'),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
primary: KeyMod.CtrlCmd | KeyCode.Home,
mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow },
weight: KeybindingWeight.WorkbenchContrib
},
keybinding: [
{
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
primary: KeyMod.CtrlCmd | KeyCode.Home,
weight: KeybindingWeight.WorkbenchContrib
},
{
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('')),
mac: { primary: KeyMod.CtrlCmd | KeyCode.UpArrow },
weight: KeybindingWeight.WorkbenchContrib
}
],
});
}

Expand All @@ -221,12 +228,19 @@ registerAction2(class extends NotebookAction {
super({
id: NOTEBOOK_FOCUS_BOTTOM,
title: localize('focusLastCell', 'Focus Last Cell'),
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
primary: KeyMod.CtrlCmd | KeyCode.End,
mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow },
weight: KeybindingWeight.WorkbenchContrib
},
keybinding: [
{
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)),
primary: KeyMod.CtrlCmd | KeyCode.End,
mac: undefined,
weight: KeybindingWeight.WorkbenchContrib
},
{
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('')),
mac: { primary: KeyMod.CtrlCmd | KeyCode.DownArrow },
weight: KeybindingWeight.WorkbenchContrib
}
],
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkey
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChatResponseFeedbackKind, InlineChatResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat';
import { CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST, 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 { 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 { NotebookChatController } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController';
import { INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, getEditorFromArgsOrActivePane } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
import { CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
Expand Down Expand Up @@ -232,6 +232,15 @@ registerAction2(class extends NotebookAction {
when: ContextKeyExpr.and(CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_INLINE_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_USER_DID_EDIT),
weight: KeybindingWeight.EditorCore + 10,
primary: KeyCode.Escape
},
{
when: ContextKeyExpr.and(
NOTEBOOK_EDITOR_FOCUSED,
ContextKeyExpr.not(InputFocusedContextKey),
CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('below')
),
primary: KeyMod.CtrlCmd | KeyCode.Enter,
weight: KeybindingWeight.WorkbenchContrib
}
],
menu: [
Expand Down Expand Up @@ -483,3 +492,82 @@ MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
ContextKeyExpr.equals(`config.${NotebookSetting.cellChat}`, true)
)
});

registerAction2(class extends NotebookAction {
constructor() {
super({
id: 'notebook.cell.chat.focus',
title: localize('focusNotebookChat', 'Focus Chat'),
keybinding: [
{
when: ContextKeyExpr.and(
NOTEBOOK_EDITOR_FOCUSED,
ContextKeyExpr.not(InputFocusedContextKey),
CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('above')
),
primary: KeyMod.CtrlCmd | KeyCode.DownArrow,
weight: KeybindingWeight.WorkbenchContrib
},
{
when: ContextKeyExpr.and(
NOTEBOOK_EDITOR_FOCUSED,
ContextKeyExpr.not(InputFocusedContextKey),
CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('below')
),
primary: KeyMod.CtrlCmd | KeyCode.UpArrow,
weight: KeybindingWeight.WorkbenchContrib
}
],
});
}

async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
NotebookChatController.get(context.notebookEditor)?.focus();
}
});

registerAction2(class extends NotebookAction {
constructor() {
super({
id: 'notebook.cell.chat.focusNextCell',
title: localize('focusNextCell', 'Focus Next Cell'),
keybinding: [
{
when: ContextKeyExpr.and(
CTX_NOTEBOOK_CELL_CHAT_FOCUSED,
CTX_INLINE_CHAT_FOCUSED,
),
primary: KeyMod.CtrlCmd | KeyCode.DownArrow,
weight: KeybindingWeight.WorkbenchContrib
}
],
});
}

async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
NotebookChatController.get(context.notebookEditor)?.focusNext();
}
});

registerAction2(class extends NotebookAction {
constructor() {
super({
id: 'notebook.cell.chat.focusPreviousCell',
title: localize('focusPreviousCell', 'Focus Previous Cell'),
keybinding: [
{
when: ContextKeyExpr.and(
CTX_NOTEBOOK_CELL_CHAT_FOCUSED,
CTX_INLINE_CHAT_FOCUSED,
),
primary: KeyMod.CtrlCmd | KeyCode.UpArrow,
weight: KeybindingWeight.WorkbenchContrib
}
],
});
}

async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
NotebookChatController.get(context.notebookEditor)?.focusAbove();
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
export const CTX_NOTEBOOK_CELL_CHAT_FOCUSED = new RawContextKey<boolean>('notebookCellChatFocused', false, localize('notebookCellChatFocused', "Whether the cell chat editor is focused"));
export const CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST = new RawContextKey<boolean>('notebookChatHasActiveRequest', false, localize('notebookChatHasActiveRequest', "Whether the cell chat editor has an active request"));
export const CTX_NOTEBOOK_CHAT_USER_DID_EDIT = new RawContextKey<boolean>('notebookChatUserDidEdit', false, localize('notebookChatUserDidEdit', "Whether the user did changes ontop of the notebook cell chat"));
export const CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION = new RawContextKey<'above' | 'below' | ''>('notebookChatOuterFocusPosition', '', localize('notebookChatOuterFocusPosition', "Whether the focus of the notebook editor is above or below the cell chat"));

export const MENU_CELL_CHAT_INPUT = MenuId.for('cellChatInput');
export const MENU_CELL_CHAT_WIDGET = MenuId.for('cellChatWidget');
export const MENU_CELL_CHAT_WIDGET_STATUS = MenuId.for('cellChatWidget.status');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { IInlineChatMessageAppender, InlineChatWidget } from 'vs/workbench/contr
import { asProgressiveEdit, performAsyncTextEdit } from 'vs/workbench/contrib/inlineChat/browser/utils';
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_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 { 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 { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl';
Expand Down Expand Up @@ -180,6 +180,7 @@ export class NotebookChatController extends Disposable implements INotebookEdito
private readonly _ctxHasActiveRequest: IContextKey<boolean>;
private readonly _ctxCellWidgetFocused: IContextKey<boolean>;
private readonly _ctxUserDidEdit: IContextKey<boolean>;
private readonly _ctxOuterFocusPosition: IContextKey<'above' | 'below' | ''>;
private readonly _userEditingDisposables = this._register(new DisposableStore());
private readonly _ctxLastResponseType: IContextKey<undefined | InlineChatResponseType>;
private _widget: NotebookChatWidget | undefined;
Expand All @@ -203,6 +204,29 @@ export class NotebookChatController extends Disposable implements INotebookEdito
this._ctxCellWidgetFocused = CTX_NOTEBOOK_CELL_CHAT_FOCUSED.bindTo(this._contextKeyService);
this._ctxLastResponseType = CTX_INLINE_CHAT_LAST_RESPONSE_TYPE.bindTo(this._contextKeyService);
this._ctxUserDidEdit = CTX_NOTEBOOK_CHAT_USER_DID_EDIT.bindTo(this._contextKeyService);
this._ctxOuterFocusPosition = CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.bindTo(this._contextKeyService);

this._registerFocusTracker();
}

private _registerFocusTracker() {
this._register(this._notebookEditor.onDidChangeFocus(() => {
if (!this._widget) {
this._ctxOuterFocusPosition.set('');
return;
}

const widgetIndex = this._widget.afterModelPosition;
const focus = this._notebookEditor.getFocus().start;

if (focus + 1 === widgetIndex) {
this._ctxOuterFocusPosition.set('above');
} else if (focus === widgetIndex) {
this._ctxOuterFocusPosition.set('below');
} else {
this._ctxOuterFocusPosition.set('');
}
}));
}

run(index: number, input: string | undefined, autoSend: boolean | undefined): void {
Expand Down Expand Up @@ -672,6 +696,25 @@ export class NotebookChatController extends Disposable implements INotebookEdito
this.dismiss();
}

async focusAbove() {
if (!this._widget) {
return;
}

const index = this._widget.afterModelPosition;
const prev = index - 1;
if (prev < 0) {
return;
}

const cell = this._notebookEditor.cellAt(prev);
if (!cell) {
return;
}

await this._notebookEditor.focusNotebookCell(cell, 'editor');
}

async focusNext() {
if (!this._widget) {
return;
Expand All @@ -686,6 +729,10 @@ export class NotebookChatController extends Disposable implements INotebookEdito
await this._notebookEditor.focusNotebookCell(cell, 'editor');
}

focus() {
this._focusWidget();
}

focusNearestWidget(index: number, direction: 'above' | 'below') {
switch (direction) {
case 'above':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { INotebookActionContext, NotebookAction } from 'vs/workbench/contrib/not
import { NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_EDITOR_EDITABLE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl';
import { CellKind, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext';

const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertCodeCellAbove';
const INSERT_CODE_CELL_BELOW_COMMAND_ID = 'notebook.cell.insertCodeCellBelow';
Expand Down Expand Up @@ -110,7 +111,7 @@ registerAction2(class InsertCodeCellBelowAction extends InsertCellCommand {
title: localize('notebookActions.insertCodeCellBelow', "Insert Code Cell Below"),
keybinding: {
primary: KeyMod.CtrlCmd | KeyCode.Enter,
when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, InputFocusedContext.toNegated()),
when: ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, InputFocusedContext.toNegated(), CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION.isEqualTo('')),
weight: KeybindingWeight.WorkbenchContrib
},
menu: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ export interface INotebookEditor {
readonly onDidChangeViewCells: Event<INotebookViewCellsUpdateEvent>;
readonly onDidChangeVisibleRanges: Event<void>;
readonly onDidChangeSelection: Event<void>;
readonly onDidChangeFocus: Event<void>;
/**
* An event emitted when the model of this editor has changed.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
readonly onDidScroll: Event<void> = this._onDidScroll.event;
private readonly _onDidChangeActiveCell = this._register(new Emitter<void>());
readonly onDidChangeActiveCell: Event<void> = this._onDidChangeActiveCell.event;
private readonly _onDidChangeFocus = this._register(new Emitter<void>());
readonly onDidChangeFocus: Event<void> = this._onDidChangeFocus.event;
private readonly _onDidChangeSelection = this._register(new Emitter<void>());
readonly onDidChangeSelection: Event<void> = this._onDidChangeSelection.event;
private readonly _onDidChangeVisibleRanges = this._register(new Emitter<void>());
Expand Down Expand Up @@ -1010,6 +1012,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
this._register(this._list.onDidChangeFocus(_e => {
this._onDidChangeActiveEditor.fire(this);
this._onDidChangeActiveCell.fire();
this._onDidChangeFocus.fire();
this._cursorNavMode.set(false);
}));

Expand Down