extHostNotebook.ts 51.1 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 } from 'vs/workbench/contrib/notebook/common/notebookCommon';
R
rebornix 已提交
18
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
19
import { CancellationToken } 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 55 56
	emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void;
}

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

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
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
		);
	}
}

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

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

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

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

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

95 96 97 98 99 100
	// 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;
101

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

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

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

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

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

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

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

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

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

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

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

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

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

R
rebornix 已提交
174
	set metadata(newMetadata: vscode.NotebookCellMetadata) {
175
		// Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata'
176
		this._metadataChangeListener.dispose();
177
		const observableMetadata = getObservable(newMetadata);
178 179
		this._metadata = observableMetadata.proxy;
		this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
180
			this._updateMetadata();
181 182
		}));

183
		this._updateMetadata();
184 185
	}

186 187
	private _updateMetadata(): Promise<void> {
		return this._proxy.$updateNotebookCellMetadata(this.notebook.viewType, this.notebook.uri, this.handle, this._metadata);
R
rebornix 已提交
188 189 190
	}
}

R
rebornix 已提交
191
export class ExtHostNotebookDocument extends Disposable implements vscode.NotebookDocument {
R
rebornix 已提交
192
	private static _handlePool: number = 0;
R
rebornix 已提交
193 194 195 196
	readonly handle = ExtHostNotebookDocument._handlePool++;

	private _cells: ExtHostCell[] = [];

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

R
rebornix 已提交
199 200 201 202
	get cells() {
		return this._cells;
	}

R
rebornix 已提交
203 204 205 206 207
	private _languages: string[] = [];

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

R
rebornix 已提交
209 210 211
	set languages(newLanguages: string[]) {
		this._languages = newLanguages;
		this._proxy.$updateNotebookLanguages(this.viewType, this.uri, this._languages);
R
rebornix 已提交
212
	}
R
rebornix 已提交
213

214 215
	private _metadata: Required<vscode.NotebookDocumentMetadata> = notebookDocumentMetadataDefaults;
	private _metadataChangeListener: IDisposable;
R
rebornix 已提交
216 217 218 219 220

	get metadata() {
		return this._metadata;
	}

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

231 232 233 234 235
		const observableMetadata = getObservable(newMetadata);
		this._metadata = observableMetadata.proxy;
		this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
			this.updateMetadata();
		}));
R
rebornix 已提交
236 237

		this.updateMetadata();
R
rebornix 已提交
238 239
	}

R
rebornix 已提交
240
	private _displayOrder: string[] = [];
R
rebornix 已提交
241 242 243 244 245

	get displayOrder() {
		return this._displayOrder;
	}

R
rebornix 已提交
246
	set displayOrder(newOrder: string[]) {
R
rebornix 已提交
247 248 249
		this._displayOrder = newOrder;
	}

R
rebornix 已提交
250 251 252 253 254 255
	private _versionId = 0;

	get versionId() {
		return this._versionId;
	}

256 257 258 259
	private _backupCounter = 1;

	private _backup?: vscode.NotebookDocumentBackup;

260 261 262 263 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

	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);
		}
	}

297 298
	private _disposed = false;

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

		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 已提交
319
	}
R
rebornix 已提交
320

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
	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 已提交
339
	dispose() {
340
		this._disposed = true;
R
rebornix 已提交
341
		super.dispose();
R
rebornix 已提交
342 343
		this._cellDisposableMapping.forEach(cell => cell.dispose());
	}
R
rebornix 已提交
344

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

R
rebornix 已提交
347 348
	get isDirty() { return false; }

349
	accpetModelChanged(event: NotebookCellsChangedEvent): void {
R
rebornix 已提交
350
		this._versionId = event.versionId;
351 352 353 354
		if (event.kind === NotebookCellsChangeType.Initialize) {
			this.$spliceNotebookCells(event.changes, true);
		} if (event.kind === NotebookCellsChangeType.ModelChange) {
			this.$spliceNotebookCells(event.changes, false);
R
rebornix 已提交
355
		} else if (event.kind === NotebookCellsChangeType.Move) {
356
			this.$moveCell(event.index, event.newIdx);
R
rebornix 已提交
357
		} else if (event.kind === NotebookCellsChangeType.CellClearOutput) {
358
			this.$clearCellOutputs(event.index);
R
rebornix 已提交
359
		} else if (event.kind === NotebookCellsChangeType.CellsClearOutput) {
360
			this.$clearAllCellOutputs();
361
		} else if (event.kind === NotebookCellsChangeType.ChangeLanguage) {
362
			this.$changeCellLanguage(event.index, event.language);
R
rebornix 已提交
363
		}
R
rebornix 已提交
364
	}
365

366
	private $spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void {
367
		if (this._disposed) {
R
rebornix 已提交
368
			return;
369 370
		}

371
		let contentChangeEvents: vscode.NotebookCellsChangeData[] = [];
R
rebornix 已提交
372

373 374 375
		splices.reverse().forEach(splice => {
			let cellDtos = splice[2];
			let newCells = cellDtos.map(cell => {
376

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

379 380 381
				if (!this._cellDisposableMapping.has(extCell.handle)) {
					this._cellDisposableMapping.set(extCell.handle, new DisposableStore());
				}
382

383
				let store = this._cellDisposableMapping.get(extCell.handle)!;
R
rebornix 已提交
384

385 386 387
				store.add(extCell.onDidChangeOutputs((diffs) => {
					this.eventuallyUpdateCellOutputs(extCell, diffs);
				}));
R
rebornix 已提交
388

389 390
				return extCell;
			});
391

392 393 394
			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 已提交
395

396
			}
397

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

400 401 402
			const event: vscode.NotebookCellsChangeData = {
				start: splice[0],
				deletedCount: splice[1],
403
				deletedItems,
404 405
				items: newCells
			};
R
rebornix 已提交
406

407 408
			contentChangeEvents.push(event);
		});
R
rebornix 已提交
409

410 411 412 413 414 415
		if (!initialization) {
			this._emitter.emitModelChange({
				document: this,
				changes: contentChangeEvents
			});
		}
R
rebornix 已提交
416
	}
R
rebornix 已提交
417

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

438
	private $clearCellOutputs(index: number): void {
R
rebornix 已提交
439 440
		const cell = this.cells[index];
		cell.outputs = [];
441 442
		const event: vscode.NotebookCellOutputsChangeEvent = { document: this, cells: [cell] };
		this._emitter.emitCellOutputsChange(event);
R
rebornix 已提交
443 444
	}

445 446 447 448 449 450 451 452 453 454
	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 已提交
455 456
	}

457
	private $changeCellLanguage(index: number, language: string): void {
458
		const cell = this.cells[index];
459
		cell.defaultDocument._acceptLanguageId(language);
R
rebornix 已提交
460
		const event: vscode.NotebookCellLanguageChangeEvent = { document: this, cell, language };
461
		this._emitter.emitCellLanguageChange(event);
462 463
	}

464
	async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<IProcessedOutput>[]) {
465 466 467
		let renderers = new Set<number>();
		let outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
			let outputs = diff.toInsert;
468
			return [diff.start, diff.deleteCount, outputs];
469 470
		});

471 472 473 474 475
		await this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos, Array.from(renderers));
		this._emitter.emitCellOutputsChange({
			document: this,
			cells: [cell]
		});
476 477
	}

R
rebornix 已提交
478
	getCell(cellHandle: number) {
R
rebornix 已提交
479 480
		return this.cells.find(cell => cell.handle === cellHandle);
	}
R
rebornix 已提交
481

482 483 484
	getCell2(cellUri: UriComponents) {
		return this.cells.find(cell => cell.uri.fragment === cellUri.fragment);
	}
R
rebornix 已提交
485 486
}

R
rebornix 已提交
487
export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellEdit {
R
rebornix 已提交
488 489 490
	private _finalized: boolean = false;
	private readonly _documentVersionId: number;
	private _collectedEdits: ICellEditOperation[] = [];
R
rebornix 已提交
491
	private _renderers = new Set<number>();
R
rebornix 已提交
492 493 494 495

	constructor(
		readonly editor: ExtHostNotebookEditor
	) {
R
rebornix 已提交
496
		this._documentVersionId = editor.document.versionId;
R
rebornix 已提交
497 498 499 500 501 502
	}

	finalize(): INotebookEditData {
		this._finalized = true;
		return {
			documentVersionId: this._documentVersionId,
R
rebornix 已提交
503 504
			edits: this._collectedEdits,
			renderers: Array.from(this._renderers)
R
rebornix 已提交
505 506 507 508 509 510 511 512 513
		};
	}

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

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

R
rebornix 已提交
517
		const sourceArr = Array.isArray(content) ? content : content.split(/\r|\n|\r\n/g);
R
rebornix 已提交
518
		let cell = {
R
rebornix 已提交
519
			source: sourceArr,
R
rebornix 已提交
520
			language,
R
rebornix 已提交
521
			cellKind: type,
522 523
			outputs: outputs.map(o => addIdToOutput(o)),
			metadata,
R
rebornix 已提交
524 525 526 527 528 529
		};

		this._collectedEdits.push({
			editType: CellEditType.Insert,
			index,
			cells: [cell]
R
rebornix 已提交
530 531 532 533 534 535 536 537
		});
	}

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

		this._collectedEdits.push({
			editType: CellEditType.Delete,
538 539
			index,
			count: 1
R
rebornix 已提交
540 541 542 543
		});
	}
}

544 545 546
class ExtHostWebviewCommWrapper extends Disposable {
	private readonly _onDidReceiveDocumentMessage = new Emitter<any>();
	private readonly _rendererIdToEmitters = new Map<string, Emitter<any>>();
547 548

	constructor(
549
		private _editorId: string,
550 551 552 553 554 555 556 557
		public uri: URI,
		private _proxy: MainThreadNotebookShape,
		private _webviewInitData: WebviewInitData,
		public document: ExtHostNotebookDocument,
	) {
		super();
	}

558 559 560 561 562
	public onDidReceiveMessage(forRendererId: string | undefined, message: any) {
		this._onDidReceiveDocumentMessage.fire(message);
		if (forRendererId !== undefined) {
			this._rendererIdToEmitters.get(forRendererId)?.fire(message);
		}
563 564
	}

565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
	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);
586 587 588
	}
}

R
rebornix 已提交
589
export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor {
R
rebornix 已提交
590
	private _viewColumn: vscode.ViewColumn | undefined;
R
rebornix 已提交
591 592

	selection?: ExtHostCell = undefined;
R
rebornix 已提交
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615

	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 已提交
616
	_acceptActive(value: boolean) {
R
rebornix 已提交
617 618 619
		this._active = value;
	}

620 621 622 623 624 625 626 627 628 629
	private _kernel?: vscode.NotebookKernel;

	get kernel() {
		return this._kernel;
	}

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

R
rebornix 已提交
630 631
	private _onDidDispose = new Emitter<void>();
	readonly onDidDispose: Event<void> = this._onDidDispose.event;
632
	private _onDidReceiveMessage = new Emitter<any>();
633
	onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
R
rebornix 已提交
634 635

	constructor(
636
		private readonly viewType: string,
R
rebornix 已提交
637 638
		readonly id: string,
		public uri: URI,
639
		private _proxy: MainThreadNotebookShape,
640
		private _webComm: vscode.NotebookCommunication,
R
rebornix 已提交
641 642
		public document: ExtHostNotebookDocument,
	) {
R
rebornix 已提交
643
		super();
644 645 646
		this._register(this._webComm.onDidReceiveMessage(e => {
			this._onDidReceiveMessage.fire(e);
		}));
R
rebornix 已提交
647 648
	}

R
rebornix 已提交
649 650
	edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable<boolean> {
		const edit = new NotebookEditorCellEditBuilder(this);
R
rebornix 已提交
651 652 653 654
		callback(edit);
		return this._applyEdit(edit);
	}

R
rebornix 已提交
655
	private _applyEdit(editBuilder: NotebookEditorCellEditBuilder): Promise<boolean> {
R
rebornix 已提交
656 657 658 659 660 661 662
		const editData = editBuilder.finalize();

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

R
rebornix 已提交
663 664 665 666 667 668 669 670 671 672 673 674 675 676
		let compressedEdits: ICellEditOperation[] = [];
		let compressedEditsIndex = -1;

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

			let prevIndex = compressedEditsIndex;
			let prev = compressedEdits[prevIndex];

			if (prev.editType === CellEditType.Insert && editData.edits[i].editType === CellEditType.Insert) {
677
				if (prev.index === editData.edits[i].index) {
R
rebornix 已提交
678 679 680 681 682
					prev.cells.push(...(editData.edits[i] as ICellInsertEdit).cells);
					continue;
				}
			}

683 684 685 686 687 688 689
			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 已提交
690 691 692
			compressedEdits.push(editData.edits[i]);
			compressedEditsIndex++;
		}
R
rebornix 已提交
693

R
rebornix 已提交
694
		return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.renderers);
R
rebornix 已提交
695 696 697 698 699 700 701 702
	}

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

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

705 706 707
	updateActiveKernel(kernel?: vscode.NotebookKernel) {
		this._kernel = kernel;
	}
708
	async postMessage(message: any): Promise<boolean> {
709
		return this._webComm.postMessage(message);
710 711
	}

R
rebornix 已提交
712
	asWebviewUri(localResource: vscode.Uri): vscode.Uri {
713
		return this._webComm.asWebviewUri(localResource);
R
rebornix 已提交
714
	}
R
rebornix 已提交
715 716 717 718
	dispose() {
		this._onDidDispose.fire();
		super.dispose();
	}
R
rebornix 已提交
719 720
}

R
rebornix 已提交
721
export class ExtHostNotebookOutputRenderer {
R
rebornix 已提交
722
	private static _handlePool: number = 0;
723
	private resolvedComms = new WeakSet<ExtHostWebviewCommWrapper>();
R
rebornix 已提交
724 725
	readonly handle = ExtHostNotebookOutputRenderer._handlePool++;

R
rebornix 已提交
726
	constructor(
R
rebornix 已提交
727 728 729
		public type: string,
		public filter: vscode.NotebookOutputSelector,
		public renderer: vscode.NotebookOutputRenderer
R
rebornix 已提交
730 731 732 733
	) {

	}

R
rebornix 已提交
734
	matches(mimeType: string): boolean {
R
rebornix 已提交
735 736
		if (this.filter.mimeTypes) {
			if (this.filter.mimeTypes.indexOf(mimeType) >= 0) {
R
rebornix 已提交
737 738 739 740 741 742
				return true;
			}
		}
		return false;
	}

743 744 745 746 747 748 749
	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);
		}
	}

750 751
	render(document: ExtHostNotebookDocument, output: vscode.CellDisplayOutput, outputId: string, mimeType: string): string {
		let html = this.renderer.render(document, { output, outputId, mimeType });
R
rebornix 已提交
752

753
		return html;
R
rebornix 已提交
754 755 756
	}
}
export interface ExtHostNotebookOutputRenderingHandler {
R
rebornix 已提交
757
	outputDisplayOrder: INotebookDisplayOrder | undefined;
R
rebornix 已提交
758
	findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[];
R
rebornix 已提交
759 760
}

R
rebornix 已提交
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
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 已提交
783 784
		let kernel_unique_pool = 0;
		let kernelIdCache = new Set<string>();
R
rebornix 已提交
785 786 787 788

		const transformedData: INotebookKernelInfoDto2[] = data.map(kernel => {
			let id = this._kernelToId.get(kernel);
			if (id === undefined) {
R
rebornix 已提交
789 790 791 792 793 794
				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 已提交
795 796 797 798 799 800 801 802 803
				this._kernelToId.set(kernel, id);
			}

			newMap.set(kernel, id);

			return {
				id,
				label: kernel.label,
				extension: this._extension.identifier,
804
				extensionLocation: this._extension.extensionLocation,
R
rebornix 已提交
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
				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 已提交
821 822 823 824
	getKernel(kernelId: string) {
		return this._idToKernel.get(kernelId);
	}

R
rebornix 已提交
825 826 827 828 829 830 831
	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);
		}
	}
832 833 834 835 836 837 838 839 840 841 842 843 844 845

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

		if (!kernel) {
			return;
		}

		if (cell) {
			return kernel.executeCell(document, cell, token);
		} else {
			return kernel.executeAllCells(document, token);
		}
	}
R
rebornix 已提交
846 847
}

R
rebornix 已提交
848
export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostNotebookOutputRenderingHandler {
R
rebornix 已提交
849 850
	private static _notebookKernelProviderHandlePool: number = 0;

R
rebornix 已提交
851
	private readonly _proxy: MainThreadNotebookShape;
R
rebornix 已提交
852
	private readonly _notebookContentProviders = new Map<string, { readonly provider: vscode.NotebookContentProvider, readonly extension: IExtensionDescription; }>();
R
rebornix 已提交
853
	private readonly _notebookKernels = new Map<string, { readonly kernel: vscode.NotebookKernel, readonly extension: IExtensionDescription; }>();
R
rebornix 已提交
854
	private readonly _notebookKernelProviders = new Map<number, ExtHostNotebookKernelProviderAdapter>();
R
rebornix 已提交
855
	private readonly _documents = new Map<string, ExtHostNotebookDocument>();
R
rebornix 已提交
856
	private readonly _unInitializedDocuments = new Map<string, ExtHostNotebookDocument>();
857
	private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor }>();
858
	private readonly _webviewComm = new Map<string, ExtHostWebviewCommWrapper>();
859
	private readonly _notebookOutputRenderers = new Map<string, ExtHostNotebookOutputRenderer>();
860
	private readonly _renderersUsedInNotebooks = new WeakMap<ExtHostNotebookDocument, Set<ExtHostNotebookOutputRenderer>>();
R
rebornix 已提交
861 862
	private readonly _onDidChangeNotebookCells = new Emitter<vscode.NotebookCellsChangeEvent>();
	readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event;
863 864
	private readonly _onDidChangeCellOutputs = new Emitter<vscode.NotebookCellOutputsChangeEvent>();
	readonly onDidChangeCellOutputs = this._onDidChangeCellOutputs.event;
R
rebornix 已提交
865 866
	private readonly _onDidChangeCellLanguage = new Emitter<vscode.NotebookCellLanguageChangeEvent>();
	readonly onDidChangeCellLanguage = this._onDidChangeCellLanguage.event;
R
rebornix 已提交
867 868
	private readonly _onDidChangeActiveNotebookEditor = new Emitter<vscode.NotebookEditor | undefined>();
	readonly onDidChangeActiveNotebookEditor = this._onDidChangeActiveNotebookEditor.event;
869

R
rebornix 已提交
870
	private _outputDisplayOrder: INotebookDisplayOrder | undefined;
R
rebornix 已提交
871

R
rebornix 已提交
872
	get outputDisplayOrder(): INotebookDisplayOrder | undefined {
R
rebornix 已提交
873
		return this._outputDisplayOrder;
R
rebornix 已提交
874 875
	}

R
rebornix 已提交
876 877 878 879 880 881
	private _activeNotebookEditor: ExtHostNotebookEditor | undefined;

	get activeNotebookEditor() {
		return this._activeNotebookEditor;
	}

882 883 884 885
	get notebookDocuments() {
		return [...this._documents.values()];
	}

R
rebornix 已提交
886 887
	private _onDidOpenNotebookDocument = new Emitter<vscode.NotebookDocument>();
	onDidOpenNotebookDocument: Event<vscode.NotebookDocument> = this._onDidOpenNotebookDocument.event;
R
rebornix 已提交
888 889
	private _onDidCloseNotebookDocument = new Emitter<vscode.NotebookDocument>();
	onDidCloseNotebookDocument: Event<vscode.NotebookDocument> = this._onDidCloseNotebookDocument.event;
R
rebornix 已提交
890
	visibleNotebookEditors: ExtHostNotebookEditor[] = [];
R
rebornix 已提交
891
	private _onDidChangeActiveNotebookKernel = new Emitter<{ document: ExtHostNotebookDocument, kernel: vscode.NotebookKernel | undefined }>();
R
rebornix 已提交
892
	onDidChangeActiveNotebookKernel = this._onDidChangeActiveNotebookKernel.event;
R
rebornix 已提交
893 894
	private _onDidChangeVisibleNotebookEditors = new Emitter<vscode.NotebookEditor[]>();
	onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event;
R
rebornix 已提交
895

896 897 898 899 900 901 902
	constructor(
		mainContext: IMainContext,
		commands: ExtHostCommands,
		private _documentsAndEditors: ExtHostDocumentsAndEditors,
		private readonly _webviewInitData: WebviewInitData,
		private readonly _extensionStoragePaths?: IExtensionStoragePaths,
	) {
R
rebornix 已提交
903
		this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook);
904 905 906 907 908 909 910 911

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

					for (let value of this._editors) {
912 913
						if (value[1].editor.document.handle === documentHandle) {
							const cell = value[1].editor.document.getCell(cellHandle);
914 915 916 917 918 919
							if (cell) {
								return cell;
							}
						}
					}
				}
B
Benjamin Pasero 已提交
920
				return arg;
921 922
			}
		});
R
rebornix 已提交
923 924
	}

R
rebornix 已提交
925
	registerNotebookOutputRenderer(
R
rebornix 已提交
926
		type: string,
R
rebornix 已提交
927
		extension: IExtensionDescription,
R
rebornix 已提交
928
		filter: vscode.NotebookOutputSelector,
R
rebornix 已提交
929
		renderer: vscode.NotebookOutputRenderer
R
rebornix 已提交
930
	): vscode.Disposable {
931 932 933 934
		if (this._notebookKernels.has(type)) {
			throw new Error(`Notebook renderer for '${type}' already registered`);
		}

R
rebornix 已提交
935
		let extHostRenderer = new ExtHostNotebookOutputRenderer(type, filter, renderer);
936 937
		this._notebookOutputRenderers.set(extHostRenderer.type, extHostRenderer);
		this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation }, type, filter, renderer.preloads || []);
R
rebornix 已提交
938
		return new extHostTypes.Disposable(() => {
939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955
			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)!;
956 957
		this.provideCommToNotebookRenderers(document, renderer);

958
		const cellsResponse: IOutputRenderResponseCellInfo<UriComponents>[] = request.items.map(cellInfo => {
959
			const cell = document.getCell2(cellInfo.key)!;
960 961 962
			const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => {
				return {
					index: output.index,
963
					outputId: output.outputId,
964 965
					mimeType: output.mimeType,
					handlerId: id,
966
					transformedOutput: renderer.render(document, cell.outputs[output.index] as vscode.CellDisplayOutput, output.outputId, output.mimeType)
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
				};
			});

			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)!;
994 995
		this.provideCommToNotebookRenderers(document, renderer);

996 997 998 999
		const cellsResponse: IOutputRenderResponseCellInfo<T>[] = request.items.map(cellInfo => {
			const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => {
				return {
					index: output.index,
1000
					outputId: output.outputId,
1001 1002
					mimeType: output.mimeType,
					handlerId: id,
1003
					transformedOutput: renderer.render(document, output.output! as vscode.CellDisplayOutput, output.outputId, output.mimeType)
1004 1005 1006 1007 1008 1009 1010
				};
			});

			return {
				key: cellInfo.key,
				outputs: outputResponse
			};
R
rebornix 已提交
1011
		});
1012 1013

		return { items: cellsResponse };
R
rebornix 已提交
1014 1015
	}

R
rebornix 已提交
1016 1017
	findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[] {
		let matches: ExtHostNotebookOutputRenderer[] = [];
R
rebornix 已提交
1018
		for (let renderer of this._notebookOutputRenderers) {
R
rebornix 已提交
1019
			if (renderer[1].matches(mimeType)) {
R
rebornix 已提交
1020
				matches.push(renderer[1]);
R
rebornix 已提交
1021 1022 1023
			}
		}

R
rebornix 已提交
1024
		return matches;
R
rebornix 已提交
1025 1026
	}

R
rebornix 已提交
1027 1028 1029 1030 1031 1032
	registerNotebookContentProvider(
		extension: IExtensionDescription,
		viewType: string,
		provider: vscode.NotebookContentProvider,
	): vscode.Disposable {

R
rebornix 已提交
1033
		if (this._notebookContentProviders.has(viewType)) {
R
rebornix 已提交
1034 1035 1036
			throw new Error(`Notebook provider for '${viewType}' already registered`);
		}

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

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

J
Johannes Rieken 已提交
1043
		const listener = provider.onDidChangeNotebook
R
rebornix 已提交
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
			? 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 已提交
1058
			: Disposable.None;
1059

R
rebornix 已提交
1060
		const supportBackup = !!provider.backupNotebook;
1061 1062

		this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined);
1063

R
rebornix 已提交
1064
		return new extHostTypes.Disposable(() => {
1065
			listener.dispose();
R
rebornix 已提交
1066 1067 1068 1069 1070
			this._notebookContentProviders.delete(viewType);
			this._proxy.$unregisterNotebookProvider(viewType);
		});
	}

R
rebornix 已提交
1071 1072 1073 1074
	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 已提交
1075 1076 1077 1078 1079
		this._proxy.$registerNotebookKernelProvider({ id: extension.identifier, location: extension.extensionLocation }, handle, {
			viewType: selector.viewType,
			filenamePattern: selector.filenamePattern ? typeConverters.GlobPattern.from(selector.filenamePattern) : undefined,
			excludeFileNamePattern: selector.excludeFileNamePattern ? typeConverters.GlobPattern.from(selector.excludeFileNamePattern) : undefined,
		});
R
rebornix 已提交
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119

		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) => {
			let webComm = this._webviewComm.get(editorId);

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

R
rebornix 已提交
1120 1121 1122 1123 1124 1125 1126 1127
	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 已提交
1128
		this._proxy.$registerNotebookKernel({ id: extension.identifier, location: extension.extensionLocation }, id, kernel.label, transformedSelectors, kernel.preloads || []);
R
rebornix 已提交
1129
		return new extHostTypes.Disposable(() => {
R
rebornix 已提交
1130 1131 1132 1133 1134
			this._notebookKernels.delete(id);
			this._proxy.$unregisterNotebookKernel(id);
		});
	}

1135
	async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto | undefined> {
R
rebornix 已提交
1136 1137 1138 1139
		const provider = this._notebookContentProviders.get(viewType);
		const revivedUri = URI.revive(uri);

		if (provider) {
1140 1141
			let storageRoot: URI | undefined;
			if (this._extensionStoragePaths) {
1142
				storageRoot = this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension);
1143 1144
			}

R
rebornix 已提交
1145 1146 1147
			let document = this._documents.get(URI.revive(uri).toString());

			if (!document) {
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
				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);
					}
1159
				}, viewType, revivedUri, this, storageRoot);
R
rebornix 已提交
1160 1161
				this._unInitializedDocuments.set(revivedUri.toString(), document);
			}
R
rebornix 已提交
1162

1163
			const rawCells = await provider.provider.openNotebook(URI.revive(uri), { backupId });
R
rebornix 已提交
1164 1165 1166 1167 1168 1169
			const dto = {
				metadata: {
					...notebookDocumentMetadataDefaults,
					...rawCells.metadata
				},
				languages: rawCells.languages,
1170 1171 1172 1173
				cells: rawCells.cells.map(cell => ({
					...cell,
					outputs: cell.outputs.map(o => addIdToOutput(o))
				})),
R
rebornix 已提交
1174 1175 1176 1177 1178 1179 1180 1181
			};

			return dto;
		}

		return;
	}

1182 1183 1184 1185 1186 1187 1188 1189
	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;
		}

1190 1191 1192 1193 1194 1195
		let webComm = this._webviewComm.get(editorId);
		if (!webComm) {
			webComm = new ExtHostWebviewCommWrapper(editorId, revivedUri, this._proxy, this._webviewInitData, document);
			this._webviewComm.set(editorId, webComm);
		}

1196 1197 1198 1199
		if (!provider.provider.resolveNotebook) {
			return;
		}

1200 1201
		await provider.provider.resolveNotebook(document, webComm.contentProviderComm);
	}
1202

1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
	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);
1215 1216
		for (const editorId of this._editors.keys()) {
			const comm = this._webviewComm.get(editorId);
1217 1218 1219
			if (comm) {
				renderer.resolveNotebook(document, comm);
			}
1220 1221 1222
		}
	}

1223
	async $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise<void> {
R
rebornix 已提交
1224
		let document = this._documents.get(URI.revive(uri).toString());
R
rebornix 已提交
1225

R
rebornix 已提交
1226
		if (!document) {
R
rebornix 已提交
1227
			return;
R
rebornix 已提交
1228
		}
R
rebornix 已提交
1229

R
rebornix 已提交
1230
		if (this._notebookContentProviders.has(viewType)) {
R
rebornix 已提交
1231 1232
			const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
			const provider = this._notebookContentProviders.get(viewType)!.provider;
R
rebornix 已提交
1233

1234
			if (provider.kernel) {
R
rebornix 已提交
1235 1236 1237 1238 1239 1240
				if (cell) {
					return provider.kernel.executeCell(document, cell, token);
				} else {
					return provider.kernel.executeAllCells(document, token);
				}
			}
R
rebornix 已提交
1241
		}
R
rebornix 已提交
1242 1243
	}

1244 1245 1246 1247 1248 1249 1250 1251
	async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined, token: CancellationToken): Promise<void> {
		await this._withAdapter(handle, uri, async (adapter, document) => {
			let cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;

			return adapter.executeNotebook(kernelId, document, cell, token);
		});
	}

R
rebornix 已提交
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
	async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise<void> {
		let document = this._documents.get(URI.revive(uri).toString());

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

		let kernelInfo = this._notebookKernels.get(kernelId);

		if (!kernelInfo) {
			return;
		}

		let cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;

		if (cell) {
			return kernelInfo.kernel.executeCell(document, cell, token);
		} else {
			return kernelInfo.kernel.executeAllCells(document, token);
		}
	}

R
rebornix 已提交
1274
	async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean> {
R
rebornix 已提交
1275
		let document = this._documents.get(URI.revive(uri).toString());
R
rebornix 已提交
1276 1277 1278 1279 1280
		if (!document) {
			return false;
		}

		if (this._notebookContentProviders.has(viewType)) {
R
rebornix 已提交
1281
			await this._notebookContentProviders.get(viewType)!.provider.saveNotebook(document, token);
R
rebornix 已提交
1282 1283 1284
			return true;
		}

R
saveAs  
rebornix 已提交
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
		return false;
	}

	async $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean> {
		let document = this._documents.get(URI.revive(uri).toString());
		if (!document) {
			return false;
		}

		if (this._notebookContentProviders.has(viewType)) {
R
rebornix 已提交
1295
			await this._notebookContentProviders.get(viewType)!.provider.saveNotebookAs(URI.revive(target), document, token);
R
rebornix 已提交
1296
			return true;
R
rebornix 已提交
1297 1298 1299 1300 1301
		}

		return false;
	}

1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
	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);
	}


1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
	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 已提交
1335
	$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void {
R
rebornix 已提交
1336
		this._outputDisplayOrder = displayOrder;
R
rebornix 已提交
1337
	}
1338

R
rebornix 已提交
1339 1340 1341 1342
	$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;
1343 1344 1345 1346 1347
				this._editors.forEach(editor => {
					if (editor.editor.document === document) {
						editor.editor.updateActiveKernel(kernel);
					}
				});
R
rebornix 已提交
1348 1349 1350 1351 1352
				this._onDidChangeActiveNotebookKernel.fire({ document, kernel });
			});
		}
	}

R
rebornix 已提交
1353 1354 1355
	// TODO: remove document - editor one on one mapping
	private _getEditorFromURI(uriComponents: UriComponents) {
		const uriStr = URI.revive(uriComponents).toString();
1356
		let editor: { editor: ExtHostNotebookEditor } | undefined;
R
rebornix 已提交
1357 1358 1359 1360 1361 1362 1363 1364 1365
		this._editors.forEach(e => {
			if (e.editor.uri.toString() === uriStr) {
				editor = e;
			}
		});

		return editor;
	}

1366 1367
	$onDidReceiveMessage(editorId: string, forRendererType: string | undefined, message: any): void {
		this._webviewComm.get(editorId)?.onDidReceiveMessage(forRendererType, message);
1368
	}
R
rebornix 已提交
1369 1370

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

R
rebornix 已提交
1373
		if (document) {
1374
			document.accpetModelChanged(event);
R
rebornix 已提交
1375 1376
		}
	}
R
rebornix 已提交
1377 1378

	$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void {
R
rebornix 已提交
1379
		let editor = this._getEditorFromURI(uriComponents);
R
rebornix 已提交
1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394

		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 已提交
1395 1396 1397 1398 1399 1400 1401

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

1404 1405
	private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[]) {
		const revivedUri = document.uri;
1406
		let webComm = this._webviewComm.get(editorId);
1407 1408

		if (!webComm) {
1409 1410
			webComm = new ExtHostWebviewCommWrapper(editorId, revivedUri, this._proxy, this._webviewInitData, document);
			this._webviewComm.set(editorId, webComm);
1411
		}
1412 1413 1414 1415 1416 1417

		let editor = new ExtHostNotebookEditor(
			document.viewType,
			editorId,
			revivedUri,
			this._proxy,
1418
			webComm.contentProviderComm,
1419
			document
1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
		);

		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();

1433 1434 1435 1436
		for (const renderer of this._renderersUsedInNotebooks.get(document) ?? []) {
			renderer.resolveNotebook(document, webComm);
		}

1437
		this._editors.set(editorId, { editor });
1438 1439
	}

R
rebornix 已提交
1440
	async $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta) {
1441 1442
		let editorChanged = false;

R
rebornix 已提交
1443 1444
		if (delta.removedDocuments) {
			delta.removedDocuments.forEach((uri) => {
1445 1446 1447
				const revivedUri = URI.revive(uri);
				const revivedUriStr = revivedUri.toString();
				let document = this._documents.get(revivedUriStr);
R
rebornix 已提交
1448 1449 1450

				if (document) {
					document.dispose();
1451
					this._documents.delete(revivedUriStr);
R
rebornix 已提交
1452 1453 1454
					this._onDidCloseNotebookDocument.fire(document);
				}

1455 1456 1457 1458
				[...this._editors.values()].forEach((e) => {
					if (e.editor.uri.toString() === revivedUriStr) {
						e.editor.dispose();
						this._editors.delete(e.editor.id);
R
rebornix 已提交
1459
						editorChanged = true;
1460 1461
					}
				});
R
rebornix 已提交
1462 1463 1464 1465 1466 1467
			});
		}

		if (delta.addedDocuments) {
			delta.addedDocuments.forEach(modelData => {
				const revivedUri = URI.revive(modelData.uri);
R
rebornix 已提交
1468
				const revivedUriStr = revivedUri.toString();
R
rebornix 已提交
1469
				const viewType = modelData.viewType;
1470 1471 1472
				const entry = this._notebookContentProviders.get(viewType);
				let storageRoot: URI | undefined;
				if (entry && this._extensionStoragePaths) {
1473
					storageRoot = this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension);
1474 1475
				}

R
rebornix 已提交
1476
				if (!this._documents.has(revivedUriStr)) {
1477
					const that = this;
1478

1479 1480 1481 1482 1483 1484 1485 1486 1487 1488
					let document = this._unInitializedDocuments.get(revivedUriStr) ?? 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);
						}
1489
					}, viewType, revivedUri, this, storageRoot);
1490

R
rebornix 已提交
1491
					this._unInitializedDocuments.delete(revivedUriStr);
R
rebornix 已提交
1492 1493 1494 1495 1496 1497 1498
					if (modelData.metadata) {
						document.metadata = {
							...notebookDocumentMetadataDefaults,
							...modelData.metadata
						};
					}

R
rebornix 已提交
1499
					document.accpetModelChanged({
1500
						kind: NotebookCellsChangeType.Initialize,
R
rebornix 已提交
1501
						versionId: modelData.versionId,
1502
						changes: [[
1503 1504 1505
							0,
							0,
							modelData.cells
1506
						]]
R
rebornix 已提交
1507 1508
					});

1509
					this._documents.get(revivedUriStr)?.dispose();
R
rebornix 已提交
1510
					this._documents.set(revivedUriStr, document);
1511 1512 1513 1514 1515 1516

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

R
rebornix 已提交
1519
				const document = this._documents.get(revivedUriStr)!;
R
rebornix 已提交
1520 1521 1522
				this._onDidOpenNotebookDocument.fire(document);
			});
		}
R
rebornix 已提交
1523

R
rebornix 已提交
1524 1525
		if (delta.addedEditors) {
			delta.addedEditors.forEach(editorModelData => {
1526 1527 1528 1529
				if (this._editors.has(editorModelData.id)) {
					return;
				}

R
rebornix 已提交
1530 1531 1532 1533
				const revivedUri = URI.revive(editorModelData.documentUri);
				const document = this._documents.get(revivedUri.toString());

				if (document) {
1534
					this._createExtHostEditor(document, editorModelData.id, editorModelData.selections);
R
rebornix 已提交
1535 1536 1537 1538 1539
					editorChanged = true;
				}
			});
		}

1540
		const removedEditors: { editor: ExtHostNotebookEditor }[] = [];
1541

R
rebornix 已提交
1542 1543 1544
		if (delta.removedEditors) {
			delta.removedEditors.forEach(editorid => {
				const editor = this._editors.get(editorid);
R
rebornix 已提交
1545

R
rebornix 已提交
1546 1547 1548 1549
				if (editor) {
					editorChanged = true;
					this._editors.delete(editorid);

1550 1551 1552 1553 1554
					if (this.activeNotebookEditor?.id === editor.editor.id) {
						this._activeNotebookEditor = undefined;
					}

					removedEditors.push(editor);
R
rebornix 已提交
1555
				}
R
rebornix 已提交
1556 1557 1558
			});
		}

R
rebornix 已提交
1559
		if (editorChanged) {
1560 1561 1562
			removedEditors.forEach(e => {
				e.editor.dispose();
			});
R
rebornix 已提交
1563 1564
		}

R
rebornix 已提交
1565
		if (delta.visibleEditors) {
1566
			this.visibleNotebookEditors = delta.visibleEditors.map(id => this._editors.get(id)!.editor).filter(editor => !!editor) as ExtHostNotebookEditor[];
R
rebornix 已提交
1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578
			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 已提交
1579 1580 1581
		if (delta.newActiveEditor !== undefined) {
			if (delta.newActiveEditor) {
				this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor;
R
rebornix 已提交
1582
				this._activeNotebookEditor?._acceptActive(true);
1583 1584 1585 1586 1587
				[...this._editors.values()].forEach((e) => {
					if (e.editor !== this.activeNotebookEditor) {
						e.editor._acceptActive(false);
					}
				});
R
rebornix 已提交
1588
			} else {
1589
				// clear active notebook as current active editor is non-notebook editor
R
rebornix 已提交
1590
				this._activeNotebookEditor = undefined;
1591 1592 1593 1594 1595

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

R
rebornix 已提交
1596
			}
R
rebornix 已提交
1597 1598

			this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor);
R
rebornix 已提交
1599 1600
		}
	}
R
rebornix 已提交
1601
}
1602 1603 1604 1605 1606

function hashPath(resource: URI): string {
	const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
	return hash(str) + '';
}
1607 1608 1609 1610 1611

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';
}