src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts/NotebookCellList/attachViewModel
class NotebookCellList:	attachViewModel(model: NotebookViewModel) {
		this._viewModel = model;
		this._viewModelStore.add(model.onDidChangeViewCells((e) => {
			if (this._isDisposed) {
				return;
			}

			const currentRanges = this._hiddenRangeIds.map(id => this._viewModel!.getTrackedRange(id)).filter(range => range !== null) as ICellRange[];
			const newVisibleViewCells: CellViewModel[] = getVisibleCells(this._viewModel!.viewCells as CellViewModel[], currentRanges);

			const oldVisibleViewCells: CellViewModel[] = [];
			const oldViewCellMapping = new Set<string>();
			for (let i = 0; i < this.length; i++) {
				oldVisibleViewCells.push(this.element(i));
				oldViewCellMapping.add(this.element(i).uri.toString());
			}

			const viewDiffs = diff<CellViewModel>(oldVisibleViewCells, newVisibleViewCells, a => {
				return oldViewCellMapping.has(a.uri.toString());
			});

			if (e.synchronous) {
				this._updateElementsInWebview(viewDiffs);
			} else {
				this._viewModelStore.add(DOM.scheduleAtNextAnimationFrame(() => {
					if (this._isDisposed) {
						return;
					}

					this._updateElementsInWebview(viewDiffs);
				}));
			}
		}));

		this._viewModelStore.add(model.onDidChangeSelection((e) => {
			if (e === 'view') {
				return;
			}

			// convert model selections to view selections
			const viewSelections = cellRangesToIndexes(model.getSelections()).map(index => model.cellAt(index)).filter(cell => !!cell).map(cell => this._getViewIndexUpperBound(cell!));
			this.setSelection(viewSelections, undefined, true);
			const primary = cellRangesToIndexes([model.getFocus()]).map(index => model.cellAt(index)).filter(cell => !!cell).map(cell => this._getViewIndexUpperBound(cell!));

			if (primary.length) {
				this.setFocus(primary, undefined, true);
			}
		}));

		const hiddenRanges = model.getHiddenRanges();
		this.setHiddenAreas(hiddenRanges, false);
		const newRanges = reduceCellRanges(hiddenRanges);
		const viewCells = model.viewCells.slice(0) as CellViewModel[];
		newRanges.reverse().forEach(range => {
			const removedCells = viewCells.splice(range.start, range.end - range.start + 1);
			this._onDidRemoveCellsFromView.fire(removedCells);
		});

		this.splice2(0, 0, viewCells);
	}