mainThreadNotebook.ts 26.7 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.
 *--------------------------------------------------------------------------------------------*/

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

	dispose() {
58
		// this._textModel.dispose();
R
rebornix 已提交
59 60
		super.dispose();
	}
R
rebornix 已提交
61 62
}

R
rebornix 已提交
63
class DocumentAndEditorState {
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
	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 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
	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: [],
106 107
				addedEditors: apiEditors,
				visibleEditors: [...after.visibleEditors].map(editor => editor[0])
R
rebornix 已提交
108 109
			};
		}
110
		const documentDelta = DocumentAndEditorState.ofSets(before.documents, after.documents);
R
rebornix 已提交
111 112 113 114
		const editorDelta = DocumentAndEditorState.ofMaps(before.textEditors, after.textEditors);
		const addedAPIEditors = editorDelta.added.map(add => ({
			id: add.getId(),
			documentUri: add.uri!,
115
			selections: add.textModel!.selections || []
R
rebornix 已提交
116 117 118 119 120 121 122
		}));

		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;

123 124
		const visibleEditorDelta = DocumentAndEditorState.ofMaps(before.visibleEditors, after.visibleEditors);

R
rebornix 已提交
125
		return {
126 127 128 129 130 131 132 133 134 135 136
			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(),
137
						eol: cell.textBuffer.getEOL(),
138 139 140 141 142 143 144 145 146 147 148 149
						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 已提交
150 151
			addedEditors: addedAPIEditors,
			removedEditors: removedAPIEditors,
152 153 154 155
			newActiveEditor: newActiveEditor,
			visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0
				? undefined
				: [...after.visibleEditors].map(editor => editor[0])
R
rebornix 已提交
156 157 158 159
		};
	}

	constructor(
160
		readonly documents: Set<NotebookTextModel>,
R
rebornix 已提交
161 162
		readonly textEditors: Map<string, IEditor>,
		readonly activeEditor: string | null | undefined,
163
		readonly visibleEditors: Map<string, IEditor>
R
rebornix 已提交
164 165 166 167 168
	) {
		//
	}
}

R
rebornix 已提交
169 170
@extHostNamedCustomer(MainContext.MainThreadNotebook)
export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape {
171
	private readonly _notebookProviders = new Map<string, IMainNotebookController>();
R
rebornix 已提交
172
	private readonly _notebookKernels = new Map<string, MainThreadNotebookKernel>();
R
rebornix 已提交
173
	private readonly _notebookKernelProviders = new Map<number, { extension: NotebookExtensionDescription, emitter: Emitter<void>, provider: IDisposable }>();
174
	private readonly _notebookRenderers = new Map<string, MainThreadNotebookRenderer>();
R
rebornix 已提交
175
	private readonly _proxy: ExtHostNotebookShape;
R
rebornix 已提交
176 177
	private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
	private _currentState?: DocumentAndEditorState;
178
	private _editorEventListenersMapping: Map<string, DisposableStore> = new Map();
R
rebornix 已提交
179 180 181

	constructor(
		extHostContext: IExtHostContext,
R
rebornix 已提交
182
		@INotebookService private _notebookService: INotebookService,
183 184
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IEditorService private readonly editorService: IEditorService,
R
rebornix 已提交
185 186
		@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
		@ILogService private readonly logService: ILogService
187

R
rebornix 已提交
188 189 190
	) {
		super();
		this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
R
rebornix 已提交
191 192 193
		this.registerListeners();
	}

R
rebornix 已提交
194
	async $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean> {
195 196 197 198
		const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
		if (textModel) {
			await this._notebookService.transformEditsOutputs(textModel, edits);
			return textModel.$applyEdit(modelVersionId, edits, true);
R
rebornix 已提交
199 200 201
		}

		return false;
R
rebornix 已提交
202 203
	}

204 205 206 207 208 209 210 211
	async removeNotebookTextModel(uri: URI): Promise<void> {
		// TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together
		await this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [uri] });
		let textModelDisposableStore = this._editorEventListenersMapping.get(uri.toString());
		textModelDisposableStore?.dispose();
		this._editorEventListenersMapping.delete(URI.from(uri).toString());
	}

R
rebornix 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
	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;
	}

240
	private _emitDelta(delta: INotebookDocumentsAndEditorsDelta) {
R
rebornix 已提交
241 242 243 244
		if (this._isDeltaEmpty(delta)) {
			return;
		}

245
		return this._proxy.$acceptDocumentAndEditorsDelta(delta);
246 247
	}

R
rebornix 已提交
248
	registerListeners() {
R
rebornix 已提交
249 250 251 252
		this._notebookService.listNotebookEditors().forEach((e) => {
			this._addNotebookEditor(e);
		});

R
rebornix 已提交
253
		this._register(this._notebookService.onDidChangeActiveEditor(e => {
254
			this._updateState();
R
rebornix 已提交
255
		}));
R
rebornix 已提交
256

R
rebornix 已提交
257
		this._register(this._notebookService.onDidChangeVisibleEditors(e => {
258 259 260 261 262 263 264 265 266
			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 已提交
267 268
		}));

R
rebornix 已提交
269 270 271 272
		this._register(this._notebookService.onNotebookEditorAdd(editor => {
			this._addNotebookEditor(editor);
		}));

273 274 275
		this._register(this._notebookService.onNotebookEditorsRemove(editors => {
			this._removeNotebookEditor(editors);
		}));
276

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
		this._register(this._notebookService.onNotebookDocumentAdd((documents) => {
			documents.forEach(doc => {
				if (!this._editorEventListenersMapping.has(doc.toString())) {
					const disposableStore = new DisposableStore();
					const textModel = this._notebookService.getNotebookTextModel(doc);
					disposableStore.add(textModel!.onDidModelChangeProxy(e => {
						this._proxy.$acceptModelChanged(textModel!.uri, e);
						this._proxy.$acceptEditorPropertiesChanged(doc, { selections: { selections: textModel!.selections }, metadata: null });
					}));
					disposableStore.add(textModel!.onDidSelectionChange(e => {
						const selectionsChange = e ? { selections: e } : null;
						this._proxy.$acceptEditorPropertiesChanged(doc, { selections: selectionsChange, metadata: null });
					}));

					this._editorEventListenersMapping.set(textModel!.uri.toString(), disposableStore);
				}
			});
R
rebornix 已提交
294 295 296
			this._updateState();
		}));

297 298 299 300 301 302
		this._register(this._notebookService.onNotebookDocumentRemove((documents) => {
			documents.forEach(doc => {
				this._editorEventListenersMapping.get(doc.toString())?.dispose();
				this._editorEventListenersMapping.delete(doc.toString());
			});

303
			this._updateState();
R
rebornix 已提交
304 305
		}));

R
rebornix 已提交
306 307 308 309
		this._register(this._notebookService.onDidChangeNotebookActiveKernel(e => {
			this._proxy.$acceptNotebookActiveKernelChange(e);
		}));

310 311 312 313
		this._register(this._notebookService.onNotebookDocumentSaved(e => {
			this._proxy.$acceptModelSaved(e);
		}));

R
rebornix 已提交
314 315 316 317 318 319 320
		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 已提交
321

R
rebornix 已提交
322
		updateOrder();
R
rebornix 已提交
323

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

		this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => {
			updateOrder();
		}));
R
rebornix 已提交
333 334 335 336

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

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

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

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

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

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

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

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

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

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

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

427 428 429
	async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, _kernel: INotebookKernelInfoDto | undefined): Promise<void> {
		const controller: IMainNotebookController = {
			kernel: _kernel,
430
			supportBackup: _supportBackup,
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
			reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => {
				const data = await this._proxy.$resolveNotebookData(_viewType, mainthreadTextModel.uri);
				if (!data) {
					return;
				}

				mainthreadTextModel.languages = data.languages;
				mainthreadTextModel.metadata = data.metadata;

				const edits: ICellEditOperation[] = [
					{ editType: CellEditType.Delete, count: mainthreadTextModel.cells.length, index: 0 },
					{ editType: CellEditType.Insert, index: 0, cells: data.cells }
				];

				await this._notebookService.transformEditsOutputs(mainthreadTextModel, edits);
				await new Promise(resolve => {
					DOM.scheduleAtNextAnimationFrame(() => {
						const ret = mainthreadTextModel!.$applyEdit(mainthreadTextModel!.versionId, edits, true);
						resolve(ret);
					});
				});
			},
453
			createNotebook: async (textModel: NotebookTextModel, backupId?: string) => {
454
				// open notebook document
455
				const data = await this._proxy.$resolveNotebookData(textModel.viewType, textModel.uri, backupId);
456 457 458 459 460 461 462 463 464 465 466 467 468 469
				if (!data) {
					return;
				}

				textModel.languages = data.languages;
				textModel.metadata = data.metadata;

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

470 471
				this._proxy.$acceptEditorPropertiesChanged(textModel.uri, { selections: null, metadata: textModel.metadata });
				return;
472 473 474 475
			},
			resolveNotebookEditor: async (viewType: string, uri: URI, editorId: string) => {
				await this._proxy.$resolveNotebookEditor(viewType, uri, editorId);
			},
R
Rob Lourens 已提交
476
			executeNotebookByAttachedKernel: async (viewType: string, uri: URI) => {
477
				return this.executeNotebookByAttachedKernel(viewType, uri, undefined);
R
Rob Lourens 已提交
478 479
			},
			cancelNotebookByAttachedKernel: async (viewType: string, uri: URI) => {
480
				return this.cancelNotebookByAttachedKernel(viewType, uri, undefined);
481 482 483 484 485 486 487
			},
			onDidReceiveMessage: (editorId: string, rendererType: string | undefined, message: unknown) => {
				this._proxy.$onDidReceiveMessage(editorId, rendererType, message);
			},
			removeNotebookDocument: async (uri: URI) => {
				return this.removeNotebookTextModel(uri);
			},
R
Rob Lourens 已提交
488
			executeNotebookCell: async (uri: URI, handle: number) => {
489
				return this.executeNotebookByAttachedKernel(_viewType, uri, handle);
R
Rob Lourens 已提交
490 491
			},
			cancelNotebookCell: async (uri: URI, handle: number) => {
492
				return this.cancelNotebookByAttachedKernel(_viewType, uri, handle);
493 494 495 496 497 498 499 500 501 502 503 504 505 506
			},
			save: async (uri: URI, token: CancellationToken) => {
				return this._proxy.$saveNotebook(_viewType, uri, token);
			},
			saveAs: async (uri: URI, target: URI, token: CancellationToken) => {
				return this._proxy.$saveNotebookAs(_viewType, uri, target, token);
			},
			backup: async (uri: URI, token: CancellationToken) => {
				return this._proxy.$backup(_viewType, uri, token);
			}
		};

		this._notebookProviders.set(_viewType, controller);
		this._notebookService.registerNotebookController(_viewType, _extension, controller);
R
rebornix 已提交
507
		return;
R
rebornix 已提交
508 509
	}

510
	async $onNotebookChange(viewType: string, uri: UriComponents): Promise<void> {
511 512
		const textModel = this._notebookService.getNotebookTextModel(URI.from(uri));
		textModel?.handleUnknownChange();
513 514
	}

R
rebornix 已提交
515
	async $unregisterNotebookProvider(viewType: string): Promise<void> {
R
rebornix 已提交
516 517
		this._notebookProviders.delete(viewType);
		this._notebookService.unregisterNotebookProvider(viewType);
R
rebornix 已提交
518 519 520
		return;
	}

R
rebornix 已提交
521
	async $registerNotebookKernel(extension: NotebookExtensionDescription, id: string, label: string, selectors: (string | IRelativePattern)[], preloads: UriComponents[]): Promise<void> {
522
		const kernel = new MainThreadNotebookKernel(this._proxy, id, label, selectors, extension.id, URI.revive(extension.location), preloads.map(preload => URI.revive(preload)), this.logService);
R
rebornix 已提交
523 524 525 526 527 528 529 530 531 532 533
		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 已提交
534 535 536 537
	async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void> {
		const emitter = new Emitter<void>();
		const that = this;
		const provider = this._notebookService.registerNotebookKernelProvider({
R
rebornix 已提交
538 539
			providerExtensionId: extension.id.value,
			providerDescription: extension.description,
R
rebornix 已提交
540 541
			onDidChangeKernels: emitter.event,
			selector: documentFilter,
R
rebornix 已提交
542 543 544 545 546 547 548 549
			provideKernels: async (uri: URI, token: CancellationToken) => {
				const kernels = await that._proxy.$provideNotebookKernels(handle, uri, token);
				return kernels.map(kernel => {
					return {
						...kernel,
						providerHandle: handle
					};
				});
R
rebornix 已提交
550 551 552 553
			},
			resolveKernel: (editorId: string, uri: URI, kernelId: string, token: CancellationToken) => {
				return that._proxy.$resolveNotebookKernel(handle, editorId, uri, kernelId, token);
			},
R
Rob Lourens 已提交
554
			executeNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => {
555
				this.logService.debug('MainthreadNotebooks.registerNotebookKernelProvider#executeNotebook', uri.path, kernelId, cellHandle);
R
Rob Lourens 已提交
556
				return that._proxy.$executeNotebookKernelFromProvider(handle, uri, kernelId, cellHandle);
557 558
			},
			cancelNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => {
559
				this.logService.debug('MainthreadNotebooks.registerNotebookKernelProvider#cancelNotebook', uri.path, kernelId, cellHandle);
560 561
				return that._proxy.$cancelNotebookKernelFromProvider(handle, uri, kernelId, cellHandle);
			},
R
rebornix 已提交
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
		});
		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 已提交
588
	async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void> {
R
rebornix 已提交
589
		this.logService.debug('MainThreadNotebooks#updateNotebookLanguages', resource.path, languages);
590 591
		const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
		textModel?.updateLanguages(languages);
R
rebornix 已提交
592
	}
R
rebornix 已提交
593

R
rebornix 已提交
594
	async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise<void> {
R
rebornix 已提交
595
		this.logService.debug('MainThreadNotebooks#updateNotebookMetadata', resource.path, metadata);
596 597
		const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
		textModel?.updateNotebookMetadata(metadata);
R
rebornix 已提交
598 599
	}

600
	async $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise<void> {
R
rebornix 已提交
601
		this.logService.debug('MainThreadNotebooks#updateNotebookCellMetadata', resource.path, handle, metadata);
602 603
		const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
		textModel?.updateNotebookCellMetadata(handle, metadata);
604 605
	}

606
	async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
R
rebornix 已提交
607
		this.logService.debug('MainThreadNotebooks#spliceNotebookCellOutputs', resource.path, cellHandle);
608 609 610 611 612 613
		const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));

		if (textModel) {
			await this._notebookService.transformSpliceOutputs(textModel, splices);
			textModel.$spliceNotebookCellOutputs(cellHandle, splices);
		}
614 615
	}

616 617 618
	async executeNotebookByAttachedKernel(viewType: string, uri: URI, handle: number | undefined): Promise<void> {
		this.logService.debug('MainthreadNotebooks#executeNotebookByAttachedKernel', uri.path, handle);
		return this._proxy.$executeNotebookByAttachedKernel(viewType, uri, handle);
R
Rob Lourens 已提交
619 620
	}

621 622 623
	async cancelNotebookByAttachedKernel(viewType: string, uri: URI, handle: number | undefined): Promise<void> {
		this.logService.debug('MainthreadNotebooks#cancelNotebookByAttachedKernel', uri.path, handle);
		return this._proxy.$cancelNotebookByAttachedKernel(viewType, uri, handle);
R
rebornix 已提交
624
	}
625

626 627 628 629 630
	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;
631 632 633 634
		}

		return false;
	}
635 636

	$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void {
637 638 639 640 641 642 643 644 645
		const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));

		if (textModel) {
			textModel.$handleEdit(label, () => {
				return this._proxy.$undoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty);
			}, () => {
				return this._proxy.$redoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty);
			});
		}
646 647 648
	}

	$onContentChange(resource: UriComponents, viewType: string): void {
649 650
		const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
		textModel?.handleUnknownChange();
651
	}
R
rebornix 已提交
652 653
}

R
rebornix 已提交
654 655 656 657
export class MainThreadNotebookKernel implements INotebookKernelInfo {
	constructor(
		private readonly _proxy: ExtHostNotebookShape,
		readonly id: string,
R
rebornix 已提交
658
		readonly label: string,
R
rebornix 已提交
659 660 661
		readonly selectors: (string | IRelativePattern)[],
		readonly extension: ExtensionIdentifier,
		readonly extensionLocation: URI,
662 663
		readonly preloads: URI[],
		readonly logService: ILogService
R
rebornix 已提交
664 665 666
	) {
	}

R
Rob Lourens 已提交
667
	async executeNotebook(viewType: string, uri: URI, handle: number | undefined): Promise<void> {
668
		this.logService.debug('MainThreadNotebookKernel#executeNotebook', uri.path, handle);
R
Rob Lourens 已提交
669
		return this._proxy.$executeNotebook2(this.id, viewType, uri, handle);
R
rebornix 已提交
670 671
	}
}
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692

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