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

import * as vscode from 'vscode';
R
rebornix 已提交
7 8
import { readonly } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
R
rebornix 已提交
9
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
10
import { ISplice } from 'vs/base/common/sequence';
11 12
import { URI, UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
R
rebornix 已提交
13
import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData } 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';
R
rebornix 已提交
16
import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType } 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
	accpetModelChanged(event: NotebookCellsChangedEvent) {
R
rebornix 已提交
250 251 252 253 254 255
		if (event.kind === NotebookCellsChangeType.ModelChange) {
			this.$spliceNotebookCells(event.changes);
		} else if (event.kind === NotebookCellsChangeType.Move) {
			this.$moveCell(event.index, event.newIdx);
		}

R
rebornix 已提交
256 257
		this._versionId = event.versionId;
	}
258

R
rebornix 已提交
259 260 261 262
	private $spliceNotebookCells(splices: NotebookCellsSplice2[]): void {
		if (!splices.length) {
			return;
		}
263

R
rebornix 已提交
264 265 266 267
		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);
268
				const documentData = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
269

270 271
				if (documentData) {
					extCell.attachTextDocument(documentData);
R
rebornix 已提交
272 273 274 275
				}

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

R
rebornix 已提交
278 279 280 281 282 283 284
				let store = this._cellDisposableMapping.get(extCell.handle)!;

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

				return extCell;
285 286
			});

R
rebornix 已提交
287 288 289 290 291
			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);

			}
292

R
rebornix 已提交
293 294
			this.cells.splice(splice[0], splice[1], ...newCells);
		});
R
rebornix 已提交
295
	}
R
rebornix 已提交
296

R
rebornix 已提交
297 298 299 300 301
	private $moveCell(index: number, newIdx: number) {
		const cells = this.cells.splice(index, 1);
		this.cells.splice(newIdx, 0, ...cells);
	}

302 303 304 305 306
	eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<vscode.CellOutput>[]) {
		let renderers = new Set<number>();
		let outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
			let outputs = diff.toInsert;

307
			let transformedOutputs = outputs.map(output => {
R
rebornix 已提交
308
				if (output.outputKind === CellOutputKind.Rich) {
R
rebornix 已提交
309
					const ret = this.transformMimeTypes(output);
310

311 312
					if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
						renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
313
					}
314 315 316
					return ret;
				} else {
					return output as IStreamOutput | IErrorOutput;
317 318 319
				}
			});

320
			return [diff.start, diff.deleteCount, transformedOutputs];
321 322 323 324 325
		});

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

R
rebornix 已提交
326
	transformMimeTypes(output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto {
327 328
		let mimeTypes = Object.keys(output.data);

R
rebornix 已提交
329
		// 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 已提交
330
		let coreDisplayOrder = this.renderingHandler.outputDisplayOrder;
R
rebornix 已提交
331
		const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], this._displayOrder, coreDisplayOrder?.defaultOrder || []);
332 333 334 335 336 337 338

		let orderMimeTypes: IOrderedMimeType[] = [];

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

			if (handlers.length) {
R
rebornix 已提交
339
				let renderedOutput = handlers[0].render(this, output, mimeType);
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371

				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 已提交
372
			outputKind: output.outputKind,
373 374 375 376 377 378
			data: output.data,
			orderedMimeTypes: orderMimeTypes,
			pickedMimeTypeIndex: 0
		};
	}

R
rebornix 已提交
379
	getCell(cellHandle: number) {
R
rebornix 已提交
380 381
		return this.cells.find(cell => cell.handle === cellHandle);
	}
R
rebornix 已提交
382

383 384
	attachCellTextDocument(textDocument: ExtHostDocumentData) {
		let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString());
R
rebornix 已提交
385 386 387 388 389
		if (cell) {
			cell.attachTextDocument(textDocument);
		}
	}

390 391
	detachCellTextDocument(textDocument: ExtHostDocumentData) {
		let cell = this.cells.find(cell => cell.uri.toString() === textDocument.document.uri.toString());
R
rebornix 已提交
392
		if (cell) {
R
rebornix 已提交
393
			cell.detachTextDocument();
R
rebornix 已提交
394 395
		}
	}
R
rebornix 已提交
396 397
}

R
rebornix 已提交
398 399 400 401
export class NotebookEditorCellEdit {
	private _finalized: boolean = false;
	private readonly _documentVersionId: number;
	private _collectedEdits: ICellEditOperation[] = [];
R
rebornix 已提交
402
	private _renderers = new Set<number>();
R
rebornix 已提交
403 404 405 406

	constructor(
		readonly editor: ExtHostNotebookEditor
	) {
R
rebornix 已提交
407
		this._documentVersionId = editor.document.versionId;
R
rebornix 已提交
408 409 410 411 412 413
	}

	finalize(): INotebookEditData {
		this._finalized = true;
		return {
			documentVersionId: this._documentVersionId,
R
rebornix 已提交
414 415
			edits: this._collectedEdits,
			renderers: Array.from(this._renderers)
R
rebornix 已提交
416 417 418 419 420 421 422 423 424
		};
	}

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

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

R
rebornix 已提交
428
		const sourceArr = Array.isArray(content) ? content : content.split(/\r|\n|\r\n/g);
R
rebornix 已提交
429
		let cell = {
R
rebornix 已提交
430
			source: sourceArr,
R
rebornix 已提交
431
			language,
R
rebornix 已提交
432
			cellKind: type,
R
rebornix 已提交
433 434
			outputs: (outputs as any[]), // TODO@rebornix
			metadata
R
rebornix 已提交
435 436 437 438
		};

		const transformedOutputs = outputs.map(output => {
			if (output.outputKind === CellOutputKind.Rich) {
R
rebornix 已提交
439
				const ret = this.editor.document.transformMimeTypes(output);
R
rebornix 已提交
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455

				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 已提交
456 457 458 459 460 461 462 463
		});
	}

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

		this._collectedEdits.push({
			editType: CellEditType.Delete,
464 465
			index,
			count: 1
R
rebornix 已提交
466 467 468 469
		});
	}
}

R
rebornix 已提交
470
export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor {
R
rebornix 已提交
471
	private _viewColumn: vscode.ViewColumn | undefined;
R
rebornix 已提交
472 473

	selection?: ExtHostCell = undefined;
474
	onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
R
rebornix 已提交
475 476

	constructor(
477
		private readonly viewType: string,
R
rebornix 已提交
478 479
		readonly id: string,
		public uri: URI,
480 481
		private _proxy: MainThreadNotebookShape,
		private _onDidReceiveMessage: Emitter<any>,
R
rebornix 已提交
482
		public document: ExtHostNotebookDocument,
R
rebornix 已提交
483
		private _documentsAndEditors: ExtHostDocumentsAndEditors
R
rebornix 已提交
484
	) {
R
rebornix 已提交
485 486
		super();
		this._register(this._documentsAndEditors.onDidAddDocuments(documents => {
487 488
			for (const documentData of documents) {
				let data = CellUri.parse(documentData.document.uri);
J
Johannes Rieken 已提交
489 490
				if (data) {
					if (this.document.uri.toString() === data.notebook.toString()) {
491
						document.attachCellTextDocument(documentData);
R
rebornix 已提交
492 493 494
					}
				}
			}
R
rebornix 已提交
495
		}));
R
rebornix 已提交
496

R
rebornix 已提交
497
		this._register(this._documentsAndEditors.onDidRemoveDocuments(documents => {
498 499
			for (const documentData of documents) {
				let data = CellUri.parse(documentData.document.uri);
J
Johannes Rieken 已提交
500 501
				if (data) {
					if (this.document.uri.toString() === data.notebook.toString()) {
502
						document.detachCellTextDocument(documentData);
R
rebornix 已提交
503 504 505
					}
				}
			}
R
rebornix 已提交
506 507 508
		}));
	}

R
rebornix 已提交
509 510 511 512 513 514 515 516 517 518 519 520 521 522
	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 已提交
523 524 525 526 527 528 529 530 531 532 533 534 535 536
		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) {
537
				if (prev.index === editData.edits[i].index) {
R
rebornix 已提交
538 539 540 541 542
					prev.cells.push(...(editData.edits[i] as ICellInsertEdit).cells);
					continue;
				}
			}

543 544 545 546 547 548 549
			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 已提交
550 551 552
			compressedEdits.push(editData.edits[i]);
			compressedEditsIndex++;
		}
R
rebornix 已提交
553

R
rebornix 已提交
554
		return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.renderers);
R
rebornix 已提交
555 556 557 558 559 560 561 562
	}

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

	set viewColumn(value) {
		throw readonly('viewColumn');
R
rebornix 已提交
563
	}
564 565 566 567 568

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

R
rebornix 已提交
569 570
}

R
rebornix 已提交
571
export class ExtHostNotebookOutputRenderer {
R
rebornix 已提交
572 573 574
	private static _handlePool: number = 0;
	readonly handle = ExtHostNotebookOutputRenderer._handlePool++;

R
rebornix 已提交
575
	constructor(
R
rebornix 已提交
576 577 578
		public type: string,
		public filter: vscode.NotebookOutputSelector,
		public renderer: vscode.NotebookOutputRenderer
R
rebornix 已提交
579 580 581 582
	) {

	}

R
rebornix 已提交
583 584 585
	matches(mimeType: string): boolean {
		if (this.filter.subTypes) {
			if (this.filter.subTypes.indexOf(mimeType) >= 0) {
R
rebornix 已提交
586 587 588 589 590 591
				return true;
			}
		}
		return false;
	}

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

595
		return html;
R
rebornix 已提交
596 597 598 599
	}
}

export interface ExtHostNotebookOutputRenderingHandler {
R
rebornix 已提交
600
	outputDisplayOrder: INotebookDisplayOrder | undefined;
R
rebornix 已提交
601
	findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[];
R
rebornix 已提交
602 603 604
}

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

R
rebornix 已提交
607
	private readonly _proxy: MainThreadNotebookShape;
608
	private readonly _notebookProviders = new Map<string, { readonly provider: vscode.NotebookProvider, readonly extension: IExtensionDescription; }>();
R
rebornix 已提交
609
	private readonly _documents = new Map<string, ExtHostNotebookDocument>();
610
	private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter<any> }>();
R
rebornix 已提交
611
	private readonly _notebookOutputRenderers = new Map<number, ExtHostNotebookOutputRenderer>();
612

R
rebornix 已提交
613 614
	private readonly _onDidChangeNotebookDocument = new Emitter<{ document: ExtHostNotebookDocument, changes: NotebookCellsChangedEvent[] }>();
	readonly onDidChangeNotebookDocument: Event<{ document: ExtHostNotebookDocument, changes: NotebookCellsChangedEvent[] }> = this._onDidChangeNotebookDocument.event;
615

R
rebornix 已提交
616
	private _outputDisplayOrder: INotebookDisplayOrder | undefined;
R
rebornix 已提交
617

R
rebornix 已提交
618
	get outputDisplayOrder(): INotebookDisplayOrder | undefined {
R
rebornix 已提交
619
		return this._outputDisplayOrder;
R
rebornix 已提交
620 621
	}

R
rebornix 已提交
622 623 624 625 626 627
	private _activeNotebookDocument: ExtHostNotebookDocument | undefined;

	get activeNotebookDocument() {
		return this._activeNotebookDocument;
	}

R
rebornix 已提交
628 629 630 631 632 633
	private _activeNotebookEditor: ExtHostNotebookEditor | undefined;

	get activeNotebookEditor() {
		return this._activeNotebookEditor;
	}

634
	constructor(mainContext: IMainContext, commands: ExtHostCommands, private _documentsAndEditors: ExtHostDocumentsAndEditors) {
R
rebornix 已提交
635
		this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook);
636 637 638 639 640 641 642 643

		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) {
644 645
						if (value[1].editor.document.handle === documentHandle) {
							const cell = value[1].editor.document.getCell(cellHandle);
646 647 648 649 650 651
							if (cell) {
								return cell;
							}
						}
					}
				}
B
Benjamin Pasero 已提交
652
				return arg;
653 654
			}
		});
R
rebornix 已提交
655 656
	}

R
rebornix 已提交
657
	registerNotebookOutputRenderer(
R
rebornix 已提交
658
		type: string,
R
rebornix 已提交
659
		extension: IExtensionDescription,
R
rebornix 已提交
660
		filter: vscode.NotebookOutputSelector,
R
rebornix 已提交
661
		renderer: vscode.NotebookOutputRenderer
R
rebornix 已提交
662
	): vscode.Disposable {
R
rebornix 已提交
663
		let extHostRenderer = new ExtHostNotebookOutputRenderer(type, filter, renderer);
R
rebornix 已提交
664
		this._notebookOutputRenderers.set(extHostRenderer.handle, extHostRenderer);
R
rebornix 已提交
665
		this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation }, type, filter, extHostRenderer.handle, renderer.preloads || []);
R
rebornix 已提交
666
		return new VSCodeDisposable(() => {
R
rebornix 已提交
667 668 669
			this._notebookOutputRenderers.delete(extHostRenderer.handle);
			this._proxy.$unregisterNotebookRenderer(extHostRenderer.handle);
		});
R
rebornix 已提交
670 671
	}

R
rebornix 已提交
672 673
	findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[] {
		let matches: ExtHostNotebookOutputRenderer[] = [];
R
rebornix 已提交
674
		for (let renderer of this._notebookOutputRenderers) {
R
rebornix 已提交
675
			if (renderer[1].matches(mimeType)) {
R
rebornix 已提交
676
				matches.push(renderer[1]);
R
rebornix 已提交
677 678 679
			}
		}

R
rebornix 已提交
680
		return matches;
R
rebornix 已提交
681 682
	}

R
rebornix 已提交
683
	registerNotebookProvider(
R
rebornix 已提交
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
		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);
		});
	}

701
	async $resolveNotebook(viewType: string, uri: UriComponents): Promise<number | undefined> {
R
rebornix 已提交
702 703 704
		let provider = this._notebookProviders.get(viewType);

		if (provider) {
R
rebornix 已提交
705
			if (!this._documents.has(URI.revive(uri).toString())) {
R
rebornix 已提交
706
				let document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, viewType, URI.revive(uri), this);
R
rebornix 已提交
707 708
				await this._proxy.$createNotebookDocument(
					document.handle,
R
rebornix 已提交
709
					viewType,
R
rebornix 已提交
710 711 712 713
					uri
				);

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

716 717
			const onDidReceiveMessage = new Emitter<any>();

R
rebornix 已提交
718 719 720
			let editor = new ExtHostNotebookEditor(
				viewType,
				`${ExtHostNotebookController._handlePool++}`,
721
				URI.revive(uri),
722 723
				this._proxy,
				onDidReceiveMessage,
R
rebornix 已提交
724 725 726
				this._documents.get(URI.revive(uri).toString())!,
				this._documentsAndEditors
			);
R
rebornix 已提交
727

728
			this._editors.set(URI.revive(uri).toString(), { editor, onDidReceiveMessage });
R
rebornix 已提交
729
			await provider.provider.resolveNotebook(editor);
730
			// await editor.document.$updateCells();
R
rebornix 已提交
731
			return editor.document.handle;
R
rebornix 已提交
732 733 734 735 736
		}

		return Promise.resolve(undefined);
	}

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

R
rebornix 已提交
740 741
		if (!provider) {
			return;
R
rebornix 已提交
742
		}
R
rebornix 已提交
743

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

R
rebornix 已提交
746 747
		if (!document) {
			return;
R
rebornix 已提交
748
		}
R
rebornix 已提交
749 750

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

754
	async $saveNotebook(viewType: string, uri: UriComponents): Promise<boolean> {
R
rebornix 已提交
755 756 757 758 759 760 761 762 763 764
		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;
	}

765
	async $updateActiveEditor(viewType: string, uri: UriComponents): Promise<void> {
R
rebornix 已提交
766
		this._activeNotebookDocument = this._documents.get(URI.revive(uri).toString());
R
rebornix 已提交
767
		this._activeNotebookEditor = this._editors.get(URI.revive(uri).toString())?.editor;
R
rebornix 已提交
768 769
	}

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

R
rebornix 已提交
773 774 775
		if (!provider) {
			return false;
		}
776

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

R
rebornix 已提交
779 780 781 782
		if (document) {
			document.dispose();
			this._documents.delete(URI.revive(uri).toString());
		}
783

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

R
rebornix 已提交
786
		if (editor) {
787 788
			editor.editor.dispose();
			editor.onDidReceiveMessage.dispose();
R
rebornix 已提交
789
			this._editors.delete(URI.revive(uri).toString());
790 791
		}

R
rebornix 已提交
792
		return true;
793 794
	}

R
rebornix 已提交
795
	$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void {
R
rebornix 已提交
796
		this._outputDisplayOrder = displayOrder;
R
rebornix 已提交
797
	}
798 799 800 801 802 803 804 805

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

		if (editor) {
			editor.onDidReceiveMessage.fire(message);
		}
	}
R
rebornix 已提交
806 807 808 809 810 811

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

		if (editor) {
			editor.editor.document.accpetModelChanged(event);
812 813
			this._onDidChangeNotebookDocument.fire({
				document: editor.editor.document,
R
rebornix 已提交
814
				changes: [event]
815
			});
R
rebornix 已提交
816 817 818
		}

	}
R
rebornix 已提交
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837

	$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void {
		let editor = this._editors.get(URI.revive(uriComponents).toString());

		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 已提交
838
}