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

Focus notebook output #97185

Merged
merged 10 commits into from May 8, 2020
81 changes: 55 additions & 26 deletions src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts
Expand Up @@ -4,24 +4,24 @@
*--------------------------------------------------------------------------------------------*/

import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { URI } from 'vs/base/common/uri';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { localize } from 'vs/nls';
import { Action2, IAction2Options, MenuId, MenuItemAction, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { InputFocusedContext, InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { BaseCellRenderTemplate, CellEditState, CellRunState, ICellViewModel, INotebookEditor, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_RUNNABLE, NOTEBOOK_CELL_TYPE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_EDITABLE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
import { BaseCellRenderTemplate, CellEditState, CellRunState, ICellViewModel, INotebookEditor, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_RUNNABLE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellKind, NOTEBOOK_EDITOR_CURSOR_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
import { URI } from 'vs/base/common/uri';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

// Notebook Commands
const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute';
Expand Down Expand Up @@ -66,6 +66,7 @@ const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow';
const CLEAR_CELL_OUTPUTS_COMMAND_ID = 'notebook.cell.clearOutputs';
const CHANGE_CELL_LANGUAGE = 'notebook.cell.changeLanguage';

const FOCUS_OUTPUT_COMMAND_ID = 'notebook.cell.focusOutput';

export const NOTEBOOK_ACTIONS_CATEGORY = localize('notebookActions.category', "Notebook");

Expand Down Expand Up @@ -207,11 +208,11 @@ registerAction2(class extends Action2 {
// Try to select below, fall back on inserting
const nextCell = editor.viewModel?.viewCells[idx + 1];
if (nextCell) {
editor.focusNotebookCell(nextCell, activeCell.editState === CellEditState.Editing);
editor.focusNotebookCell(nextCell, activeCell.editState === CellEditState.Editing ? 'editor' : 'container');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can add an enum to notebookCommon

} else {
const newCell = editor.insertNotebookCell(activeCell, CellKind.Code, 'below');
if (newCell) {
editor.focusNotebookCell(newCell, true);
editor.focusNotebookCell(newCell, 'editor');
}
}
}
Expand Down Expand Up @@ -245,7 +246,7 @@ registerAction2(class extends Action2 {

const newCell = editor.insertNotebookCell(activeCell, CellKind.Code, 'below');
if (newCell) {
editor.focusNotebookCell(newCell, true);
editor.focusNotebookCell(newCell, 'editor');
}
}
});
Expand Down Expand Up @@ -320,7 +321,7 @@ registerAction2(class extends Action2 {
activeCell.editState = CellEditState.Preview;
}

editor.focusNotebookCell(activeCell, false);
editor.focusNotebookCell(activeCell, 'container');
}
}
});
Expand Down Expand Up @@ -473,7 +474,7 @@ export async function changeCellToKind(kind: CellKind, context: INotebookCellAct
newCell.model.language = language;
}

notebookEditor.focusNotebookCell(newCell, cell.editState === CellEditState.Editing);
notebookEditor.focusNotebookCell(newCell, cell.editState === CellEditState.Editing ? 'editor' : 'container');
notebookEditor.deleteNotebookCell(cell);

return newCell;
Expand Down Expand Up @@ -528,7 +529,7 @@ abstract class InsertCellCommand extends Action2 {

const newCell = context.notebookEditor.insertNotebookCell(context.cell, this.kind, this.direction, undefined, context.ui);
if (newCell) {
context.notebookEditor.focusNotebookCell(newCell, true);
context.notebookEditor.focusNotebookCell(newCell, 'editor');
}
}
}
Expand Down Expand Up @@ -699,7 +700,6 @@ registerAction2(class extends Action2 {
}
});


registerAction2(class extends Action2 {
constructor() {
super(
Expand Down Expand Up @@ -740,12 +740,12 @@ registerAction2(class extends Action2 {
// deletion succeeds, move focus to the next cell
const nextCellIdx = index < context.notebookEditor.viewModel!.length ? index : context.notebookEditor.viewModel!.length - 1;
if (nextCellIdx >= 0) {
context.notebookEditor.focusNotebookCell(context.notebookEditor.viewModel!.viewCells[nextCellIdx], false);
context.notebookEditor.focusNotebookCell(context.notebookEditor.viewModel!.viewCells[nextCellIdx], 'container');
} else {
// No cells left, insert a new empty one
const newCell = context.notebookEditor.insertNotebookCell(undefined, context.cell.cellKind);
if (newCell) {
context.notebookEditor.focusNotebookCell(newCell, true);
context.notebookEditor.focusNotebookCell(newCell, 'editor');
}
}
}
Expand All @@ -759,7 +759,7 @@ async function moveCell(context: INotebookCellActionContext, direction: 'up' | '

if (result) {
// move cell command only works when the cell container has focus
context.notebookEditor.focusNotebookCell(context.cell, false);
context.notebookEditor.focusNotebookCell(context.cell, 'container');
}
}

Expand All @@ -768,7 +768,7 @@ async function copyCell(context: INotebookCellActionContext, direction: 'up' | '
const newCellDirection = direction === 'up' ? 'above' : 'below';
const newCell = context.notebookEditor.insertNotebookCell(context.cell, context.cell.cellKind, newCellDirection, text);
if (newCell) {
context.notebookEditor.focusNotebookCell(newCell, false);
context.notebookEditor.focusNotebookCell(newCell, 'container');
}
}

Expand Down Expand Up @@ -1074,7 +1074,7 @@ registerAction2(class extends Action2 {
return;
}

editor.focusNotebookCell(newCell, true);
editor.focusNotebookCell(newCell, 'editor');
}
});

Expand Down Expand Up @@ -1119,10 +1119,39 @@ registerAction2(class extends Action2 {
return;
}

editor.focusNotebookCell(newCell, true);
editor.focusNotebookCell(newCell, 'editor');
}
});

registerAction2(class extends Action2 {
constructor() {
super({
id: FOCUS_OUTPUT_COMMAND_ID,
title: localize('focusOutput', 'Focus output'),
category: NOTEBOOK_ACTIONS_CATEGORY,
keybinding: {
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED),
primary: KeyMod.CtrlCmd | KeyCode.DownArrow,
weight: EDITOR_WIDGET_ACTION_WEIGHT
}
});
}

async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise<void> {
if (!isCellActionContext(context)) {
context = getActiveCellContext(accessor);
if (!context) {
return;
}
}

const editor = context.notebookEditor;
const activeCell = context.cell;
editor.focusNotebookCell(activeCell, 'output');
}
});


registerAction2(class extends Action2 {
constructor() {
super({
Expand Down Expand Up @@ -1217,7 +1246,7 @@ registerAction2(class extends Action2 {
}

const firstCell = editor.viewModel.viewCells[0];
editor.focusNotebookCell(firstCell, false);
editor.focusNotebookCell(firstCell, 'container');
}
});

Expand Down Expand Up @@ -1251,7 +1280,7 @@ registerAction2(class extends Action2 {
}

const firstCell = editor.viewModel.viewCells[editor.viewModel.length - 1];
editor.focusNotebookCell(firstCell, false);
editor.focusNotebookCell(firstCell, 'container');
}
});

Expand Down Expand Up @@ -1366,7 +1395,7 @@ export class ChangeCellLanguageAction extends Action2 {
if (selection.languageId === 'markdown' && context.cell?.language !== 'markdown') {
const newCell = await changeCellToKind(CellKind.Markdown, { cell: context.cell, notebookEditor: context.notebookEditor });
if (newCell) {
context.notebookEditor.focusNotebookCell(newCell, true);
context.notebookEditor.focusNotebookCell(newCell, 'editor');
}
} else if (selection.languageId !== 'markdown' && context.cell?.language === 'markdown') {
await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor }, selection.languageId);
Expand Down Expand Up @@ -1435,7 +1464,7 @@ async function splitCell(context: INotebookCellActionContext): Promise<void> {
if (context.cell.cellKind === CellKind.Code) {
const newCells = await context.notebookEditor.splitNotebookCell(context.cell);
if (newCells) {
context.notebookEditor.focusNotebookCell(newCells[newCells.length - 1], true);
context.notebookEditor.focusNotebookCell(newCells[newCells.length - 1], 'editor');
}
}
}
Expand Down Expand Up @@ -1473,7 +1502,7 @@ registerAction2(class extends Action2 {
async function joinCells(context: INotebookCellActionContext, direction: 'above' | 'below'): Promise<void> {
const cell = await context.notebookEditor.joinNotebookCells(context.cell, direction, CellKind.Code);
if (cell) {
context.notebookEditor.focusNotebookCell(cell, true);
context.notebookEditor.focusNotebookCell(cell, 'editor');
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts
Expand Up @@ -15,10 +15,10 @@ import { ScrollEvent } from 'vs/base/common/scrollable';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { Range } from 'vs/editor/common/core/range';
import { IPosition } from 'vs/editor/common/core/position';
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Range } from 'vs/editor/common/core/range';
import { FindMatch, IReadonlyTextBuffer, ITextModel } from 'vs/editor/common/model';
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
import { CellLanguageStatusBarItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
Expand Down Expand Up @@ -223,7 +223,7 @@ export interface INotebookEditor {
/**
* Focus the container of a cell (the monaco editor inside is not focused).
*/
focusNotebookCell(cell: ICellViewModel, focusEditor: boolean): void;
focusNotebookCell(cell: ICellViewModel, focus: 'editor' | 'container' | 'output'): void;

/**
* Execute the given notebook cell
Expand Down
38 changes: 25 additions & 13 deletions src/vs/workbench/contrib/notebook/browser/notebookEditor.ts
Expand Up @@ -3,19 +3,22 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/notebook';
import { getZoomLevel } from 'vs/base/browser/browser';
import * as DOM from 'vs/base/browser/dom';
import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Color, RGBA } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore, MutableDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { combinedDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import 'vs/css!./media/notebook';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon';
import { IReadonlyTextBuffer } from 'vs/editor/common/model';
import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
Expand All @@ -28,26 +31,23 @@ import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/com
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { EditorOptions, IEditorCloseEvent, IEditorMemento } from 'vs/workbench/common/editor';
import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, INotebookEditorContribution, NOTEBOOK_EDITOR_RUNNABLE, IEditableCellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_BOTTOM_PADDING, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, IEditableCellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
import { CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate, CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
import { CellDragAndDropController, CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellKind, CellUri, IOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IReadonlyTextBuffer } from 'vs/editor/common/model';

const $ = DOM.$;
const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState';
Expand Down Expand Up @@ -1118,14 +1118,26 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor {
}
}

focusNotebookCell(cell: ICellViewModel, focusEditor: boolean) {
if (focusEditor) {
focusNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') {
if (focusItem === 'editor') {
this.selectElement(cell);
this.list?.focusView();

cell.editState = CellEditState.Editing;
cell.focusMode = CellFocusMode.Editor;
this.revealInCenterIfOutsideViewport(cell);
} else if (focusItem === 'output') {
this.selectElement(cell);
this.list?.focusView();

if (!this.webview) {
return;
}
this.webview.focusOutput(cell.id);

cell.editState = CellEditState.Preview;
cell.focusMode = CellFocusMode.Container;
this.revealInCenterIfOutsideViewport(cell);
} else {
let itemDOM = this.list?.domElementOfElement(cell);
if (document.activeElement && itemDOM && itemDOM.contains(document.activeElement)) {
Expand Down