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

6
import * as nls from 'vs/nls';
R
rebornix 已提交
7
import * as DOM from 'vs/base/browser/dom';
R
rebornix 已提交
8
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
9
import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext, INotebookDocumentsAndEditorsDelta, INotebookModelAddedData } from '../common/extHost.protocol';
R
rebornix 已提交
10
import { Disposable, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
R
rebornix 已提交
11
import { URI, UriComponents } from 'vs/base/common/uri';
12
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService';
R
rebornix 已提交
13
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, INotebookTextModelBackup, IEditor, INotebookRendererInfo, IOutputRenderRequest, IOutputRenderResponse, INotebookDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon';
R
rebornix 已提交
14
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
15
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
16 17
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
18
import { CancellationToken } from 'vs/base/common/cancellation';
R
rebornix 已提交
19
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
R
rebornix 已提交
20 21
import { IRelativePattern } from 'vs/base/common/glob';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
22 23
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
24
import { ITextModelService } from 'vs/editor/common/services/resolverService';
R
rebornix 已提交
25
import { Emitter } from 'vs/base/common/event';
R
rebornix 已提交
26

27 28
export class MainThreadNotebookDocument extends Disposable {
	private _textModel: NotebookTextModel;
R
rebornix 已提交
29

30 31
	get textModel() {
		return this._textModel;
R
rebornix 已提交
32
	}
R
rebornix 已提交
33 34

	constructor(
R
rebornix 已提交
35
		private readonly _proxy: ExtHostNotebookShape,
R
rebornix 已提交
36
		public handle: number,
R
rebornix 已提交
37
		public viewType: string,
38
		public supportBackup: boolean,
39
		public uri: URI,
40
		@INotebookService readonly notebookService: INotebookService,
41 42
		@IUndoRedoService readonly undoRedoService: IUndoRedoService,
		@ITextModelService modelService: ITextModelService
43

R
rebornix 已提交
44
	) {
R
rebornix 已提交
45
		super();
46

47
		this._textModel = new NotebookTextModel(handle, viewType, supportBackup, uri, undoRedoService, modelService);
48
		this._register(this._textModel.onDidModelChangeProxy(e => {
R
rebornix 已提交
49
			this._proxy.$acceptModelChanged(this.uri, e);
50
			this._proxy.$acceptEditorPropertiesChanged(uri, { selections: { selections: this._textModel.selections }, metadata: null });
R
rebornix 已提交
51
		}));
R
rebornix 已提交
52 53
		this._register(this._textModel.onDidSelectionChange(e => {
			const selectionsChange = e ? { selections: e } : null;
R
rebornix 已提交
54
			this._proxy.$acceptEditorPropertiesChanged(uri, { selections: selectionsChange, metadata: null });
R
rebornix 已提交
55
		}));
R
rebornix 已提交
56
	}
R
rebornix 已提交
57

R
rebornix 已提交
58
	async applyEdit(modelVersionId: number, edits: ICellEditOperation[], emitToExtHost: boolean, synchronous: boolean): Promise<boolean> {
59
		await this.notebookService.transformEditsOutputs(this.textModel, edits);
R
rebornix 已提交
60 61 62 63 64 65 66 67 68 69
		if (synchronous) {
			return this._textModel.$applyEdit(modelVersionId, edits, emitToExtHost, synchronous);
		} else {
			return new Promise(resolve => {
				this._register(DOM.scheduleAtNextAnimationFrame(() => {
					const ret = this._textModel.$applyEdit(modelVersionId, edits, emitToExtHost, true);
					resolve(ret);
				}));
			});
		}
R
rebornix 已提交
70
	}
R
rebornix 已提交
71

72 73 74 75
	async spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]) {
		await this.notebookService.transformSpliceOutputs(this.textModel, splices);
		this._textModel.$spliceNotebookCellOutputs(cellHandle, splices);
	}
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

	handleEdit(editId: number, label: string | undefined): void {
		this.undoRedoService.pushElement({
			type: UndoRedoElementType.Resource,
			resource: this._textModel.uri,
			label: label ?? nls.localize('defaultEditLabel', "Edit"),
			undo: async () => {
				await this._proxy.$undoNotebook(this._textModel.viewType, this._textModel.uri, editId, this._textModel.isDirty);
			},
			redo: async () => {
				await this._proxy.$redoNotebook(this._textModel.viewType, this._textModel.uri, editId, this._textModel.isDirty);
			},
		});
		this._textModel.setDirty(true);
	}

R
rebornix 已提交
92
	dispose() {
93
		this._textModel.dispose();
R
rebornix 已提交
94 95
		super.dispose();
	}
R
rebornix 已提交
96 97
}

R
rebornix 已提交
98
class DocumentAndEditorState {
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
	static ofSets<T>(before: Set<T>, after: Set<T>): { removed: T[], added: T[] } {
		const removed: T[] = [];
		const added: T[] = [];
		before.forEach(element => {
			if (!after.has(element)) {
				removed.push(element);
			}
		});
		after.forEach(element => {
			if (!before.has(element)) {
				added.push(element);
			}
		});
		return { removed, added };
	}

R
rebornix 已提交
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
	static ofMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[], added: V[] } {
		const removed: V[] = [];
		const added: V[] = [];
		before.forEach((value, index) => {
			if (!after.has(index)) {
				removed.push(value);
			}
		});
		after.forEach((value, index) => {
			if (!before.has(index)) {
				added.push(value);
			}
		});
		return { removed, added };
	}

	static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): INotebookDocumentsAndEditorsDelta {
		if (!before) {
			const apiEditors = [];
			for (let id in after.textEditors) {
				const editor = after.textEditors.get(id)!;
				apiEditors.push({ id, documentUri: editor.uri!, selections: editor!.textModel!.selections });
			}

			return {
				addedDocuments: [],
141 142
				addedEditors: apiEditors,
				visibleEditors: [...after.visibleEditors].map(editor => editor[0])
R
rebornix 已提交
143 144
			};
		}
145
		const documentDelta = DocumentAndEditorState.ofSets(before.documents, after.documents);
R
rebornix 已提交
146 147 148 149
		const editorDelta = DocumentAndEditorState.ofMaps(before.textEditors, after.textEditors);
		const addedAPIEditors = editorDelta.added.map(add => ({
			id: add.getId(),
			documentUri: add.uri!,
150
			selections: add.textModel!.selections || []
R
rebornix 已提交
151 152 153 154 155 156 157
		}));

		const removedAPIEditors = editorDelta.removed.map(removed => removed.getId());

		// const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined;
		const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;

158 159
		const visibleEditorDelta = DocumentAndEditorState.ofMaps(before.visibleEditors, after.visibleEditors);

R
rebornix 已提交
160
		return {
161 162 163 164 165 166 167 168 169 170 171
			addedDocuments: documentDelta.added.map(e => {
				return {
					viewType: e.viewType,
					handle: e.handle,
					uri: e.uri,
					metadata: e.metadata,
					versionId: e.versionId,
					cells: e.cells.map(cell => ({
						handle: cell.handle,
						uri: cell.uri,
						source: cell.textBuffer.getLinesContent(),
172
						eol: cell.textBuffer.getEOL(),
173 174 175 176 177 178 179 180 181 182 183 184
						language: cell.language,
						cellKind: cell.cellKind,
						outputs: cell.outputs,
						metadata: cell.metadata
					})),
					// attachedEditor: editorId ? {
					// 	id: editorId,
					// 	selections: document.textModel.selections
					// } : undefined
				};
			}),
			removedDocuments: documentDelta.removed.map(e => e.uri),
R
rebornix 已提交
185 186
			addedEditors: addedAPIEditors,
			removedEditors: removedAPIEditors,
187 188 189 190
			newActiveEditor: newActiveEditor,
			visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0
				? undefined
				: [...after.visibleEditors].map(editor => editor[0])
R
rebornix 已提交
191 192 193 194
		};
	}

	constructor(
195
		readonly documents: Set<NotebookTextModel>,
R
rebornix 已提交
196 197
		readonly textEditors: Map<string, IEditor>,
		readonly activeEditor: string | null | undefined,
198
		readonly visibleEditors: Map<string, IEditor>
R
rebornix 已提交
199 200 201 202 203
	) {
		//
	}
}

R
rebornix 已提交
204 205 206
@extHostNamedCustomer(MainContext.MainThreadNotebook)
export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape {
	private readonly _notebookProviders = new Map<string, MainThreadNotebookController>();
R
rebornix 已提交
207
	private readonly _notebookKernels = new Map<string, MainThreadNotebookKernel>();
R
rebornix 已提交
208
	private readonly _notebookKernelProviders = new Map<number, { extension: NotebookExtensionDescription, emitter: Emitter<void>, provider: IDisposable }>();
209
	private readonly _notebookRenderers = new Map<string, MainThreadNotebookRenderer>();
R
rebornix 已提交
210
	private readonly _proxy: ExtHostNotebookShape;
R
rebornix 已提交
211 212
	private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
	private _currentState?: DocumentAndEditorState;
R
rebornix 已提交
213 214 215

	constructor(
		extHostContext: IExtHostContext,
R
rebornix 已提交
216
		@INotebookService private _notebookService: INotebookService,
217 218
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IEditorService private readonly editorService: IEditorService,
219 220
		@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
		@IInstantiationService private readonly _instantiationService: IInstantiationService
221

R
rebornix 已提交
222 223 224
	) {
		super();
		this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
R
rebornix 已提交
225 226 227
		this.registerListeners();
	}

R
rebornix 已提交
228 229 230 231 232 233 234 235
	async $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean> {
		let controller = this._notebookProviders.get(viewType);

		if (controller) {
			return controller.tryApplyEdits(resource, modelVersionId, edits, renderers);
		}

		return false;
R
rebornix 已提交
236 237
	}

R
rebornix 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
	private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta) {
		if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) {
			return false;
		}

		if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) {
			return false;
		}

		if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) {
			return false;
		}

		if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) {
			return false;
		}

		if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) {
			return false;
		}

		if (delta.newActiveEditor !== undefined) {
			return false;
		}

		return true;
	}

266
	private _emitDelta(delta: INotebookDocumentsAndEditorsDelta) {
R
rebornix 已提交
267 268 269 270
		if (this._isDeltaEmpty(delta)) {
			return;
		}

271
		return this._proxy.$acceptDocumentAndEditorsDelta(delta);
272 273
	}

R
rebornix 已提交
274
	registerListeners() {
R
rebornix 已提交
275 276 277 278
		this._notebookService.listNotebookEditors().forEach((e) => {
			this._addNotebookEditor(e);
		});

R
rebornix 已提交
279
		this._register(this._notebookService.onDidChangeActiveEditor(e => {
280
			this._updateState();
R
rebornix 已提交
281
		}));
R
rebornix 已提交
282

R
rebornix 已提交
283
		this._register(this._notebookService.onDidChangeVisibleEditors(e => {
284 285 286 287 288 289 290 291 292
			if (this._notebookProviders.size > 0) {
				if (!this._currentState) {
					// no current state means we didn't even create editors in ext host yet.
					return;
				}

				// we can't simply update visibleEditors as we need to check if we should create editors first.
				this._updateState();
			}
R
rebornix 已提交
293 294
		}));

R
rebornix 已提交
295 296 297 298
		this._register(this._notebookService.onNotebookEditorAdd(editor => {
			this._addNotebookEditor(editor);
		}));

299 300 301
		this._register(this._notebookService.onNotebookEditorsRemove(editors => {
			this._removeNotebookEditor(editors);
		}));
302

R
rebornix 已提交
303 304 305 306
		this._register(this._notebookService.onNotebookDocumentAdd(() => {
			this._updateState();
		}));

307 308
		this._register(this._notebookService.onNotebookDocumentRemove(() => {
			this._updateState();
R
rebornix 已提交
309 310
		}));

R
rebornix 已提交
311 312 313 314 315 316 317
		const updateOrder = () => {
			let userOrder = this.configurationService.getValue<string[]>('notebook.displayOrder');
			this._proxy.$acceptDisplayOrder({
				defaultOrder: this.accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER,
				userOrder: userOrder
			});
		};
R
rebornix 已提交
318

R
rebornix 已提交
319
		updateOrder();
R
rebornix 已提交
320

R
rebornix 已提交
321 322 323
		this._register(this.configurationService.onDidChangeConfiguration(e => {
			if (e.affectedKeys.indexOf('notebook.displayOrder') >= 0) {
				updateOrder();
R
rebornix 已提交
324
			}
R
rebornix 已提交
325 326 327 328 329
		}));

		this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => {
			updateOrder();
		}));
R
rebornix 已提交
330 331 332 333

		const activeEditorPane = this.editorService.activeEditorPane as any | undefined;
		const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined;
		this._updateState(notebookEditor);
R
rebornix 已提交
334 335
	}

336
	async addNotebookDocument(data: INotebookModelAddedData) {
337
		this._updateState();
338 339
	}

R
rebornix 已提交
340 341 342
	private _addNotebookEditor(e: IEditor) {
		this._toDisposeOnEditorRemove.set(e.getId(), combinedDisposable(
			e.onDidChangeModel(() => this._updateState()),
R
rebornix 已提交
343 344 345
			e.onDidFocusEditorWidget(() => {
				this._updateState(e);
			}),
R
rebornix 已提交
346 347
		));

R
rebornix 已提交
348 349 350
		const activeEditorPane = this.editorService.activeEditorPane as any | undefined;
		const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined;
		this._updateState(notebookEditor);
R
rebornix 已提交
351 352
	}

353 354 355 356 357 358 359 360 361 362
	private _removeNotebookEditor(editors: IEditor[]) {
		editors.forEach(e => {
			const sub = this._toDisposeOnEditorRemove.get(e.getId());
			if (sub) {
				this._toDisposeOnEditorRemove.delete(e.getId());
				sub.dispose();
			}
		});

		this._updateState();
R
rebornix 已提交
363 364 365 366 367
	}

	private async _updateState(focusedNotebookEditor?: IEditor) {
		let activeEditor: string | null = null;

368 369 370 371 372 373 374 375 376 377
		const activeEditorPane = this.editorService.activeEditorPane as any | undefined;
		if (activeEditorPane?.isNotebookEditor) {
			const notebookEditor = (activeEditorPane.getControl() as INotebookEditor);
			activeEditor = notebookEditor && notebookEditor.hasModel() ? notebookEditor!.getId() : null;
		}

		const documentEditorsMap = new Map<string, IEditor>();

		const editors = new Map<string, IEditor>();
		this._notebookService.listNotebookEditors().forEach(editor => {
R
rebornix 已提交
378 379
			if (editor.hasModel()) {
				editors.set(editor.getId(), editor);
380 381 382 383 384 385 386 387 388 389
				documentEditorsMap.set(editor.textModel!.uri.toString(), editor);
			}
		});

		const visibleEditorsMap = new Map<string, IEditor>();
		this.editorService.visibleEditorPanes.forEach(editor => {
			if ((editor as any).isNotebookEditor) {
				const nbEditorWidget = (editor as any).getControl() as INotebookEditor;
				if (nbEditorWidget && editors.has(nbEditorWidget.getId())) {
					visibleEditorsMap.set(nbEditorWidget.getId(), nbEditorWidget);
R
rebornix 已提交
390 391
				}
			}
392 393 394 395
		});

		const documents = new Set<NotebookTextModel>();
		this._notebookService.listNotebookDocuments().forEach(document => {
R
rebornix 已提交
396
			documents.add(document);
397
		});
R
rebornix 已提交
398

399
		if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.hasModel()) {
R
rebornix 已提交
400 401 402
			activeEditor = focusedNotebookEditor.getId();
		}

R
rebornix 已提交
403
		// editors always have view model attached, which means there is already a document in exthost.
404
		const newState = new DocumentAndEditorState(documents, editors, activeEditor, visibleEditorsMap);
R
rebornix 已提交
405 406 407 408 409 410 411 412 413
		const delta = DocumentAndEditorState.compute(this._currentState, newState);
		// const isEmptyChange = (!delta.addedDocuments || delta.addedDocuments.length === 0)
		// 	&& (!delta.removedDocuments || delta.removedDocuments.length === 0)
		// 	&& (!delta.addedEditors || delta.addedEditors.length === 0)
		// 	&& (!delta.removedEditors || delta.removedEditors.length === 0)
		// 	&& (delta.newActiveEditor === undefined)

		// if (!isEmptyChange) {
		this._currentState = newState;
414
		await this._emitDelta(delta);
R
rebornix 已提交
415 416 417
		// }
	}

418 419 420 421
	async $registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: UriComponents[]): Promise<void> {
		const renderer = new MainThreadNotebookRenderer(this._proxy, type, extension.id, URI.revive(extension.location), selectors, preloads.map(uri => URI.revive(uri)));
		this._notebookRenderers.set(type, renderer);
		this._notebookService.registerNotebookRenderer(type, renderer);
R
rebornix 已提交
422 423
	}

424 425
	async $unregisterNotebookRenderer(id: string): Promise<void> {
		this._notebookService.unregisterNotebookRenderer(id);
R
rebornix 已提交
426 427
	}

428
	async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernel: INotebookKernelInfoDto | undefined): Promise<void> {
429
		let controller = new MainThreadNotebookController(this._proxy, this, viewType, supportBackup, kernel, this._notebookService, this._instantiationService);
R
rebornix 已提交
430
		this._notebookProviders.set(viewType, controller);
R
rebornix 已提交
431
		this._notebookService.registerNotebookController(viewType, extension, controller);
R
rebornix 已提交
432
		return;
R
rebornix 已提交
433 434
	}

435 436 437 438 439 440 441
	async $onNotebookChange(viewType: string, uri: UriComponents): Promise<void> {
		let controller = this._notebookProviders.get(viewType);
		if (controller) {
			controller.handleNotebookChange(uri);
		}
	}

R
rebornix 已提交
442
	async $unregisterNotebookProvider(viewType: string): Promise<void> {
R
rebornix 已提交
443 444
		this._notebookProviders.delete(viewType);
		this._notebookService.unregisterNotebookProvider(viewType);
R
rebornix 已提交
445 446 447
		return;
	}

R
rebornix 已提交
448 449
	async $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise<void> {
		const kernel = new MainThreadNotebookKernel(this._proxy, id, label, selectors, extension.id, URI.revive(extension.location), preloads.map(preload => URI.revive(preload)));
R
rebornix 已提交
450 451 452 453 454 455 456 457 458 459 460
		this._notebookKernels.set(id, kernel);
		this._notebookService.registerNotebookKernel(kernel);
		return;
	}

	async $unregisterNotebookKernel(id: string): Promise<void> {
		this._notebookKernels.delete(id);
		this._notebookService.unregisterNotebookKernel(id);
		return;
	}

R
rebornix 已提交
461 462 463 464 465 466 467 468 469 470 471 472
	async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void> {
		const emitter = new Emitter<void>();
		const that = this;
		const provider = this._notebookService.registerNotebookKernelProvider({
			onDidChangeKernels: emitter.event,
			selector: documentFilter,
			provideKernels: (uri: URI, token: CancellationToken) => {
				return that._proxy.$provideNotebookKernels(handle, uri, token);
			},
			resolveKernel: (editorId: string, uri: URI, kernelId: string, token: CancellationToken) => {
				return that._proxy.$resolveNotebookKernel(handle, editorId, uri, kernelId, token);
			},
473 474
			executeNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined, token: CancellationToken) => {
				return that._proxy.$executeNotebookKernelFromProvider(handle, uri, kernelId, cellHandle, token);
R
rebornix 已提交
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
			}
		});
		this._notebookKernelProviders.set(handle, {
			extension,
			emitter,
			provider
		});

		return;
	}

	async $unregisterNotebookKernelProvider(handle: number): Promise<void> {
		const entry = this._notebookKernelProviders.get(handle);

		if (entry) {
			entry.emitter.dispose();
			entry.provider.dispose();
			this._notebookKernelProviders.delete(handle);
		}
	}

	$onNotebookKernelChange(handle: number): void {
		const entry = this._notebookKernelProviders.get(handle);

		entry?.emitter.fire();
	}

R
rebornix 已提交
502
	async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void> {
R
rebornix 已提交
503 504 505
		let controller = this._notebookProviders.get(viewType);

		if (controller) {
R
rebornix 已提交
506
			controller.updateLanguages(resource, languages);
R
rebornix 已提交
507 508
		}
	}
R
rebornix 已提交
509

R
rebornix 已提交
510
	async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise<void> {
R
rebornix 已提交
511 512 513 514 515 516 517
		let controller = this._notebookProviders.get(viewType);

		if (controller) {
			controller.updateNotebookMetadata(resource, metadata);
		}
	}

518 519 520 521 522 523 524 525
	async $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise<void> {
		let controller = this._notebookProviders.get(viewType);

		if (controller) {
			controller.updateNotebookCellMetadata(resource, handle, metadata);
		}
	}

526
	async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
527
		let controller = this._notebookProviders.get(viewType);
528
		await controller?.spliceNotebookCellOutputs(resource, cellHandle, splices, renderers);
529 530
	}

531 532
	async executeNotebookByAttachedKernel(viewType: string, uri: URI, token: CancellationToken): Promise<void> {
		return this._proxy.$executeNotebookByAttachedKernel(viewType, uri, undefined, token);
R
rebornix 已提交
533
	}
534

535 536 537 538 539
	async $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean> {
		const editor = this._notebookService.getNotebookEditor(editorId) as INotebookEditor | undefined;
		if (editor?.isNotebookEditor) {
			editor.postMessage(forRendererId, value);
			return true;
540 541 542 543
		}

		return false;
	}
544 545 546 547 548 549 550 551 552 553

	$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void {
		let controller = this._notebookProviders.get(viewType);
		controller?.handleEdit(resource, editId, label);
	}

	$onContentChange(resource: UriComponents, viewType: string): void {
		let controller = this._notebookProviders.get(viewType);
		controller?.handleNotebookChange(resource);
	}
R
rebornix 已提交
554 555 556
}

export class MainThreadNotebookController implements IMainNotebookController {
R
rebornix 已提交
557
	private _mapping: Map<string, MainThreadNotebookDocument> = new Map();
R
rebornix 已提交
558
	static documentHandle: number = 0;
R
rebornix 已提交
559 560

	constructor(
R
rebornix 已提交
561 562
		private readonly _proxy: ExtHostNotebookShape,
		private _mainThreadNotebook: MainThreadNotebooks,
R
rebornix 已提交
563
		private _viewType: string,
564
		private _supportBackup: boolean,
565 566
		readonly kernel: INotebookKernelInfoDto | undefined,
		readonly notebookService: INotebookService,
567
		readonly _instantiationService: IInstantiationService
568

R
rebornix 已提交
569 570 571
	) {
	}

572
	async createNotebook(viewType: string, uri: URI, backup: INotebookTextModelBackup | undefined, forceReload: boolean, editorId?: string, backupId?: string): Promise<NotebookTextModel | undefined> {
R
rebornix 已提交
573 574 575
		let mainthreadNotebook = this._mapping.get(URI.from(uri).toString());

		if (mainthreadNotebook) {
R
revert.  
rebornix 已提交
576 577 578 579 580 581 582 583
			if (forceReload) {
				const data = await this._proxy.$resolveNotebookData(viewType, uri);
				if (!data) {
					return;
				}

				mainthreadNotebook.textModel.languages = data.languages;
				mainthreadNotebook.textModel.metadata = data.metadata;
584
				await mainthreadNotebook.applyEdit(mainthreadNotebook.textModel.versionId, [
R
revert.  
rebornix 已提交
585 586
					{ editType: CellEditType.Delete, count: mainthreadNotebook.textModel.cells.length, index: 0 },
					{ editType: CellEditType.Insert, index: 0, cells: data.cells }
R
rebornix 已提交
587
				], true, false);
R
revert.  
rebornix 已提交
588
			}
R
rebornix 已提交
589 590 591
			return mainthreadNotebook.textModel;
		}

592
		let document = this._instantiationService.createInstance(MainThreadNotebookDocument, this._proxy, MainThreadNotebookController.documentHandle++, viewType, this._supportBackup, uri);
R
rebornix 已提交
593 594 595 596 597 598 599
		this._mapping.set(document.uri.toString(), document);

		if (backup) {
			// trigger events
			document.textModel.metadata = backup.metadata;
			document.textModel.languages = backup.languages;

600
			// restored from backup, update the text model without emitting any event to exthost
601
			await document.applyEdit(document.textModel.versionId, [
R
rebornix 已提交
602 603 604
				{
					editType: CellEditType.Insert,
					index: 0,
605
					cells: backup.cells || []
R
rebornix 已提交
606
				}
R
rebornix 已提交
607
			], false, true);
R
rebornix 已提交
608

609
			// create document in ext host with cells data
610
			await this._mainThreadNotebook.addNotebookDocument({
611 612 613 614 615 616 617 618 619
				viewType: document.viewType,
				handle: document.handle,
				uri: document.uri,
				metadata: document.textModel.metadata,
				versionId: document.textModel.versionId,
				cells: document.textModel.cells.map(cell => ({
					handle: cell.handle,
					uri: cell.uri,
					source: cell.textBuffer.getLinesContent(),
620
					eol: cell.textBuffer.getEOL(),
621 622 623 624 625 626 627 628 629
					language: cell.language,
					cellKind: cell.cellKind,
					outputs: cell.outputs,
					metadata: cell.metadata
				})),
				attachedEditor: editorId ? {
					id: editorId,
					selections: document.textModel.selections
				} : undefined
R
rebornix 已提交
630
			});
R
rebornix 已提交
631 632 633 634 635

			return document.textModel;
		}

		// open notebook document
636
		const data = await this._proxy.$resolveNotebookData(viewType, uri, backupId);
R
rebornix 已提交
637 638 639 640 641 642
		if (!data) {
			return;
		}

		document.textModel.languages = data.languages;
		document.textModel.metadata = data.metadata;
643 644 645 646 647 648 649

		if (data.cells.length) {
			document.textModel.initialize(data!.cells);
		} else {
			const mainCell = document.textModel.createCellTextModel([''], document.textModel.languages.length ? document.textModel.languages[0] : '', CellKind.Code, [], undefined);
			document.textModel.insertTemplateCell(mainCell);
		}
R
rebornix 已提交
650

651 652 653 654 655 656 657 658 659 660
		await this._mainThreadNotebook.addNotebookDocument({
			viewType: document.viewType,
			handle: document.handle,
			uri: document.uri,
			metadata: document.textModel.metadata,
			versionId: document.textModel.versionId,
			cells: document.textModel.cells.map(cell => ({
				handle: cell.handle,
				uri: cell.uri,
				source: cell.textBuffer.getLinesContent(),
661
				eol: cell.textBuffer.getEOL(),
662 663 664 665 666 667 668 669 670
				language: cell.language,
				cellKind: cell.cellKind,
				outputs: cell.outputs,
				metadata: cell.metadata
			})),
			attachedEditor: editorId ? {
				id: editorId,
				selections: document.textModel.selections
			} : undefined
R
rebornix 已提交
671 672
		});

R
rebornix 已提交
673 674
		this._proxy.$acceptEditorPropertiesChanged(uri, { selections: null, metadata: document.textModel.metadata });

R
rebornix 已提交
675 676 677
		return document.textModel;
	}

678 679 680 681
	async resolveNotebookEditor(viewType: string, uri: URI, editorId: string) {
		await this._proxy.$resolveNotebookEditor(viewType, uri, editorId);
	}

R
rebornix 已提交
682
	async tryApplyEdits(resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean> {
683
		let mainthreadNotebook = this._mapping.get(URI.from(resource).toString());
R
rebornix 已提交
684 685

		if (mainthreadNotebook) {
R
rebornix 已提交
686
			return await mainthreadNotebook.applyEdit(modelVersionId, edits, true, true);
R
rebornix 已提交
687 688 689
		}

		return false;
690 691
	}

692
	async spliceNotebookCellOutputs(resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
693
		let mainthreadNotebook = this._mapping.get(URI.from(resource).toString());
694
		await mainthreadNotebook?.spliceNotebookCellOutputs(cellHandle, splices);
695 696
	}

697 698
	async executeNotebookByAttachedKernel(viewType: string, uri: URI, token: CancellationToken): Promise<void> {
		return this._mainThreadNotebook.executeNotebookByAttachedKernel(viewType, uri, token);
R
rebornix 已提交
699 700
	}

701 702
	onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: unknown): void {
		this._proxy.$onDidReceiveMessage(editorId, rendererType, message);
703 704
	}

R
rebornix 已提交
705 706 707 708 709 710 711
	async removeNotebookDocument(notebook: INotebookTextModel): Promise<void> {
		let document = this._mapping.get(URI.from(notebook.uri).toString());

		if (!document) {
			return;
		}

712
		// TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together
R
rebornix 已提交
713 714 715 716 717 718
		await this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] });
		document.dispose();
		this._mapping.delete(URI.from(notebook.uri).toString());
	}

	// Methods for ExtHost
R
rebornix 已提交
719

720 721 722 723 724
	handleNotebookChange(resource: UriComponents) {
		let document = this._mapping.get(URI.from(resource).toString());
		document?.textModel.handleUnknownChange();
	}

725 726 727 728 729
	handleEdit(resource: UriComponents, editId: number, label: string | undefined): void {
		let document = this._mapping.get(URI.from(resource).toString());
		document?.handleEdit(editId, label);
	}

R
rebornix 已提交
730 731
	updateLanguages(resource: UriComponents, languages: string[]) {
		let document = this._mapping.get(URI.from(resource).toString());
732
		document?.textModel.updateLanguages(languages);
R
rebornix 已提交
733 734
	}

R
rebornix 已提交
735
	updateNotebookMetadata(resource: UriComponents, metadata: NotebookDocumentMetadata) {
R
rebornix 已提交
736 737 738 739
		let document = this._mapping.get(URI.from(resource).toString());
		document?.textModel.updateNotebookMetadata(metadata);
	}

740 741 742 743 744
	updateNotebookCellMetadata(resource: UriComponents, handle: number, metadata: NotebookCellMetadata) {
		let document = this._mapping.get(URI.from(resource).toString());
		document?.textModel.updateNotebookCellMetadata(handle, metadata);
	}

745 746
	async executeNotebookCell(uri: URI, handle: number, token: CancellationToken): Promise<void> {
		return this._proxy.$executeNotebookByAttachedKernel(this._viewType, uri, handle, token);
747 748
	}

R
rebornix 已提交
749 750
	async save(uri: URI, token: CancellationToken): Promise<boolean> {
		return this._proxy.$saveNotebook(this._viewType, uri, token);
751
	}
R
saveAs  
rebornix 已提交
752 753 754

	async saveAs(uri: URI, target: URI, token: CancellationToken): Promise<boolean> {
		return this._proxy.$saveNotebookAs(this._viewType, uri, target, token);
755 756 757 758 759 760
	}

	async backup(uri: URI, token: CancellationToken): Promise<string | undefined> {
		const backupId = await this._proxy.$backup(this._viewType, uri, token);
		return backupId;
	}
R
rebornix 已提交
761
}
R
rebornix 已提交
762 763 764 765 766

export class MainThreadNotebookKernel implements INotebookKernelInfo {
	constructor(
		private readonly _proxy: ExtHostNotebookShape,
		readonly id: string,
R
rebornix 已提交
767
		readonly label: string,
R
rebornix 已提交
768 769 770 771 772 773 774 775 776 777 778
		readonly selectors: (string | IRelativePattern)[],
		readonly extension: ExtensionIdentifier,
		readonly extensionLocation: URI,
		readonly preloads: URI[]
	) {
	}

	async executeNotebook(viewType: string, uri: URI, handle: number | undefined, token: CancellationToken): Promise<void> {
		return this._proxy.$executeNotebook2(this.id, viewType, uri, handle, token);
	}
}
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799

export class MainThreadNotebookRenderer implements INotebookRendererInfo {
	constructor(
		private readonly _proxy: ExtHostNotebookShape,
		readonly id: string,
		readonly extensionId: ExtensionIdentifier,
		readonly extensionLocation: URI,
		readonly selectors: INotebookMimeTypeSelector,
		readonly preloads: URI[]
	) {

	}

	render(uri: URI, request: IOutputRenderRequest<UriComponents>): Promise<IOutputRenderResponse<UriComponents> | undefined> {
		return this._proxy.$renderOutputs(uri, this.id, request);
	}

	render2<T>(uri: URI, request: IOutputRenderRequest<T>): Promise<IOutputRenderResponse<T> | undefined> {
		return this._proxy.$renderOutputs2(uri, this.id, request);
	}
}