mainThreadNotebook.ts 27.5 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';
13
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, INotebookTextModelBackup, IEditor, INotebookRendererInfo, IOutputRenderRequest, IOutputRenderResponse } 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

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

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

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

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

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

R
rebornix 已提交
57
	async applyEdit(modelVersionId: number, edits: ICellEditOperation[], emitToExtHost: boolean, synchronous: boolean): Promise<boolean> {
58
		await this.notebookService.transformEditsOutputs(this.textModel, edits);
R
rebornix 已提交
59 60 61 62 63 64 65 66 67 68
		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 已提交
69
	}
R
rebornix 已提交
70

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

	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 已提交
91
	dispose() {
92
		this._textModel.dispose();
R
rebornix 已提交
93 94
		super.dispose();
	}
R
rebornix 已提交
95 96
}

R
rebornix 已提交
97
class DocumentAndEditorState {
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
	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 已提交
114 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
	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: [],
140 141
				addedEditors: apiEditors,
				visibleEditors: [...after.visibleEditors].map(editor => editor[0])
R
rebornix 已提交
142 143
			};
		}
144
		const documentDelta = DocumentAndEditorState.ofSets(before.documents, after.documents);
R
rebornix 已提交
145 146 147 148
		const editorDelta = DocumentAndEditorState.ofMaps(before.textEditors, after.textEditors);
		const addedAPIEditors = editorDelta.added.map(add => ({
			id: add.getId(),
			documentUri: add.uri!,
149
			selections: add.textModel!.selections || []
R
rebornix 已提交
150 151 152 153 154 155 156
		}));

		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;

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

R
rebornix 已提交
159
		return {
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
			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(),
						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 已提交
183 184
			addedEditors: addedAPIEditors,
			removedEditors: removedAPIEditors,
185 186 187 188
			newActiveEditor: newActiveEditor,
			visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0
				? undefined
				: [...after.visibleEditors].map(editor => editor[0])
R
rebornix 已提交
189 190 191 192
		};
	}

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

R
rebornix 已提交
202 203 204
@extHostNamedCustomer(MainContext.MainThreadNotebook)
export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape {
	private readonly _notebookProviders = new Map<string, MainThreadNotebookController>();
R
rebornix 已提交
205
	private readonly _notebookKernels = new Map<string, MainThreadNotebookKernel>();
206
	private readonly _notebookRenderers = new Map<string, MainThreadNotebookRenderer>();
R
rebornix 已提交
207
	private readonly _proxy: ExtHostNotebookShape;
R
rebornix 已提交
208 209
	private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
	private _currentState?: DocumentAndEditorState;
R
rebornix 已提交
210 211 212

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

R
rebornix 已提交
219 220 221
	) {
		super();
		this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
R
rebornix 已提交
222 223 224
		this.registerListeners();
	}

R
rebornix 已提交
225 226 227 228 229 230 231 232
	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 已提交
233 234
	}

R
rebornix 已提交
235 236 237 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
	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;
	}

263
	private _emitDelta(delta: INotebookDocumentsAndEditorsDelta) {
R
rebornix 已提交
264 265 266 267
		if (this._isDeltaEmpty(delta)) {
			return;
		}

268
		return this._proxy.$acceptDocumentAndEditorsDelta(delta);
269 270
	}

R
rebornix 已提交
271
	registerListeners() {
R
rebornix 已提交
272 273 274 275
		this._notebookService.listNotebookEditors().forEach((e) => {
			this._addNotebookEditor(e);
		});

R
rebornix 已提交
276
		this._register(this._notebookService.onDidChangeActiveEditor(e => {
277
			this._updateState();
R
rebornix 已提交
278
		}));
R
rebornix 已提交
279

R
rebornix 已提交
280
		this._register(this._notebookService.onDidChangeVisibleEditors(e => {
281 282 283 284 285 286 287 288 289
			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 已提交
290 291
		}));

R
rebornix 已提交
292 293 294 295
		this._register(this._notebookService.onNotebookEditorAdd(editor => {
			this._addNotebookEditor(editor);
		}));

296 297 298
		this._register(this._notebookService.onNotebookEditorsRemove(editors => {
			this._removeNotebookEditor(editors);
		}));
299

R
rebornix 已提交
300 301 302 303
		this._register(this._notebookService.onNotebookDocumentAdd(() => {
			this._updateState();
		}));

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

R
rebornix 已提交
308 309 310 311 312 313 314
		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 已提交
315

R
rebornix 已提交
316
		updateOrder();
R
rebornix 已提交
317

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

		this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => {
			updateOrder();
		}));
R
rebornix 已提交
327 328 329 330

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

333
	async addNotebookDocument(data: INotebookModelAddedData) {
334
		this._updateState();
335 336
	}

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

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

350 351 352 353 354 355 356 357 358 359
	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 已提交
360 361 362 363 364
	}

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

365 366 367 368 369 370 371 372 373 374
		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 已提交
375 376
			if (editor.hasModel()) {
				editors.set(editor.getId(), editor);
377 378 379 380 381 382 383 384 385 386
				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 已提交
387 388
				}
			}
389 390 391 392
		});

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

396
		if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.hasModel()) {
R
rebornix 已提交
397 398 399
			activeEditor = focusedNotebookEditor.getId();
		}

R
rebornix 已提交
400
		// editors always have view model attached, which means there is already a document in exthost.
401
		const newState = new DocumentAndEditorState(documents, editors, activeEditor, visibleEditorsMap);
R
rebornix 已提交
402 403 404 405 406 407 408 409 410
		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;
411
		await this._emitDelta(delta);
R
rebornix 已提交
412 413 414
		// }
	}

415 416 417 418
	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 已提交
419 420
	}

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

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

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

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

R
rebornix 已提交
445 446
	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 已提交
447 448 449 450 451 452 453 454 455 456 457
		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 已提交
458
	async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void> {
R
rebornix 已提交
459 460 461
		let controller = this._notebookProviders.get(viewType);

		if (controller) {
R
rebornix 已提交
462
			controller.updateLanguages(resource, languages);
R
rebornix 已提交
463 464
		}
	}
R
rebornix 已提交
465

R
rebornix 已提交
466
	async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise<void> {
R
rebornix 已提交
467 468 469 470 471 472 473
		let controller = this._notebookProviders.get(viewType);

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

474 475 476 477 478 479 480 481
	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);
		}
	}

482
	async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
483
		let controller = this._notebookProviders.get(viewType);
484
		await controller?.spliceNotebookCellOutputs(resource, cellHandle, splices, renderers);
485 486
	}

R
rebornix 已提交
487 488
	async executeNotebook(viewType: string, uri: URI, useAttachedKernel: boolean, token: CancellationToken): Promise<void> {
		return this._proxy.$executeNotebook(viewType, uri, undefined, useAttachedKernel, token);
R
rebornix 已提交
489
	}
490

491 492 493 494 495
	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;
496 497 498 499
		}

		return false;
	}
500 501 502 503 504 505 506 507 508 509

	$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 已提交
510 511 512
}

export class MainThreadNotebookController implements IMainNotebookController {
R
rebornix 已提交
513
	private _mapping: Map<string, MainThreadNotebookDocument> = new Map();
R
rebornix 已提交
514
	static documentHandle: number = 0;
R
rebornix 已提交
515 516

	constructor(
R
rebornix 已提交
517 518
		private readonly _proxy: ExtHostNotebookShape,
		private _mainThreadNotebook: MainThreadNotebooks,
R
rebornix 已提交
519
		private _viewType: string,
520
		private _supportBackup: boolean,
521 522
		readonly kernel: INotebookKernelInfoDto | undefined,
		readonly notebookService: INotebookService,
523
		readonly _instantiationService: IInstantiationService
524

R
rebornix 已提交
525 526 527
	) {
	}

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

		if (mainthreadNotebook) {
R
revert.  
rebornix 已提交
532 533 534 535 536 537 538 539
			if (forceReload) {
				const data = await this._proxy.$resolveNotebookData(viewType, uri);
				if (!data) {
					return;
				}

				mainthreadNotebook.textModel.languages = data.languages;
				mainthreadNotebook.textModel.metadata = data.metadata;
540
				await mainthreadNotebook.applyEdit(mainthreadNotebook.textModel.versionId, [
R
revert.  
rebornix 已提交
541 542
					{ editType: CellEditType.Delete, count: mainthreadNotebook.textModel.cells.length, index: 0 },
					{ editType: CellEditType.Insert, index: 0, cells: data.cells }
R
rebornix 已提交
543
				], true, false);
R
revert.  
rebornix 已提交
544
			}
R
rebornix 已提交
545 546 547
			return mainthreadNotebook.textModel;
		}

548
		let document = this._instantiationService.createInstance(MainThreadNotebookDocument, this._proxy, MainThreadNotebookController.documentHandle++, viewType, this._supportBackup, uri);
R
rebornix 已提交
549 550 551 552 553 554 555
		this._mapping.set(document.uri.toString(), document);

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

556
			// restored from backup, update the text model without emitting any event to exthost
557
			await document.applyEdit(document.textModel.versionId, [
R
rebornix 已提交
558 559 560
				{
					editType: CellEditType.Insert,
					index: 0,
561
					cells: backup.cells || []
R
rebornix 已提交
562
				}
R
rebornix 已提交
563
			], false, true);
R
rebornix 已提交
564

565
			// create document in ext host with cells data
566
			await this._mainThreadNotebook.addNotebookDocument({
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
				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(),
					language: cell.language,
					cellKind: cell.cellKind,
					outputs: cell.outputs,
					metadata: cell.metadata
				})),
				attachedEditor: editorId ? {
					id: editorId,
					selections: document.textModel.selections
				} : undefined
R
rebornix 已提交
585
			});
R
rebornix 已提交
586 587 588 589 590

			return document.textModel;
		}

		// open notebook document
591
		const data = await this._proxy.$resolveNotebookData(viewType, uri, backupId);
R
rebornix 已提交
592 593 594 595 596 597
		if (!data) {
			return;
		}

		document.textModel.languages = data.languages;
		document.textModel.metadata = data.metadata;
598 599 600 601 602 603 604

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

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
		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(),
				language: cell.language,
				cellKind: cell.cellKind,
				outputs: cell.outputs,
				metadata: cell.metadata
			})),
			attachedEditor: editorId ? {
				id: editorId,
				selections: document.textModel.selections
			} : undefined
R
rebornix 已提交
625 626
		});

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

R
rebornix 已提交
629 630 631
		return document.textModel;
	}

632 633 634 635
	async resolveNotebookEditor(viewType: string, uri: URI, editorId: string) {
		await this._proxy.$resolveNotebookEditor(viewType, uri, editorId);
	}

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

		if (mainthreadNotebook) {
R
rebornix 已提交
640
			return await mainthreadNotebook.applyEdit(modelVersionId, edits, true, true);
R
rebornix 已提交
641 642 643
		}

		return false;
644 645
	}

646
	async spliceNotebookCellOutputs(resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
647
		let mainthreadNotebook = this._mapping.get(URI.from(resource).toString());
648
		await mainthreadNotebook?.spliceNotebookCellOutputs(cellHandle, splices);
649 650
	}

R
rebornix 已提交
651 652
	async executeNotebook(viewType: string, uri: URI, useAttachedKernel: boolean, token: CancellationToken): Promise<void> {
		return this._mainThreadNotebook.executeNotebook(viewType, uri, useAttachedKernel, token);
R
rebornix 已提交
653 654
	}

655 656
	onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: unknown): void {
		this._proxy.$onDidReceiveMessage(editorId, rendererType, message);
657 658
	}

R
rebornix 已提交
659 660 661 662 663 664 665
	async removeNotebookDocument(notebook: INotebookTextModel): Promise<void> {
		let document = this._mapping.get(URI.from(notebook.uri).toString());

		if (!document) {
			return;
		}

666
		// TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together
R
rebornix 已提交
667 668 669 670 671 672
		await this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] });
		document.dispose();
		this._mapping.delete(URI.from(notebook.uri).toString());
	}

	// Methods for ExtHost
R
rebornix 已提交
673

674 675 676 677 678
	handleNotebookChange(resource: UriComponents) {
		let document = this._mapping.get(URI.from(resource).toString());
		document?.textModel.handleUnknownChange();
	}

679 680 681 682 683
	handleEdit(resource: UriComponents, editId: number, label: string | undefined): void {
		let document = this._mapping.get(URI.from(resource).toString());
		document?.handleEdit(editId, label);
	}

R
rebornix 已提交
684 685
	updateLanguages(resource: UriComponents, languages: string[]) {
		let document = this._mapping.get(URI.from(resource).toString());
686
		document?.textModel.updateLanguages(languages);
R
rebornix 已提交
687 688
	}

R
rebornix 已提交
689
	updateNotebookMetadata(resource: UriComponents, metadata: NotebookDocumentMetadata) {
R
rebornix 已提交
690 691 692 693
		let document = this._mapping.get(URI.from(resource).toString());
		document?.textModel.updateNotebookMetadata(metadata);
	}

694 695 696 697 698
	updateNotebookCellMetadata(resource: UriComponents, handle: number, metadata: NotebookCellMetadata) {
		let document = this._mapping.get(URI.from(resource).toString());
		document?.textModel.updateNotebookCellMetadata(handle, metadata);
	}

R
rebornix 已提交
699 700
	async executeNotebookCell(uri: URI, handle: number, useAttachedKernel: boolean, token: CancellationToken): Promise<void> {
		return this._proxy.$executeNotebook(this._viewType, uri, handle, useAttachedKernel, token);
701 702
	}

R
rebornix 已提交
703 704
	async save(uri: URI, token: CancellationToken): Promise<boolean> {
		return this._proxy.$saveNotebook(this._viewType, uri, token);
705
	}
R
saveAs  
rebornix 已提交
706 707 708

	async saveAs(uri: URI, target: URI, token: CancellationToken): Promise<boolean> {
		return this._proxy.$saveNotebookAs(this._viewType, uri, target, token);
709 710 711 712 713 714
	}

	async backup(uri: URI, token: CancellationToken): Promise<string | undefined> {
		const backupId = await this._proxy.$backup(this._viewType, uri, token);
		return backupId;
	}
R
rebornix 已提交
715
}
R
rebornix 已提交
716 717 718 719 720

export class MainThreadNotebookKernel implements INotebookKernelInfo {
	constructor(
		private readonly _proxy: ExtHostNotebookShape,
		readonly id: string,
R
rebornix 已提交
721
		readonly label: string,
R
rebornix 已提交
722 723 724 725 726 727 728 729 730 731 732
		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);
	}
}
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753

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