extHostNotebook.ts 24.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 12
import { URI, UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
13
import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
14
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
15
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
16
import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
17
import { Disposable as VSCodeDisposable } from './extHostTypes';
18
import { CancellationToken } from 'vs/base/common/cancellation';
19 20
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import { NotImplementedProxy } from 'vs/base/common/types';
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

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 已提交
42

43
export class ExtHostCell extends Disposable implements vscode.NotebookCell {
R
rebornix 已提交
44

45
	// private originalSource: string[];
R
rebornix 已提交
46
	private _outputs: any[];
47 48
	private _onDidChangeOutputs = new Emitter<ISplice<vscode.CellOutput>[]>();
	onDidChangeOutputs: Event<ISplice<vscode.CellOutput>[]> = this._onDidChangeOutputs.event;
49 50
	// private _textDocument: vscode.TextDocument | undefined;
	// private _initalVersion: number = -1;
51
	private _outputMapping = new Set<vscode.CellOutput>();
52 53 54
	private _metadata: vscode.NotebookCellMetadata;

	private _metadataChangeListener: IDisposable;
R
rebornix 已提交
55

56 57 58 59 60 61
	private _documentData: ExtHostDocumentData;

	get document(): vscode.TextDocument {
		return this._documentData.document;
	}

R
rebornix 已提交
62
	get source() {
63 64
		// todo@jrieken remove this
		return this._documentData.getText();
R
rebornix 已提交
65 66
	}

R
rebornix 已提交
67
	constructor(
68 69
		private readonly viewType: string,
		private readonly documentUri: URI,
70 71
		readonly handle: number,
		readonly uri: URI,
72
		content: string,
R
rebornix 已提交
73
		public readonly cellKind: CellKind,
R
rebornix 已提交
74
		public language: string,
R
rebornix 已提交
75
		outputs: any[],
76
		_metadata: vscode.NotebookCellMetadata | undefined,
77
		private _proxy: MainThreadNotebookShape,
R
rebornix 已提交
78
	) {
79
		super();
80 81 82 83 84 85 86
		this._documentData = new ExtHostDocumentData(
			new class extends NotImplementedProxy<MainThreadDocumentsShape>('document') { },
			uri,
			content.split(/\r|\n|\r\n/g), '\n',
			language, 0, false
		);

R
rebornix 已提交
87
		this._outputs = outputs;
88

89
		const observableMetadata = getObservable(_metadata || {});
90 91 92 93
		this._metadata = observableMetadata.proxy;
		this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
			this.updateMetadata();
		}));
R
rebornix 已提交
94 95 96 97 98 99
	}

	get outputs() {
		return this._outputs;
	}

R
rebornix 已提交
100
	set outputs(newOutputs: vscode.CellOutput[]) {
101
		let diffs = diff<vscode.CellOutput>(this._outputs || [], newOutputs || [], (a) => {
102 103 104 105 106 107 108 109 110 111 112 113 114
			return this._outputMapping.has(a);
		});

		diffs.forEach(diff => {
			for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
				this._outputMapping.delete(this._outputs[i]);
			}

			diff.toInsert.forEach(output => {
				this._outputMapping.add(output);
			});
		});

R
rebornix 已提交
115
		this._outputs = newOutputs;
116
		this._onDidChangeOutputs.fire(diffs);
R
rebornix 已提交
117 118
	}

119 120 121 122
	get metadata() {
		return this._metadata;
	}

R
rebornix 已提交
123
	set metadata(newMetadata: vscode.NotebookCellMetadata) {
124
		// Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata'
125
		this._metadataChangeListener.dispose();
126
		const observableMetadata = getObservable(newMetadata);
127 128 129 130 131 132 133
		this._metadata = observableMetadata.proxy;
		this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
			this.updateMetadata();
		}));

		this.updateMetadata();
	}
134

135 136
	private updateMetadata(): Promise<void> {
		return this._proxy.$updateNotebookCellMetadata(this.viewType, this.documentUri, this.handle, this._metadata);
137 138
	}

139 140 141
	attachTextDocument(document: ExtHostDocumentData) {
		this._documentData = document;
		// this._initalVersion = this._documentData.version;
R
rebornix 已提交
142 143
	}

R
rebornix 已提交
144
	detachTextDocument() {
145
		// no-op? keep stale document until new comes along?
R
rebornix 已提交
146

147 148 149 150 151
		// if (this._textDocument && this._textDocument.version !== this._initalVersion) {
		// 	this.originalSource = this._textDocument.getText().split(/\r|\n|\r\n/g);
		// }
		// this._textDocument = undefined;
		// this._initalVersion = -1;
R
rebornix 已提交
152 153 154
	}
}

R
rebornix 已提交
155
export class ExtHostNotebookDocument extends Disposable implements vscode.NotebookDocument {
R
rebornix 已提交
156
	private static _handlePool: number = 0;
R
rebornix 已提交
157 158 159 160
	readonly handle = ExtHostNotebookDocument._handlePool++;

	private _cells: ExtHostCell[] = [];

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

R
rebornix 已提交
163 164 165 166
	get cells() {
		return this._cells;
	}

R
rebornix 已提交
167 168 169 170 171
	private _languages: string[] = [];

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

R
rebornix 已提交
173 174 175
	set languages(newLanguages: string[]) {
		this._languages = newLanguages;
		this._proxy.$updateNotebookLanguages(this.viewType, this.uri, this._languages);
R
rebornix 已提交
176
	}
R
rebornix 已提交
177

178 179
	private _metadata: Required<vscode.NotebookDocumentMetadata> = notebookDocumentMetadataDefaults;
	private _metadataChangeListener: IDisposable;
R
rebornix 已提交
180 181 182 183 184

	get metadata() {
		return this._metadata;
	}

185 186 187 188 189 190
	set metadata(newMetadata: Required<vscode.NotebookDocumentMetadata>) {
		this._metadataChangeListener.dispose();
		newMetadata = {
			...notebookDocumentMetadataDefaults,
			...newMetadata
		};
R
rebornix 已提交
191 192 193 194
		if (this._metadataChangeListener) {
			this._metadataChangeListener.dispose();
		}

195 196 197 198 199
		const observableMetadata = getObservable(newMetadata);
		this._metadata = observableMetadata.proxy;
		this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
			this.updateMetadata();
		}));
R
rebornix 已提交
200 201

		this.updateMetadata();
R
rebornix 已提交
202 203
	}

R
rebornix 已提交
204
	private _displayOrder: string[] = [];
R
rebornix 已提交
205 206 207 208 209

	get displayOrder() {
		return this._displayOrder;
	}

R
rebornix 已提交
210
	set displayOrder(newOrder: string[]) {
R
rebornix 已提交
211 212 213
		this._displayOrder = newOrder;
	}

R
rebornix 已提交
214 215 216 217 218 219
	private _versionId = 0;

	get versionId() {
		return this._versionId;
	}

R
rebornix 已提交
220
	constructor(
R
rebornix 已提交
221
		private readonly _proxy: MainThreadNotebookShape,
R
rebornix 已提交
222
		private _documentsAndEditors: ExtHostDocumentsAndEditors,
R
rebornix 已提交
223
		public viewType: string,
R
rebornix 已提交
224 225
		public uri: URI,
		public renderingHandler: ExtHostNotebookOutputRenderingHandler
R
rebornix 已提交
226
	) {
R
rebornix 已提交
227
		super();
228 229 230 231 232 233 234 235 236 237

		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 已提交
238
	}
R
rebornix 已提交
239

R
rebornix 已提交
240
	dispose() {
R
rebornix 已提交
241
		super.dispose();
R
rebornix 已提交
242 243
		this._cellDisposableMapping.forEach(cell => cell.dispose());
	}
R
rebornix 已提交
244

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

R
rebornix 已提交
247 248
	get isDirty() { return false; }

R
rebornix 已提交
249 250 251 252
	accpetModelChanged(event: NotebookCellsChangedEvent) {
		this.$spliceNotebookCells(event.changes);
		this._versionId = event.versionId;
	}
253

R
rebornix 已提交
254 255 256 257
	private $spliceNotebookCells(splices: NotebookCellsSplice2[]): void {
		if (!splices.length) {
			return;
		}
258

R
rebornix 已提交
259 260 261 262
		splices.reverse().forEach(splice => {
			let cellDtos = splice[2];
			let newCells = cellDtos.map(cell => {
				const extCell = new ExtHostCell(this.viewType, this.uri, cell.handle, URI.revive(cell.uri), cell.source.join('\n'), cell.cellKind, cell.language, cell.outputs, cell.metadata, this._proxy);
263
				const documentData = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
264

265 266
				if (documentData) {
					extCell.attachTextDocument(documentData);
R
rebornix 已提交
267 268 269 270
				}

				if (!this._cellDisposableMapping.has(extCell.handle)) {
					this._cellDisposableMapping.set(extCell.handle, new DisposableStore());
271 272
				}

R
rebornix 已提交
273 274 275 276 277 278 279
				let store = this._cellDisposableMapping.get(extCell.handle)!;

				store.add(extCell.onDidChangeOutputs((diffs) => {
					this.eventuallyUpdateCellOutputs(extCell, diffs);
				}));

				return extCell;
280 281
			});

R
rebornix 已提交
282 283 284 285 286
			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);

			}
287

R
rebornix 已提交
288 289
			this.cells.splice(splice[0], splice[1], ...newCells);
		});
R
rebornix 已提交
290
	}
R
rebornix 已提交
291

292 293 294 295 296
	eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<vscode.CellOutput>[]) {
		let renderers = new Set<number>();
		let outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
			let outputs = diff.toInsert;

297
			let transformedOutputs = outputs.map(output => {
R
rebornix 已提交
298
				if (output.outputKind === CellOutputKind.Rich) {
R
rebornix 已提交
299
					const ret = this.transformMimeTypes(output);
300

301 302
					if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
						renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
303
					}
304 305 306
					return ret;
				} else {
					return output as IStreamOutput | IErrorOutput;
307 308 309
				}
			});

310
			return [diff.start, diff.deleteCount, transformedOutputs];
311 312 313 314 315
		});

		this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos, Array.from(renderers));
	}

R
rebornix 已提交
316
	transformMimeTypes(output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto {
317 318
		let mimeTypes = Object.keys(output.data);

R
rebornix 已提交
319
		// TODO@rebornix, the document display order might be assigned a bit later. We need to postpone sending the outputs to the core side.
R
rebornix 已提交
320
		let coreDisplayOrder = this.renderingHandler.outputDisplayOrder;
R
rebornix 已提交
321
		const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], this._displayOrder, coreDisplayOrder?.defaultOrder || []);
322 323 324 325 326 327 328

		let orderMimeTypes: IOrderedMimeType[] = [];

		sorted.forEach(mimeType => {
			let handlers = this.renderingHandler.findBestMatchedRenderer(mimeType);

			if (handlers.length) {
R
rebornix 已提交
329
				let renderedOutput = handlers[0].render(this, output, mimeType);
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

				orderMimeTypes.push({
					mimeType: mimeType,
					isResolved: true,
					rendererId: handlers[0].handle,
					output: renderedOutput
				});

				for (let i = 1; i < handlers.length; i++) {
					orderMimeTypes.push({
						mimeType: mimeType,
						isResolved: false,
						rendererId: handlers[i].handle
					});
				}

				if (mimeTypeSupportedByCore(mimeType)) {
					orderMimeTypes.push({
						mimeType: mimeType,
						isResolved: false,
						rendererId: -1
					});
				}
			} else {
				orderMimeTypes.push({
					mimeType: mimeType,
					isResolved: false
				});
			}
		});

		return {
R
rebornix 已提交
362
			outputKind: output.outputKind,
363 364 365 366 367 368
			data: output.data,
			orderedMimeTypes: orderMimeTypes,
			pickedMimeTypeIndex: 0
		};
	}

R
rebornix 已提交
369
	getCell(cellHandle: number) {
R
rebornix 已提交
370 371
		return this.cells.find(cell => cell.handle === cellHandle);
	}
R
rebornix 已提交
372

373 374
	attachCellTextDocument(textDocument: ExtHostDocumentData) {
		let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString());
R
rebornix 已提交
375 376 377 378 379
		if (cell) {
			cell.attachTextDocument(textDocument);
		}
	}

380 381
	detachCellTextDocument(textDocument: ExtHostDocumentData) {
		let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString());
R
rebornix 已提交
382
		if (cell) {
R
rebornix 已提交
383
			cell.detachTextDocument();
R
rebornix 已提交
384 385
		}
	}
R
rebornix 已提交
386 387
}

R
rebornix 已提交
388 389 390 391
export class NotebookEditorCellEdit {
	private _finalized: boolean = false;
	private readonly _documentVersionId: number;
	private _collectedEdits: ICellEditOperation[] = [];
R
rebornix 已提交
392
	private _renderers = new Set<number>();
R
rebornix 已提交
393 394 395 396

	constructor(
		readonly editor: ExtHostNotebookEditor
	) {
R
rebornix 已提交
397
		this._documentVersionId = editor.document.versionId;
R
rebornix 已提交
398 399 400 401 402 403
	}

	finalize(): INotebookEditData {
		this._finalized = true;
		return {
			documentVersionId: this._documentVersionId,
R
rebornix 已提交
404 405
			edits: this._collectedEdits,
			renderers: Array.from(this._renderers)
R
rebornix 已提交
406 407 408 409 410 411 412 413 414
		};
	}

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

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

R
rebornix 已提交
418
		const sourceArr = Array.isArray(content) ? content : content.split(/\r|\n|\r\n/g);
R
rebornix 已提交
419
		let cell = {
R
rebornix 已提交
420
			source: sourceArr,
R
rebornix 已提交
421
			language,
R
rebornix 已提交
422
			cellKind: type,
R
rebornix 已提交
423 424
			outputs: (outputs as any[]), // TODO@rebornix
			metadata
R
rebornix 已提交
425 426 427 428
		};

		const transformedOutputs = outputs.map(output => {
			if (output.outputKind === CellOutputKind.Rich) {
R
rebornix 已提交
429
				const ret = this.editor.document.transformMimeTypes(output);
R
rebornix 已提交
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445

				if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
					this._renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
				}
				return ret;
			} else {
				return output as IStreamOutput | IErrorOutput;
			}
		});

		cell.outputs = transformedOutputs;

		this._collectedEdits.push({
			editType: CellEditType.Insert,
			index,
			cells: [cell]
R
rebornix 已提交
446 447 448 449 450 451 452 453
		});
	}

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

		this._collectedEdits.push({
			editType: CellEditType.Delete,
454 455
			index,
			count: 1
R
rebornix 已提交
456 457 458 459
		});
	}
}

R
rebornix 已提交
460
export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor {
R
rebornix 已提交
461
	private _viewColumn: vscode.ViewColumn | undefined;
462
	onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
R
rebornix 已提交
463 464

	constructor(
465
		private readonly viewType: string,
R
rebornix 已提交
466 467
		readonly id: string,
		public uri: URI,
468 469
		private _proxy: MainThreadNotebookShape,
		private _onDidReceiveMessage: Emitter<any>,
R
rebornix 已提交
470
		public document: ExtHostNotebookDocument,
R
rebornix 已提交
471
		private _documentsAndEditors: ExtHostDocumentsAndEditors
R
rebornix 已提交
472
	) {
R
rebornix 已提交
473 474
		super();
		this._register(this._documentsAndEditors.onDidAddDocuments(documents => {
475 476
			for (const documentData of documents) {
				let data = CellUri.parse(documentData.document.uri);
J
Johannes Rieken 已提交
477 478
				if (data) {
					if (this.document.uri.toString() === data.notebook.toString()) {
479
						document.attachCellTextDocument(documentData);
R
rebornix 已提交
480 481 482
					}
				}
			}
R
rebornix 已提交
483
		}));
R
rebornix 已提交
484

R
rebornix 已提交
485
		this._register(this._documentsAndEditors.onDidRemoveDocuments(documents => {
486 487
			for (const documentData of documents) {
				let data = CellUri.parse(documentData.document.uri);
J
Johannes Rieken 已提交
488 489
				if (data) {
					if (this.document.uri.toString() === data.notebook.toString()) {
490
						document.detachCellTextDocument(documentData);
R
rebornix 已提交
491 492 493
					}
				}
			}
R
rebornix 已提交
494 495 496
		}));
	}

R
rebornix 已提交
497 498 499 500 501 502 503 504 505 506 507 508 509 510
	edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable<boolean> {
		const edit = new NotebookEditorCellEdit(this);
		callback(edit);
		return this._applyEdit(edit);
	}

	private _applyEdit(editBuilder: NotebookEditorCellEdit): Promise<boolean> {
		const editData = editBuilder.finalize();

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

R
rebornix 已提交
511 512 513 514 515 516 517 518 519 520 521 522 523 524
		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) {
525
				if (prev.index === editData.edits[i].index) {
R
rebornix 已提交
526 527 528 529 530
					prev.cells.push(...(editData.edits[i] as ICellInsertEdit).cells);
					continue;
				}
			}

531 532 533 534 535 536 537
			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 已提交
538 539 540
			compressedEdits.push(editData.edits[i]);
			compressedEditsIndex++;
		}
R
rebornix 已提交
541

R
rebornix 已提交
542
		return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.renderers);
R
rebornix 已提交
543 544 545 546 547 548 549 550
	}

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

	set viewColumn(value) {
		throw readonly('viewColumn');
R
rebornix 已提交
551
	}
552 553 554 555 556

	async postMessage(message: any): Promise<boolean> {
		return this._proxy.$postMessage(this.document.handle, message);
	}

R
rebornix 已提交
557 558
}

R
rebornix 已提交
559
export class ExtHostNotebookOutputRenderer {
R
rebornix 已提交
560 561 562
	private static _handlePool: number = 0;
	readonly handle = ExtHostNotebookOutputRenderer._handlePool++;

R
rebornix 已提交
563
	constructor(
R
rebornix 已提交
564 565 566
		public type: string,
		public filter: vscode.NotebookOutputSelector,
		public renderer: vscode.NotebookOutputRenderer
R
rebornix 已提交
567 568 569 570
	) {

	}

R
rebornix 已提交
571 572 573
	matches(mimeType: string): boolean {
		if (this.filter.subTypes) {
			if (this.filter.subTypes.indexOf(mimeType) >= 0) {
R
rebornix 已提交
574 575 576 577 578 579
				return true;
			}
		}
		return false;
	}

R
rebornix 已提交
580
	render(document: ExtHostNotebookDocument, output: vscode.CellDisplayOutput, mimeType: string): string {
R
rebornix 已提交
581
		let html = this.renderer.render(document, output, mimeType);
R
rebornix 已提交
582

583
		return html;
R
rebornix 已提交
584 585 586 587
	}
}

export interface ExtHostNotebookOutputRenderingHandler {
R
rebornix 已提交
588
	outputDisplayOrder: INotebookDisplayOrder | undefined;
R
rebornix 已提交
589
	findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[];
R
rebornix 已提交
590 591 592
}

export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostNotebookOutputRenderingHandler {
R
rebornix 已提交
593 594
	private static _handlePool: number = 0;

R
rebornix 已提交
595
	private readonly _proxy: MainThreadNotebookShape;
596
	private readonly _notebookProviders = new Map<string, { readonly provider: vscode.NotebookProvider, readonly extension: IExtensionDescription; }>();
R
rebornix 已提交
597
	private readonly _documents = new Map<string, ExtHostNotebookDocument>();
598
	private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter<any> }>();
R
rebornix 已提交
599
	private readonly _notebookOutputRenderers = new Map<number, ExtHostNotebookOutputRenderer>();
600 601 602 603

	private readonly _onDidChangeNotebookDocument = new Emitter<{ document: ExtHostNotebookDocument, changes: NotebookCellsSplice2[] }>();
	readonly onDidChangeNotebookDocument: Event<{ document: ExtHostNotebookDocument, changes: NotebookCellsSplice2[] }> = this._onDidChangeNotebookDocument.event;

R
rebornix 已提交
604
	private _outputDisplayOrder: INotebookDisplayOrder | undefined;
R
rebornix 已提交
605

R
rebornix 已提交
606
	get outputDisplayOrder(): INotebookDisplayOrder | undefined {
R
rebornix 已提交
607
		return this._outputDisplayOrder;
R
rebornix 已提交
608 609
	}

R
rebornix 已提交
610 611 612 613 614 615
	private _activeNotebookDocument: ExtHostNotebookDocument | undefined;

	get activeNotebookDocument() {
		return this._activeNotebookDocument;
	}

R
rebornix 已提交
616 617 618 619 620 621
	private _activeNotebookEditor: ExtHostNotebookEditor | undefined;

	get activeNotebookEditor() {
		return this._activeNotebookEditor;
	}

622
	constructor(mainContext: IMainContext, commands: ExtHostCommands, private _documentsAndEditors: ExtHostDocumentsAndEditors) {
R
rebornix 已提交
623
		this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook);
624 625 626 627 628 629 630 631

		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) {
632 633
						if (value[1].editor.document.handle === documentHandle) {
							const cell = value[1].editor.document.getCell(cellHandle);
634 635 636 637 638 639
							if (cell) {
								return cell;
							}
						}
					}
				}
B
Benjamin Pasero 已提交
640
				return arg;
641 642
			}
		});
R
rebornix 已提交
643 644
	}

R
rebornix 已提交
645
	registerNotebookOutputRenderer(
R
rebornix 已提交
646
		type: string,
R
rebornix 已提交
647
		extension: IExtensionDescription,
R
rebornix 已提交
648
		filter: vscode.NotebookOutputSelector,
R
rebornix 已提交
649
		renderer: vscode.NotebookOutputRenderer
R
rebornix 已提交
650
	): vscode.Disposable {
R
rebornix 已提交
651
		let extHostRenderer = new ExtHostNotebookOutputRenderer(type, filter, renderer);
R
rebornix 已提交
652
		this._notebookOutputRenderers.set(extHostRenderer.handle, extHostRenderer);
R
rebornix 已提交
653
		this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation }, type, filter, extHostRenderer.handle, renderer.preloads || []);
R
rebornix 已提交
654
		return new VSCodeDisposable(() => {
R
rebornix 已提交
655 656 657
			this._notebookOutputRenderers.delete(extHostRenderer.handle);
			this._proxy.$unregisterNotebookRenderer(extHostRenderer.handle);
		});
R
rebornix 已提交
658 659
	}

R
rebornix 已提交
660 661
	findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[] {
		let matches: ExtHostNotebookOutputRenderer[] = [];
R
rebornix 已提交
662
		for (let renderer of this._notebookOutputRenderers) {
R
rebornix 已提交
663
			if (renderer[1].matches(mimeType)) {
R
rebornix 已提交
664
				matches.push(renderer[1]);
R
rebornix 已提交
665 666 667
			}
		}

R
rebornix 已提交
668
		return matches;
R
rebornix 已提交
669 670
	}

R
rebornix 已提交
671
	registerNotebookProvider(
R
rebornix 已提交
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
		extension: IExtensionDescription,
		viewType: string,
		provider: vscode.NotebookProvider,
	): vscode.Disposable {

		if (this._notebookProviders.has(viewType)) {
			throw new Error(`Notebook provider for '${viewType}' already registered`);
		}

		this._notebookProviders.set(viewType, { extension, provider });
		this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation }, viewType);
		return new VSCodeDisposable(() => {
			this._notebookProviders.delete(viewType);
			this._proxy.$unregisterNotebookProvider(viewType);
		});
	}

689
	async $resolveNotebook(viewType: string, uri: UriComponents): Promise<number | undefined> {
R
rebornix 已提交
690 691 692
		let provider = this._notebookProviders.get(viewType);

		if (provider) {
R
rebornix 已提交
693
			if (!this._documents.has(URI.revive(uri).toString())) {
R
rebornix 已提交
694
				let document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, viewType, URI.revive(uri), this);
R
rebornix 已提交
695 696
				await this._proxy.$createNotebookDocument(
					document.handle,
R
rebornix 已提交
697
					viewType,
R
rebornix 已提交
698 699 700 701
					uri
				);

				this._documents.set(URI.revive(uri).toString(), document);
R
rebornix 已提交
702
			}
R
rebornix 已提交
703

704 705
			const onDidReceiveMessage = new Emitter<any>();

R
rebornix 已提交
706 707 708
			let editor = new ExtHostNotebookEditor(
				viewType,
				`${ExtHostNotebookController._handlePool++}`,
709
				URI.revive(uri),
710 711
				this._proxy,
				onDidReceiveMessage,
R
rebornix 已提交
712 713 714
				this._documents.get(URI.revive(uri).toString())!,
				this._documentsAndEditors
			);
R
rebornix 已提交
715

716
			this._editors.set(URI.revive(uri).toString(), { editor, onDidReceiveMessage });
R
rebornix 已提交
717
			await provider.provider.resolveNotebook(editor);
718
			// await editor.document.$updateCells();
R
rebornix 已提交
719
			return editor.document.handle;
R
rebornix 已提交
720 721 722 723 724
		}

		return Promise.resolve(undefined);
	}

725
	async $executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise<void> {
R
rebornix 已提交
726 727
		let provider = this._notebookProviders.get(viewType);

R
rebornix 已提交
728 729
		if (!provider) {
			return;
R
rebornix 已提交
730
		}
R
rebornix 已提交
731

R
rebornix 已提交
732
		let document = this._documents.get(URI.revive(uri).toString());
R
rebornix 已提交
733

R
rebornix 已提交
734 735
		if (!document) {
			return;
R
rebornix 已提交
736
		}
R
rebornix 已提交
737 738

		let cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
739
		return provider.provider.executeCell(document!, cell, token);
R
rebornix 已提交
740 741
	}

742
	async $saveNotebook(viewType: string, uri: UriComponents): Promise<boolean> {
R
rebornix 已提交
743 744 745 746 747 748 749 750 751 752
		let provider = this._notebookProviders.get(viewType);
		let document = this._documents.get(URI.revive(uri).toString());

		if (provider && document) {
			return await provider.provider.save(document);
		}

		return false;
	}

753
	async $updateActiveEditor(viewType: string, uri: UriComponents): Promise<void> {
R
rebornix 已提交
754
		this._activeNotebookDocument = this._documents.get(URI.revive(uri).toString());
R
rebornix 已提交
755
		this._activeNotebookEditor = this._editors.get(URI.revive(uri).toString())?.editor;
R
rebornix 已提交
756 757
	}

758
	async $destoryNotebookDocument(viewType: string, uri: UriComponents): Promise<boolean> {
759 760
		let provider = this._notebookProviders.get(viewType);

R
rebornix 已提交
761 762 763
		if (!provider) {
			return false;
		}
764

R
rebornix 已提交
765
		let document = this._documents.get(URI.revive(uri).toString());
766

R
rebornix 已提交
767 768 769 770
		if (document) {
			document.dispose();
			this._documents.delete(URI.revive(uri).toString());
		}
771

R
rebornix 已提交
772
		let editor = this._editors.get(URI.revive(uri).toString());
773

R
rebornix 已提交
774
		if (editor) {
775 776
			editor.editor.dispose();
			editor.onDidReceiveMessage.dispose();
R
rebornix 已提交
777
			this._editors.delete(URI.revive(uri).toString());
778 779
		}

R
rebornix 已提交
780
		return true;
781 782
	}

R
rebornix 已提交
783
	$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void {
R
rebornix 已提交
784
		this._outputDisplayOrder = displayOrder;
R
rebornix 已提交
785
	}
786 787 788 789 790 791 792 793

	$onDidReceiveMessage(uri: UriComponents, message: any): void {
		let editor = this._editors.get(URI.revive(uri).toString());

		if (editor) {
			editor.onDidReceiveMessage.fire(message);
		}
	}
R
rebornix 已提交
794 795 796 797 798 799

	$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void {
		let editor = this._editors.get(URI.revive(uriComponents).toString());

		if (editor) {
			editor.editor.document.accpetModelChanged(event);
800 801 802 803
			this._onDidChangeNotebookDocument.fire({
				document: editor.editor.document,
				changes: event.changes
			});
R
rebornix 已提交
804 805 806
		}

	}
R
rebornix 已提交
807
}