From 37f51742d5f75038ccd51a08cfee61282e3dbaec Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 4 Mar 2024 11:49:27 +0100 Subject: [PATCH 01/13] Fixed context keys for notebook editor contexts this makes commands from jupyter available Signed-off-by: Jonah Iden --- packages/notebook/src/browser/notebook-editor-widget.tsx | 1 + .../src/browser/service/notebook-editor-widget-service.ts | 6 +++++- .../notebook/src/browser/service/notebook-kernel-service.ts | 1 + .../plugin-ext/src/main/browser/notebooks/notebooks-main.ts | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index f78cd147fb8fa..6991763ca2567 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -146,6 +146,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.renderers.set(CellKind.Markup, this.markdownCellRenderer); this.renderers.set(CellKind.Code, this.codeCellRenderer); this._ready.resolve(this.waitForData()); + } protected async waitForData(): Promise { diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index e0dd0419977bf..57ecec394a008 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -21,7 +21,7 @@ import { Emitter } from '@theia/core'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; -import { ApplicationShell } from '@theia/core/lib/browser'; +import { ApplicationShell, OpenerService, WidgetFactory } from '@theia/core/lib/browser'; import { NotebookEditorWidget } from '../notebook-editor-widget'; @injectable() @@ -30,6 +30,9 @@ export class NotebookEditorWidgetService { @inject(ApplicationShell) protected applicationShell: ApplicationShell; + @inject(WidgetFactory) + protected openerService: OpenerService; + private readonly notebookEditors = new Map(); private readonly onNotebookEditorAddEmitter = new Emitter(); @@ -56,6 +59,7 @@ export class NotebookEditorWidgetService { this.onDidChangeFocusedEditorEmitter.fire(undefined); } }); + this.openerService. } // --- editor management diff --git a/packages/notebook/src/browser/service/notebook-kernel-service.ts b/packages/notebook/src/browser/service/notebook-kernel-service.ts index 9f5e0b81f3a38..f0c0ca775de79 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-service.ts @@ -266,6 +266,7 @@ export class NotebookKernelService { } this.storageService.setData(NOTEBOOK_KERNEL_BINDING_STORAGE_KEY, this.notebookBindings); this.onDidChangeSelectedNotebookKernelBindingEmitter.fire({ notebook: notebook.uri, oldKernel, newKernel: kernel?.id }); + } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts index 7263af2d788a6..8fd4f662d1390 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts @@ -18,6 +18,7 @@ import { CancellationToken, DisposableCollection, Emitter, Event } from '@theia/ import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { NotebookCellStatusBarItem, NotebookData, TransientOptions } from '@theia/notebook/lib/common'; import { NotebookService } from '@theia/notebook/lib/browser'; +import { NotebookContextManager } from '@theia/notebook/lib/browser/service/notebook-context-manager'; import { Disposable } from '@theia/plugin'; import { CommandRegistryMain, MAIN_RPC_CONTEXT, NotebooksExt, NotebooksMain } from '../../../common'; import { RPCProtocol } from '../../../common/rpc-protocol'; From 116d2c39910f4f13a6f3bdb42e3a7af15906ea39 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 4 Mar 2024 12:12:58 +0100 Subject: [PATCH 02/13] smaller fix and removed unnecessary change Signed-off-by: Jonah Iden --- .../src/browser/service/notebook-editor-widget-service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index 57ecec394a008..e60fcfa3bfabc 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -59,7 +59,6 @@ export class NotebookEditorWidgetService { this.onDidChangeFocusedEditorEmitter.fire(undefined); } }); - this.openerService. } // --- editor management From bed758dea7e3cb297a0c9bd67012ce3eb6f8251b Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 4 Mar 2024 12:17:20 +0100 Subject: [PATCH 03/13] fixed build Signed-off-by: Jonah Iden --- .../src/browser/service/notebook-editor-widget-service.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index e60fcfa3bfabc..e0dd0419977bf 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -21,7 +21,7 @@ import { Emitter } from '@theia/core'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; -import { ApplicationShell, OpenerService, WidgetFactory } from '@theia/core/lib/browser'; +import { ApplicationShell } from '@theia/core/lib/browser'; import { NotebookEditorWidget } from '../notebook-editor-widget'; @injectable() @@ -30,9 +30,6 @@ export class NotebookEditorWidgetService { @inject(ApplicationShell) protected applicationShell: ApplicationShell; - @inject(WidgetFactory) - protected openerService: OpenerService; - private readonly notebookEditors = new Map(); private readonly onNotebookEditorAddEmitter = new Emitter(); From 7dd8620138d62c667ea30d8f0d9fffd83005bd4d Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 4 Mar 2024 14:21:05 +0100 Subject: [PATCH 04/13] review comments Signed-off-by: Jonah Iden --- packages/notebook/src/browser/notebook-editor-widget.tsx | 1 - packages/notebook/src/browser/service/notebook-kernel-service.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 6991763ca2567..f78cd147fb8fa 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -146,7 +146,6 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.renderers.set(CellKind.Markup, this.markdownCellRenderer); this.renderers.set(CellKind.Code, this.codeCellRenderer); this._ready.resolve(this.waitForData()); - } protected async waitForData(): Promise { diff --git a/packages/notebook/src/browser/service/notebook-kernel-service.ts b/packages/notebook/src/browser/service/notebook-kernel-service.ts index f0c0ca775de79..9f5e0b81f3a38 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-service.ts @@ -266,7 +266,6 @@ export class NotebookKernelService { } this.storageService.setData(NOTEBOOK_KERNEL_BINDING_STORAGE_KEY, this.notebookBindings); this.onDidChangeSelectedNotebookKernelBindingEmitter.fire({ notebook: notebook.uri, oldKernel, newKernel: kernel?.id }); - } } From 54ae0aaa2710d58c90416044f9b6c6c7fa279dc8 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 13 Mar 2024 16:16:10 +0100 Subject: [PATCH 05/13] fixed context key management for notebook main toolbar Signed-off-by: Jonah Iden --- packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts index 8fd4f662d1390..7263af2d788a6 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts @@ -18,7 +18,6 @@ import { CancellationToken, DisposableCollection, Emitter, Event } from '@theia/ import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { NotebookCellStatusBarItem, NotebookData, TransientOptions } from '@theia/notebook/lib/common'; import { NotebookService } from '@theia/notebook/lib/browser'; -import { NotebookContextManager } from '@theia/notebook/lib/browser/service/notebook-context-manager'; import { Disposable } from '@theia/plugin'; import { CommandRegistryMain, MAIN_RPC_CONTEXT, NotebooksExt, NotebooksMain } from '../../../common'; import { RPCProtocol } from '../../../common/rpc-protocol'; From 805c8b605311a1df4b964cdff2f3fd76ba73cfa1 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 14 Mar 2024 13:03:57 +0100 Subject: [PATCH 06/13] refactored notebook context key management Signed-off-by: Jonah Iden --- .../contributions/notebook-keybindings.ts | 37 ++++++++++ .../src/browser/notebook-frontend-module.ts | 3 +- .../service/notebook-cell-context-manager.ts | 72 ------------------- .../service/notebook-context-manager.ts | 59 ++++++++++++++- .../service/notebook-editor-widget-service.ts | 7 ++ .../browser/view-model/notebook-cell-model.ts | 37 +++------- .../src/browser/view-model/notebook-model.ts | 4 ++ .../browser/view/notebook-cell-list-view.tsx | 5 +- .../view/notebook-cell-toolbar-factory.tsx | 10 ++- 9 files changed, 123 insertions(+), 111 deletions(-) create mode 100644 packages/notebook/src/browser/contributions/notebook-keybindings.ts delete mode 100644 packages/notebook/src/browser/service/notebook-cell-context-manager.ts diff --git a/packages/notebook/src/browser/contributions/notebook-keybindings.ts b/packages/notebook/src/browser/contributions/notebook-keybindings.ts new file mode 100644 index 0000000000000..bf5f9e0ad3c35 --- /dev/null +++ b/packages/notebook/src/browser/contributions/notebook-keybindings.ts @@ -0,0 +1,37 @@ + +// ***************************************************************************** +// 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 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser'; +import { NotebookCellCommands } from './notebook-cell-actions-contribution'; +import { NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED } from './notebook-context-keys'; + +export class NotebookKeybinding implements KeybindingContribution { + + registerKeybindings(keybindings: KeybindingRegistry): void { + keybindings.registerKeybindings( + { + command: NotebookCellCommands.EDIT_COMMAND.id, + keybinding: 'Enter', + when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && !${NOTEBOOK_CELL_EDITOR_FOCUSED}`, + } + ); + } +} diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index 0a9704163672d..ec7e3774b30fa 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -42,7 +42,6 @@ import { NotebookKernelHistoryService } from './service/notebook-kernel-history- import { NotebookEditorWidgetService } from './service/notebook-editor-widget-service'; import { NotebookRendererMessagingService } from './service/notebook-renderer-messaging-service'; import { NotebookColorContribution } from './contributions/notebook-color-contribution'; -import { NotebookCellContextManager } from './service/notebook-cell-context-manager'; export default new ContainerModule(bind => { bind(NotebookColorContribution).toSelf().inSingletonScope(); @@ -88,6 +87,6 @@ export default new ContainerModule(bind => { createNotebookModelContainer(ctx.container, props).get(NotebookModel) ); bind(NotebookCellModelFactory).toFactory(ctx => (props: NotebookCellModelProps) => - createNotebookCellModelContainer(ctx.container, props, NotebookCellContextManager).get(NotebookCellModel) + createNotebookCellModelContainer(ctx.container, props).get(NotebookCellModel) ); }); diff --git a/packages/notebook/src/browser/service/notebook-cell-context-manager.ts b/packages/notebook/src/browser/service/notebook-cell-context-manager.ts deleted file mode 100644 index d49a2bc861cd1..0000000000000 --- a/packages/notebook/src/browser/service/notebook-cell-context-manager.ts +++ /dev/null @@ -1,72 +0,0 @@ -// ***************************************************************************** -// 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 { inject, injectable } from '@theia/core/shared/inversify'; -import { ContextKeyChangeEvent, ContextKeyService, ScopedValueStore } from '@theia/core/lib/browser/context-key-service'; -import { NotebookCellModel } from '../view-model/notebook-cell-model'; -import { NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE } from '../contributions/notebook-context-keys'; -import { Disposable, DisposableCollection, Emitter } from '@theia/core'; -import { CellKind } from '../../common'; -import { NotebookExecutionStateService } from '../service/notebook-execution-state-service'; - -@injectable() -export class NotebookCellContextManager implements NotebookCellContextManager, Disposable { - @inject(ContextKeyService) protected contextKeyService: ContextKeyService; - - @inject(NotebookExecutionStateService) - protected readonly executionStateService: NotebookExecutionStateService; - - protected readonly toDispose = new DisposableCollection(); - - protected currentStore: ScopedValueStore; - protected currentContext: HTMLLIElement; - - protected readonly onDidChangeContextEmitter = new Emitter(); - readonly onDidChangeContext = this.onDidChangeContextEmitter.event; - - updateCellContext(cell: NotebookCellModel, newHtmlContext: HTMLLIElement): void { - if (newHtmlContext !== this.currentContext) { - this.toDispose.dispose(); - - this.currentContext = newHtmlContext; - this.currentStore = this.contextKeyService.createScoped(newHtmlContext); - - this.currentStore.setContext(NOTEBOOK_CELL_TYPE, cell.cellKind === CellKind.Code ? 'code' : 'markdown'); - - this.toDispose.push(this.contextKeyService.onDidChange(e => { - this.onDidChangeContextEmitter.fire(e); - })); - - this.toDispose.push(cell.onDidRequestCellEditChange(cellEdit => { - this.currentStore?.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit); - this.onDidChangeContextEmitter.fire({ affects: keys => keys.has(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE) }); - })); - this.toDispose.push(this.executionStateService.onDidChangeExecution(e => { - if (e.affectsCell(cell.uri)) { - this.currentStore?.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed); - this.currentStore?.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle'); - this.onDidChangeContextEmitter.fire({ affects: keys => keys.has(NOTEBOOK_CELL_EXECUTING) || keys.has(NOTEBOOK_CELL_EXECUTION_STATE) }); - } - })); - this.onDidChangeContextEmitter.fire({ affects: keys => true }); - } - } - - dispose(): void { - this.toDispose.dispose(); - this.onDidChangeContextEmitter.dispose(); - } -} diff --git a/packages/notebook/src/browser/service/notebook-context-manager.ts b/packages/notebook/src/browser/service/notebook-context-manager.ts index d5b33c3bcac9a..4f645270d1ccd 100644 --- a/packages/notebook/src/browser/service/notebook-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-context-manager.ts @@ -15,11 +15,19 @@ // ***************************************************************************** import { inject, injectable } from '@theia/core/shared/inversify'; -import { ContextKeyChangeEvent, ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { ContextKeyChangeEvent, ContextKeyService, ScopedValueStore } from '@theia/core/lib/browser/context-key-service'; import { DisposableCollection, Emitter } from '@theia/core'; import { NotebookKernelService } from './notebook-kernel-service'; -import { NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_VIEW_TYPE } from '../contributions/notebook-context-keys'; +import { + NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, + NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, + NOTEBOOK_CELL_TYPE, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED, + NOTEBOOK_VIEW_TYPE +} from '../contributions/notebook-context-keys'; import { NotebookEditorWidget } from '../notebook-editor-widget'; +import { NotebookCellModel } from '../view-model/notebook-cell-model'; +import { CellKind } from '../../common'; +import { NotebookExecutionStateService } from './notebook-execution-state-service'; @injectable() export class NotebookContextManager { @@ -28,18 +36,29 @@ export class NotebookContextManager { @inject(NotebookKernelService) protected readonly notebookKernelService: NotebookKernelService; + @inject(NotebookExecutionStateService) + protected readonly executionStateService: NotebookExecutionStateService; + protected readonly toDispose = new DisposableCollection(); protected readonly onDidChangeContextEmitter = new Emitter(); readonly onDidChangeContext = this.onDidChangeContextEmitter.event; + protected _context?: HTMLElement; + + get context(): HTMLElement | undefined { + return this._context; + } + init(widget: NotebookEditorWidget): void { + this._context = widget.node; const scopedStore = this.contextKeyService.createScoped(widget.node); this.toDispose.dispose(); scopedStore.setContext(NOTEBOOK_VIEW_TYPE, widget?.notebookType); + // Kernel related keys const kernel = widget?.model ? this.notebookKernelService.getSelectedNotebookKernel(widget.model) : undefined; scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!kernel); scopedStore.setContext(NOTEBOOK_KERNEL, kernel?.id); @@ -50,9 +69,45 @@ export class NotebookContextManager { this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL])); } })); + + // Cell Selection realted keys + scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!widget.model?.selectedCell); + widget.model?.onDidChangeSelectedCell(e => { + scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!e); + this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_FOCUSED])); + }); + + widget.model?.onDidChangeSelectedCell(e => this.selectedCellChanged(e, scopedStore)); + this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_VIEW_TYPE, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL])); } + protected cellDisposables = new DisposableCollection(); + + selectedCellChanged(cell: NotebookCellModel | undefined, scopedStore: ScopedValueStore): void { + this.cellDisposables.dispose(); + + scopedStore.setContext(NOTEBOOK_CELL_TYPE, cell ? cell.cellKind === CellKind.Code ? 'code' : 'markdown' : undefined); + + if (cell) { + scopedStore.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cell.editing); + this.cellDisposables.push(cell.onDidRequestCellEditChange(cellEdit => { + scopedStore?.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit); + this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_MARKDOWN_EDIT_MODE])); + })); + this.cellDisposables.push(this.executionStateService.onDidChangeExecution(e => { + if (cell && e.affectsCell(cell.uri)) { + scopedStore?.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed); + scopedStore?.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle'); + this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE])); + } + })); + } + + this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_TYPE])); + + } + createContextKeyChangedEvent(affectedKeys: string[]): ContextKeyChangeEvent { return { affects: keys => affectedKeys.some(key => keys.has(key)) }; } diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index e0dd0419977bf..8038b6cdf90c2 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -23,6 +23,8 @@ import { Emitter } from '@theia/core'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { ApplicationShell } from '@theia/core/lib/browser'; import { NotebookEditorWidget } from '../notebook-editor-widget'; +import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { NOTEBOOK_EDITOR_FOCUSED } from '../contributions/notebook-context-keys'; @injectable() export class NotebookEditorWidgetService { @@ -30,6 +32,9 @@ export class NotebookEditorWidgetService { @inject(ApplicationShell) protected applicationShell: ApplicationShell; + @inject(ContextKeyService) + protected contextKeyService: ContextKeyService; + private readonly notebookEditors = new Map(); private readonly onNotebookEditorAddEmitter = new Emitter(); @@ -48,11 +53,13 @@ export class NotebookEditorWidgetService { if (event.newValue instanceof NotebookEditorWidget) { if (event.newValue !== this.focusedEditor) { this.focusedEditor = event.newValue; + this.contextKeyService.setContext(NOTEBOOK_EDITOR_FOCUSED, true); this.onDidChangeFocusedEditorEmitter.fire(this.focusedEditor); } } else if (event.newValue) { // Only unfocus editor if a new widget has been focused this.focusedEditor = undefined; + this.contextKeyService.setContext(NOTEBOOK_EDITOR_FOCUSED, true); this.onDidChangeFocusedEditorEmitter.fire(undefined); } }); diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index 8a02da4c5c7d7..a6b670e771eca 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -20,7 +20,6 @@ import { Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; -import { ContextKeyChangeEvent } from '@theia/core/lib/browser/context-key-service'; import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { @@ -33,25 +32,15 @@ import { NotebookCellOutputModel } from './notebook-cell-output-model'; export const NotebookCellModelFactory = Symbol('NotebookModelFactory'); export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel; -export function createNotebookCellModelContainer(parent: interfaces.Container, props: NotebookCellModelProps, - notebookCellContextManager: new (...args: never[]) => unknown): interfaces.Container { +export function createNotebookCellModelContainer(parent: interfaces.Container, props: NotebookCellModelProps): interfaces.Container { const child = parent.createChild(); child.bind(NotebookCellModelProps).toConstantValue(props); - // We need the constructor as property here to avoid circular dependencies for the context manager - child.bind(NotebookCellContextManager).to(notebookCellContextManager).inSingletonScope(); child.bind(NotebookCellModel).toSelf(); return child; } -const NotebookCellContextManager = Symbol('NotebookCellContextManager'); -interface NotebookCellContextManager { - updateCellContext(cell: NotebookCellModel, context: HTMLElement): void; - dispose(): void; - onDidChangeContext: Event; -} - export interface CellInternalMetadataChangedEvent { readonly lastRunSuccessChanged?: boolean; } @@ -111,9 +100,6 @@ export class NotebookCellModel implements NotebookCell, Disposable { protected readonly onDidRequestCellEditChangeEmitter = new Emitter(); readonly onDidRequestCellEditChange = this.onDidRequestCellEditChangeEmitter.event; - @inject(NotebookCellContextManager) - readonly notebookCellContextManager: NotebookCellContextManager; - @inject(NotebookCellModelProps) protected readonly props: NotebookCellModelProps; @inject(MonacoTextModelService) @@ -152,12 +138,6 @@ export class NotebookCellModel implements NotebookCell, Disposable { protected textModel?: MonacoEditorModel; - protected htmlContext: HTMLLIElement; - - get context(): HTMLLIElement { - return this.htmlContext; - } - get text(): string { return this.textModel ? this.textModel.getText() : this.source; } @@ -198,6 +178,11 @@ export class NotebookCellModel implements NotebookCell, Disposable { return this.props.cellKind; } + protected _editing: boolean = false; + get editing(): boolean { + return this._editing; + } + @postConstruct() protected init(): void { this._outputs = this.props.outputs.map(op => new NotebookCellOutputModel(op)); @@ -205,13 +190,6 @@ export class NotebookCellModel implements NotebookCell, Disposable { this._internalMetadata = this.props.internalMetadata ?? {}; } - refChanged(node: HTMLLIElement): void { - if (node) { - this.htmlContext = node; - this.notebookCellContextManager.updateCellContext(this, node); - } - } - dispose(): void { this.onDidChangeOutputsEmitter.dispose(); this.onDidChangeOutputItemsEmitter.dispose(); @@ -219,18 +197,19 @@ export class NotebookCellModel implements NotebookCell, Disposable { this.onDidChangeMetadataEmitter.dispose(); this.onDidChangeInternalMetadataEmitter.dispose(); this.onDidChangeLanguageEmitter.dispose(); - this.notebookCellContextManager.dispose(); this.textModel?.dispose(); this.toDispose.dispose(); } requestEdit(): void { if (!this.textModel || !this.textModel.readOnly) { + this._editing = true; this.onDidRequestCellEditChangeEmitter.fire(true); } } requestStopEdit(): void { + this._editing = false; this.onDidRequestCellEditChangeEmitter.fire(false); } diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index a1236f63d18c7..77b2f49aeb96d 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -64,6 +64,9 @@ export class NotebookModel implements Saveable, Disposable { protected readonly onDidChangeContentEmitter = new Emitter(); readonly onDidChangeContent = this.onDidChangeContentEmitter.event; + protected readonly onDidChangeSelectedCellEmitter = new Emitter(); + readonly onDidChangeSelectedCell = this.onDidChangeSelectedCellEmitter.event; + get onDidChangeReadOnly(): Event { return this.props.resource.onDidChangeReadOnly ?? Event.None; } @@ -232,6 +235,7 @@ export class NotebookModel implements Saveable, Disposable { setSelectedCell(cell: NotebookCellModel): void { this.selectedCell = cell; + this.onDidChangeSelectedCellEmitter.fire(cell); } private addCellOutputListeners(cells: NotebookCellModel[]): void { 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 6b17495120953..3f46372b90d2c 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -50,7 +50,7 @@ export class NotebookCellListView extends React.Component 0) { this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(model => model.handle === e.newCellIds![e.newCellIds!.length - 1]) }); } else { - this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(cell => cell === this.state.selectedCell)}); + this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(cell => cell === this.state.selectedCell) }); } })); } @@ -78,8 +78,7 @@ export class NotebookCellListView extends React.Component this.onDragStart(e, index)} onDragOver={e => this.onDragOver(e, cell)} onDrop={e => this.onDrop(e, index)} - draggable={true} - ref={(node: HTMLLIElement) => cell.refChanged(node)}> + draggable={true}>
{this.renderCellContent(cell, index)} diff --git a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx index ae11556c25be4..f6798745bcd65 100644 --- a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx @@ -23,6 +23,7 @@ import { ContextMenuRenderer } from '@theia/core/lib/browser'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NotebookCellOutputModel } from '../view-model/notebook-cell-output-model'; +import { NotebookContextManager } from '../service/notebook-context-manager'; export interface NotebookCellToolbarItem { id: string; @@ -48,21 +49,24 @@ export class NotebookCellToolbarFactory { @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; + @inject(NotebookContextManager) + protected readonly notebookContextManager: NotebookContextManager; + renderCellToolbar(menuPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode { return this.getMenuItems(menuPath, notebookModel, cell)} - onContextKeysChanged={cell.notebookCellContextManager.onDidChangeContext} />; + onContextKeysChanged={this.notebookContextManager.onDidChangeContext} />; } renderSidebar(menuPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel): React.ReactNode { return this.getMenuItems(menuPath, notebookModel, cell, output)} - onContextKeysChanged={cell.notebookCellContextManager.onDidChangeContext} />; + onContextKeysChanged={this.notebookContextManager.onDidChangeContext} />; } private getMenuItems(menuItemPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel): NotebookCellToolbarItem[] { const inlineItems: NotebookCellToolbarItem[] = []; for (const menuNode of this.menuRegistry.getMenu(menuItemPath).children) { - if (!menuNode.when || this.contextKeyService.match(menuNode.when, cell.context ?? undefined)) { + if (!menuNode.when || this.contextKeyService.match(menuNode.when, this.notebookContextManager.context)) { if (menuNode.role === CompoundMenuNodeRole.Flat) { inlineItems.push(...menuNode.children?.map(child => this.createToolbarItem(child, notebookModel, cell, output)) ?? []); } else { From c5e1a66fd6c97ddac3dd083aff3b723cd22d861c Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 15 Mar 2024 08:42:23 +0100 Subject: [PATCH 07/13] contextKey scope refactor and markdown close editor keybinding Signed-off-by: Jonah Iden --- packages/notebook/package.json | 1 + .../notebook-actions-contribution.ts | 52 ++++++++++++++++++- .../notebook-cell-actions-contribution.ts | 37 +++++++++++-- .../contributions/notebook-keybindings.ts | 37 ------------- .../src/browser/notebook-editor-widget.tsx | 2 + .../src/browser/notebook-frontend-module.ts | 9 ++-- .../service/notebook-context-manager.ts | 35 +++++++------ .../src/browser/view-model/notebook-model.ts | 4 -- .../src/browser/view/notebook-cell-editor.tsx | 16 +++--- .../browser/view/notebook-cell-list-view.tsx | 4 ++ .../browser/view/notebook-code-cell-view.tsx | 6 ++- .../view/notebook-markdown-cell-view.tsx | 11 ++-- 12 files changed, 135 insertions(+), 79 deletions(-) delete mode 100644 packages/notebook/src/browser/contributions/notebook-keybindings.ts diff --git a/packages/notebook/package.json b/packages/notebook/package.json index afb6eaaa116bd..d50da607b4c9a 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -7,6 +7,7 @@ "@theia/editor": "1.47.0", "@theia/filesystem": "1.47.0", "@theia/monaco": "1.47.0", + "@theia/monaco-editor-core": "1.83.101", "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" }, diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 4795d8da7b582..ea9d89ada299e 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -16,13 +16,14 @@ import { Command, CommandContribution, CommandHandler, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { ApplicationShell, codicon, CommonCommands } from '@theia/core/lib/browser'; +import { ApplicationShell, codicon, CommonCommands, KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookService } from '../service/notebook-service'; import { CellEditType, CellKind, NotebookCommand } from '../../common'; import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service'; import { NotebookExecutionService } from '../service/notebook-execution-service'; import { NotebookEditorWidget } from '../notebook-editor-widget'; +import { NotebookEditorWidgetService } from '..'; export namespace NotebookCommands { export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({ @@ -59,10 +60,20 @@ export namespace NotebookCommands { category: 'Notebook', iconClass: codicon('clear-all') }); + + export const CHANGE_SELECTED_CELL = Command.toDefaultLocalizedCommand({ + id: 'notebook.change-selected-cell', + category: 'Notebook', + }); +} + +export enum CellChangeDirection { + Up = 'up', + Down = 'down' } @injectable() -export class NotebookActionsContribution implements CommandContribution, MenuContribution { +export class NotebookActionsContribution implements CommandContribution, MenuContribution, KeybindingContribution { @inject(NotebookService) protected notebookService: NotebookService; @@ -76,6 +87,9 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon @inject(ApplicationShell) protected shell: ApplicationShell; + @inject(NotebookEditorWidgetService) + protected notebookEditorWidgetService: NotebookEditorWidgetService; + registerCommands(commands: CommandRegistry): void { commands.registerCommand(NotebookCommands.ADD_NEW_CELL_COMMAND, { execute: (notebookModel: NotebookModel, cellKind: CellKind, index?: number) => { @@ -120,6 +134,24 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon notebookModel => notebookModel.cells.forEach(cell => cell.spliceNotebookCellOutputs({ start: 0, deleteCount: cell.outputs.length, newOutputs: [] })) )); + commands.registerCommand(NotebookCommands.CHANGE_SELECTED_CELL, + { + execute: (change: number | CellChangeDirection) => { + const model = this.notebookEditorWidgetService.focusedEditor?.model; + if (model && typeof change === 'number') { + model.setSelectedCell(model.cells[change]); + } else if (model && model.selectedCell) { + const currentIndex = model.cells.indexOf(model.selectedCell); + if (change === CellChangeDirection.Up && currentIndex > 0) { + model.setSelectedCell(model.cells[currentIndex - 1]); + } else if (currentIndex < model.cells.length - 1) { + model.setSelectedCell(model.cells[currentIndex + 1]); + } + } + } + } + ); + commands.registerHandler(CommonCommands.UNDO.id, { isEnabled: () => { const widget = this.shell.activeWidget; @@ -134,6 +166,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon }, execute: () => (this.shell.activeWidget as NotebookEditorWidget).redo() }); + } protected editableCommandHandler(execute: (notebookModel: NotebookModel) => void): CommandHandler { @@ -179,6 +212,21 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon // other items } + registerKeybindings(keybindings: KeybindingRegistry): void { + keybindings.registerKeybindings( + { + command: NotebookCommands.CHANGE_SELECTED_CELL.id, + keybinding: 'ArrowUp', + args: CellChangeDirection.Up + }, + { + command: NotebookCommands.CHANGE_SELECTED_CELL.id, + keybinding: 'ArrowDown', + args: CellChangeDirection.Down + } + ); + } + } export namespace NotebookMenus { 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 b3e1eade2be7d..eaad4f705078e 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -15,15 +15,20 @@ // ***************************************************************************** import { Command, CommandContribution, CommandHandler, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls } from '@theia/core'; -import { codicon } from '@theia/core/lib/browser'; +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 } from './notebook-context-keys'; +import { + NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, + NotebookContextKeys, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_EDITOR_FOCUSED, + NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_EDITABLE +} 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 { CellEditType } from '../../common'; +import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ @@ -69,7 +74,7 @@ export namespace NotebookCellCommands { } @injectable() -export class NotebookCellActionContribution implements MenuContribution, CommandContribution { +export class NotebookCellActionContribution implements MenuContribution, CommandContribution, KeybindingContribution { @inject(ContextKeyService) protected contextKeyService: ContextKeyService; @@ -77,6 +82,9 @@ export class NotebookCellActionContribution implements MenuContribution, Command @inject(NotebookExecutionService) protected notebookExecutionService: NotebookExecutionService; + @inject(NotebookEditorWidgetService) + protected notebookEditorWidgetService: NotebookEditorWidgetService; + @postConstruct() protected init(): void { NotebookContextKeys.initNotebookContextKeys(this.contextKeyService); @@ -172,8 +180,8 @@ export class NotebookCellActionContribution implements MenuContribution, Command } registerCommands(commands: CommandRegistry): void { - commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, this.editableCellCommandHandler((_, cell) => cell.requestEdit())); - commands.registerCommand(NotebookCellCommands.STOP_EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => cell.requestStopEdit() }); + commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, this.editableCellCommandHandler((_, cell) => (cell ?? this.getSelectedCell()).requestEdit())); + commands.registerCommand(NotebookCellCommands.STOP_EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => (cell ?? this.getSelectedCell()).requestStopEdit() }); commands.registerCommand(NotebookCellCommands.DELETE_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => notebookModel.applyEdits([{ editType: CellEditType.Replace, @@ -206,6 +214,25 @@ export class NotebookCellActionContribution implements MenuContribution, Command } }; } + + protected getSelectedCell(): NotebookCellModel | undefined { + return this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell; + } + + registerKeybindings(keybindings: KeybindingRegistry): void { + keybindings.registerKeybindings( + { + command: NotebookCellCommands.EDIT_COMMAND.id, + keybinding: 'Enter', + when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_EDITABLE}`, + }, + { + command: NotebookCellCommands.STOP_EDIT_COMMAND.id, + keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(), + when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`, + } + ); + } } export namespace NotebookCellActionContribution { diff --git a/packages/notebook/src/browser/contributions/notebook-keybindings.ts b/packages/notebook/src/browser/contributions/notebook-keybindings.ts deleted file mode 100644 index bf5f9e0ad3c35..0000000000000 --- a/packages/notebook/src/browser/contributions/notebook-keybindings.ts +++ /dev/null @@ -1,37 +0,0 @@ - -// ***************************************************************************** -// 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 -// ***************************************************************************** -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser'; -import { NotebookCellCommands } from './notebook-cell-actions-contribution'; -import { NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED } from './notebook-context-keys'; - -export class NotebookKeybinding implements KeybindingContribution { - - registerKeybindings(keybindings: KeybindingRegistry): void { - keybindings.registerKeybindings( - { - command: NotebookCellCommands.EDIT_COMMAND.id, - keybinding: 'Enter', - when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && !${NOTEBOOK_CELL_EDITOR_FOCUSED}`, - } - ); - } -} diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index f78cd147fb8fa..ca894b04aae98 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -43,6 +43,8 @@ export function createNotebookEditorWidgetContainer(parent: interfaces.Container child.bind(NotebookContextManager).toSelf().inSingletonScope(); child.bind(NotebookMainToolbarRenderer).toSelf().inSingletonScope(); + child.bind(NotebookCodeCellRenderer).toSelf().inSingletonScope(); + child.bind(NotebookMarkdownCellRenderer).toSelf().inSingletonScope(); child.bind(NotebookEditorWidget).toSelf(); diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index ec7e3774b30fa..cb6814b219bd6 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -16,7 +16,7 @@ import '../../src/browser/style/index.css'; import { ContainerModule } from '@theia/core/shared/inversify'; -import { OpenHandler, WidgetFactory } from '@theia/core/lib/browser'; +import { KeybindingContribution, OpenHandler, WidgetFactory } from '@theia/core/lib/browser'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { NotebookOpenHandler } from './notebook-open-handler'; import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core'; @@ -31,8 +31,6 @@ import { NotebookCellToolbarFactory } from './view/notebook-cell-toolbar-factory import { createNotebookModelContainer, NotebookModel, NotebookModelFactory, NotebookModelProps } from './view-model/notebook-model'; import { createNotebookCellModelContainer, NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from './view-model/notebook-cell-model'; import { createNotebookEditorWidgetContainer, NotebookEditorWidgetContainerFactory, NotebookEditorProps, NotebookEditorWidget } from './notebook-editor-widget'; -import { NotebookCodeCellRenderer } from './view/notebook-code-cell-view'; -import { NotebookMarkdownCellRenderer } from './view/notebook-markdown-cell-view'; import { NotebookActionsContribution } from './contributions/notebook-actions-contribution'; import { NotebookExecutionService } from './service/notebook-execution-service'; import { NotebookExecutionStateService } from './service/notebook-execution-state-service'; @@ -72,13 +70,12 @@ export default new ContainerModule(bind => { bind(NotebookCellActionContribution).toSelf().inSingletonScope(); bind(MenuContribution).toService(NotebookCellActionContribution); bind(CommandContribution).toService(NotebookCellActionContribution); + bind(KeybindingContribution).toService(NotebookCellActionContribution); bind(NotebookActionsContribution).toSelf().inSingletonScope(); bind(CommandContribution).toService(NotebookActionsContribution); bind(MenuContribution).toService(NotebookActionsContribution); - - bind(NotebookCodeCellRenderer).toSelf().inSingletonScope(); - bind(NotebookMarkdownCellRenderer).toSelf().inSingletonScope(); + bind(KeybindingContribution).toService(NotebookActionsContribution); bind(NotebookEditorWidgetContainerFactory).toFactory(ctx => (props: NotebookEditorProps) => createNotebookEditorWidgetContainer(ctx.container, props).get(NotebookEditorWidget) diff --git a/packages/notebook/src/browser/service/notebook-context-manager.ts b/packages/notebook/src/browser/service/notebook-context-manager.ts index 4f645270d1ccd..53fa382868f67 100644 --- a/packages/notebook/src/browser/service/notebook-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-context-manager.ts @@ -19,6 +19,7 @@ import { ContextKeyChangeEvent, ContextKeyService, ScopedValueStore } from '@the import { DisposableCollection, Emitter } from '@theia/core'; import { NotebookKernelService } from './notebook-kernel-service'; import { + NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED, @@ -46,59 +47,63 @@ export class NotebookContextManager { protected _context?: HTMLElement; + scopedStore: ScopedValueStore; + get context(): HTMLElement | undefined { return this._context; } init(widget: NotebookEditorWidget): void { this._context = widget.node; - const scopedStore = this.contextKeyService.createScoped(widget.node); + this.scopedStore = this.contextKeyService.createScoped(widget.node); this.toDispose.dispose(); - scopedStore.setContext(NOTEBOOK_VIEW_TYPE, widget?.notebookType); + this.scopedStore.setContext(NOTEBOOK_VIEW_TYPE, widget?.notebookType); // Kernel related keys const kernel = widget?.model ? this.notebookKernelService.getSelectedNotebookKernel(widget.model) : undefined; - scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!kernel); - scopedStore.setContext(NOTEBOOK_KERNEL, kernel?.id); + this.scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!kernel); + this.scopedStore.setContext(NOTEBOOK_KERNEL, kernel?.id); this.toDispose.push(this.notebookKernelService.onDidChangeSelectedKernel(e => { if (e.notebook.toString() === widget?.getResourceUri()?.toString()) { - scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!e.newKernel); - scopedStore.setContext(NOTEBOOK_KERNEL, e.newKernel); + this.scopedStore.setContext(NOTEBOOK_KERNEL_SELECTED, !!e.newKernel); + this.scopedStore.setContext(NOTEBOOK_KERNEL, e.newKernel); this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL])); } })); // Cell Selection realted keys - scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!widget.model?.selectedCell); + this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!widget.model?.selectedCell); widget.model?.onDidChangeSelectedCell(e => { - scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!e); + this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!e); this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_FOCUSED])); }); - widget.model?.onDidChangeSelectedCell(e => this.selectedCellChanged(e, scopedStore)); + widget.model?.onDidChangeSelectedCell(e => this.selectedCellChanged(e)); this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_VIEW_TYPE, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL])); } protected cellDisposables = new DisposableCollection(); - selectedCellChanged(cell: NotebookCellModel | undefined, scopedStore: ScopedValueStore): void { + selectedCellChanged(cell: NotebookCellModel | undefined): void { this.cellDisposables.dispose(); - scopedStore.setContext(NOTEBOOK_CELL_TYPE, cell ? cell.cellKind === CellKind.Code ? 'code' : 'markdown' : undefined); + this.scopedStore.setContext(NOTEBOOK_CELL_TYPE, cell ? cell.cellKind === CellKind.Code ? 'code' : 'markdown' : undefined); if (cell) { - scopedStore.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cell.editing); + this.scopedStore.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cell.editing); + this.scopedStore.setContext(NOTEBOOK_CELL_EDITABLE, cell.cellKind === CellKind.Markup && !cell.editing); this.cellDisposables.push(cell.onDidRequestCellEditChange(cellEdit => { - scopedStore?.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit); + this.scopedStore.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit); + this.scopedStore.setContext(NOTEBOOK_CELL_EDITABLE, cell.cellKind === CellKind.Markup && !cellEdit); this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_MARKDOWN_EDIT_MODE])); })); this.cellDisposables.push(this.executionStateService.onDidChangeExecution(e => { if (cell && e.affectsCell(cell.uri)) { - scopedStore?.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed); - scopedStore?.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle'); + this.scopedStore.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed); + this.scopedStore.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle'); this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE])); } })); diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 77b2f49aeb96d..1294d1604985c 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -25,7 +25,6 @@ import { NotebookContentChangedEvent, NotebookModelWillAddRemoveEvent, CellEditO import { NotebookSerializer } from '../service/notebook-service'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-model'; -import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; @@ -80,9 +79,6 @@ export class NotebookModel implements Saveable, Disposable { @inject(NotebookModelProps) protected props: NotebookModelProps; - @inject(MonacoTextModelService) - protected modelService: MonacoTextModelService; - @inject(NotebookCellModelFactory) protected cellModelFactory: NotebookCellModelFactory; readonly autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange'; diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 723e2ef3b5b10..31cdc04c35f2b 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -18,17 +18,20 @@ import * as React from '@theia/core/shared/react'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor'; -import { MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor'; +import { MonacoEditor, MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor'; import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider'; import { DisposableCollection } from '@theia/core'; +import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; +import { NotebookContextManager } from '../service/notebook-context-manager'; interface CellEditorProps { notebookModel: NotebookModel, cell: NotebookCellModel, - monacoServices: MonacoEditorServices + monacoServices: MonacoEditorServices, + notebookContextManager: NotebookContextManager; } -const DEFAULT_EDITOR_OPTIONS = { +const DEFAULT_EDITOR_OPTIONS: MonacoEditor.IOptions = { ...MonacoEditorProvider.inlineOptions, minHeight: -1, maxHeight: -1, @@ -68,7 +71,8 @@ export class CellEditor extends React.Component { editorModel, editorNode, monacoServices, - DEFAULT_EDITOR_OPTIONS); + DEFAULT_EDITOR_OPTIONS, + [[IContextKeyService, this.props.notebookContextManager.scopedStore]]); this.toDispose.push(this.editor); this.editor.setLanguage(cell.language); this.toDispose.push(this.editor.getControl().onDidContentSizeChange(() => { @@ -91,9 +95,9 @@ export class CellEditor extends React.Component { override render(): React.ReactNode { return
this.setContainer(container)}> + ref={container => this.setContainer(container)}>
; - } + } } 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 3f46372b90d2c..3121d0ac6842c 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -53,6 +53,10 @@ export class NotebookCellListView extends React.Component cell === this.state.selectedCell) }); } })); + + this.toDispose.push(props.notebookModel.onDidChangeSelectedCell(cell => { + this.setState({ ...this.state, selectedCell: cell }); + })); } override componentWillUnmount(): void { diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 6be50f8812666..6d32dbd4e413d 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -29,6 +29,7 @@ import { CellExecution, NotebookExecutionStateService } from '../service/noteboo import { codicon } from '@theia/core/lib/browser'; import { NotebookCellExecutionState } from '../../common'; import { DisposableCollection } from '@theia/core'; +import { NotebookContextManager } from '../service/notebook-context-manager'; @injectable() export class NotebookCodeCellRenderer implements CellRenderer { @@ -47,6 +48,9 @@ export class NotebookCodeCellRenderer implements CellRenderer { @inject(NotebookExecutionStateService) protected readonly executionStateService: NotebookExecutionStateService; + @inject(NotebookContextManager) + protected readonly notebookContextManager: NotebookContextManager; + render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode { return
@@ -56,7 +60,7 @@ export class NotebookCodeCellRenderer implements CellRenderer {

{`[${cell.exec ?? ' '}]`}

*/}
- +
diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index af84d9fe361fc..5d2d0cff1e605 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -24,6 +24,7 @@ import { CellEditor } from './notebook-cell-editor'; import { inject, injectable } from '@theia/core/shared/inversify'; import { MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor'; import { nls } from '@theia/core'; +import { NotebookContextManager } from '../service/notebook-context-manager'; @injectable() export class NotebookMarkdownCellRenderer implements CellRenderer { @@ -33,9 +34,12 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { @inject(MonacoEditorServices) protected readonly monacoServices: MonacoEditorServices; + @inject(NotebookContextManager) + protected readonly notebookContextManager: NotebookContextManager; + render(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode { return ; + cell={cell} notebookModel={notebookModel} notebookContextManager={this.notebookContextManager} />; } } @@ -46,9 +50,10 @@ interface MarkdownCellProps { cell: NotebookCellModel, notebookModel: NotebookModel + notebookContextManager: NotebookContextManager; } -function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel }: MarkdownCellProps): React.JSX.Element { +function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager }: MarkdownCellProps): React.JSX.Element { const [editMode, setEditMode] = React.useState(false); React.useEffect(() => { @@ -62,7 +67,7 @@ function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel }: } return editMode ? - : + :
cell.requestEdit()} // This sets the non React HTML node from the markdown renderers output as a child node to this react component From 15122ec605a4e3990e95808f70e1c8f8b02d0c24 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 15 Mar 2024 13:47:14 +0100 Subject: [PATCH 08/13] more keybindings Signed-off-by: Jonah Iden --- .../notebook-actions-contribution.ts | 15 ++++++++------ .../notebook-cell-actions-contribution.ts | 20 +++++++++++++++++++ .../browser/view/notebook-cell-list-view.tsx | 7 ++++--- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index ea9d89ada299e..f58dde514d2f0 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -23,7 +23,8 @@ import { CellEditType, CellKind, NotebookCommand } from '../../common'; import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service'; import { NotebookExecutionService } from '../service/notebook-execution-service'; import { NotebookEditorWidget } from '../notebook-editor-widget'; -import { NotebookEditorWidgetService } from '..'; +import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; +import { NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED } from './notebook-context-keys'; export namespace NotebookCommands { export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({ @@ -144,7 +145,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon const currentIndex = model.cells.indexOf(model.selectedCell); if (change === CellChangeDirection.Up && currentIndex > 0) { model.setSelectedCell(model.cells[currentIndex - 1]); - } else if (currentIndex < model.cells.length - 1) { + } else if (change === CellChangeDirection.Down && currentIndex < model.cells.length - 1) { model.setSelectedCell(model.cells[currentIndex + 1]); } } @@ -216,13 +217,15 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon keybindings.registerKeybindings( { command: NotebookCommands.CHANGE_SELECTED_CELL.id, - keybinding: 'ArrowUp', - args: CellChangeDirection.Up + keybinding: 'up', + args: CellChangeDirection.Up, + when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` }, { command: NotebookCommands.CHANGE_SELECTED_CELL.id, - keybinding: 'ArrowDown', - args: CellChangeDirection.Down + keybinding: 'down', + args: CellChangeDirection.Down, + when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` } ); } 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 eaad4f705078e..c937c6695f9cc 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -230,6 +230,26 @@ export class NotebookCellActionContribution implements MenuContribution, Command command: NotebookCellCommands.STOP_EDIT_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(), when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`, + }, + { + command: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, + keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(), + when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, + }, + { + command: NotebookCellCommands.DELETE_COMMAND.id, + keybinding: 'd d', + when: `${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' && ${NOTEBOOK_CELL_EXECUTING}`, + }, + { + 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' && ${NOTEBOOK_CELL_EXECUTING}`, } ); } 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 3121d0ac6842c..904144b49e868 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -75,14 +75,15 @@ export class NotebookCellListView extends React.Component this.onDragOver(e, cell, 'top')} /> {this.shouldRenderDragOverIndicator(cell, 'top') && }
  • { - this.setState({ selectedCell: cell }); + onClick={e => { + this.setState({ ...this.state, selectedCell: cell }); this.props.notebookModel.setSelectedCell(cell); }} onDragStart={e => this.onDragStart(e, index)} onDragOver={e => this.onDragOver(e, cell)} onDrop={e => this.onDrop(e, index)} - draggable={true}> + draggable={true} + ref={ref => cell === this.state.selectedCell && ref?.getBoundingClientRect(). && ref?.scrollIntoView()}>
    {this.renderCellContent(cell, index)} From 37e5a1d51a2a4fa5c096e433a0096554c25b6772 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 15 Mar 2024 15:48:14 +0100 Subject: [PATCH 09/13] added insert cell commands Signed-off-by: Jonah Iden --- .../notebook-actions-contribution.ts | 15 ++++-- .../notebook-cell-actions-contribution.ts | 54 ++++++++++++++----- .../browser/view/notebook-cell-list-view.tsx | 2 +- 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index f58dde514d2f0..05b4bbb377195 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -93,8 +93,17 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon registerCommands(commands: CommandRegistry): void { commands.registerCommand(NotebookCommands.ADD_NEW_CELL_COMMAND, { - execute: (notebookModel: NotebookModel, cellKind: CellKind, index?: number) => { - const insertIndex = index ?? (notebookModel.selectedCell ? notebookModel.cells.indexOf(notebookModel.selectedCell) : 0); + execute: (notebookModel: NotebookModel, cellKind: CellKind = CellKind.Markup, index?: number | 'above' | 'below') => { + notebookModel = notebookModel ?? this.notebookEditorWidgetService.focusedEditor?.model; + + let insertIndex: number = 0; + if (index && index >= 0) { + insertIndex = index as number; + } else if (notebookModel.selectedCell && typeof index === 'string') { + // if index is -1 insert below otherwise at the index of the selected cell which is above the selected. + insertIndex = notebookModel.cells.indexOf(notebookModel.selectedCell) + (index === 'below' ? 1 : 0); + } + let firstCodeCell; if (cellKind === CellKind.Code) { firstCodeCell = notebookModel.cells.find(cell => cell.cellKind === CellKind.Code); @@ -226,7 +235,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon keybinding: 'down', args: CellChangeDirection.Down, when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` - } + }, ); } 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 c937c6695f9cc..1905046fa48f4 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -27,8 +27,9 @@ import { 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 { CellEditType } from '../../common'; +import { CellEditType, CellKind } from '../../common'; import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; +import { NotebookCommands } from './notebook-actions-contribution'; export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ @@ -71,6 +72,25 @@ export namespace NotebookCellCommands { id: 'notebook.cell.change-presentation', 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' + }); } @injectable() @@ -183,12 +203,17 @@ export class NotebookCellActionContribution implements MenuContribution, Command commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, this.editableCellCommandHandler((_, cell) => (cell ?? this.getSelectedCell()).requestEdit())); commands.registerCommand(NotebookCellCommands.STOP_EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => (cell ?? this.getSelectedCell()).requestStopEdit() }); commands.registerCommand(NotebookCellCommands.DELETE_COMMAND, - this.editableCellCommandHandler((notebookModel, cell) => notebookModel.applyEdits([{ - editType: CellEditType.Replace, - index: notebookModel.cells.indexOf(cell), - count: 1, - cells: [] - }], true))); + this.editableCellCommandHandler((notebookModel, cell) => { + notebookModel = notebookModel ?? this.notebookEditorWidgetService.focusedEditor?.model; + cell = cell ?? this.getSelectedCell(); + notebookModel.applyEdits([{ + editType: CellEditType.Replace, + index: notebookModel.cells.indexOf(cell), + count: 1, + cells: [] + }] + , true); + })); commands.registerCommand(NotebookCellCommands.SPLIT_CELL_COMMAND); commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND, this.editableCellCommandHandler( @@ -203,6 +228,16 @@ export class NotebookCellActionContribution implements MenuContribution, Command commands.registerCommand(NotebookCellCommands.CHANGE_OUTPUT_PRESENTATION_COMMAND, this.editableCellCommandHandler( (_, __, output) => output?.requestOutputPresentationUpdate() )); + + const insertCommand = (type: CellKind, index: number | 'above' | 'below'): CommandHandler => ({ + execute: () => { + commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, undefined, type, index); + } + }); + commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_ABOVE_COMMAND, insertCommand(CellKind.Code, 'above')); + commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND, insertCommand(CellKind.Code, 'below')); + commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_ABOVE_COMMAND, insertCommand(CellKind.Markup, 'above')); + commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND, insertCommand(CellKind.Markup, 'below')); } protected editableCellCommandHandler(execute: (notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel) => void): CommandHandler { @@ -236,11 +271,6 @@ export class NotebookCellActionContribution implements MenuContribution, Command keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(), when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, }, - { - command: NotebookCellCommands.DELETE_COMMAND.id, - keybinding: 'd d', - when: `${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/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 904144b49e868..6ad3465386d8a 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -83,7 +83,7 @@ export class NotebookCellListView extends React.Component this.onDragOver(e, cell)} onDrop={e => this.onDrop(e, index)} draggable={true} - ref={ref => cell === this.state.selectedCell && ref?.getBoundingClientRect(). && ref?.scrollIntoView()}> + ref={ref => cell === this.state.selectedCell && ref?.scrollIntoView({ block: 'nearest' })}>
    {this.renderCellContent(cell, index)} From 7b82b9ce67e7794efb7ef6a63dddbf21c470236f Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 18 Mar 2024 11:37:43 +0100 Subject: [PATCH 10/13] smaller fixes, refactoring, changing cell type command Signed-off-by: Jonah Iden --- packages/core/src/browser/keybinding.ts | 3 + .../browser/contributions/cell-operations.ts | 38 +++++++++ .../notebook-actions-contribution.ts | 4 +- .../notebook-cell-actions-contribution.ts | 83 +++++++++++++++---- .../browser/view-model/notebook-cell-model.ts | 2 +- 5 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 packages/notebook/src/browser/contributions/cell-operations.ts diff --git a/packages/core/src/browser/keybinding.ts b/packages/core/src/browser/keybinding.ts index a550ce746be3d..a88eba3057be2 100644 --- a/packages/core/src/browser/keybinding.ts +++ b/packages/core/src/browser/keybinding.ts @@ -497,6 +497,9 @@ export class KeybindingRegistry { isEnabledInScope(binding: common.Keybinding, target: HTMLElement | undefined): boolean { const context = binding.context && this.contexts[binding.context]; + if (binding.command && !this.commandRegistry.isEnabled(binding.command, binding.args)) { + return false; + } if (context && !context.isEnabled(binding)) { return false; } diff --git a/packages/notebook/src/browser/contributions/cell-operations.ts b/packages/notebook/src/browser/contributions/cell-operations.ts new file mode 100644 index 0000000000000..c77c0665f4d00 --- /dev/null +++ b/packages/notebook/src/browser/contributions/cell-operations.ts @@ -0,0 +1,38 @@ +// ***************************************************************************** +// 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 { CellEditType, CellKind } from '../../common'; +import { NotebookCellModel } from '../view-model/notebook-cell-model'; +import { NotebookModel } from '../view-model/notebook-model'; + +/** + * a collection of different reusable notbook cell operations + */ + +export function changeCellType(notebookModel: NotebookModel, cell: NotebookCellModel, type: CellKind): void { + if (cell.cellKind === type) { + return; + } + notebookModel.applyEdits([{ + editType: CellEditType.Replace, + index: notebookModel.cells.indexOf(cell), + count: 1, + cells: [{ + ...cell.getData(), + cellKind: type + }] + }], true); +} diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 05b4bbb377195..50f3ebf73a9ef 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -125,11 +125,11 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon }); commands.registerCommand(NotebookCommands.ADD_NEW_MARKDOWN_CELL_COMMAND, this.editableCommandHandler( - notebookModel => commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, notebookModel, CellKind.Markup) + notebookModel => commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, notebookModel, CellKind.Markup, 'below') )); commands.registerCommand(NotebookCommands.ADD_NEW_CODE_CELL_COMMAND, this.editableCommandHandler( - notebookModel => commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, notebookModel, CellKind.Code) + notebookModel => commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, notebookModel, CellKind.Code, 'below') )); commands.registerCommand(NotebookCommands.SELECT_KERNEL_COMMAND, this.editableCommandHandler( 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 1905046fa48f4..73f7c049c966e 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -30,6 +30,7 @@ import { NotebookCellOutputModel } from '../view-model/notebook-cell-output-mode import { CellEditType, CellKind } from '../../common'; import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; import { NotebookCommands } from './notebook-actions-contribution'; +import { changeCellType } from './cell-operations'; export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ @@ -58,6 +59,10 @@ export namespace NotebookCellCommands { 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', + }); + /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const STOP_CELL_EXECUTION_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.stop-cell-execution', iconClass: codicon('stop'), @@ -91,6 +96,16 @@ export namespace NotebookCellCommands { id: 'notebook.cell.insertMarkdownCellBelow', label: 'Insert Markdown Cell Below' }); + + export const TO_CODE_CELL_COMMAND = Command.toLocalizedCommand({ + id: 'notebook.cell.to-code-cell', + label: 'Change Cell to Code' + }); + + export const TO_MARKDOWN_CELL_COMMAND = Command.toLocalizedCommand({ + id: 'notebook.cell.to-markdown-cell', + label: 'Change Cell to Mardown' + }); } @injectable() @@ -200,12 +215,10 @@ export class NotebookCellActionContribution implements MenuContribution, Command } registerCommands(commands: CommandRegistry): void { - commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, this.editableCellCommandHandler((_, cell) => (cell ?? this.getSelectedCell()).requestEdit())); + commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, this.editableCellCommandHandler((_, cell) => cell.requestEdit())); commands.registerCommand(NotebookCellCommands.STOP_EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => (cell ?? this.getSelectedCell()).requestStopEdit() }); commands.registerCommand(NotebookCellCommands.DELETE_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { - notebookModel = notebookModel ?? this.notebookEditorWidgetService.focusedEditor?.model; - cell = cell ?? this.getSelectedCell(); notebookModel.applyEdits([{ editType: CellEditType.Replace, index: notebookModel.cells.indexOf(cell), @@ -217,27 +230,51 @@ export class NotebookCellActionContribution implements MenuContribution, Command commands.registerCommand(NotebookCellCommands.SPLIT_CELL_COMMAND); commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND, this.editableCellCommandHandler( - (notebookModel, cell) => this.notebookExecutionService.executeNotebookCells(notebookModel, [cell]) - )); + (notebookModel, cell) => { + this.notebookExecutionService.executeNotebookCells(notebookModel, [cell]); + }) + ); + + commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND, this.editableCellCommandHandler( + (notebookModel, cell) => { + commands.executeCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, notebookModel, cell); + const index = notebookModel.cells.indexOf(cell); + if (index < notebookModel.cells.length - 1) { + notebookModel.setSelectedCell(notebookModel.cells[index + 1]); + } else { + commands.executeCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND.id, notebookModel, CellKind.Code, 'below'); + } + }) + ); + commands.registerCommand(NotebookCellCommands.STOP_CELL_EXECUTION_COMMAND, { - execute: (notebookModel: NotebookModel, cell: NotebookCellModel) => this.notebookExecutionService.cancelNotebookCells(notebookModel, [cell]) + 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( - (_, cell) => cell.spliceNotebookCellOutputs({ start: 0, deleteCount: cell.outputs.length, newOutputs: [] }) + (_, cell) => cell.spliceNotebookCellOutputs({ start: 0, deleteCount: (cell ?? this.getSelectedCell()).outputs.length, newOutputs: [] }) )); commands.registerCommand(NotebookCellCommands.CHANGE_OUTPUT_PRESENTATION_COMMAND, this.editableCellCommandHandler( (_, __, output) => output?.requestOutputPresentationUpdate() )); - const insertCommand = (type: CellKind, index: number | 'above' | 'below'): CommandHandler => ({ - execute: () => { - commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, undefined, type, index); - } - }); + const insertCommand = (type: CellKind, index: number | 'above' | 'below'): CommandHandler => this.editableCellCommandHandler(() => + commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, undefined, type, index) + ); commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_ABOVE_COMMAND, insertCommand(CellKind.Code, 'above')); commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND, insertCommand(CellKind.Code, 'below')); commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_ABOVE_COMMAND, insertCommand(CellKind.Markup, 'above')); commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND, insertCommand(CellKind.Markup, 'below')); + + commands.registerCommand(NotebookCellCommands.TO_CODE_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { + changeCellType(notebookModel, cell, CellKind.Code); + })); + commands.registerCommand(NotebookCellCommands.TO_MARKDOWN_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { + changeCellType(notebookModel, cell, CellKind.Markup); + })); } protected editableCellCommandHandler(execute: (notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel) => void): CommandHandler { @@ -245,6 +282,8 @@ export class NotebookCellActionContribution implements MenuContribution, Command 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); } }; @@ -271,15 +310,30 @@ export class NotebookCellActionContribution implements MenuContribution, Command keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(), when: `${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_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, + }, { 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' && ${NOTEBOOK_CELL_EXECUTING}`, + 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' && ${NOTEBOOK_CELL_EXECUTING}`, + when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, + }, + { + command: NotebookCellCommands.TO_CODE_CELL_COMMAND.id, + keybinding: 'Y', + when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'markdown'`, + }, + { + command: NotebookCellCommands.TO_MARKDOWN_CELL_COMMAND.id, + keybinding: 'M', + when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, } ); } @@ -295,3 +349,4 @@ export namespace NotebookCellActionContribution { export const ADDITIONAL_OUTPUT_SIDEBAR_MENU = [...OUTPUT_SIDEBAR_MENU, 'more']; } + diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index a6b670e771eca..015295117a3ff 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -139,7 +139,7 @@ export class NotebookCellModel implements NotebookCell, Disposable { protected textModel?: MonacoEditorModel; get text(): string { - return this.textModel ? this.textModel.getText() : this.source; + return this.textModel && !this.textModel.isDisposed() ? this.textModel.getText() : this.source; } get source(): string { From 9128397ba2a8e1fbcc2eafe2afef96bace02ba6a Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 19 Mar 2024 09:29:10 +0100 Subject: [PATCH 11/13] review changes remove unused inject --- .../notebook/src/browser/service/notebook-kernel-service.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/notebook/src/browser/service/notebook-kernel-service.ts b/packages/notebook/src/browser/service/notebook-kernel-service.ts index 9f5e0b81f3a38..5693ab8469283 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-service.ts @@ -158,9 +158,6 @@ export class NotebookKernelService { @inject(StorageService) protected storageService: StorageService; - @inject(NotebookEditorWidgetService) - protected notebookEditorService: NotebookEditorWidgetService; - protected readonly kernels = new Map(); protected notebookBindings: Record = {}; From 39d7aa5769bf44abdf9b8dcb42119dfe793ca012 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 19 Mar 2024 10:17:34 +0100 Subject: [PATCH 12/13] remove unneccessary import Signed-off-by: Jonah Iden --- packages/notebook/src/browser/service/notebook-kernel-service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/notebook/src/browser/service/notebook-kernel-service.ts b/packages/notebook/src/browser/service/notebook-kernel-service.ts index 5693ab8469283..f6271b2cbcb4d 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-service.ts @@ -24,7 +24,6 @@ import { StorageService } from '@theia/core/lib/browser'; import { NotebookKernelSourceAction } from '../../common'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookService } from './notebook-service'; -import { NotebookEditorWidgetService } from './notebook-editor-widget-service'; export interface SelectedNotebookKernelChangeEvent { notebook: URI; From 2e74302d32536c98381d7f299cc5247a2edee0f0 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 19 Mar 2024 10:44:00 +0100 Subject: [PATCH 13/13] update cell context when selecte cell has been replaced Signed-off-by: Jonah Iden --- packages/notebook/src/browser/view-model/notebook-model.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 1294d1604985c..7e88e23358eab 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -298,9 +298,13 @@ export class NotebookModel implements Saveable, Disposable { case CellEditType.Move: this.moveCellToIndex(cellIndex, edit.length, edit.newIdx, computeUndoRedo); break; - + } + // if selected cell is affected update it because it can potentially have been replaced + if (cell === this.selectedCell) { + this.setSelectedCell(this.cells[cellIndex]); } } + } protected async replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): Promise {