diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 6aea044f3680fa4e9af0565fed1fb81bf638acef..a3324c98df20a4405c3aba6e1ccea57c3b0f22b8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -421,6 +421,15 @@ export enum CursorAtBoundary { Both } +export interface CellViewModelStateChangeEvent { + metadataChanged?: boolean; + selectionChanged?: boolean; + focusModeChanged?: boolean; + runStateChanged?: boolean; + editStateChanged?: boolean; + languageChanged?: boolean; +} + /** * [start, start + length - 1] */ diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index ad05e14402eeb761fc2bcd221cf43fc4f0948081..5b0fc9681422ece49a9bda9b219400b370a942d5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -98,8 +98,10 @@ export class NotebookCellList extends WorkbenchList implements ID // we only validate the first focused element const focusedElement = e.elements[0]; - cursorSelectionListener = focusedElement.onDidChangeCursorSelection(() => { - recomputeContext(focusedElement); + cursorSelectionListener = focusedElement.onDidChangeState((e) => { + if (e.selectionChanged) { + recomputeContext(focusedElement); + } }); textEditorAttachListener = focusedElement.onDidChangeEditorAttachState(() => { @@ -478,7 +480,9 @@ export class NotebookCellList extends WorkbenchList implements ID } const editorAttachedPromise = new Promise((resolve, reject) => { - element.onDidChangeEditorAttachState(state => state ? resolve() : reject()); + element.onDidChangeEditorAttachState(() => { + element.editorAttached ? resolve() : reject(); + }); }); editorAttachedPromise.then(() => { @@ -750,7 +754,7 @@ export class NotebookCellList extends WorkbenchList implements ID function getEditorAttachedPromise(element: CellViewModel) { return new Promise((resolve, reject) => { - Event.once(element.onDidChangeEditorAttachState)(state => state ? resolve() : reject()); + Event.once(element.onDidChangeEditorAttachState)(() => element.editorAttached ? resolve() : reject()); }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 0ffb8221bf503e05dc30be87ec056ef8fab7a7bb..098402f540bda28565ae38874960be023ba9343d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -327,13 +327,17 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR }; updateForMetadata(); - elementDisposable.add(element.onDidChangeMetadata(() => { - updateForMetadata(); + elementDisposable.add(element.onDidChangeState((e) => { + if (e.metadataChanged) { + updateForMetadata(); + } })); const editModeKey = contextKeyService.createKey(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY, element.editState === CellEditState.Editing); - elementDisposable.add(element.onDidChangeCellEditState(() => { - editModeKey.set(element.editState === CellEditState.Editing); + elementDisposable.add(element.onDidChangeState((e) => { + if (e.editStateChanged) { + editModeKey.set(element.editState === CellEditState.Editing); + } })); this.setupCellToolbarActions(contextKeyService, templateData, elementDisposable); @@ -517,13 +521,21 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const runStateKey = contextKeyService.createKey(NOTEBOOK_CELL_RUN_STATE_CONTEXT_KEY, CellRunState[element.runState]); this.updateForRunState(element, templateData, runStateKey); - elementDisposable.add(element.onDidChangeCellRunState(() => this.updateForRunState(element, templateData, runStateKey))); + elementDisposable.add(element.onDidChangeState((e) => { + if (e.runStateChanged) { + this.updateForRunState(element, templateData, runStateKey); + } + })); contextKeyService.createKey(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'code'); contextKeyService.createKey(NOTEBOOK_VIEW_TYPE, element.viewType); const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, !!(element.metadata?.editable)); this.updateForMetadata(element, templateData, cellEditableKey); - elementDisposable.add(element.onDidChangeMetadata(() => this.updateForMetadata(element, templateData, cellEditableKey))); + elementDisposable.add(element.onDidChangeState((e) => { + if (e.metadataChanged) { + this.updateForMetadata(element, templateData, cellEditableKey); + } + })); this.setupCellToolbarActions(contextKeyService, templateData, elementDisposable); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 80eab0b057e373d584558d55bc7f5a28989a7dbb..990b4c27937021b5486c9bf3a596fd86c87744f1 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -69,19 +69,29 @@ export class CodeCell extends Disposable { } }); - this._register(viewCell.onDidChangeFocusMode(() => { + this._register(viewCell.onDidChangeState((e) => { + if (!e.focusModeChanged) { + return; + } + if (viewCell.focusMode === CellFocusMode.Editor) { templateData.editor?.focus(); } })); templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel?.metadata).editable) }); - this._register(viewCell.onDidChangeMetadata(() => { - templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel?.metadata).editable) }); + this._register(viewCell.onDidChangeState((e) => { + if (e.metadataChanged) { + templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel?.metadata).editable) }); + } })); - this._register(viewCell.onDidChangeLanguage((e) => { - const mode = this._modeService.create(e); + this._register(viewCell.onDidChangeState((e) => { + if (!e.languageChanged) { + return; + } + + const mode = this._modeService.create(viewCell.language); templateData.editor?.getModel()?.setMode(mode.languageIdentifier); })); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index fa46b5a9b32b81f31cf54e47950bb8440cb85464..4f8fd8dc74bd5393d7bb1c3436e9f0ce07802838 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -142,12 +142,18 @@ export class StatefullMarkdownCell extends Disposable { } }; - this._register(viewCell.onDidChangeCellEditState(() => { - this.localDisposables.clear(); - viewUpdate(); + this._register(viewCell.onDidChangeState((e) => { + if (e.editStateChanged) { + this.localDisposables.clear(); + viewUpdate(); + } })); - this._register(viewCell.onDidChangeFocusMode(() => { + this._register(viewCell.onDidChangeState((e) => { + if (!e.focusModeChanged) { + return; + } + if (viewCell.focusMode === CellFocusMode.Editor) { this.editor?.focus(); } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index 4af9f361af59b16e310de1334373f8719c03a85b..4ddf8a2203cafca630bd27334436d005674e280e 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -12,7 +12,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; import { EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, CellRunState, CursorAtBoundary, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, CellRunState, CursorAtBoundary, ICellViewModel, CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; @@ -22,22 +22,12 @@ export const NotebookCellMetadataDefaults = { }; export abstract class BaseCellViewModel extends Disposable implements ICellViewModel { - protected readonly _onDidDispose = new Emitter(); - readonly onDidDispose = this._onDidDispose.event; - protected readonly _onDidChangeCellEditState = new Emitter(); - readonly onDidChangeCellEditState = this._onDidChangeCellEditState.event; - protected readonly _onDidChangeCellRunState = new Emitter(); - readonly onDidChangeCellRunState = this._onDidChangeCellRunState.event; - protected readonly _onDidChangeFocusMode = new Emitter(); - readonly onDidChangeFocusMode = this._onDidChangeFocusMode.event; - protected readonly _onDidChangeEditorAttachState = new Emitter(); + protected readonly _onDidChangeEditorAttachState = new Emitter(); + // Do not merge this event with `onDidChangeState` as we are using `Event.once(onDidChangeEditorAttachState)` elsewhere. readonly onDidChangeEditorAttachState = this._onDidChangeEditorAttachState.event; - protected readonly _onDidChangeCursorSelection: Emitter = this._register(new Emitter()); - public readonly onDidChangeCursorSelection: Event = this._onDidChangeCursorSelection.event; - protected readonly _onDidChangeMetadata: Emitter = this._register(new Emitter()); - public readonly onDidChangeMetadata: Event = this._onDidChangeMetadata.event; - protected readonly _onDidChangeLanguage: Emitter = this._register(new Emitter()); - public readonly onDidChangeLanguage: Event = this._onDidChangeLanguage.event; + protected readonly _onDidChangeState: Emitter = this._register(new Emitter()); + public readonly onDidChangeState: Event = this._onDidChangeState.event; + get handle() { return this.model.handle; } @@ -50,6 +40,9 @@ export abstract class BaseCellViewModel extends Disposable implements ICellViewM get metadata() { return this.model.metadata; } + get language() { + return this.model.language; + } abstract cellKind: CellKind; @@ -65,14 +58,14 @@ export abstract class BaseCellViewModel extends Disposable implements ICellViewM } this._editState = newState; - this._onDidChangeCellEditState.fire(); + this._onDidChangeState.fire({ editStateChanged: true }); } // TODO - move any "run"/"status" concept to Code-specific places private _currentTokenSource: CancellationTokenSource | undefined; public set currentTokenSource(v: CancellationTokenSource | undefined) { this._currentTokenSource = v; - this._onDidChangeCellRunState.fire(); + this._onDidChangeState.fire({ runStateChanged: true }); } public get currentTokenSource(): CancellationTokenSource | undefined { @@ -89,7 +82,7 @@ export abstract class BaseCellViewModel extends Disposable implements ICellViewM } set focusMode(newMode: CellFocusMode) { this._focusMode = newMode; - this._onDidChangeFocusMode.fire(); + this._onDidChangeState.fire({ focusModeChanged: true }); } protected _textEditor?: ICodeEditor; @@ -108,12 +101,12 @@ export abstract class BaseCellViewModel extends Disposable implements ICellViewM constructor(readonly viewType: string, readonly notebookHandle: number, readonly model: NotebookCellTextModel, public id: string) { super(); - this._register(model.onDidChangeLanguage((e) => { - this._onDidChangeLanguage.fire(e); + this._register(model.onDidChangeLanguage(() => { + this._onDidChangeState.fire({ languageChanged: true }); })); this._register(model.onDidChangeMetadata(() => { - this._onDidChangeMetadata.fire(); + this._onDidChangeState.fire({ metadataChanged: true }); })); } @@ -136,8 +129,8 @@ export abstract class BaseCellViewModel extends Disposable implements ICellViewM if (this._textEditor === editor) { if (this._cursorChangeListener === null) { - this._cursorChangeListener = this._textEditor.onDidChangeCursorSelection(() => this._onDidChangeCursorSelection.fire()); - this._onDidChangeCursorSelection.fire(); + this._cursorChangeListener = this._textEditor.onDidChangeCursorSelection(() => { this._onDidChangeState.fire({ selectionChanged: true }); }); + this._onDidChangeState.fire({ selectionChanged: true }); } return; } @@ -160,9 +153,9 @@ export abstract class BaseCellViewModel extends Disposable implements ICellViewM } }); - this._cursorChangeListener = this._textEditor.onDidChangeCursorSelection(() => this._onDidChangeCursorSelection.fire()); - this._onDidChangeCursorSelection.fire(); - this._onDidChangeEditorAttachState.fire(true); + this._cursorChangeListener = this._textEditor.onDidChangeCursorSelection(() => { this._onDidChangeState.fire({ selectionChanged: true }); }); + this._onDidChangeState.fire({ selectionChanged: true }); + this._onDidChangeEditorAttachState.fire(); } detachTextEditor() { @@ -179,7 +172,7 @@ export abstract class BaseCellViewModel extends Disposable implements ICellViewM this._textEditor = undefined; this._cursorChangeListener?.dispose(); this._cursorChangeListener = null; - this._onDidChangeEditorAttachState.fire(false); + this._onDidChangeEditorAttachState.fire(); } getText(): string { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 6f371d9512ea0bca732a582bb122e01bd5f54399..297bc15519e8e05ba6cb354718d5777637b3bb23 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -73,10 +73,6 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod this._onDidChangeOutputs.fire(splices); })); - this._register(this.model.onDidChangeMetadata(() => { - this._onDidChangeMetadata.fire(); - })); - this._outputCollection = new Array(this.model.outputs.length); this._buffer = null; @@ -97,7 +93,11 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } })); - this._register(this.onDidChangeLanguage((e) => { + this._register(this.onDidChangeState((e) => { + if (!e.languageChanged) { + return; + } + if (this._textModel && !this._textModel.isDisposed()) { }