// ***************************************************************************** // Copyright (C) 2023 TypeFox and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { Command, CommandContribution, CommandHandler, CommandRegistry, MenuContribution, MenuModelRegistry, nls } from '@theia/core'; import { codicon, Key, KeybindingContribution, KeybindingRegistry, KeyCode, KeyModifier } from '@theia/core/lib/browser'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NotebookContextKeys, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_LIST_FOCUSED } from './notebook-context-keys'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { NotebookExecutionService } from '../service/notebook-execution-service'; import { NotebookCellOutputModel } from '../view-model/notebook-cell-output-model'; import { CellData, CellEditType, CellKind } from '../../common'; import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; import { NotebookCommands } from './notebook-actions-contribution'; import { changeCellType } from './cell-operations'; import { EditorLanguageQuickPickService } from '@theia/editor/lib/browser/editor-language-quick-pick-service'; import { NotebookService } from '../service/notebook-service'; import { Selection } from '@theia/monaco-editor-core/esm/vs/editor/common/core/selection'; import { Range } from '@theia/core/shared/vscode-languageserver-protocol'; import { NOTEBOOK_EDITOR_ID_PREFIX } from '../notebook-editor-widget'; 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 */ export const DELETE_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.delete', iconClass: codicon('trash') }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const SPLIT_CELL_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.split', iconClass: codicon('split-vertical'), }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const EXECUTE_SINGLE_CELL_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.execute-cell', category: 'Notebook', label: '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: '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: 'Execute Notebook Cell and Insert Below', category: 'Notebook', }); export const EXECUTE_ABOVE_CELLS_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebookActions.executeAbove', label: 'Execute Above Cells', iconClass: codicon('run-above') }); export const EXECUTE_CELL_AND_BELOW_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebookActions.executeBelow', label: 'Execute Cell and Below', iconClass: codicon('run-below') }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const STOP_CELL_EXECUTION_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.stop-cell-execution', iconClass: codicon('stop'), }); /** 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', }); export const INSERT_NEW_CELL_ABOVE_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.insertCodeCellAboveAndFocusContainer', label: 'Insert Code Cell Above and Focus Container' }); export const INSERT_NEW_CELL_BELOW_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.insertCodeCellBelowAndFocusContainer', label: 'Insert Code Cell Below and Focus Container' }); export const INSERT_MARKDOWN_CELL_ABOVE_COMMAND = Command.toLocalizedCommand({ id: 'notebook.cell.insertMarkdownCellAbove', label: 'Insert Markdown Cell Above' }); export const INSERT_MARKDOWN_CELL_BELOW_COMMAND = Command.toLocalizedCommand({ id: 'notebook.cell.insertMarkdownCellBelow', label: 'Insert Markdown Cell Below' }); 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' }); export const TOGGLE_CELL_OUTPUT = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.toggleOutputs', category: 'Notebook', label: 'Collapse Cell Output', }); export const CHANGE_CELL_LANGUAGE = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.changeLanguage', category: 'Notebook', label: 'Change Cell Language', }); export const TOGGLE_LINE_NUMBERS = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.toggleLineNumbers', category: 'Notebook', label: 'Show Cell Line Numbers', }); } @injectable() export class NotebookCellActionContribution implements MenuContribution, CommandContribution, KeybindingContribution { @inject(ContextKeyService) protected contextKeyService: ContextKeyService; @inject(NotebookService) protected notebookService: NotebookService; @inject(NotebookExecutionService) protected notebookExecutionService: NotebookExecutionService; @inject(NotebookEditorWidgetService) protected notebookEditorWidgetService: NotebookEditorWidgetService; @inject(EditorLanguageQuickPickService) protected languageQuickPickService: EditorLanguageQuickPickService; @postConstruct() protected init(): void { NotebookContextKeys.initNotebookContextKeys(this.contextKeyService); } registerMenus(menus: MenuModelRegistry): void { menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { commandId: NotebookCellCommands.EDIT_COMMAND.id, icon: NotebookCellCommands.EDIT_COMMAND.iconClass, when: `${NOTEBOOK_CELL_TYPE} == 'markdown' && !${NOTEBOOK_CELL_MARKDOWN_EDIT_MODE}`, label: nls.localizeByDefault('Edit Cell'), order: '10' }); menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { commandId: NotebookCellCommands.STOP_EDIT_COMMAND.id, icon: NotebookCellCommands.STOP_EDIT_COMMAND.iconClass, when: `${NOTEBOOK_CELL_TYPE} == 'markdown' && ${NOTEBOOK_CELL_MARKDOWN_EDIT_MODE}`, label: nls.localizeByDefault('Stop Editing Cell'), order: '10' }); menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { commandId: NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND.id, icon: NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND.iconClass, when: `${NOTEBOOK_CELL_TYPE} == 'code'`, label: nls.localizeByDefault('Execute Above Cells'), order: '10' }); menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { commandId: NotebookCellCommands.EXECUTE_CELL_AND_BELOW_COMMAND.id, icon: NotebookCellCommands.EXECUTE_CELL_AND_BELOW_COMMAND.iconClass, when: `${NOTEBOOK_CELL_TYPE} == 'code'`, label: nls.localizeByDefault('Execute Cell and Below'), order: '20' }); menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { commandId: NotebookCellCommands.SPLIT_CELL_COMMAND.id, icon: NotebookCellCommands.SPLIT_CELL_COMMAND.iconClass, label: nls.localizeByDefault('Split Cell'), order: '20' }); menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { commandId: NotebookCellCommands.DELETE_COMMAND.id, icon: NotebookCellCommands.DELETE_COMMAND.iconClass, label: nls.localizeByDefault('Delete Cell'), order: '999' }); menus.registerSubmenu( NotebookCellActionContribution.ADDITIONAL_ACTION_MENU, nls.localizeByDefault('More'), { sortString: '30', icon: codicon('ellipsis') } ); menus.registerSubmenu(NotebookCellActionContribution.CONTRIBUTED_CELL_ACTION_MENU, ''); // since contributions are adding to an independent submenu we have to manually add it to the more submenu menus.linkCompoundMenuNode({ newParentPath: NotebookCellActionContribution.ADDITIONAL_ACTION_MENU, submenuPath: NotebookCellActionContribution.CONTRIBUTED_CELL_ACTION_MENU }); // code cell sidebar menu menus.registerMenuAction(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU, { commandId: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, icon: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.iconClass, label: nls.localizeByDefault('Execute Cell'), when: `!${NOTEBOOK_CELL_EXECUTING}` }); menus.registerMenuAction(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU, { commandId: NotebookCellCommands.STOP_CELL_EXECUTION_COMMAND.id, icon: NotebookCellCommands.STOP_CELL_EXECUTION_COMMAND.iconClass, label: nls.localizeByDefault('Stop Cell Execution'), when: NOTEBOOK_CELL_EXECUTING }); // Notebook Cell extra execution options menus.registerSubmenu(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU, nls.localizeByDefault('More...'), { icon: codicon('chevron-down') }); // menus.getMenu(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU).addNode(menus.getMenuNode(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU)); // code cell output sidebar menu menus.registerSubmenu( NotebookCellActionContribution.ADDITIONAL_OUTPUT_SIDEBAR_MENU, nls.localizeByDefault('More'), { icon: codicon('ellipsis') } ); menus.registerMenuAction(NotebookCellActionContribution.ADDITIONAL_OUTPUT_SIDEBAR_MENU, { commandId: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id, label: nls.localizeByDefault('Clear Cell Outputs'), }); menus.registerMenuAction(NotebookCellActionContribution.ADDITIONAL_OUTPUT_SIDEBAR_MENU, { commandId: NotebookCellCommands.CHANGE_OUTPUT_PRESENTATION_COMMAND.id, label: nls.localizeByDefault('Change Presentation'), }); } registerCommands(commands: CommandRegistry): void { commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, this.editableCellCommandHandler((_, cell) => { const cellViewModel = this.notebookEditorWidgetService.focusedEditor?.viewModel.cellViewModels.get(cell.handle); cellViewModel?.requestFocusEditor(); })); commands.registerCommand(NotebookCellCommands.STOP_EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => { const cellViewModel = this.notebookEditorWidgetService.focusedEditor?.viewModel.cellViewModels.get(cell.handle); cellViewModel?.requestBlurEditor(); } }); commands.registerCommand(NotebookCellCommands.DELETE_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { notebookModel.applyEdits([{ editType: CellEditType.Replace, index: notebookModel.cells.indexOf(cell), count: 1, cells: [] }] , true); })); commands.registerCommand(NotebookCellCommands.SPLIT_CELL_COMMAND, this.editableCellCommandHandler( async (notebookModel, cell) => { // selection (0,0,0,0) should also be used in !cell.editing mode, but `cell.editing` // is not properly implemented for Code cells. const cellSelection: Range = cell.selection ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }; const textModel = await cell.resolveTextModel(); // Create new cell with the text after the cursor const splitOffset = textModel.offsetAt({ line: cellSelection.start.line, character: cellSelection.start.character }); const newCell: CellData = { cellKind: cell.cellKind, language: cell.language, outputs: [], source: textModel.getText().substring(splitOffset), }; // add new cell below const index = notebookModel.cells.indexOf(cell); notebookModel.applyEdits([{ editType: CellEditType.Replace, index: index + 1, count: 0, cells: [newCell] }], true); // update current cell text (undo-able) const selection = new Selection(cellSelection.start.line + 1, cellSelection.start.character + 1, cellSelection.end.line + 1, cellSelection.end.character + 1); const endPosition = textModel.positionAt(textModel.getText().length); const deleteOp = { range: { startLineNumber: selection.startLineNumber, startColumn: selection.startColumn, endLineNumber: endPosition.line + 1, endColumn: endPosition.character + 1 }, // eslint-disable-next-line no-null/no-null text: null }; // Create a new undo/redo stack entry textModel.textEditorModel.pushStackElement(); textModel.textEditorModel.pushEditOperations([selection], [deleteOp], () => [selection]); }) ); commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND, this.editableCellCommandHandler( (notebookModel, cell) => { this.notebookExecutionService.executeNotebookCells(notebookModel, [cell]); }) ); commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND, this.editableCellCommandHandler( (notebookModel, cell) => { const viewModel = this.notebookEditorWidgetService.focusedEditor?.viewModel; if (cell.cellKind === CellKind.Code) { commands.executeCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, notebookModel, cell); } else { commands.executeCommand(NotebookCellCommands.STOP_EDIT_COMMAND.id, notebookModel, cell); } const index = notebookModel.cells.indexOf(cell); if (index < notebookModel.cells.length - 1) { viewModel?.setSelectedCell(notebookModel.cells[index + 1]); } else if (cell.cellKind === CellKind.Code) { commands.executeCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND.id); } else { commands.executeCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND.id); } }) ); 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); const viewModel = this.notebookEditorWidgetService.focusedEditor?.viewModel; viewModel?.setSelectedCell(notebookModel.cells[index + 1]); }) ); commands.registerCommand(NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND, this.editableCellCommandHandler( (notebookModel, cell) => { const index = notebookModel.cells.indexOf(cell); if (index > 0) { this.notebookExecutionService.executeNotebookCells(notebookModel, notebookModel.cells.slice(0, index).filter(c => c.cellKind === CellKind.Code)); } }) ); commands.registerCommand(NotebookCellCommands.EXECUTE_CELL_AND_BELOW_COMMAND, this.editableCellCommandHandler( (notebookModel, cell) => { const index = notebookModel.cells.indexOf(cell); if (index >= 0) { this.notebookExecutionService.executeNotebookCells(notebookModel, notebookModel.cells.slice(index).filter(c => c.cellKind === CellKind.Code)); } }) ); commands.registerCommand(NotebookCellCommands.STOP_CELL_EXECUTION_COMMAND, { execute: (notebookModel: NotebookModel, cell: NotebookCellModel) => { notebookModel = notebookModel ?? this.notebookEditorWidgetService.focusedEditor?.model; cell = cell ?? this.getSelectedCell(); this.notebookExecutionService.cancelNotebookCells(notebookModel, [cell]); } }); commands.registerCommand(NotebookCellCommands.CLEAR_OUTPUTS_COMMAND, this.editableCellCommandHandler( (notebook, cell) => (notebook ?? this.notebookEditorWidgetService.focusedEditor?.model)?.applyEdits([{ editType: CellEditType.Output, handle: cell.handle, outputs: [], deleteCount: cell.outputs.length, append: false }], true) )); commands.registerCommand(NotebookCellCommands.CHANGE_OUTPUT_PRESENTATION_COMMAND, this.editableCellCommandHandler( (notebook, cell, output) => { this.notebookEditorWidgetService.getNotebookEditor(NOTEBOOK_EDITOR_ID_PREFIX + notebook.uri.toString())?.requestOuputPresentationChange(cell.handle, output); } )); const insertCommand = (type: CellKind, index: number | 'above' | 'below', focusContainer: boolean): CommandHandler => this.editableCellCommandHandler(() => commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, undefined, type, index, focusContainer) ); commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_ABOVE_COMMAND, insertCommand(CellKind.Code, 'above', true)); commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND, insertCommand(CellKind.Code, 'below', true)); commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_ABOVE_COMMAND, insertCommand(CellKind.Markup, 'above', false)); commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND, insertCommand(CellKind.Markup, 'below', false)); commands.registerCommand(NotebookCellCommands.TO_CODE_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { changeCellType(notebookModel, cell, CellKind.Code, this.notebookService.getCodeCellLanguage(notebookModel)); })); commands.registerCommand(NotebookCellCommands.TO_MARKDOWN_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { changeCellType(notebookModel, cell, CellKind.Markup); })); commands.registerCommand(NotebookCellCommands.TOGGLE_CELL_OUTPUT, { execute: () => { const selectedCell = this.notebookEditorWidgetService.focusedEditor?.viewModel?.selectedCell; if (selectedCell) { selectedCell.outputVisible = !selectedCell.outputVisible; } } }); commands.registerCommand(NotebookCellCommands.CHANGE_CELL_LANGUAGE, { isVisible: () => !!this.notebookEditorWidgetService.focusedEditor?.viewModel?.selectedCell, execute: async (notebook?: NotebookModel, cell?: NotebookCellModel) => { const selectedCell = cell ?? this.notebookEditorWidgetService.focusedEditor?.viewModel?.selectedCell; const activeNotebook = notebook ?? this.notebookEditorWidgetService.focusedEditor?.model; if (!selectedCell || !activeNotebook) { return; } const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language); if (!language?.value || language.value === 'autoDetect' || language.value.id === selectedCell.language) { return; } const isMarkdownCell = selectedCell.cellKind === CellKind.Markup; const isMarkdownLanguage = language.value.id === 'markdown'; if (isMarkdownLanguage) { changeCellType(activeNotebook, selectedCell, CellKind.Markup, language.value.id); } else { if (isMarkdownCell) { changeCellType(activeNotebook, selectedCell, CellKind.Code, language.value.id); } else { this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{ editType: CellEditType.CellLanguage, index: activeNotebook.cells.indexOf(selectedCell), language: language.value.id }], true); } } } }); commands.registerCommand(NotebookCellCommands.TOGGLE_LINE_NUMBERS, { execute: () => { const selectedCell = this.notebookEditorWidgetService.focusedEditor?.viewModel?.selectedCell; if (selectedCell) { const currentLineNumber = selectedCell.editorOptions?.lineNumbers; selectedCell.editorOptions = { ...selectedCell.editorOptions, lineNumbers: !currentLineNumber || currentLineNumber === 'off' ? 'on' : 'off' }; } } }); } protected editableCellCommandHandler(execute: (notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel) => void): CommandHandler { return { isEnabled: (notebookModel: NotebookModel) => !Boolean(notebookModel?.readOnly), isVisible: (notebookModel: NotebookModel) => !Boolean(notebookModel?.readOnly), execute: (notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel) => { notebookModel = notebookModel ?? this.notebookEditorWidgetService.focusedEditor?.model; cell = cell ?? this.getSelectedCell(); execute(notebookModel, cell, output); } }; } protected getSelectedCell(): NotebookCellModel | undefined { return this.notebookEditorWidgetService.focusedEditor?.viewModel?.selectedCell; } registerKeybindings(keybindings: KeybindingRegistry): void { keybindings.registerKeybindings( { command: NotebookCellCommands.EDIT_COMMAND.id, keybinding: 'Enter', when: `!editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, }, { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, 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, keybinding: 'esc', when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && !suggestWidgetVisible`, }, { command: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(), when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, }, { command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND.id, 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(), when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, }, { command: NotebookCellCommands.CHANGE_OUTPUT_PRESENTATION_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.KEY_P, modifiers: [KeyModifier.Alt] }).toString(), when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, }, { command: NotebookCellCommands.TO_CODE_CELL_COMMAND.id, keybinding: 'Y', when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'markdown'`, }, { command: NotebookCellCommands.TO_MARKDOWN_CELL_COMMAND.id, keybinding: 'M', when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, }, { command: NotebookCellCommands.SPLIT_CELL_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.MINUS, modifiers: [KeyModifier.CtrlCmd, KeyModifier.Shift] }).toString(), when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, } ); } } export namespace NotebookCellActionContribution { export const ACTION_MENU = ['notebook-cell-actions-menu']; export const ADDITIONAL_ACTION_MENU = [...ACTION_MENU, 'more']; export const CONTRIBUTED_CELL_ACTION_MENU = ['notebook/cell/title']; export const CONTRIBUTED_CELL_EXECUTION_MENU = ['notebook/cell/execute']; export const CODE_CELL_SIDEBAR_MENU = ['code-cell-sidebar-menu']; export const OUTPUT_SIDEBAR_MENU = ['code-cell-output-sidebar-menu']; export const ADDITIONAL_OUTPUT_SIDEBAR_MENU = [...OUTPUT_SIDEBAR_MENU, 'more']; }