diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 4f781114e12e7..dd11108e0704f 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -187,12 +187,14 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon if (change === CellChangeDirection.Up && currentIndex > 0) { model.setSelectedCell(model.cells[currentIndex - 1]); - if (model.selectedCell?.cellKind === CellKind.Code && shouldFocusEditor) { + if ((model.selectedCell?.cellKind === CellKind.Code + || (model.selectedCell?.cellKind === CellKind.Markup && model.selectedCell?.editing)) && shouldFocusEditor) { model.selectedCell.requestFocusEditor('lastLine'); } } else if (change === CellChangeDirection.Down && currentIndex < model.cells.length - 1) { model.setSelectedCell(model.cells[currentIndex + 1]); - if (model.selectedCell?.cellKind === CellKind.Code && shouldFocusEditor) { + if ((model.selectedCell?.cellKind === CellKind.Code + || (model.selectedCell?.cellKind === CellKind.Markup && model.selectedCell?.editing)) && shouldFocusEditor) { model.selectedCell.requestFocusEditor(); } } diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 69606906c6ba9..8242947a4d116 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -39,11 +39,13 @@ export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ export const EDIT_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.edit', + category: 'Notebook', iconClass: codicon('edit') }); /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ export const STOP_EDIT_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.stop-edit', + category: 'Notebook', iconClass: codicon('check') }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ @@ -59,11 +61,21 @@ export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const EXECUTE_SINGLE_CELL_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.execute-cell', + category: 'Notebook', + label: nls.localizeByDefault('Execute Cell'), iconClass: codicon('play'), }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.execute-cell-and-focus-next', + label: nls.localizeByDefault('Execute Notebook Cell and Select Below'), + category: 'Notebook', + }); + /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ + export const EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND = Command.toDefaultLocalizedCommand({ + id: 'notebook.cell.execute-cell-and-insert-below', + label: nls.localizeByDefault('Execute Notebook Cell and Insert Below'), + category: 'Notebook', }); export const EXECUTE_ABOVE_CELLS_COMMAND = Command.toDefaultLocalizedCommand({ @@ -85,11 +97,13 @@ export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ export const CLEAR_OUTPUTS_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.clear-outputs', + category: 'Notebook', label: 'Clear Cell Outputs', }); /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel | undefined, output: NotebookCellOutputModel */ export const CHANGE_OUTPUT_PRESENTATION_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.change-presentation', + category: 'Notebook', label: 'Change Presentation', }); @@ -114,11 +128,13 @@ export namespace NotebookCellCommands { export const TO_CODE_CELL_COMMAND = Command.toLocalizedCommand({ id: 'notebook.cell.changeToCode', + category: 'Notebook', label: 'Change Cell to Code' }); export const TO_MARKDOWN_CELL_COMMAND = Command.toLocalizedCommand({ id: 'notebook.cell.changeToMarkdown', + category: 'Notebook', label: 'Change Cell to Markdown' }); @@ -302,6 +318,23 @@ export class NotebookCellActionContribution implements MenuContribution, Command } }) ); + commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND, this.editableCellCommandHandler( + async (notebookModel, cell) => { + if (cell.cellKind === CellKind.Code) { + await commands.executeCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, notebookModel, cell); + } + await commands.executeCommand(NotebookCellCommands.STOP_EDIT_COMMAND.id, notebookModel, cell); + + if (cell.cellKind === CellKind.Code) { + await commands.executeCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND.id); + } else { + await commands.executeCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND.id); + } + + const index = notebookModel.cells.indexOf(cell); + notebookModel.setSelectedCell(notebookModel.cells[index + 1]); + }) + ); commands.registerCommand(NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND, this.editableCellCommandHandler( (notebookModel, cell) => { @@ -432,8 +465,8 @@ export class NotebookCellActionContribution implements MenuContribution, Command }, { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, - keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(), - when: `editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED}`, + keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt, KeyModifier.CtrlCmd] }).toString(), + when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'markdown'`, }, { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, @@ -450,6 +483,11 @@ export class NotebookCellActionContribution implements MenuContribution, Command keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Shift] }).toString(), when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, }, + { + command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND.id, + keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(), + when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, + }, { command: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.KEY_O, modifiers: [KeyModifier.Alt] }).toString(), diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 2dc5723fcfc03..a88ed0a77b692 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -154,7 +154,6 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa @postConstruct() protected init(): void { this.id = NOTEBOOK_EDITOR_ID_PREFIX + this.props.uri.toString(); - this.node.tabIndex = -1; this.scrollOptions = { suppressScrollY: true @@ -174,8 +173,6 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.commandRegistry.executeCommand(NotebookCellCommands.EDIT_COMMAND.id, model, model.cells[0]); model.setSelectedCell(model.cells[0]); } - model.cells.forEach(cell => cell.onWillBlurCellEditor(() => this.node.focus())); - model.onDidAddOrRemoveCell(e => e.newCellIds?.forEach(cellId => model.cells.find(cell => cell.handle === cellId)?.onWillBlurCellEditor(() => this.node.focus()))); }); } diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 969c031dc6753..db72b0765c267 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -101,6 +101,16 @@ export class CellEditor extends React.Component { this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, currentLine === lineCount); })); + this.toDispose.push(this.props.cell.onWillBlurCellEditor(() => { + let parent = this.container?.parentElement; + while (parent && !parent.classList.contains('theia-notebook-cell')) { + parent = parent.parentElement; + } + if (parent) { + parent.focus(); + } + })); + this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => { this.editor?.getControl().updateOptions(options); })); diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 0c66a6f6531ce..14e7a42ef5b5a 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -132,7 +132,14 @@ export class NotebookCellListView extends React.Component this.onDrop(e, index)} draggable={true} tabIndex={-1} - ref={ref => cell === this.state.selectedCell && this.state.scrollIntoView && ref?.scrollIntoView({ block: 'nearest' })}> + ref={ref => { + if (ref && cell === this.state.selectedCell && this.state.scrollIntoView) { + ref.scrollIntoView({ block: 'nearest' }); + if (cell.cellKind === CellKind.Markup && !cell.editing) { + ref.focus(); + } + } + }}>
{this.renderCellContent(cell, index)}