extHostNotebook.ts 54.0 KB
Newer Older
R
rebornix 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
R
rebornix 已提交
7 8
import { readonly } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
R
rebornix 已提交
9
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
10
import { ISplice } from 'vs/base/common/sequence';
11
import { URI, UriComponents } from 'vs/base/common/uri';
R
rebornix 已提交
12
import * as UUID from 'vs/base/common/uuid';
13
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
14
import { CellKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData, INotebookDocumentsAndEditorsDelta } from 'vs/workbench/api/common/extHost.protocol';
15
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
16
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
17
import { CellEditType, diff, ICellEditOperation, ICellInsertEdit, INotebookDisplayOrder, INotebookEditData, NotebookCellsChangedEvent, NotebookCellsSplice2, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType, NotebookDataDto, IOutputRenderRequest, IOutputRenderResponse, IOutputRenderResponseOutputInfo, IOutputRenderResponseCellInfo, IRawOutput, CellOutputKind, IProcessedOutput, INotebookKernelInfoDto2, IMainCellDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
R
rebornix 已提交
18
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
R
Rob Lourens 已提交
19
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
20 21
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import { NotImplementedProxy } from 'vs/base/common/types';
R
rebornix 已提交
22
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
R
rebornix 已提交
23
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
24 25 26 27
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import { joinPath } from 'vs/base/common/resources';
import { Schemas } from 'vs/base/common/network';
import { hash } from 'vs/base/common/hash';
28
import { Cache } from './cache';
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

interface IObservable<T> {
	proxy: T;
	onDidChange: Event<void>;
}

function getObservable<T extends Object>(obj: T): IObservable<T> {
	const onDidChange = new Emitter<void>();
	const proxy = new Proxy(obj, {
		set(target: T, p: PropertyKey, value: any, _receiver: any): boolean {
			target[p as keyof T] = value;
			onDidChange.fire();
			return true;
		}
	});

	return {
		proxy,
		onDidChange: onDidChange.event
	};
}
R
rebornix 已提交
50

R
rebornix 已提交
51 52
interface INotebookEventEmitter {
	emitModelChange(events: vscode.NotebookCellsChangeEvent): void;
53
	emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void;
R
rebornix 已提交
54
	emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void;
55
	emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void;
R
rebornix 已提交
56 57
}

R
rebornix 已提交
58
const addIdToOutput = (output: IRawOutput, id = UUID.generateUuid()): IProcessedOutput => output.outputKind === CellOutputKind.Rich
59 60
	? ({ ...output, outputId: id }) : output;

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
class DettachedCellDocumentData extends ExtHostDocumentData {

	private static readonly _fakeProxy = new class extends NotImplementedProxy<MainThreadDocumentsShape>('document') {
		$trySaveDocument() {
			return Promise.reject('Cell-document cannot be saved');
		}
	};

	constructor(cell: IMainCellDto) {
		super(DettachedCellDocumentData._fakeProxy,
			URI.revive(cell.uri),
			cell.source,
			cell.eol,
			cell.language,
			0,
			false
		);
	}
}

81
export class ExtHostCell extends Disposable implements vscode.NotebookCell {
R
rebornix 已提交
82

83
	private _onDidChangeOutputs = new Emitter<ISplice<IProcessedOutput>[]>();
84 85 86
	readonly onDidChangeOutputs: Event<ISplice<IProcessedOutput>[]> = this._onDidChangeOutputs.event;

	private _outputs: any[];
87
	private _outputMapping = new WeakMap<vscode.CellOutput, string | undefined /* output ID */>();
88

89
	private _metadata: vscode.NotebookCellMetadata;
90
	private _metadataChangeListener: IDisposable;
R
rebornix 已提交
91

92 93 94
	readonly handle: number;
	readonly uri: URI;
	readonly cellKind: CellKind;
95

96 97 98 99 100 101
	// todo@jrieken this is a little fish because we have
	// vscode.TextDocument for which we never fired an onDidOpen
	// event and which doesn't appear in the list of documents.
	// this will change once the "real" document comes along. We
	// should come up with a better approach here...
	readonly defaultDocument: DettachedCellDocumentData;
102

R
rebornix 已提交
103
	constructor(
104
		private _proxy: MainThreadNotebookShape,
105 106 107
		readonly notebook: ExtHostNotebookDocument,
		private _extHostDocument: ExtHostDocumentsAndEditors,
		cell: IMainCellDto,
R
rebornix 已提交
108
	) {
109
		super();
110

111 112 113 114 115 116
		this.handle = cell.handle;
		this.uri = URI.revive(cell.uri);
		this.cellKind = cell.cellKind;
		this.defaultDocument = new DettachedCellDocumentData(cell);

		this._outputs = cell.outputs;
117 118 119 120
		for (const output of this._outputs) {
			this._outputMapping.set(output, output.outputId);
			delete output.outputId;
		}
121

122
		const observableMetadata = getObservable(cell.metadata ?? {});
123 124
		this._metadata = observableMetadata.proxy;
		this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
125
			this._updateMetadata();
126
		}));
R
rebornix 已提交
127 128
	}

129 130 131 132 133 134 135 136
	get document(): vscode.TextDocument {
		return this._extHostDocument.getDocument(this.uri)?.document ?? this.defaultDocument.document;
	}

	get language(): string {
		return this.document.languageId;
	}

R
rebornix 已提交
137 138 139 140
	get outputs() {
		return this._outputs;
	}

R
rebornix 已提交
141
	set outputs(newOutputs: vscode.CellOutput[]) {
R
Rob Lourens 已提交
142
		const rawDiffs = diff<vscode.CellOutput>(this._outputs || [], newOutputs || [], (a) => {
143 144 145
			return this._outputMapping.has(a);
		});

146
		const transformedDiffs: ISplice<IProcessedOutput>[] = rawDiffs.map(diff => {
147 148 149 150
			for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
				this._outputMapping.delete(this._outputs[i]);
			}

151 152 153 154 155
			return {
				deleteCount: diff.deleteCount,
				start: diff.start,
				toInsert: diff.toInsert.map((output): IProcessedOutput => {
					if (output.outputKind === CellOutputKind.Rich) {
156
						const uuid = UUID.generateUuid();
157 158 159 160 161 162 163 164
						this._outputMapping.set(output, uuid);
						return { ...output, outputId: uuid };
					}

					this._outputMapping.set(output, undefined);
					return output;
				})
			};
165 166
		});

R
rebornix 已提交
167
		this._outputs = newOutputs;
168
		this._onDidChangeOutputs.fire(transformedDiffs);
R
rebornix 已提交
169 170
	}

171 172 173 174
	get metadata() {
		return this._metadata;
	}

R
rebornix 已提交
175
	set metadata(newMetadata: vscode.NotebookCellMetadata) {
176 177 178 179 180
		this.setMetadata(newMetadata);
		this._updateMetadata();
	}

	setMetadata(newMetadata: vscode.NotebookCellMetadata): void {
181
		// Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata'
182
		this._metadataChangeListener.dispose();
183
		const observableMetadata = getObservable(newMetadata);
184 185
		this._metadata = observableMetadata.proxy;
		this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
186
			this._updateMetadata();
187
		}));
188 189
	}

190 191
	private _updateMetadata(): Promise<void> {
		return this._proxy.$updateNotebookCellMetadata(this.notebook.viewType, this.notebook.uri, this.handle, this._metadata);
R
rebornix 已提交
192 193 194
	}
}

R
rebornix 已提交
195
export class ExtHostNotebookDocument extends Disposable implements vscode.NotebookDocument {
R
rebornix 已提交
196
	private static _handlePool: number = 0;
R
rebornix 已提交
197 198 199 200
	readonly handle = ExtHostNotebookDocument._handlePool++;

	private _cells: ExtHostCell[] = [];

R
rebornix 已提交
201 202
	private _cellDisposableMapping = new Map<number, DisposableStore>();

R
rebornix 已提交
203 204 205 206
	get cells() {
		return this._cells;
	}

R
rebornix 已提交
207 208 209 210 211
	private _languages: string[] = [];

	get languages() {
		return this._languages = [];
	}
R
rebornix 已提交
212

R
rebornix 已提交
213 214 215
	set languages(newLanguages: string[]) {
		this._languages = newLanguages;
		this._proxy.$updateNotebookLanguages(this.viewType, this.uri, this._languages);
R
rebornix 已提交
216
	}
R
rebornix 已提交
217

218 219
	private _metadata: Required<vscode.NotebookDocumentMetadata> = notebookDocumentMetadataDefaults;
	private _metadataChangeListener: IDisposable;
R
rebornix 已提交
220 221 222 223 224

	get metadata() {
		return this._metadata;
	}

225 226 227 228 229 230
	set metadata(newMetadata: Required<vscode.NotebookDocumentMetadata>) {
		this._metadataChangeListener.dispose();
		newMetadata = {
			...notebookDocumentMetadataDefaults,
			...newMetadata
		};
R
rebornix 已提交
231 232 233 234
		if (this._metadataChangeListener) {
			this._metadataChangeListener.dispose();
		}

235 236 237 238 239
		const observableMetadata = getObservable(newMetadata);
		this._metadata = observableMetadata.proxy;
		this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
			this.updateMetadata();
		}));
R
rebornix 已提交
240 241

		this.updateMetadata();
R
rebornix 已提交
242 243
	}

R
rebornix 已提交
244
	private _displayOrder: string[] = [];
R
rebornix 已提交
245 246 247 248 249

	get displayOrder() {
		return this._displayOrder;
	}

R
rebornix 已提交
250
	set displayOrder(newOrder: string[]) {
R
rebornix 已提交
251 252 253
		this._displayOrder = newOrder;
	}

R
rebornix 已提交
254 255 256 257 258 259
	private _versionId = 0;

	get versionId() {
		return this._versionId;
	}

260 261 262 263
	private _backupCounter = 1;

	private _backup?: vscode.NotebookDocumentBackup;

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300

	private readonly _edits = new Cache<vscode.NotebookDocumentEditEvent>('notebook documents');


	addEdit(item: vscode.NotebookDocumentEditEvent): number {
		return this._edits.add([item]);
	}

	async undo(editId: number, isDirty: boolean): Promise<void> {
		await this.getEdit(editId).undo();
		// if (!isDirty) {
		// 	this.disposeBackup();
		// }
	}

	async redo(editId: number, isDirty: boolean): Promise<void> {
		await this.getEdit(editId).redo();
		// if (!isDirty) {
		// 	this.disposeBackup();
		// }
	}

	private getEdit(editId: number): vscode.NotebookDocumentEditEvent {
		const edit = this._edits.get(editId, 0);
		if (!edit) {
			throw new Error('No edit found');
		}

		return edit;
	}

	disposeEdits(editIds: number[]): void {
		for (const id of editIds) {
			this._edits.delete(id);
		}
	}

301 302
	private _disposed = false;

R
rebornix 已提交
303
	constructor(
R
rebornix 已提交
304
		private readonly _proxy: MainThreadNotebookShape,
R
rebornix 已提交
305
		private _documentsAndEditors: ExtHostDocumentsAndEditors,
306
		private _emitter: INotebookEventEmitter,
R
rebornix 已提交
307
		public viewType: string,
R
rebornix 已提交
308
		public uri: URI,
309 310
		public renderingHandler: ExtHostNotebookOutputRenderingHandler,
		private readonly _storagePath: URI | undefined
R
rebornix 已提交
311
	) {
R
rebornix 已提交
312
		super();
313 314 315 316 317 318 319 320 321 322

		const observableMetadata = getObservable(notebookDocumentMetadataDefaults);
		this._metadata = observableMetadata.proxy;
		this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
			this.updateMetadata();
		}));
	}

	private updateMetadata() {
		this._proxy.$updateNotebookMetadata(this.viewType, this.uri, this._metadata);
R
rebornix 已提交
323
	}
R
rebornix 已提交
324

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
	getNewBackupUri(): URI {
		if (!this._storagePath) {
			throw new Error('Backup requires a valid storage path');
		}
		const fileName = hashPath(this.uri) + (this._backupCounter++);
		return joinPath(this._storagePath, fileName);
	}

	updateBackup(backup: vscode.NotebookDocumentBackup): void {
		this._backup?.delete();
		this._backup = backup;
	}

	disposeBackup(): void {
		this._backup?.delete();
		this._backup = undefined;
	}

R
rebornix 已提交
343
	dispose() {
344
		this._disposed = true;
R
rebornix 已提交
345
		super.dispose();
R
rebornix 已提交
346 347
		this._cellDisposableMapping.forEach(cell => cell.dispose());
	}
R
rebornix 已提交
348

R
rebornix 已提交
349
	get fileName() { return this.uri.fsPath; }
R
rebornix 已提交
350

R
rebornix 已提交
351 352
	get isDirty() { return false; }

353
	acceptModelChanged(event: NotebookCellsChangedEvent): void {
R
rebornix 已提交
354
		this._versionId = event.versionId;
355 356 357 358
		if (event.kind === NotebookCellsChangeType.Initialize) {
			this.$spliceNotebookCells(event.changes, true);
		} if (event.kind === NotebookCellsChangeType.ModelChange) {
			this.$spliceNotebookCells(event.changes, false);
R
rebornix 已提交
359
		} else if (event.kind === NotebookCellsChangeType.Move) {
360
			this.$moveCell(event.index, event.newIdx);
R
rebornix 已提交
361
		} else if (event.kind === NotebookCellsChangeType.CellClearOutput) {
362
			this.$clearCellOutputs(event.index);
R
rebornix 已提交
363
		} else if (event.kind === NotebookCellsChangeType.CellsClearOutput) {
364
			this.$clearAllCellOutputs();
365
		} else if (event.kind === NotebookCellsChangeType.ChangeLanguage) {
366
			this.$changeCellLanguage(event.index, event.language);
367 368
		} else if (event.kind === NotebookCellsChangeType.ChangeMetadata) {
			this.$changeCellMetadata(event.index, event.metadata);
R
rebornix 已提交
369
		}
R
rebornix 已提交
370
	}
371

372
	private $spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void {
373
		if (this._disposed) {
R
rebornix 已提交
374
			return;
375 376
		}

R
Rob Lourens 已提交
377
		const contentChangeEvents: vscode.NotebookCellsChangeData[] = [];
R
rebornix 已提交
378

379
		splices.reverse().forEach(splice => {
R
Rob Lourens 已提交
380 381
			const cellDtos = splice[2];
			const newCells = cellDtos.map(cell => {
382

383
				const extCell = new ExtHostCell(this._proxy, this, this._documentsAndEditors, cell);
R
rebornix 已提交
384

385 386 387
				if (!this._cellDisposableMapping.has(extCell.handle)) {
					this._cellDisposableMapping.set(extCell.handle, new DisposableStore());
				}
388

R
Rob Lourens 已提交
389
				const store = this._cellDisposableMapping.get(extCell.handle)!;
R
rebornix 已提交
390

391 392 393
				store.add(extCell.onDidChangeOutputs((diffs) => {
					this.eventuallyUpdateCellOutputs(extCell, diffs);
				}));
R
rebornix 已提交
394

395 396
				return extCell;
			});
397

398 399 400
			for (let j = splice[0]; j < splice[0] + splice[1]; j++) {
				this._cellDisposableMapping.get(this.cells[j].handle)?.dispose();
				this._cellDisposableMapping.delete(this.cells[j].handle);
R
rebornix 已提交
401

402
			}
403

404
			const deletedItems = this.cells.splice(splice[0], splice[1], ...newCells);
R
rebornix 已提交
405

406 407 408
			const event: vscode.NotebookCellsChangeData = {
				start: splice[0],
				deletedCount: splice[1],
409
				deletedItems,
410 411
				items: newCells
			};
R
rebornix 已提交
412

413 414
			contentChangeEvents.push(event);
		});
R
rebornix 已提交
415

416 417 418 419 420 421
		if (!initialization) {
			this._emitter.emitModelChange({
				document: this,
				changes: contentChangeEvents
			});
		}
R
rebornix 已提交
422
	}
R
rebornix 已提交
423

424
	private $moveCell(index: number, newIdx: number): void {
R
rebornix 已提交
425 426
		const cells = this.cells.splice(index, 1);
		this.cells.splice(newIdx, 0, ...cells);
427 428 429
		const changes: vscode.NotebookCellsChangeData[] = [{
			start: index,
			deletedCount: 1,
430
			deletedItems: cells,
431 432 433 434
			items: []
		}, {
			start: newIdx,
			deletedCount: 0,
435
			deletedItems: [],
436 437 438 439 440 441
			items: cells
		}];
		this._emitter.emitModelChange({
			document: this,
			changes
		});
R
rebornix 已提交
442 443
	}

444
	private $clearCellOutputs(index: number): void {
R
rebornix 已提交
445 446
		const cell = this.cells[index];
		cell.outputs = [];
447 448
		const event: vscode.NotebookCellOutputsChangeEvent = { document: this, cells: [cell] };
		this._emitter.emitCellOutputsChange(event);
R
rebornix 已提交
449 450
	}

451 452 453 454 455 456 457 458 459 460
	private $clearAllCellOutputs(): void {
		const modifedCells: vscode.NotebookCell[] = [];
		this.cells.forEach(cell => {
			if (cell.outputs.length !== 0) {
				cell.outputs = [];
				modifedCells.push(cell);
			}
		});
		const event: vscode.NotebookCellOutputsChangeEvent = { document: this, cells: modifedCells };
		this._emitter.emitCellOutputsChange(event);
R
rebornix 已提交
461 462
	}

463
	private $changeCellLanguage(index: number, language: string): void {
464
		const cell = this.cells[index];
465
		cell.defaultDocument._acceptLanguageId(language);
R
rebornix 已提交
466
		const event: vscode.NotebookCellLanguageChangeEvent = { document: this, cell, language };
467
		this._emitter.emitCellLanguageChange(event);
468 469
	}

470 471 472 473 474 475 476
	private $changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void {
		const cell = this.cells[index];
		cell.setMetadata(newMetadata);
		const event: vscode.NotebookCellMetadataChangeEvent = { document: this, cell };
		this._emitter.emitCellMetadataChange(event);
	}

477
	async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<IProcessedOutput>[]) {
R
Rob Lourens 已提交
478 479 480
		const renderers = new Set<number>();
		const outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
			const outputs = diff.toInsert;
481
			return [diff.start, diff.deleteCount, outputs];
482 483
		});

484 485 486 487 488
		await this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos, Array.from(renderers));
		this._emitter.emitCellOutputsChange({
			document: this,
			cells: [cell]
		});
489 490
	}

R
rebornix 已提交
491
	getCell(cellHandle: number) {
R
rebornix 已提交
492 493
		return this.cells.find(cell => cell.handle === cellHandle);
	}
R
rebornix 已提交
494

495 496 497
	getCell2(cellUri: UriComponents) {
		return this.cells.find(cell => cell.uri.fragment === cellUri.fragment);
	}
R
rebornix 已提交
498 499
}

R
rebornix 已提交
500
export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellEdit {
R
rebornix 已提交
501 502 503
	private _finalized: boolean = false;
	private readonly _documentVersionId: number;
	private _collectedEdits: ICellEditOperation[] = [];
R
rebornix 已提交
504
	private _renderers = new Set<number>();
R
rebornix 已提交
505 506 507 508

	constructor(
		readonly editor: ExtHostNotebookEditor
	) {
R
rebornix 已提交
509
		this._documentVersionId = editor.document.versionId;
R
rebornix 已提交
510 511 512 513 514 515
	}

	finalize(): INotebookEditData {
		this._finalized = true;
		return {
			documentVersionId: this._documentVersionId,
R
rebornix 已提交
516 517
			edits: this._collectedEdits,
			renderers: Array.from(this._renderers)
R
rebornix 已提交
518 519 520 521 522 523 524 525 526
		};
	}

	private _throwIfFinalized() {
		if (this._finalized) {
			throw new Error('Edit is only valid while callback runs');
		}
	}

R
rebornix 已提交
527
	insert(index: number, content: string | string[], language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void {
R
rebornix 已提交
528 529
		this._throwIfFinalized();

R
rebornix 已提交
530
		const sourceArr = Array.isArray(content) ? content : content.split(/\r|\n|\r\n/g);
R
Rob Lourens 已提交
531
		const cell = {
R
rebornix 已提交
532
			source: sourceArr,
R
rebornix 已提交
533
			language,
R
rebornix 已提交
534
			cellKind: type,
535 536
			outputs: outputs.map(o => addIdToOutput(o)),
			metadata,
R
rebornix 已提交
537 538 539 540 541 542
		};

		this._collectedEdits.push({
			editType: CellEditType.Insert,
			index,
			cells: [cell]
R
rebornix 已提交
543 544 545 546 547 548 549 550
		});
	}

	delete(index: number): void {
		this._throwIfFinalized();

		this._collectedEdits.push({
			editType: CellEditType.Delete,
551 552
			index,
			count: 1
R
rebornix 已提交
553 554 555 556
		});
	}
}

557 558 559
class ExtHostWebviewCommWrapper extends Disposable {
	private readonly _onDidReceiveDocumentMessage = new Emitter<any>();
	private readonly _rendererIdToEmitters = new Map<string, Emitter<any>>();
560 561

	constructor(
562
		private _editorId: string,
563 564 565 566 567 568 569 570
		public uri: URI,
		private _proxy: MainThreadNotebookShape,
		private _webviewInitData: WebviewInitData,
		public document: ExtHostNotebookDocument,
	) {
		super();
	}

571 572 573 574 575
	public onDidReceiveMessage(forRendererId: string | undefined, message: any) {
		this._onDidReceiveDocumentMessage.fire(message);
		if (forRendererId !== undefined) {
			this._rendererIdToEmitters.get(forRendererId)?.fire(message);
		}
576 577
	}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
	public readonly contentProviderComm: vscode.NotebookCommunication = {
		editorId: this._editorId,
		onDidReceiveMessage: this._onDidReceiveDocumentMessage.event,
		postMessage: (message: any) => this._proxy.$postMessage(this._editorId, undefined, message),
		asWebviewUri: (uri: vscode.Uri) => this._asWebviewUri(uri),
	};

	public getRendererComm(rendererId: string): vscode.NotebookCommunication {
		const emitter = new Emitter<any>();
		this._rendererIdToEmitters.set(rendererId, emitter);
		return {
			editorId: this._editorId,
			onDidReceiveMessage: emitter.event,
			postMessage: (message: any) => this._proxy.$postMessage(this._editorId, rendererId, message),
			asWebviewUri: (uri: vscode.Uri) => this._asWebviewUri(uri),
		};
	}


	private _asWebviewUri(localResource: vscode.Uri): vscode.Uri {
		return asWebviewUri(this._webviewInitData, this._editorId, localResource);
599 600 601
	}
}

R
rebornix 已提交
602
export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor {
R
rebornix 已提交
603
	private _viewColumn: vscode.ViewColumn | undefined;
R
rebornix 已提交
604 605

	selection?: ExtHostCell = undefined;
R
rebornix 已提交
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628

	private _active: boolean = false;
	get active(): boolean {
		return this._active;
	}

	set active(_state: boolean) {
		throw readonly('active');
	}

	private _visible: boolean = false;
	get visible(): boolean {
		return this._visible;
	}

	set visible(_state: boolean) {
		throw readonly('visible');
	}

	_acceptVisibility(value: boolean) {
		this._visible = value;
	}

R
rebornix 已提交
629
	_acceptActive(value: boolean) {
R
rebornix 已提交
630 631 632
		this._active = value;
	}

633 634 635 636 637 638 639 640 641 642
	private _kernel?: vscode.NotebookKernel;

	get kernel() {
		return this._kernel;
	}

	set kernel(_kernel: vscode.NotebookKernel | undefined) {
		throw readonly('kernel');
	}

R
rebornix 已提交
643 644
	private _onDidDispose = new Emitter<void>();
	readonly onDidDispose: Event<void> = this._onDidDispose.event;
645
	private _onDidReceiveMessage = new Emitter<any>();
646
	onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
R
rebornix 已提交
647 648

	constructor(
649
		private readonly viewType: string,
R
rebornix 已提交
650 651
		readonly id: string,
		public uri: URI,
652
		private _proxy: MainThreadNotebookShape,
653
		private _webComm: vscode.NotebookCommunication,
R
rebornix 已提交
654 655
		public document: ExtHostNotebookDocument,
	) {
R
rebornix 已提交
656
		super();
657 658 659
		this._register(this._webComm.onDidReceiveMessage(e => {
			this._onDidReceiveMessage.fire(e);
		}));
R
rebornix 已提交
660 661
	}

R
rebornix 已提交
662 663
	edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable<boolean> {
		const edit = new NotebookEditorCellEditBuilder(this);
R
rebornix 已提交
664 665 666 667
		callback(edit);
		return this._applyEdit(edit);
	}

R
rebornix 已提交
668
	private _applyEdit(editBuilder: NotebookEditorCellEditBuilder): Promise<boolean> {
R
rebornix 已提交
669 670 671 672 673 674 675
		const editData = editBuilder.finalize();

		// return when there is nothing to do
		if (editData.edits.length === 0) {
			return Promise.resolve(true);
		}

R
Rob Lourens 已提交
676
		const compressedEdits: ICellEditOperation[] = [];
R
rebornix 已提交
677 678 679 680 681 682 683 684 685
		let compressedEditsIndex = -1;

		for (let i = 0; i < editData.edits.length; i++) {
			if (compressedEditsIndex < 0) {
				compressedEdits.push(editData.edits[i]);
				compressedEditsIndex++;
				continue;
			}

R
Rob Lourens 已提交
686 687
			const prevIndex = compressedEditsIndex;
			const prev = compressedEdits[prevIndex];
R
rebornix 已提交
688 689

			if (prev.editType === CellEditType.Insert && editData.edits[i].editType === CellEditType.Insert) {
690
				if (prev.index === editData.edits[i].index) {
R
rebornix 已提交
691 692 693 694 695
					prev.cells.push(...(editData.edits[i] as ICellInsertEdit).cells);
					continue;
				}
			}

696 697 698 699 700 701 702
			if (prev.editType === CellEditType.Delete && editData.edits[i].editType === CellEditType.Delete) {
				if (prev.index === editData.edits[i].index) {
					prev.count += (editData.edits[i] as ICellDeleteEdit).count;
					continue;
				}
			}

R
rebornix 已提交
703 704 705
			compressedEdits.push(editData.edits[i]);
			compressedEditsIndex++;
		}
R
rebornix 已提交
706

R
rebornix 已提交
707
		return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.renderers);
R
rebornix 已提交
708 709 710 711 712 713 714 715
	}

	get viewColumn(): vscode.ViewColumn | undefined {
		return this._viewColumn;
	}

	set viewColumn(value) {
		throw readonly('viewColumn');
R
rebornix 已提交
716
	}
717

718 719 720
	updateActiveKernel(kernel?: vscode.NotebookKernel) {
		this._kernel = kernel;
	}
721
	async postMessage(message: any): Promise<boolean> {
722
		return this._webComm.postMessage(message);
723 724
	}

R
rebornix 已提交
725
	asWebviewUri(localResource: vscode.Uri): vscode.Uri {
726
		return this._webComm.asWebviewUri(localResource);
R
rebornix 已提交
727
	}
R
rebornix 已提交
728 729 730 731
	dispose() {
		this._onDidDispose.fire();
		super.dispose();
	}
R
rebornix 已提交
732 733
}

R
rebornix 已提交
734
export class ExtHostNotebookOutputRenderer {
R
rebornix 已提交
735
	private static _handlePool: number = 0;
736
	private resolvedComms = new WeakSet<ExtHostWebviewCommWrapper>();
R
rebornix 已提交
737 738
	readonly handle = ExtHostNotebookOutputRenderer._handlePool++;

R
rebornix 已提交
739
	constructor(
R
rebornix 已提交
740 741 742
		public type: string,
		public filter: vscode.NotebookOutputSelector,
		public renderer: vscode.NotebookOutputRenderer
R
rebornix 已提交
743 744 745 746
	) {

	}

R
rebornix 已提交
747
	matches(mimeType: string): boolean {
R
rebornix 已提交
748 749
		if (this.filter.mimeTypes) {
			if (this.filter.mimeTypes.indexOf(mimeType) >= 0) {
R
rebornix 已提交
750 751 752 753 754 755
				return true;
			}
		}
		return false;
	}

756 757 758 759 760 761 762
	resolveNotebook(document: ExtHostNotebookDocument, comm: ExtHostWebviewCommWrapper) {
		if (!this.resolvedComms.has(comm) && this.renderer.resolveNotebook) {
			this.renderer.resolveNotebook(document, comm.getRendererComm(this.type));
			this.resolvedComms.add(comm);
		}
	}

763
	render(document: ExtHostNotebookDocument, output: vscode.CellDisplayOutput, outputId: string, mimeType: string): string {
R
Rob Lourens 已提交
764
		const html = this.renderer.render(document, { output, outputId, mimeType });
R
rebornix 已提交
765

766
		return html;
R
rebornix 已提交
767 768 769
	}
}
export interface ExtHostNotebookOutputRenderingHandler {
R
rebornix 已提交
770
	outputDisplayOrder: INotebookDisplayOrder | undefined;
R
rebornix 已提交
771
	findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[];
R
rebornix 已提交
772 773
}

R
rebornix 已提交
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
export class ExtHostNotebookKernelProviderAdapter extends Disposable {
	private _kernelToId = new Map<vscode.NotebookKernel, string>();
	private _idToKernel = new Map<string, vscode.NotebookKernel>();
	constructor(
		private readonly _proxy: MainThreadNotebookShape,
		private readonly _handle: number,
		private readonly _extension: IExtensionDescription,
		private readonly _provider: vscode.NotebookKernelProvider
	) {
		super();

		if (this._provider.onDidChangeKernels) {
			this._register(this._provider.onDidChangeKernels(() => {
				this._proxy.$onNotebookKernelChange(this._handle);
			}));
		}
	}

	async provideKernels(document: ExtHostNotebookDocument, token: vscode.CancellationToken): Promise<INotebookKernelInfoDto2[]> {
		const data = await this._provider.provideKernels(document, token) || [];

		const newMap = new Map<vscode.NotebookKernel, string>();
R
rebornix 已提交
796
		let kernel_unique_pool = 0;
R
Rob Lourens 已提交
797
		const kernelIdCache = new Set<string>();
R
rebornix 已提交
798 799 800 801

		const transformedData: INotebookKernelInfoDto2[] = data.map(kernel => {
			let id = this._kernelToId.get(kernel);
			if (id === undefined) {
R
rebornix 已提交
802 803 804 805 806 807
				if (kernel.id && kernelIdCache.has(kernel.id)) {
					id = `${this._extension.identifier.value}_${kernel.id}_${kernel_unique_pool++}`;
				} else {
					id = `${this._extension.identifier.value}_${kernel.id || UUID.generateUuid()}`;
				}

R
rebornix 已提交
808 809 810 811 812 813 814 815 816
				this._kernelToId.set(kernel, id);
			}

			newMap.set(kernel, id);

			return {
				id,
				label: kernel.label,
				extension: this._extension.identifier,
817
				extensionLocation: this._extension.extensionLocation,
R
rebornix 已提交
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
				description: kernel.description,
				isPreferred: kernel.isPreferred,
				preloads: kernel.preloads
			};
		});

		this._kernelToId = newMap;

		this._idToKernel.clear();
		this._kernelToId.forEach((value, key) => {
			this._idToKernel.set(value, key);
		});

		return transformedData;
	}

R
rebornix 已提交
834 835 836 837
	getKernel(kernelId: string) {
		return this._idToKernel.get(kernelId);
	}

R
rebornix 已提交
838 839 840 841 842 843 844
	async resolveNotebook(kernelId: string, document: ExtHostNotebookDocument, webview: vscode.NotebookCommunication, token: CancellationToken) {
		const kernel = this._idToKernel.get(kernelId);

		if (kernel && this._provider.resolveKernel) {
			return this._provider.resolveKernel(kernel, document, webview, token);
		}
	}
845

R
Rob Lourens 已提交
846
	async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
847 848 849 850 851 852 853
		const kernel = this._idToKernel.get(kernelId);

		if (!kernel) {
			return;
		}

		if (cell) {
R
Rob Lourens 已提交
854
			return withToken(token => (kernel.executeCell as any)(document, cell, token));
855
		} else {
R
Rob Lourens 已提交
856
			return withToken(token => (kernel.executeAllCells as any)(document, token));
857 858
		}
	}
R
Rob Lourens 已提交
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882

	async cancelNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
		const kernel = this._idToKernel.get(kernelId);

		if (!kernel) {
			return;
		}

		if (cell) {
			return kernel.cancelCellExecution(document, cell);
		} else {
			return kernel.cancelAllCellsExecution(document);
		}
	}
}

// TODO@roblou remove 'token' passed to all execute APIs once extensions are updated
async function withToken(cb: (token: CancellationToken) => any) {
	const source = new CancellationTokenSource();
	try {
		await cb(source.token);
	} finally {
		source.dispose();
	}
R
rebornix 已提交
883 884
}

R
rebornix 已提交
885
export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostNotebookOutputRenderingHandler {
R
rebornix 已提交
886 887
	private static _notebookKernelProviderHandlePool: number = 0;

R
rebornix 已提交
888
	private readonly _proxy: MainThreadNotebookShape;
R
rebornix 已提交
889
	private readonly _notebookContentProviders = new Map<string, { readonly provider: vscode.NotebookContentProvider, readonly extension: IExtensionDescription; }>();
R
rebornix 已提交
890
	private readonly _notebookKernels = new Map<string, { readonly kernel: vscode.NotebookKernel, readonly extension: IExtensionDescription; }>();
R
rebornix 已提交
891
	private readonly _notebookKernelProviders = new Map<number, ExtHostNotebookKernelProviderAdapter>();
R
rebornix 已提交
892
	private readonly _documents = new Map<string, ExtHostNotebookDocument>();
R
rebornix 已提交
893
	private readonly _unInitializedDocuments = new Map<string, ExtHostNotebookDocument>();
894
	private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor }>();
895
	private readonly _webviewComm = new Map<string, ExtHostWebviewCommWrapper>();
896
	private readonly _notebookOutputRenderers = new Map<string, ExtHostNotebookOutputRenderer>();
897
	private readonly _renderersUsedInNotebooks = new WeakMap<ExtHostNotebookDocument, Set<ExtHostNotebookOutputRenderer>>();
R
rebornix 已提交
898 899
	private readonly _onDidChangeNotebookCells = new Emitter<vscode.NotebookCellsChangeEvent>();
	readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event;
900 901
	private readonly _onDidChangeCellOutputs = new Emitter<vscode.NotebookCellOutputsChangeEvent>();
	readonly onDidChangeCellOutputs = this._onDidChangeCellOutputs.event;
R
rebornix 已提交
902 903
	private readonly _onDidChangeCellLanguage = new Emitter<vscode.NotebookCellLanguageChangeEvent>();
	readonly onDidChangeCellLanguage = this._onDidChangeCellLanguage.event;
904 905
	private readonly _onDidChangeCellMetadata = new Emitter<vscode.NotebookCellMetadataChangeEvent>();
	readonly onDidChangeCellMetadata = this._onDidChangeCellMetadata.event;
R
rebornix 已提交
906 907
	private readonly _onDidChangeActiveNotebookEditor = new Emitter<vscode.NotebookEditor | undefined>();
	readonly onDidChangeActiveNotebookEditor = this._onDidChangeActiveNotebookEditor.event;
908

R
rebornix 已提交
909
	private _outputDisplayOrder: INotebookDisplayOrder | undefined;
R
rebornix 已提交
910

R
rebornix 已提交
911
	get outputDisplayOrder(): INotebookDisplayOrder | undefined {
R
rebornix 已提交
912
		return this._outputDisplayOrder;
R
rebornix 已提交
913 914
	}

R
rebornix 已提交
915 916 917 918 919 920
	private _activeNotebookEditor: ExtHostNotebookEditor | undefined;

	get activeNotebookEditor() {
		return this._activeNotebookEditor;
	}

921 922 923 924
	get notebookDocuments() {
		return [...this._documents.values()];
	}

R
rebornix 已提交
925 926
	private _onDidOpenNotebookDocument = new Emitter<vscode.NotebookDocument>();
	onDidOpenNotebookDocument: Event<vscode.NotebookDocument> = this._onDidOpenNotebookDocument.event;
R
rebornix 已提交
927 928
	private _onDidCloseNotebookDocument = new Emitter<vscode.NotebookDocument>();
	onDidCloseNotebookDocument: Event<vscode.NotebookDocument> = this._onDidCloseNotebookDocument.event;
R
rebornix 已提交
929
	visibleNotebookEditors: ExtHostNotebookEditor[] = [];
R
rebornix 已提交
930
	private _onDidChangeActiveNotebookKernel = new Emitter<{ document: ExtHostNotebookDocument, kernel: vscode.NotebookKernel | undefined }>();
R
rebornix 已提交
931
	onDidChangeActiveNotebookKernel = this._onDidChangeActiveNotebookKernel.event;
R
rebornix 已提交
932 933
	private _onDidChangeVisibleNotebookEditors = new Emitter<vscode.NotebookEditor[]>();
	onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event;
R
rebornix 已提交
934

935 936 937 938 939 940 941
	constructor(
		mainContext: IMainContext,
		commands: ExtHostCommands,
		private _documentsAndEditors: ExtHostDocumentsAndEditors,
		private readonly _webviewInitData: WebviewInitData,
		private readonly _extensionStoragePaths?: IExtensionStoragePaths,
	) {
R
rebornix 已提交
942
		this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook);
943 944 945 946 947 948 949

		commands.registerArgumentProcessor({
			processArgument: arg => {
				if (arg && arg.$mid === 12) {
					const documentHandle = arg.notebookEditor?.notebookHandle;
					const cellHandle = arg.cell.handle;

R
Rob Lourens 已提交
950
					for (const value of this._editors) {
951 952
						if (value[1].editor.document.handle === documentHandle) {
							const cell = value[1].editor.document.getCell(cellHandle);
953 954 955 956 957 958
							if (cell) {
								return cell;
							}
						}
					}
				}
B
Benjamin Pasero 已提交
959
				return arg;
960 961
			}
		});
R
rebornix 已提交
962 963
	}

R
rebornix 已提交
964
	registerNotebookOutputRenderer(
R
rebornix 已提交
965
		type: string,
R
rebornix 已提交
966
		extension: IExtensionDescription,
R
rebornix 已提交
967
		filter: vscode.NotebookOutputSelector,
R
rebornix 已提交
968
		renderer: vscode.NotebookOutputRenderer
R
rebornix 已提交
969
	): vscode.Disposable {
970 971 972 973
		if (this._notebookKernels.has(type)) {
			throw new Error(`Notebook renderer for '${type}' already registered`);
		}

R
Rob Lourens 已提交
974
		const extHostRenderer = new ExtHostNotebookOutputRenderer(type, filter, renderer);
975
		this._notebookOutputRenderers.set(extHostRenderer.type, extHostRenderer);
R
rebornix 已提交
976
		this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, type, filter, renderer.preloads || []);
R
rebornix 已提交
977
		return new extHostTypes.Disposable(() => {
978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
			this._notebookOutputRenderers.delete(extHostRenderer.type);
			this._proxy.$unregisterNotebookRenderer(extHostRenderer.type);
		});
	}

	async $renderOutputs(uriComponents: UriComponents, id: string, request: IOutputRenderRequest<UriComponents>): Promise<IOutputRenderResponse<UriComponents> | undefined> {
		if (!this._notebookOutputRenderers.has(id)) {
			throw new Error(`Notebook renderer for '${id}' is not registered`);
		}

		const document = this._documents.get(URI.revive(uriComponents).toString());

		if (!document) {
			return;
		}

		const renderer = this._notebookOutputRenderers.get(id)!;
995 996
		this.provideCommToNotebookRenderers(document, renderer);

997
		const cellsResponse: IOutputRenderResponseCellInfo<UriComponents>[] = request.items.map(cellInfo => {
998
			const cell = document.getCell2(cellInfo.key)!;
999 1000 1001
			const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => {
				return {
					index: output.index,
1002
					outputId: output.outputId,
1003 1004
					mimeType: output.mimeType,
					handlerId: id,
1005
					transformedOutput: renderer.render(document, cell.outputs[output.index] as vscode.CellDisplayOutput, output.outputId, output.mimeType)
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
				};
			});

			return {
				key: cellInfo.key,
				outputs: outputResponse
			};
		});

		return { items: cellsResponse };
	}

	/**
	 * The request carry the raw data for outputs so we don't look up in the existing document
	 */
	async $renderOutputs2<T>(uriComponents: UriComponents, id: string, request: IOutputRenderRequest<T>): Promise<IOutputRenderResponse<T> | undefined> {
		if (!this._notebookOutputRenderers.has(id)) {
			throw new Error(`Notebook renderer for '${id}' is not registered`);
		}

		const document = this._documents.get(URI.revive(uriComponents).toString());

		if (!document) {
			return;
		}

		const renderer = this._notebookOutputRenderers.get(id)!;
1033 1034
		this.provideCommToNotebookRenderers(document, renderer);

1035 1036 1037 1038
		const cellsResponse: IOutputRenderResponseCellInfo<T>[] = request.items.map(cellInfo => {
			const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => {
				return {
					index: output.index,
1039
					outputId: output.outputId,
1040 1041
					mimeType: output.mimeType,
					handlerId: id,
1042
					transformedOutput: renderer.render(document, output.output! as vscode.CellDisplayOutput, output.outputId, output.mimeType)
1043 1044 1045 1046 1047 1048 1049
				};
			});

			return {
				key: cellInfo.key,
				outputs: outputResponse
			};
R
rebornix 已提交
1050
		});
1051 1052

		return { items: cellsResponse };
R
rebornix 已提交
1053 1054
	}

R
rebornix 已提交
1055
	findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[] {
R
Rob Lourens 已提交
1056 1057
		const matches: ExtHostNotebookOutputRenderer[] = [];
		for (const renderer of this._notebookOutputRenderers) {
R
rebornix 已提交
1058
			if (renderer[1].matches(mimeType)) {
R
rebornix 已提交
1059
				matches.push(renderer[1]);
R
rebornix 已提交
1060 1061 1062
			}
		}

R
rebornix 已提交
1063
		return matches;
R
rebornix 已提交
1064 1065
	}

R
rebornix 已提交
1066 1067 1068 1069 1070 1071
	registerNotebookContentProvider(
		extension: IExtensionDescription,
		viewType: string,
		provider: vscode.NotebookContentProvider,
	): vscode.Disposable {

R
rebornix 已提交
1072
		if (this._notebookContentProviders.has(viewType)) {
R
rebornix 已提交
1073 1074 1075
			throw new Error(`Notebook provider for '${viewType}' already registered`);
		}

R
rebornix 已提交
1076 1077 1078
		// if ((<any>provider).executeCell) {
		// 	throw new Error('NotebookContentKernel.executeCell is removed, please use vscode.notebook.registerNotebookKernel instead.');
		// }
R
rebornix 已提交
1079

R
rebornix 已提交
1080
		this._notebookContentProviders.set(viewType, { extension, provider });
1081

J
Johannes Rieken 已提交
1082
		const listener = provider.onDidChangeNotebook
R
rebornix 已提交
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
			? provider.onDidChangeNotebook(e => {
				const document = this._documents.get(URI.revive(e.document.uri).toString());

				if (!document) {
					throw new Error(`Notebook document ${e.document.uri.toString()} not found`);
				}

				if (isEditEvent(e)) {
					const editId = document.addEdit(e);
					this._proxy.$onDidEdit(e.document.uri, viewType, editId, e.label);
				} else {
					this._proxy.$onContentChange(e.document.uri, viewType);
				}
			})
J
Johannes Rieken 已提交
1097
			: Disposable.None;
1098

R
rebornix 已提交
1099
		const supportBackup = !!provider.backupNotebook;
1100

R
rebornix 已提交
1101
		this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined);
1102

R
rebornix 已提交
1103
		return new extHostTypes.Disposable(() => {
1104
			listener.dispose();
R
rebornix 已提交
1105 1106 1107 1108 1109
			this._notebookContentProviders.delete(viewType);
			this._proxy.$unregisterNotebookProvider(viewType);
		});
	}

R
rebornix 已提交
1110 1111 1112 1113
	registerNotebookKernelProvider(extension: IExtensionDescription, selector: vscode.NotebookDocumentFilter, provider: vscode.NotebookKernelProvider) {
		const handle = ExtHostNotebookController._notebookKernelProviderHandlePool++;
		const adapter = new ExtHostNotebookKernelProviderAdapter(this._proxy, handle, extension, provider);
		this._notebookKernelProviders.set(handle, adapter);
R
rebornix 已提交
1114
		this._proxy.$registerNotebookKernelProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, handle, {
R
rebornix 已提交
1115 1116 1117 1118
			viewType: selector.viewType,
			filenamePattern: selector.filenamePattern ? typeConverters.GlobPattern.from(selector.filenamePattern) : undefined,
			excludeFileNamePattern: selector.excludeFileNamePattern ? typeConverters.GlobPattern.from(selector.excludeFileNamePattern) : undefined,
		});
R
rebornix 已提交
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150

		return new extHostTypes.Disposable(() => {
			adapter.dispose();
			this._notebookKernelProviders.delete(handle);
			this._proxy.$unregisterNotebookKernelProvider(handle);
		});
	}

	private _withAdapter<T>(handle: number, uri: UriComponents, callback: (adapter: ExtHostNotebookKernelProviderAdapter, document: ExtHostNotebookDocument) => Promise<T>) {
		const document = this._documents.get(URI.revive(uri).toString());

		if (!document) {
			return [];
		}

		const provider = this._notebookKernelProviders.get(handle);

		if (!provider) {
			return [];
		}

		return callback(provider, document);
	}

	async $provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise<INotebookKernelInfoDto2[]> {
		return this._withAdapter<INotebookKernelInfoDto2[]>(handle, uri, (adapter, document) => {
			return adapter.provideKernels(document, token);
		});
	}

	async $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise<void> {
		await this._withAdapter<void>(handle, uri, async (adapter, document) => {
R
Rob Lourens 已提交
1151
			const webComm = this._webviewComm.get(editorId);
R
rebornix 已提交
1152 1153 1154 1155 1156 1157 1158

			if (webComm) {
				await adapter.resolveNotebook(kernelId, document, webComm.contentProviderComm, token);
			}
		});
	}

R
rebornix 已提交
1159 1160 1161 1162 1163 1164 1165 1166
	registerNotebookKernel(extension: IExtensionDescription, id: string, selectors: vscode.GlobPattern[], kernel: vscode.NotebookKernel): vscode.Disposable {
		if (this._notebookKernels.has(id)) {
			throw new Error(`Notebook kernel for '${id}' already registered`);
		}

		this._notebookKernels.set(id, { kernel, extension });
		const transformedSelectors = selectors.map(selector => typeConverters.GlobPattern.from(selector));

R
rebornix 已提交
1167
		this._proxy.$registerNotebookKernel({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, id, kernel.label, transformedSelectors, kernel.preloads || []);
R
rebornix 已提交
1168
		return new extHostTypes.Disposable(() => {
R
rebornix 已提交
1169 1170 1171 1172 1173
			this._notebookKernels.delete(id);
			this._proxy.$unregisterNotebookKernel(id);
		});
	}

1174
	async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto | undefined> {
R
rebornix 已提交
1175 1176 1177 1178
		const provider = this._notebookContentProviders.get(viewType);
		const revivedUri = URI.revive(uri);

		if (provider) {
1179 1180
			let storageRoot: URI | undefined;
			if (this._extensionStoragePaths) {
1181
				storageRoot = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension);
1182 1183
			}

R
rebornix 已提交
1184 1185 1186
			let document = this._documents.get(URI.revive(uri).toString());

			if (!document) {
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
				const that = this;
				document = this._unInitializedDocuments.get(revivedUri.toString()) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, {
					emitModelChange(event: vscode.NotebookCellsChangeEvent): void {
						that._onDidChangeNotebookCells.fire(event);
					},
					emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void {
						that._onDidChangeCellOutputs.fire(event);
					},
					emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
						that._onDidChangeCellLanguage.fire(event);
1197 1198 1199 1200
					},
					emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void {
						that._onDidChangeCellMetadata.fire(event);
					},
1201
				}, viewType, revivedUri, this, storageRoot);
R
rebornix 已提交
1202 1203
				this._unInitializedDocuments.set(revivedUri.toString(), document);
			}
R
rebornix 已提交
1204

1205
			const rawCells = await provider.provider.openNotebook(URI.revive(uri), { backupId });
R
rebornix 已提交
1206 1207 1208 1209 1210 1211
			const dto = {
				metadata: {
					...notebookDocumentMetadataDefaults,
					...rawCells.metadata
				},
				languages: rawCells.languages,
1212 1213 1214 1215
				cells: rawCells.cells.map(cell => ({
					...cell,
					outputs: cell.outputs.map(o => addIdToOutput(o))
				})),
R
rebornix 已提交
1216 1217 1218 1219 1220 1221 1222 1223
			};

			return dto;
		}

		return;
	}

1224 1225 1226 1227 1228 1229 1230 1231
	async $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise<void> {
		const provider = this._notebookContentProviders.get(viewType);
		const revivedUri = URI.revive(uri);
		const document = this._documents.get(revivedUri.toString());
		if (!document || !provider) {
			return;
		}

1232 1233 1234 1235 1236 1237
		let webComm = this._webviewComm.get(editorId);
		if (!webComm) {
			webComm = new ExtHostWebviewCommWrapper(editorId, revivedUri, this._proxy, this._webviewInitData, document);
			this._webviewComm.set(editorId, webComm);
		}

1238 1239 1240 1241
		if (!provider.provider.resolveNotebook) {
			return;
		}

1242 1243
		await provider.provider.resolveNotebook(document, webComm.contentProviderComm);
	}
1244

1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
	private provideCommToNotebookRenderers(document: ExtHostNotebookDocument, renderer: ExtHostNotebookOutputRenderer) {
		let alreadyRegistered = this._renderersUsedInNotebooks.get(document);
		if (!alreadyRegistered) {
			alreadyRegistered = new Set();
			this._renderersUsedInNotebooks.set(document, alreadyRegistered);
		}

		if (alreadyRegistered.has(renderer)) {
			return;
		}

		alreadyRegistered.add(renderer);
1257 1258
		for (const editorId of this._editors.keys()) {
			const comm = this._webviewComm.get(editorId);
1259 1260 1261
			if (comm) {
				renderer.resolveNotebook(document, comm);
			}
1262 1263 1264
		}
	}

R
Rob Lourens 已提交
1265
	async $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void> {
R
Rob Lourens 已提交
1266
		const document = this._documents.get(URI.revive(uri).toString());
R
rebornix 已提交
1267

R
rebornix 已提交
1268
		if (!document) {
R
rebornix 已提交
1269
			return;
R
rebornix 已提交
1270
		}
R
rebornix 已提交
1271

R
rebornix 已提交
1272
		if (this._notebookContentProviders.has(viewType)) {
R
rebornix 已提交
1273 1274
			const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
			const provider = this._notebookContentProviders.get(viewType)!.provider;
R
rebornix 已提交
1275

1276
			if (provider.kernel) {
R
rebornix 已提交
1277
				if (cell) {
R
Rob Lourens 已提交
1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299
					return withToken(token => (provider.kernel!.executeCell as any)(document, cell, token));
				} else {
					return withToken(token => (provider.kernel!.executeAllCells as any)(document, token));
				}
			}
		}
	}

	async $cancelNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void> {
		const document = this._documents.get(URI.revive(uri).toString());

		if (!document) {
			return;
		}

		if (this._notebookContentProviders.has(viewType)) {
			const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
			const provider = this._notebookContentProviders.get(viewType)!.provider;

			if (provider.kernel) {
				if (cell) {
					return provider.kernel.cancelCellExecution(document, cell);
R
rebornix 已提交
1300
				} else {
R
Rob Lourens 已提交
1301
					return provider.kernel.cancelAllCellsExecution(document);
R
rebornix 已提交
1302 1303
				}
			}
R
rebornix 已提交
1304
		}
R
rebornix 已提交
1305 1306
	}

R
Rob Lourens 已提交
1307
	async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void> {
1308
		await this._withAdapter(handle, uri, async (adapter, document) => {
R
Rob Lourens 已提交
1309
			const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
1310

R
Rob Lourens 已提交
1311
			return adapter.executeNotebook(kernelId, document, cell);
1312 1313 1314
		});
	}

1315 1316
	async $cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void> {
		await this._withAdapter(handle, uri, async (adapter, document) => {
R
Rob Lourens 已提交
1317
			const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
1318 1319 1320 1321 1322

			return adapter.cancelNotebook(kernelId, document, cell);
		});
	}

R
Rob Lourens 已提交
1323
	async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void> {
R
Rob Lourens 已提交
1324
		const document = this._documents.get(URI.revive(uri).toString());
R
rebornix 已提交
1325 1326 1327 1328 1329

		if (!document || document.viewType !== viewType) {
			return;
		}

R
Rob Lourens 已提交
1330
		const kernelInfo = this._notebookKernels.get(kernelId);
R
rebornix 已提交
1331 1332 1333 1334 1335

		if (!kernelInfo) {
			return;
		}

R
Rob Lourens 已提交
1336
		const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
R
rebornix 已提交
1337 1338

		if (cell) {
R
Rob Lourens 已提交
1339
			return withToken(token => (kernelInfo!.kernel.executeCell as any)(document, cell, token));
R
rebornix 已提交
1340
		} else {
R
Rob Lourens 已提交
1341
			return withToken(token => (kernelInfo!.kernel.executeAllCells as any)(document, token));
R
rebornix 已提交
1342 1343 1344
		}
	}

R
rebornix 已提交
1345
	async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean> {
R
Rob Lourens 已提交
1346
		const document = this._documents.get(URI.revive(uri).toString());
R
rebornix 已提交
1347 1348 1349 1350 1351
		if (!document) {
			return false;
		}

		if (this._notebookContentProviders.has(viewType)) {
R
rebornix 已提交
1352
			await this._notebookContentProviders.get(viewType)!.provider.saveNotebook(document, token);
R
rebornix 已提交
1353 1354 1355
			return true;
		}

R
saveAs  
rebornix 已提交
1356 1357 1358 1359
		return false;
	}

	async $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean> {
R
Rob Lourens 已提交
1360
		const document = this._documents.get(URI.revive(uri).toString());
R
saveAs  
rebornix 已提交
1361 1362 1363 1364 1365
		if (!document) {
			return false;
		}

		if (this._notebookContentProviders.has(viewType)) {
R
rebornix 已提交
1366
			await this._notebookContentProviders.get(viewType)!.provider.saveNotebookAs(URI.revive(target), document, token);
R
rebornix 已提交
1367
			return true;
R
rebornix 已提交
1368 1369 1370 1371 1372
		}

		return false;
	}

1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392
	async $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
		const document = this._documents.get(URI.revive(uri).toString());
		if (!document) {
			return;
		}

		document.undo(editId, isDirty);

	}

	async $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
		const document = this._documents.get(URI.revive(uri).toString());
		if (!document) {
			return;
		}

		document.redo(editId, isDirty);
	}


1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405
	async $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string | undefined> {
		const document = this._documents.get(URI.revive(uri).toString());
		const provider = this._notebookContentProviders.get(viewType);

		if (document && provider && provider.provider.backupNotebook) {
			const backup = await provider.provider.backupNotebook(document, { destination: document.getNewBackupUri() }, cancellation);
			document.updateBackup(backup);
			return backup.id;
		}

		return;
	}

R
rebornix 已提交
1406
	$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void {
R
rebornix 已提交
1407
		this._outputDisplayOrder = displayOrder;
R
rebornix 已提交
1408
	}
1409

R
rebornix 已提交
1410 1411 1412 1413
	$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelId: string | undefined }) {
		if (event.providerHandle !== undefined) {
			this._withAdapter(event.providerHandle, event.uri, async (adapter, document) => {
				const kernel = event.kernelId ? adapter.getKernel(event.kernelId) : undefined;
1414 1415 1416 1417 1418
				this._editors.forEach(editor => {
					if (editor.editor.document === document) {
						editor.editor.updateActiveKernel(kernel);
					}
				});
R
rebornix 已提交
1419 1420 1421 1422 1423
				this._onDidChangeActiveNotebookKernel.fire({ document, kernel });
			});
		}
	}

R
rebornix 已提交
1424 1425 1426
	// TODO: remove document - editor one on one mapping
	private _getEditorFromURI(uriComponents: UriComponents) {
		const uriStr = URI.revive(uriComponents).toString();
1427
		let editor: { editor: ExtHostNotebookEditor } | undefined;
R
rebornix 已提交
1428 1429 1430 1431 1432 1433 1434 1435 1436
		this._editors.forEach(e => {
			if (e.editor.uri.toString() === uriStr) {
				editor = e;
			}
		});

		return editor;
	}

1437 1438
	$onDidReceiveMessage(editorId: string, forRendererType: string | undefined, message: any): void {
		this._webviewComm.get(editorId)?.onDidReceiveMessage(forRendererType, message);
1439
	}
R
rebornix 已提交
1440 1441

	$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void {
R
rebornix 已提交
1442
		const document = this._documents.get(URI.revive(uriComponents).toString());
R
rebornix 已提交
1443

R
rebornix 已提交
1444
		if (document) {
1445
			document.acceptModelChanged(event);
R
rebornix 已提交
1446 1447
		}
	}
R
rebornix 已提交
1448 1449

	$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void {
R
Rob Lourens 已提交
1450
		const editor = this._getEditorFromURI(uriComponents);
R
rebornix 已提交
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465

		if (!editor) {
			return;
		}

		if (data.selections) {
			const cells = editor.editor.document.cells;

			if (data.selections.selections.length) {
				const firstCell = data.selections.selections[0];
				editor.editor.selection = cells.find(cell => cell.handle === firstCell);
			} else {
				editor.editor.selection = undefined;
			}
		}
R
rebornix 已提交
1466 1467 1468 1469 1470 1471 1472

		if (data.metadata) {
			editor.editor.document.metadata = {
				...notebookDocumentMetadataDefaults,
				...data.metadata
			};
		}
R
rebornix 已提交
1473
	}
R
rebornix 已提交
1474

1475 1476
	private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[]) {
		const revivedUri = document.uri;
1477
		let webComm = this._webviewComm.get(editorId);
1478 1479

		if (!webComm) {
1480 1481
			webComm = new ExtHostWebviewCommWrapper(editorId, revivedUri, this._proxy, this._webviewInitData, document);
			this._webviewComm.set(editorId, webComm);
1482
		}
1483

R
Rob Lourens 已提交
1484
		const editor = new ExtHostNotebookEditor(
1485 1486 1487 1488
			document.viewType,
			editorId,
			revivedUri,
			this._proxy,
1489
			webComm.contentProviderComm,
1490
			document
1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503
		);

		const cells = editor.document.cells;

		if (selections.length) {
			const firstCell = selections[0];
			editor.selection = cells.find(cell => cell.handle === firstCell);
		} else {
			editor.selection = undefined;
		}

		this._editors.get(editorId)?.editor.dispose();

1504 1505 1506 1507
		for (const renderer of this._renderersUsedInNotebooks.get(document) ?? []) {
			renderer.resolveNotebook(document, webComm);
		}

1508
		this._editors.set(editorId, { editor });
1509 1510
	}

R
rebornix 已提交
1511
	async $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta) {
1512 1513
		let editorChanged = false;

R
rebornix 已提交
1514 1515
		if (delta.removedDocuments) {
			delta.removedDocuments.forEach((uri) => {
1516 1517
				const revivedUri = URI.revive(uri);
				const revivedUriStr = revivedUri.toString();
R
Rob Lourens 已提交
1518
				const document = this._documents.get(revivedUriStr);
R
rebornix 已提交
1519 1520 1521

				if (document) {
					document.dispose();
1522
					this._documents.delete(revivedUriStr);
R
rebornix 已提交
1523 1524 1525
					this._onDidCloseNotebookDocument.fire(document);
				}

1526 1527 1528 1529
				[...this._editors.values()].forEach((e) => {
					if (e.editor.uri.toString() === revivedUriStr) {
						e.editor.dispose();
						this._editors.delete(e.editor.id);
R
rebornix 已提交
1530
						editorChanged = true;
1531 1532
					}
				});
R
rebornix 已提交
1533 1534 1535 1536 1537 1538
			});
		}

		if (delta.addedDocuments) {
			delta.addedDocuments.forEach(modelData => {
				const revivedUri = URI.revive(modelData.uri);
R
rebornix 已提交
1539
				const revivedUriStr = revivedUri.toString();
R
rebornix 已提交
1540
				const viewType = modelData.viewType;
1541 1542 1543
				const entry = this._notebookContentProviders.get(viewType);
				let storageRoot: URI | undefined;
				if (entry && this._extensionStoragePaths) {
1544
					storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension);
1545 1546
				}

R
rebornix 已提交
1547
				if (!this._documents.has(revivedUriStr)) {
1548
					const that = this;
1549

R
Rob Lourens 已提交
1550
					const document = this._unInitializedDocuments.get(revivedUriStr) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, {
1551 1552 1553 1554 1555 1556 1557 1558
						emitModelChange(event: vscode.NotebookCellsChangeEvent): void {
							that._onDidChangeNotebookCells.fire(event);
						},
						emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void {
							that._onDidChangeCellOutputs.fire(event);
						},
						emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
							that._onDidChangeCellLanguage.fire(event);
1559 1560 1561
						},
						emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void {
							that._onDidChangeCellMetadata.fire(event);
1562
						}
1563
					}, viewType, revivedUri, this, storageRoot);
1564

R
rebornix 已提交
1565
					this._unInitializedDocuments.delete(revivedUriStr);
R
rebornix 已提交
1566 1567 1568 1569 1570 1571 1572
					if (modelData.metadata) {
						document.metadata = {
							...notebookDocumentMetadataDefaults,
							...modelData.metadata
						};
					}

1573
					document.acceptModelChanged({
1574
						kind: NotebookCellsChangeType.Initialize,
R
rebornix 已提交
1575
						versionId: modelData.versionId,
1576
						changes: [[
1577 1578 1579
							0,
							0,
							modelData.cells
1580
						]]
R
rebornix 已提交
1581 1582
					});

1583
					this._documents.get(revivedUriStr)?.dispose();
R
rebornix 已提交
1584
					this._documents.set(revivedUriStr, document);
1585 1586 1587 1588 1589 1590

					// create editor if populated
					if (modelData.attachedEditor) {
						this._createExtHostEditor(document, modelData.attachedEditor.id, modelData.attachedEditor.selections);
						editorChanged = true;
					}
R
rebornix 已提交
1591 1592
				}

R
rebornix 已提交
1593
				const document = this._documents.get(revivedUriStr)!;
R
rebornix 已提交
1594 1595 1596
				this._onDidOpenNotebookDocument.fire(document);
			});
		}
R
rebornix 已提交
1597

R
rebornix 已提交
1598 1599
		if (delta.addedEditors) {
			delta.addedEditors.forEach(editorModelData => {
1600 1601 1602 1603
				if (this._editors.has(editorModelData.id)) {
					return;
				}

R
rebornix 已提交
1604 1605 1606 1607
				const revivedUri = URI.revive(editorModelData.documentUri);
				const document = this._documents.get(revivedUri.toString());

				if (document) {
1608
					this._createExtHostEditor(document, editorModelData.id, editorModelData.selections);
R
rebornix 已提交
1609 1610 1611 1612 1613
					editorChanged = true;
				}
			});
		}

1614
		const removedEditors: { editor: ExtHostNotebookEditor }[] = [];
1615

R
rebornix 已提交
1616 1617 1618
		if (delta.removedEditors) {
			delta.removedEditors.forEach(editorid => {
				const editor = this._editors.get(editorid);
R
rebornix 已提交
1619

R
rebornix 已提交
1620 1621 1622 1623
				if (editor) {
					editorChanged = true;
					this._editors.delete(editorid);

1624 1625 1626 1627 1628
					if (this.activeNotebookEditor?.id === editor.editor.id) {
						this._activeNotebookEditor = undefined;
					}

					removedEditors.push(editor);
R
rebornix 已提交
1629
				}
R
rebornix 已提交
1630 1631 1632
			});
		}

R
rebornix 已提交
1633
		if (editorChanged) {
1634 1635 1636
			removedEditors.forEach(e => {
				e.editor.dispose();
			});
R
rebornix 已提交
1637 1638
		}

R
rebornix 已提交
1639
		if (delta.visibleEditors) {
1640
			this.visibleNotebookEditors = delta.visibleEditors.map(id => this._editors.get(id)!.editor).filter(editor => !!editor) as ExtHostNotebookEditor[];
R
rebornix 已提交
1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652
			const visibleEditorsSet = new Set<string>();
			this.visibleNotebookEditors.forEach(editor => visibleEditorsSet.add(editor.id));

			[...this._editors.values()].forEach((e) => {
				const newValue = visibleEditorsSet.has(e.editor.id);
				e.editor._acceptVisibility(newValue);
			});

			this.visibleNotebookEditors = [...this._editors.values()].map(e => e.editor).filter(e => e.visible);
			this._onDidChangeVisibleNotebookEditors.fire(this.visibleNotebookEditors);
		}

R
rebornix 已提交
1653 1654 1655
		if (delta.newActiveEditor !== undefined) {
			if (delta.newActiveEditor) {
				this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor;
R
rebornix 已提交
1656
				this._activeNotebookEditor?._acceptActive(true);
1657 1658 1659 1660 1661
				[...this._editors.values()].forEach((e) => {
					if (e.editor !== this.activeNotebookEditor) {
						e.editor._acceptActive(false);
					}
				});
R
rebornix 已提交
1662
			} else {
1663
				// clear active notebook as current active editor is non-notebook editor
R
rebornix 已提交
1664
				this._activeNotebookEditor = undefined;
1665 1666 1667 1668 1669

				[...this._editors.values()].forEach((e) => {
					e.editor._acceptActive(false);
				});

R
rebornix 已提交
1670
			}
R
rebornix 已提交
1671 1672

			this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor);
R
rebornix 已提交
1673 1674
		}
	}
R
rebornix 已提交
1675
}
1676 1677 1678 1679 1680

function hashPath(resource: URI): string {
	const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
	return hash(str) + '';
}
1681 1682 1683 1684 1685

function isEditEvent(e: vscode.NotebookDocumentEditEvent | vscode.NotebookDocumentContentChangeEvent): e is vscode.NotebookDocumentEditEvent {
	return typeof (e as vscode.NotebookDocumentEditEvent).undo === 'function'
		&& typeof (e as vscode.NotebookDocumentEditEvent).redo === 'function';
}