From edd9dc989d44d386744a894b2dea1d650455aec0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 19 Oct 2022 09:20:06 -0700 Subject: [PATCH] Add CellPartCollection This new class wraps up a list of cell parts. This lets us avoid writing loops everywhere. In the future, we can also use this to optimize cell parts rendering (such as batching context changes) --- .../contrib/notebook/browser/view/cellPart.ts | 47 +++++++++++++++++++ .../browser/view/cellParts/codeCell.ts | 25 ++++------ .../browser/view/cellParts/markupCell.ts | 13 ++--- .../browser/view/notebookRenderingCommon.ts | 4 +- .../browser/view/renderers/cellRenderer.ts | 9 ++-- 5 files changed, 69 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts index ecebf3c976507..c61b9864c17b5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts @@ -56,3 +56,50 @@ export abstract class CellPart extends Disposable { */ updateForExecutionState(element: ICellViewModel, e: ICellExecutionStateChangedEvent): void { } } + +export class CellPartsCollection { + + constructor( + private readonly parts: readonly CellPart[], + ) { } + + concat(other: readonly CellPart[]): CellPartsCollection { + return new CellPartsCollection(this.parts.concat(other)); + } + + renderCell(element: ICellViewModel): void { + for (const part of this.parts) { + part.renderCell(element); + } + } + + unrenderCell(element: ICellViewModel): void { + for (const part of this.parts) { + part.unrenderCell(element); + } + } + + updateInternalLayoutNow(viewCell: ICellViewModel) { + for (const part of this.parts) { + part.updateInternalLayoutNow(viewCell); + } + } + + prepareLayout() { + for (const part of this.parts) { + part.prepareLayout(); + } + } + + updateState(viewCell: ICellViewModel, e: CellViewModelStateChangeEvent) { + for (const part of this.parts) { + part.updateState(viewCell, e); + } + } + + updateForExecutionState(viewCell: ICellViewModel, e: ICellExecutionStateChangedEvent) { + for (const part of this.parts) { + part.updateForExecutionState(viewCell, e); + } + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index 6426149d79daa..bc47297e81003 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -20,9 +20,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { CellFocusMode, EXPAND_CELL_INPUT_COMMAND_ID, IActiveNotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellPartsCollection } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions'; import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput'; -import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { CollapsedCodeCellExecutionIcon } from 'vs/workbench/contrib/notebook/browser/view/cellParts/codeCellExecutionIcon'; import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; @@ -35,7 +35,7 @@ export class CodeCell extends Disposable { private _renderedInputCollapseState: boolean | undefined; private _renderedOutputCollapseState: boolean | undefined; private _isDisposed: boolean = false; - private readonly cellParts: CellPart[]; + private readonly cellParts: CellPartsCollection; private _collapsedExecutionIcon: CollapsedCodeCellExecutionIcon; @@ -55,7 +55,7 @@ export class CodeCell extends Disposable { const cellEditorOptions = this._register(new CellEditorOptions(this.notebookEditor.getBaseCellEditorOptions(viewCell.language), this.notebookEditor.notebookOptions, this.configurationService)); this._outputContainerRenderer = this.instantiationService.createInstance(CellOutputContainer, notebookEditor, viewCell, templateData, { limit: 500 }); - this.cellParts = [...templateData.cellParts, cellEditorOptions, this._outputContainerRenderer]; + this.cellParts = templateData.cellParts.concat([cellEditorOptions, this._outputContainerRenderer]); const editorHeight = this.calculateInitEditorHeight(); this.initializeEditor(editorHeight); @@ -67,16 +67,12 @@ export class CodeCell extends Disposable { this._register(notebookExecutionStateService.onDidChangeCellExecution(e => { if (e.affectsCell(this.viewCell.uri)) { - this.cellParts.forEach(cellPart => { - cellPart.updateForExecutionState(this.viewCell, e); - }); + this.cellParts.updateForExecutionState(this.viewCell, e); } })); this._register(this.viewCell.onDidChangeState(e => { - this.cellParts.forEach(cellPart => { - cellPart.updateState(this.viewCell, e); - }); + this.cellParts.updateState(this.viewCell, e); if (e.outputIsHoveredChanged) { this.updateForOutputHover(); @@ -104,9 +100,10 @@ export class CodeCell extends Disposable { } })); - this.cellParts.forEach(cellPart => cellPart.renderCell(this.viewCell)); + this.cellParts.renderCell(this.viewCell); + this._register(toDisposable(() => { - this.cellParts.forEach(cellPart => cellPart.unrenderCell(this.viewCell)); + this.cellParts.unrenderCell(this.viewCell); })); this.updateEditorOptions(); @@ -123,7 +120,7 @@ export class CodeCell extends Disposable { } this._register(this.viewCell.onLayoutInfoRead(() => { - this.cellParts.forEach(cellPart => cellPart.prepareLayout()); + this.cellParts.prepareLayout(); })); const executionItemElement = DOM.append(this.templateData.cellInputCollapsedContainer, DOM.$('.collapsed-execution-icon')); @@ -146,9 +143,7 @@ export class CodeCell extends Disposable { private updateForLayout(): void { this._pendingLayout?.dispose(); this._pendingLayout = DOM.modify(() => { - this.cellParts.forEach(part => { - part.updateInternalLayoutNow(this.viewCell); - }); + this.cellParts.updateInternalLayoutNow(this.viewCell); }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts index 269be782dd6c6..8dbe08da1df4a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts @@ -69,9 +69,10 @@ export class MarkupCell extends Disposable { this.registerListeners(); // update for init state - this.templateData.cellParts.forEach(cellPart => cellPart.renderCell(this.viewCell)); + this.templateData.cellParts.renderCell(this.viewCell); + this._register(toDisposable(() => { - this.templateData.cellParts.forEach(cellPart => cellPart.unrenderCell(this.viewCell)); + this.templateData.cellParts.unrenderCell(this.viewCell); })); this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => { @@ -99,9 +100,7 @@ export class MarkupCell extends Disposable { } layoutCellParts() { - this.templateData.cellParts.forEach(part => { - part.updateInternalLayoutNow(this.viewCell); - }); + this.templateData.cellParts.updateInternalLayoutNow(this.viewCell); } private constructDOM() { @@ -123,9 +122,7 @@ export class MarkupCell extends Disposable { private registerListeners() { this._register(this.viewCell.onDidChangeState(e => { - this.templateData.cellParts.forEach(cellPart => { - cellPart.updateState(this.viewCell, e); - }); + this.templateData.cellParts.updateState(this.viewCell, e); })); this._register(this.viewCell.model.onDidChangeMetadata(() => { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 37b932a0398db..ec2b622dea66c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -15,7 +15,7 @@ import { Range } from 'vs/editor/common/core/range'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICellOutputViewModel, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; +import { CellPartsCollection } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -101,7 +101,7 @@ export interface BaseCellRenderTemplate { readonly templateDisposables: DisposableStore; readonly elementDisposables: DisposableStore; currentRenderedCell?: ICellViewModel; - cellParts: CellPart[]; + cellParts: CellPartsCollection; toJSON: () => object; } 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 3e7b8a61362fa..7832f49b3653b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -24,6 +24,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellPartsCollection } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { CellComments } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellComments'; import { CellContextKeyPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys'; import { CellDecorations } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations'; @@ -164,7 +165,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen this.notebookEditor)); const focusIndicatorBottom = new FastDomNode(DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom'))); - const cellParts = [ + const cellParts = new CellPartsCollection([ titleToolbar, templateDisposables.add(scopedInstaService.createInstance(BetweenCellToolbar, this.notebookEditor, titleToolbarContainer, bottomCellContainer)), templateDisposables.add(scopedInstaService.createInstance(CellEditorStatusBar, this.notebookEditor, container, editorPart, undefined)), @@ -176,7 +177,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen templateDisposables.add(new CellFocusPart(container, undefined, this.notebookEditor)), templateDisposables.add(new CellDragAndDropPart(container)), templateDisposables.add(scopedInstaService.createInstance(CellContextKeyPart, this.notebookEditor)), - ]; + ]); const templateData: MarkdownCellRenderTemplate = { rootContainer, @@ -304,7 +305,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende this.notebookEditor)); const focusIndicatorPart = templateDisposables.add(new CellFocusIndicator(this.notebookEditor, titleToolbar, focusIndicatorTop, focusIndicatorLeft, focusIndicatorRight, focusIndicatorBottom)); - const cellParts = [ + const cellParts = new CellPartsCollection([ focusIndicatorPart, titleToolbar, templateDisposables.add(scopedInstaService.createInstance(BetweenCellToolbar, this.notebookEditor, titleToolbarContainer, bottomCellToolbarContainer)), @@ -319,7 +320,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende templateDisposables.add(new CellFocusPart(container, focusSinkElement, this.notebookEditor)), templateDisposables.add(new CellDragAndDropPart(container)), templateDisposables.add(scopedInstaService.createInstance(CellContextKeyPart, this.notebookEditor)), - ]; + ]); const templateData: CodeCellRenderTemplate = { rootContainer,