UNPKG

4.31 kBPlain TextView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3
4import {
5 CellModel,
6 CodeCellModel,
7 ICellModel,
8 MarkdownCellModel,
9 RawCellModel
10} from '@jupyterlab/cells';
11import { IObservableList } from '@jupyterlab/observables';
12import {
13 ISharedCell,
14 ISharedCodeCell,
15 ISharedMarkdownCell,
16 ISharedNotebook,
17 ISharedRawCell,
18 NotebookChange
19} from '@jupyter/ydoc';
20import { ISignal, Signal } from '@lumino/signaling';
21
22/**
23 * A cell list object that supports undo/redo.
24 */
25export class CellList {
26 /**
27 * Construct the cell list.
28 */
29 constructor(protected model: ISharedNotebook) {
30 this._insertCells(0, this.model.cells);
31
32 this.model.changed.connect(this._onSharedModelChanged, this);
33 }
34
35 /**
36 * A signal emitted when the cell list has changed.
37 */
38 get changed(): ISignal<this, IObservableList.IChangedArgs<ICellModel>> {
39 return this._changed;
40 }
41
42 /**
43 * Test whether the cell list has been disposed.
44 */
45 get isDisposed(): boolean {
46 return this._isDisposed;
47 }
48
49 /**
50 * Get the length of the cell list.
51 *
52 * @returns The number of cells in the cell list.
53 */
54 get length(): number {
55 return this.model.cells.length;
56 }
57
58 /**
59 * Create an iterator over the cells in the cell list.
60 *
61 * @returns A new iterator starting at the front of the cell list.
62 */
63 *[Symbol.iterator](): IterableIterator<ICellModel> {
64 for (const cell of this.model.cells) {
65 yield this._cellMap.get(cell)!;
66 }
67 }
68
69 /**
70 * Dispose of the resources held by the cell list.
71 */
72 dispose(): void {
73 if (this._isDisposed) {
74 return;
75 }
76 this._isDisposed = true;
77
78 // Clean up the cell map and cell order objects.
79 for (const cell of this.model.cells) {
80 this._cellMap.get(cell)?.dispose();
81 }
82 Signal.clearData(this);
83 }
84
85 /**
86 * Get the cell at the specified index.
87 *
88 * @param index - The positive integer index of interest.
89 *
90 * @returns The cell at the specified index.
91 */
92 get(index: number): ICellModel {
93 return this._cellMap.get(this.model.cells[index])!;
94 }
95
96 private _insertCells(index: number, cells: Array<ISharedCell>) {
97 cells.forEach(sharedModel => {
98 let cellModel: CellModel;
99 switch (sharedModel.cell_type) {
100 case 'code': {
101 cellModel = new CodeCellModel({
102 sharedModel: sharedModel as ISharedCodeCell
103 });
104 break;
105 }
106 case 'markdown': {
107 cellModel = new MarkdownCellModel({
108 sharedModel: sharedModel as ISharedMarkdownCell
109 });
110 break;
111 }
112 default: {
113 cellModel = new RawCellModel({
114 sharedModel: sharedModel as ISharedRawCell
115 });
116 }
117 }
118
119 this._cellMap.set(sharedModel, cellModel);
120 sharedModel.disposed.connect(() => {
121 cellModel.dispose();
122 this._cellMap.delete(sharedModel);
123 });
124 });
125
126 return this.length;
127 }
128
129 private _onSharedModelChanged(self: ISharedNotebook, change: NotebookChange) {
130 let currpos = 0;
131 // We differ emitting the list changes to ensure cell model for all current shared cell have been created.
132 const events = new Array<IObservableList.IChangedArgs<ICellModel>>();
133 change.cellsChange?.forEach(delta => {
134 if (delta.insert != null) {
135 this._insertCells(currpos, delta.insert);
136 events.push({
137 type: 'add',
138 newIndex: currpos,
139 newValues: delta.insert.map(c => this._cellMap.get(c)!),
140 oldIndex: -2,
141 oldValues: []
142 });
143
144 currpos += delta.insert.length;
145 } else if (delta.delete != null) {
146 events.push({
147 type: 'remove',
148 newIndex: -1,
149 newValues: [],
150 oldIndex: currpos,
151 // Cells have been disposed, so we don't know which one are gone.
152 oldValues: new Array(delta.delete).fill(undefined)
153 });
154 } else if (delta.retain != null) {
155 currpos += delta.retain;
156 }
157 });
158
159 events.forEach(msg => this._changed.emit(msg));
160 }
161
162 private _cellMap = new WeakMap<ISharedCell, ICellModel>();
163 private _changed = new Signal<this, IObservableList.IChangedArgs<ICellModel>>(
164 this
165 );
166 private _isDisposed = false;
167}