notebookServiceImpl.ts 35.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 { flatten } from 'vs/base/common/arrays';
R
rebornix 已提交
7
import { CancellationToken } from 'vs/base/common/cancellation';
8 9 10
import { Emitter, Event } from 'vs/base/common/event';
import { Iterable } from 'vs/base/common/iterator';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
J
Johannes Rieken 已提交
11
import { ResourceMap } from 'vs/base/common/map';
12
import { Schemas } from 'vs/base/common/network';
13
import { URI } from 'vs/base/common/uri';
R
rebornix 已提交
14
import * as UUID from 'vs/base/common/uuid';
R
rebornix 已提交
15
import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions';
16
import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard';
17 18
import * as nls from 'vs/nls';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
19
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
20 21 22 23 24 25 26 27
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.protocol';
import { Memento } from 'vs/workbench/common/memento';
import { INotebookEditorContribution, notebookProviderExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
import { getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRegistry, updateNotebookKernelProvideAssociationSchema } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation';
28
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
29 30
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
R
rebornix 已提交
31
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellEditType, CellKind, CellOutputKind, DisplayOrderKey, ICellEditOperation, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellOutputsSplice, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
32 33 34 35 36 37
import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer';
import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { ICustomEditorInfo, ICustomEditorViewTypesHandler, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
38

R
rebornix 已提交
39 40 41 42 43 44 45 46 47 48 49 50
export class NotebookKernelProviderInfoStore extends Disposable {
	private readonly _notebookKernelProviders: INotebookKernelProvider[] = [];

	constructor() {
		super();
	}

	add(provider: INotebookKernelProvider) {
		this._notebookKernelProviders.push(provider);
		this._updateProviderExtensionsInfo();

		return toDisposable(() => {
R
Rob Lourens 已提交
51
			const idx = this._notebookKernelProviders.indexOf(provider);
R
rebornix 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
			if (idx >= 0) {
				this._notebookKernelProviders.splice(idx, 1);
			}

			this._updateProviderExtensionsInfo();
		});
	}

	get(viewType: string, resource: URI) {
		return this._notebookKernelProviders.filter(provider => notebookDocumentFilterMatch(provider.selector, viewType, resource));
	}

	private _updateProviderExtensionsInfo() {
		NotebookKernelProviderAssociationRegistry.extensionIds.length = 0;
		NotebookKernelProviderAssociationRegistry.extensionDescriptions.length = 0;

		this._notebookKernelProviders.forEach(provider => {
			NotebookKernelProviderAssociationRegistry.extensionIds.push(provider.providerExtensionId);
			NotebookKernelProviderAssociationRegistry.extensionDescriptions.push(provider.providerDescription || '');
		});

		updateNotebookKernelProvideAssociationSchema();
	}
}

77
export class NotebookProviderInfoStore extends Disposable {
78 79 80 81
	private static readonly CUSTOM_EDITORS_STORAGE_ID = 'notebookEditors';
	private static readonly CUSTOM_EDITORS_ENTRY_ID = 'editors';

	private readonly _memento: Memento;
82 83 84 85 86 87 88
	private _handled: boolean = false;
	constructor(
		storageService: IStorageService,
		extensionService: IExtensionService

	) {
		super();
89 90 91 92 93 94
		this._memento = new Memento(NotebookProviderInfoStore.CUSTOM_EDITORS_STORAGE_ID, storageService);

		const mementoObject = this._memento.getMemento(StorageScope.GLOBAL);
		for (const info of (mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] || []) as NotebookEditorDescriptor[]) {
			this.add(new NotebookProviderInfo(info));
		}
95

R
rebornix 已提交
96 97
		this._updateProviderExtensionsInfo();

98 99 100 101 102 103 104
		this._register(extensionService.onDidRegisterExtensions(() => {
			if (!this._handled) {
				// there is no extension point registered for notebook content provider
				// clear the memento and cache
				this.clear();
				mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = [];
				this._memento.saveMemento();
R
rebornix 已提交
105 106

				this._updateProviderExtensionsInfo();
107 108
			}
		}));
109 110
	}

111 112
	setupHandler(extensions: readonly IExtensionPointUser<INotebookEditorContribution[]>[]) {
		this._handled = true;
113 114 115 116 117 118 119
		this.clear();

		for (const extension of extensions) {
			for (const notebookContribution of extension.value) {
				this.add(new NotebookProviderInfo({
					id: notebookContribution.viewType,
					displayName: notebookContribution.displayName,
R
rebornix 已提交
120
					selectors: notebookContribution.selector || [],
R
rebornix 已提交
121
					priority: this._convertPriority(notebookContribution.priority),
R
rebornix 已提交
122 123
					providerExtensionId: extension.description.identifier.value,
					providerDescription: extension.description.description,
124
					providerDisplayName: extension.description.isBuiltin ? nls.localize('builtinProviderDisplayName', "Built-in") : extension.description.displayName || extension.description.identifier.value,
125 126 127
					providerExtensionLocation: extension.description.extensionLocation,
					dynamicContribution: false,
					exclusive: false
128 129 130 131 132
				}));
			}
		}

		const mementoObject = this._memento.getMemento(StorageScope.GLOBAL);
R
rebornix 已提交
133
		mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values());
134
		this._memento.saveMemento();
R
rebornix 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

		this._updateProviderExtensionsInfo();
	}

	private _updateProviderExtensionsInfo() {
		NotebookViewTypesExtensionRegistry.viewTypes.length = 0;
		NotebookViewTypesExtensionRegistry.viewTypeDescriptions.length = 0;

		for (const contribute of this._contributedEditors) {
			if (contribute[1].providerExtensionId) {
				NotebookViewTypesExtensionRegistry.viewTypes.push(contribute[1].id);
				NotebookViewTypesExtensionRegistry.viewTypeDescriptions.push(`${contribute[1].displayName}`);
			}
		}

		updateNotebookKernelProvideAssociationSchema();
151 152
	}

R
rebornix 已提交
153
	private _convertPriority(priority?: string) {
154 155 156 157 158 159 160 161 162 163 164 165
		if (!priority) {
			return NotebookEditorPriority.default;
		}

		if (priority === NotebookEditorPriority.default) {
			return NotebookEditorPriority.default;
		}

		return NotebookEditorPriority.option;

	}

R
rebornix 已提交
166
	private readonly _contributedEditors = new Map<string, NotebookProviderInfo>();
R
rebornix 已提交
167

R
rebornix 已提交
168
	clear() {
R
rebornix 已提交
169
		this._contributedEditors.clear();
R
rebornix 已提交
170 171
	}

R
rebornix 已提交
172
	get(viewType: string): NotebookProviderInfo | undefined {
R
rebornix 已提交
173
		return this._contributedEditors.get(viewType);
R
rebornix 已提交
174 175
	}

R
rebornix 已提交
176
	add(info: NotebookProviderInfo): void {
R
rebornix 已提交
177
		if (this._contributedEditors.has(info.id)) {
R
rebornix 已提交
178 179
			return;
		}
R
rebornix 已提交
180
		this._contributedEditors.set(info.id, info);
181 182 183 184

		const mementoObject = this._memento.getMemento(StorageScope.GLOBAL);
		mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values());
		this._memento.saveMemento();
R
rebornix 已提交
185 186
	}

R
rebornix 已提交
187
	getContributedNotebook(resource: URI): readonly NotebookProviderInfo[] {
R
rebornix 已提交
188
		return [...Iterable.filter(this._contributedEditors.values(), customEditor => resource.scheme === 'untitled' || customEditor.matches(resource))];
R
rebornix 已提交
189
	}
190 191

	public [Symbol.iterator](): Iterator<NotebookProviderInfo> {
R
rebornix 已提交
192
		return this._contributedEditors.values();
193
	}
R
rebornix 已提交
194 195
}

R
rebornix 已提交
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
export class NotebookOutputRendererInfoStore {
	private readonly contributedRenderers = new Map<string, NotebookOutputRendererInfo>();

	clear() {
		this.contributedRenderers.clear();
	}

	get(viewType: string): NotebookOutputRendererInfo | undefined {
		return this.contributedRenderers.get(viewType);
	}

	add(info: NotebookOutputRendererInfo): void {
		if (this.contributedRenderers.has(info.id)) {
			return;
		}
		this.contributedRenderers.set(info.id, info);
	}

	getContributedRenderer(mimeType: string): readonly NotebookOutputRendererInfo[] {
		return Array.from(this.contributedRenderers.values()).filter(customEditor =>
			customEditor.matches(mimeType));
	}
}

R
rebornix 已提交
220 221 222 223
class ModelData implements IDisposable {
	private readonly _modelEventListeners = new DisposableStore();

	constructor(
224 225
		public model: NotebookTextModel,
		onWillDispose: (model: INotebookTextModel) => void
R
rebornix 已提交
226 227 228 229
	) {
		this._modelEventListeners.add(model.onWillDispose(() => onWillDispose(model)));
	}

R
rebornix 已提交
230
	dispose(): void {
R
rebornix 已提交
231 232 233
		this._modelEventListeners.dispose();
	}
}
234
export class NotebookService extends Disposable implements INotebookService, ICustomEditorViewTypesHandler {
235
	declare readonly _serviceBrand: undefined;
R
rebornix 已提交
236
	private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, extensionData: NotebookExtensionDescription }>();
237
	notebookProviderInfoStore: NotebookProviderInfoStore;
R
rebornix 已提交
238
	notebookRenderersInfoStore: NotebookOutputRendererInfoStore = new NotebookOutputRendererInfoStore();
R
rebornix 已提交
239
	notebookKernelProviderInfoStore: NotebookKernelProviderInfoStore = new NotebookKernelProviderInfoStore();
J
Johannes Rieken 已提交
240
	private readonly _models = new ResourceMap<ModelData>();
R
rebornix 已提交
241 242
	private _onDidChangeActiveEditor = new Emitter<string | null>();
	onDidChangeActiveEditor: Event<string | null> = this._onDidChangeActiveEditor.event;
R
rebornix 已提交
243
	private _activeEditorDisposables = new DisposableStore();
R
rebornix 已提交
244 245
	private _onDidChangeVisibleEditors = new Emitter<string[]>();
	onDidChangeVisibleEditors: Event<string[]> = this._onDidChangeVisibleEditors.event;
R
rebornix 已提交
246 247
	private readonly _onNotebookEditorAdd: Emitter<INotebookEditor> = this._register(new Emitter<INotebookEditor>());
	public readonly onNotebookEditorAdd: Event<INotebookEditor> = this._onNotebookEditorAdd.event;
248 249
	private readonly _onNotebookEditorsRemove: Emitter<INotebookEditor[]> = this._register(new Emitter<INotebookEditor[]>());
	public readonly onNotebookEditorsRemove: Event<INotebookEditor[]> = this._onNotebookEditorsRemove.event;
250 251 252 253 254 255

	private readonly _onDidAddNotebookDocument = this._register(new Emitter<NotebookTextModel>());
	private readonly _onDidRemoveNotebookDocument = this._register(new Emitter<URI>());
	readonly onDidAddNotebookDocument = this._onDidAddNotebookDocument.event;
	readonly onDidRemoveNotebookDocument = this._onDidRemoveNotebookDocument.event;

256 257
	private readonly _onNotebookDocumentSaved: Emitter<URI> = this._register(new Emitter<URI>());
	public readonly onNotebookDocumentSaved: Event<URI> = this._onNotebookDocumentSaved.event;
258
	private readonly _notebookEditors = new Map<string, INotebookEditor>();
R
rebornix 已提交
259

260 261
	private readonly _onDidChangeViewTypes = new Emitter<void>();
	onDidChangeViewTypes: Event<void> = this._onDidChangeViewTypes.event;
R
rebornix 已提交
262

R
rebornix 已提交
263 264
	private readonly _onDidChangeKernels = new Emitter<URI | undefined>();
	onDidChangeKernels: Event<URI | undefined> = this._onDidChangeKernels.event;
R
rebornix 已提交
265 266
	private readonly _onDidChangeNotebookActiveKernel = new Emitter<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }>();
	onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelId: string | undefined }> = this._onDidChangeNotebookActiveKernel.event;
R
rebornix 已提交
267
	private cutItems: NotebookCellTextModel[] | undefined;
R
rebornix 已提交
268
	private _lastClipboardIsCopy: boolean = true;
269

270
	private _displayOrder: { userOrder: string[], defaultOrder: string[] } = Object.create(null);
R
rebornix 已提交
271
	private readonly _decorationOptionProviders = new Map<string, INotebookDecorationRenderOptions>();
272

273
	constructor(
R
rebornix 已提交
274 275 276 277
		@IExtensionService private readonly _extensionService: IExtensionService,
		@IEditorService private readonly _editorService: IEditorService,
		@IConfigurationService private readonly _configurationService: IConfigurationService,
		@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
278 279
		@IStorageService private readonly _storageService: IStorageService,
		@IInstantiationService private readonly _instantiationService: IInstantiationService
280
	) {
R
rebornix 已提交
281 282
		super();

283
		this.notebookProviderInfoStore = new NotebookProviderInfoStore(this._storageService, this._extensionService);
284
		this._register(this.notebookProviderInfoStore);
285

R
rebornix 已提交
286
		notebookProviderExtensionPoint.setHandler((extensions) => {
287
			this.notebookProviderInfoStore.setupHandler(extensions);
R
rebornix 已提交
288 289
		});

R
rebornix 已提交
290 291 292 293 294
		notebookRendererExtensionPoint.setHandler((renderers) => {
			this.notebookRenderersInfoStore.clear();

			for (const extension of renderers) {
				for (const notebookContribution of extension.value) {
295 296 297 298 299
					if (!notebookContribution.entrypoint) { // avoid crashing
						console.error(`Cannot register renderer for ${extension.description.identifier.value} since it did not have an entrypoint. This is now required: https://github.com/microsoft/vscode/issues/102644`);
						continue;
					}

300 301 302 303 304 305
					const id = notebookContribution.id ?? notebookContribution.viewType;
					if (!id) {
						console.error(`Notebook renderer from ${extension.description.identifier.value} is missing an 'id'`);
						continue;
					}

R
rebornix 已提交
306
					this.notebookRenderersInfoStore.add(new NotebookOutputRendererInfo({
307
						id,
308 309
						extension: extension.description,
						entrypoint: notebookContribution.entrypoint,
R
rebornix 已提交
310
						displayName: notebookContribution.displayName,
311
						mimeTypes: notebookContribution.mimeTypes || [],
R
rebornix 已提交
312 313 314 315
					}));
				}
			}
		});
316

R
rebornix 已提交
317
		this._editorService.registerCustomEditorViewTypesHandler('Notebook', this);
318 319

		const updateOrder = () => {
320
			const userOrder = this._configurationService.getValue<string[]>(DisplayOrderKey);
321
			this._displayOrder = {
R
rebornix 已提交
322
				defaultOrder: this._accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER,
323 324 325 326 327 328
				userOrder: userOrder
			};
		};

		updateOrder();

R
rebornix 已提交
329
		this._register(this._configurationService.onDidChangeConfiguration(e => {
330
			if (e.affectedKeys.indexOf(DisplayOrderKey) >= 0) {
331 332 333 334
				updateOrder();
			}
		}));

R
rebornix 已提交
335
		this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => {
336 337
			updateOrder();
		}));
R
rebornix 已提交
338

339
		const getContext = () => {
R
rebornix 已提交
340
			const editor = getActiveNotebookEditor(this._editorService);
341 342 343 344 345 346 347 348 349 350 351
			const activeCell = editor?.getActiveCell();

			return {
				editor,
				activeCell
			};
		};

		const PRIORITY = 50;
		this._register(UndoCommand.addImplementation(PRIORITY, () => {
			const { editor } = getContext();
R
rebornix 已提交
352
			if (editor?.viewModel) {
R
rebornix 已提交
353 354 355 356 357
				editor?.viewModel.undo().then(cellResources => {
					if (cellResources?.length) {
						editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] } }));
					}
				});
R
rebornix 已提交
358 359 360 361 362 363 364
				return true;
			}

			return false;
		}));

		this._register(RedoCommand.addImplementation(PRIORITY, () => {
365
			const { editor } = getContext();
R
rebornix 已提交
366
			if (editor?.viewModel) {
R
rebornix 已提交
367 368 369 370 371
				editor?.viewModel.redo().then(cellResources => {
					if (cellResources?.length) {
						editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] } }));
					}
				});
R
rebornix 已提交
372 373 374 375 376
				return true;
			}

			return false;
		}));
377 378 379

		if (CopyAction) {
			this._register(CopyAction.addImplementation(PRIORITY, accessor => {
R
rebornix 已提交
380 381 382 383 384
				const activeElement = <HTMLElement>document.activeElement;
				if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
					return false;
				}

385 386 387 388 389
				const { editor, activeCell } = getContext();
				if (!editor || !activeCell) {
					return false;
				}

R
rebornix 已提交
390 391 392 393 394
				if (editor.hasOutputTextSelection()) {
					document.execCommand('copy');
					return true;
				}

395 396 397 398 399 400 401 402 403 404 405
				const clipboardService = accessor.get<IClipboardService>(IClipboardService);
				const notebookService = accessor.get<INotebookService>(INotebookService);
				clipboardService.writeText(activeCell.getText());
				notebookService.setToCopy([activeCell.model], true);

				return true;
			}));
		}

		if (PasteAction) {
			PasteAction.addImplementation(PRIORITY, () => {
R
rebornix 已提交
406 407 408 409 410
				const activeElement = <HTMLElement>document.activeElement;
				if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
					return false;
				}

411 412 413 414 415 416 417
				const pasteCells = this.getToCopy();

				if (!pasteCells) {
					return false;
				}

				const { editor, activeCell } = getContext();
R
rebornix 已提交
418
				if (!editor) {
419 420 421 422 423 424 425 426 427
					return false;
				}

				const viewModel = editor.viewModel;

				if (!viewModel) {
					return false;
				}

R
rebornix 已提交
428 429 430 431
				if (!viewModel.metadata.editable) {
					return false;
				}

R
rebornix 已提交
432 433 434 435 436
				if (activeCell) {
					const currCellIndex = viewModel.getCellIndex(activeCell);

					let topPastedCell: CellViewModel | undefined = undefined;
					pasteCells.items.reverse().map(cell => {
437 438 439 440
						return {
							source: cell.getValue(),
							language: cell.language,
							cellKind: cell.cellKind,
R
rebornix 已提交
441 442 443 444 445 446 447 448 449 450
							outputs: cell.outputs.map(output => {
								if (output.outputKind === CellOutputKind.Rich) {
									return {
										...output,
										outputId: UUID.generateUuid()
									};
								}

								return output;
							}),
451 452 453 454 455 456 457 458 459 460
							metadata: {
								editable: cell.metadata?.editable,
								runnable: cell.metadata?.runnable,
								breakpointMargin: cell.metadata?.breakpointMargin,
								hasExecutionOrder: cell.metadata?.hasExecutionOrder,
								inputCollapsed: cell.metadata?.inputCollapsed,
								outputCollapsed: cell.metadata?.outputCollapsed,
								custom: cell.metadata?.custom
							}
						};
R
rebornix 已提交
461 462
					}).forEach(pasteCell => {
						const newIdx = typeof currCellIndex === 'number' ? currCellIndex + 1 : 0;
463
						topPastedCell = viewModel.createCell(newIdx, pasteCell.source, pasteCell.language, pasteCell.cellKind, pasteCell.metadata, pasteCell.outputs, true);
R
rebornix 已提交
464 465 466 467 468 469 470 471
					});

					if (topPastedCell) {
						editor.focusNotebookCell(topPastedCell, 'container');
					}
				} else {
					if (viewModel.length !== 0) {
						return false;
472 473
					}

R
rebornix 已提交
474 475
					let topPastedCell: CellViewModel | undefined = undefined;
					pasteCells.items.reverse().map(cell => {
476 477 478 479
						return {
							source: cell.getValue(),
							language: cell.language,
							cellKind: cell.cellKind,
R
rebornix 已提交
480 481 482 483 484 485 486 487 488 489
							outputs: cell.outputs.map(output => {
								if (output.outputKind === CellOutputKind.Rich) {
									return {
										...output,
										outputId: UUID.generateUuid()
									};
								}

								return output;
							}),
490 491 492 493 494 495 496 497 498 499
							metadata: {
								editable: cell.metadata?.editable,
								runnable: cell.metadata?.runnable,
								breakpointMargin: cell.metadata?.breakpointMargin,
								hasExecutionOrder: cell.metadata?.hasExecutionOrder,
								inputCollapsed: cell.metadata?.inputCollapsed,
								outputCollapsed: cell.metadata?.outputCollapsed,
								custom: cell.metadata?.custom
							}
						};
R
rebornix 已提交
500
					}).forEach(pasteCell => {
501
						topPastedCell = viewModel.createCell(0, pasteCell.source, pasteCell.language, pasteCell.cellKind, pasteCell.metadata, pasteCell.outputs, true);
R
rebornix 已提交
502 503 504 505 506
					});

					if (topPastedCell) {
						editor.focusNotebookCell(topPastedCell, 'container');
					}
507 508
				}

R
rebornix 已提交
509

510 511 512 513 514 515
				return true;
			});
		}

		if (CutAction) {
			CutAction.addImplementation(PRIORITY, accessor => {
R
rebornix 已提交
516 517 518 519 520
				const activeElement = <HTMLElement>document.activeElement;
				if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
					return false;
				}

521 522 523 524 525 526 527 528 529 530 531
				const { editor, activeCell } = getContext();
				if (!editor || !activeCell) {
					return false;
				}

				const viewModel = editor.viewModel;

				if (!viewModel) {
					return false;
				}

R
rebornix 已提交
532 533 534 535
				if (!viewModel.metadata.editable) {
					return false;
				}

536 537 538 539 540 541 542 543 544 545
				const clipboardService = accessor.get<IClipboardService>(IClipboardService);
				const notebookService = accessor.get<INotebookService>(INotebookService);
				clipboardService.writeText(activeCell.getText());
				viewModel.deleteCell(viewModel.getCellIndex(activeCell), true);
				notebookService.setToCopy([activeCell.model], false);

				return true;
			});
		}

546 547
	}

R
rebornix 已提交
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565
	registerEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void {
		if (this._decorationOptionProviders.has(key)) {
			return;
		}

		this._decorationOptionProviders.set(key, options);
	}

	removeEditorDecorationType(key: string): void {
		this._decorationOptionProviders.delete(key);

		this.listNotebookEditors().forEach(editor => editor.removeEditorDecorations(key));
	}

	resolveEditorDecorationOptions(key: string): INotebookDecorationRenderOptions | undefined {
		return this._decorationOptionProviders.get(key);
	}

566 567 568 569 570 571
	getViewTypes(): ICustomEditorInfo[] {
		return [...this.notebookProviderInfoStore].map(info => ({
			id: info.id,
			displayName: info.displayName,
			providerDisplayName: info.providerDisplayName
		}));
R
rebornix 已提交
572 573
	}

574 575
	async canResolve(viewType: string): Promise<boolean> {
		if (!this._notebookProviders.has(viewType)) {
R
rebornix 已提交
576
			await this._extensionService.whenInstalledExtensionsRegistered();
577
			// notebook providers/kernels/renderers might use `*` as activation event.
578
			// TODO, only activate by `*` if this._notebookProviders.get(viewType).dynamicContribution === true
R
rebornix 已提交
579
			await this._extensionService.activateByEvent(`*`);
580
			// this awaits full activation of all matching extensions
581
			await this._extensionService.activateByEvent(`onNotebook:${viewType}`);
R
rebornix 已提交
582
		}
583
		return this._notebookProviders.has(viewType);
584 585
	}

586
	registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable {
R
rebornix 已提交
587
		this._notebookProviders.set(viewType, { extensionData, controller });
588 589 590

		if (controller.viewOptions && !this.notebookProviderInfoStore.get(viewType)) {
			// register this content provider to the static contribution, if it does not exist
591
			const info = new NotebookProviderInfo({
592 593 594
				displayName: controller.viewOptions.displayName,
				id: viewType,
				priority: NotebookEditorPriority.default,
595
				selectors: [],
596 597 598 599 600 601
				providerExtensionId: extensionData.id.value,
				providerDescription: extensionData.description,
				providerDisplayName: extensionData.id.value,
				providerExtensionLocation: URI.revive(extensionData.location),
				dynamicContribution: true,
				exclusive: controller.viewOptions.exclusive
602 603 604 605 606
			});

			info.update({ selectors: controller.viewOptions.filenamePattern });
			info.update({ options: controller.options });
			this.notebookProviderInfoStore.add(info);
607 608
		}

609 610
		this.notebookProviderInfoStore.get(viewType)?.update({ options: controller.options });

611
		this._onDidChangeViewTypes.fire();
612 613 614 615
		return toDisposable(() => {
			this._notebookProviders.delete(viewType);
			this._onDidChangeViewTypes.fire();
		});
R
rebornix 已提交
616 617
	}

R
rebornix 已提交
618
	registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable {
R
rebornix 已提交
619
		const d = this.notebookKernelProviderInfoStore.add(provider);
R
rebornix 已提交
620 621
		const kernelChangeEventListener = provider.onDidChangeKernels((e) => {
			this._onDidChangeKernels.fire(e);
R
rebornix 已提交
622
		});
623

R
rebornix 已提交
624
		this._onDidChangeKernels.fire(undefined);
R
rebornix 已提交
625
		return toDisposable(() => {
R
rebornix 已提交
626
			kernelChangeEventListener.dispose();
R
rebornix 已提交
627
			d.dispose();
R
rebornix 已提交
628 629 630 631
		});
	}

	async getContributedNotebookKernels2(viewType: string, resource: URI, token: CancellationToken): Promise<INotebookKernelInfo2[]> {
R
rebornix 已提交
632
		const filteredProvider = this.notebookKernelProviderInfoStore.get(viewType, resource);
R
rebornix 已提交
633 634 635 636 637 638 639
		const result = new Array<INotebookKernelInfo2[]>(filteredProvider.length);

		const promises = filteredProvider.map(async (provider, index) => {
			const data = await provider.provideKernels(resource, token);
			result[index] = data.map(dto => {
				return {
					extension: dto.extension,
R
rebornix 已提交
640
					extensionLocation: URI.revive(dto.extensionLocation),
R
rebornix 已提交
641 642 643
					id: dto.id,
					label: dto.label,
					description: dto.description,
R
rebornix 已提交
644
					detail: dto.detail,
R
rebornix 已提交
645 646
					isPreferred: dto.isPreferred,
					preloads: dto.preloads,
R
rebornix 已提交
647
					providerHandle: dto.providerHandle,
R
rebornix 已提交
648 649
					resolve: async (uri: URI, editorId: string, token: CancellationToken) => {
						return provider.resolveKernel(editorId, uri, dto.id, token);
R
rebornix 已提交
650
					},
R
Rob Lourens 已提交
651 652
					executeNotebookCell: async (uri: URI, handle: number | undefined) => {
						return provider.executeNotebook(uri, dto.id, handle);
653 654 655
					},
					cancelNotebookCell: (uri: URI, handle: number | undefined): Promise<void> => {
						return provider.cancelNotebook(uri, dto.id, handle);
R
rebornix 已提交
656 657 658 659 660 661 662 663 664
					}
				};
			});
		});

		await Promise.all(promises);

		return flatten(result);
	}
R
rebornix 已提交
665

666
	getRendererInfo(id: string): INotebookRendererInfo | undefined {
667
		return this.notebookRenderersInfoStore.get(id);
668 669
	}

670
	async resolveNotebook(viewType: string, uri: URI, forceReload: boolean, backupId?: string): Promise<NotebookTextModel> {
671

672
		if (!await this.canResolve(viewType)) {
673
			throw new Error(`CANNOT load notebook, no provider for '${viewType}'`);
R
rebornix 已提交
674 675
		}

676 677
		const provider = this._notebookProviders.get(viewType)!;
		let notebookModel: NotebookTextModel;
J
Johannes Rieken 已提交
678
		if (this._models.has(uri)) {
679
			// the model already exists
J
Johannes Rieken 已提交
680
			notebookModel = this._models.get(uri)!.model;
681 682 683
			if (forceReload) {
				await provider.controller.reloadNotebook(notebookModel);
			}
684
			return notebookModel;
685

686
		} else {
687 688 689 690 691 692 693 694 695 696
			const dataDto = await provider.controller.resolveNotebookDocument(viewType, uri, backupId);
			let cells = dataDto.data.cells.length ? dataDto.data.cells : (uri.scheme === Schemas.untitled ? [{
				cellKind: CellKind.Code,
				language: dataDto.data.languages.length ? dataDto.data.languages[0] : '',
				outputs: [],
				metadata: undefined,
				source: ''
			}] : []);

			notebookModel = this._instantiationService.createInstance(NotebookTextModel, viewType, provider.controller.supportBackup, uri, cells, dataDto.data.languages, dataDto.data.metadata, dataDto.transientOptions);
R
rebornix 已提交
697 698
		}

R
rebornix 已提交
699 700
		// new notebook model created
		const modelData = new ModelData(
701
			notebookModel,
702
			(model) => this._onWillDisposeDocument(model),
R
rebornix 已提交
703 704
		);

J
Johannes Rieken 已提交
705
		this._models.set(uri, modelData);
706
		this._onDidAddNotebookDocument.fire(notebookModel);
707
		// after the document is added to the store and sent to ext host, we transform the ouputs
708
		await this.transformTextModelOutputs(notebookModel);
709

710
		return modelData.model;
R
rebornix 已提交
711 712
	}

713
	getNotebookTextModel(uri: URI): NotebookTextModel | undefined {
J
Johannes Rieken 已提交
714
		return this._models.get(uri)?.model;
715 716
	}

717 718 719 720
	getNotebookTextModels(): Iterable<NotebookTextModel> {
		return Iterable.map(this._models.values(), data => data.model);
	}

721
	private async transformTextModelOutputs(textModel: NotebookTextModel) {
722 723 724
		for (let i = 0; i < textModel.cells.length; i++) {
			const cell = textModel.cells[i];

725
			cell.outputs.forEach((output) => {
726
				if (output.outputKind === CellOutputKind.Rich) {
727
					// TODO@rebornix no string[] casting
728
					const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []);
729 730 731 732 733 734 735 736 737
					const orderedMimeTypes = ret.orderedMimeTypes!;
					const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!;
					output.pickedMimeTypeIndex = pickedMimeTypeIndex;
					output.orderedMimeTypes = orderedMimeTypes;
				}
			});
		}
	}

738 739
	transformEditsOutputs(textModel: NotebookTextModel, edits: ICellEditOperation[]) {
		edits.forEach((edit) => {
740
			if (edit.editType === CellEditType.Replace) {
741
				edit.cells.forEach((cell) => {
742
					const outputs = cell.outputs;
743
					outputs.map((output) => {
744
						if (output.outputKind === CellOutputKind.Rich) {
745
							const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []);
746 747 748 749 750 751 752
							const orderedMimeTypes = ret.orderedMimeTypes!;
							const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!;
							output.pickedMimeTypeIndex = pickedMimeTypeIndex;
							output.orderedMimeTypes = orderedMimeTypes;
						}
					});
				});
753 754 755 756 757 758 759 760 761 762
			} else if (edit.editType === CellEditType.Output) {
				edit.outputs.map((output) => {
					if (output.outputKind === CellOutputKind.Rich) {
						const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []);
						const orderedMimeTypes = ret.orderedMimeTypes!;
						const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!;
						output.pickedMimeTypeIndex = pickedMimeTypeIndex;
						output.orderedMimeTypes = orderedMimeTypes;
					}
				});
763 764 765 766
			}
		});
	}

767 768
	transformSpliceOutputs(textModel: NotebookTextModel, splices: NotebookCellOutputsSplice[]) {
		splices.forEach((splice) => {
769
			const outputs = splice[2];
770
			outputs.map((output) => {
771
				if (output.outputKind === CellOutputKind.Rich) {
772
					const ret = this._transformMimeTypes(output, output.outputId, textModel.metadata.displayOrder as string[] || []);
773 774 775 776 777 778 779
					const orderedMimeTypes = ret.orderedMimeTypes!;
					const pickedMimeTypeIndex = ret.pickedMimeTypeIndex!;
					output.pickedMimeTypeIndex = pickedMimeTypeIndex;
					output.orderedMimeTypes = orderedMimeTypes;
				}
			});
		});
780 781
	}

782
	private _transformMimeTypes(output: IDisplayOutput, outputId: string, documentDisplayOrder: string[]): ITransformedDisplayOutputDto {
R
Rob Lourens 已提交
783 784
		const mimeTypes = Object.keys(output.data);
		const coreDisplayOrder = this._displayOrder;
785 786
		const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], documentDisplayOrder, coreDisplayOrder?.defaultOrder || []);

R
Rob Lourens 已提交
787
		const orderMimeTypes: IOrderedMimeType[] = [];
788 789

		sorted.forEach(mimeType => {
R
Rob Lourens 已提交
790
			const handlers = this._findBestMatchedRenderer(mimeType);
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809

			if (handlers.length) {
				const handler = handlers[0];

				orderMimeTypes.push({
					mimeType: mimeType,
					rendererId: handler.id,
				});

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

				if (mimeTypeSupportedByCore(mimeType)) {
					orderMimeTypes.push({
						mimeType: mimeType,
810
						rendererId: BUILTIN_RENDERER_ID
811 812 813 814 815
					});
				}
			} else {
				orderMimeTypes.push({
					mimeType: mimeType,
816
					rendererId: BUILTIN_RENDERER_ID
817 818 819 820 821 822
				});
			}
		});

		return {
			outputKind: output.outputKind,
823
			outputId,
824 825 826 827 828 829
			data: output.data,
			orderedMimeTypes: orderMimeTypes,
			pickedMimeTypeIndex: 0
		};
	}

830
	private _findBestMatchedRenderer(mimeType: string): readonly NotebookOutputRendererInfo[] {
831 832 833
		return this.notebookRenderersInfoStore.getContributedRenderer(mimeType);
	}

834 835 836 837 838 839
	getContributedNotebookProviders(resource?: URI): readonly NotebookProviderInfo[] {
		if (resource) {
			return this.notebookProviderInfoStore.getContributedNotebook(resource);
		}

		return [...this.notebookProviderInfoStore];
R
rebornix 已提交
840
	}
R
rebornix 已提交
841

R
rebornix 已提交
842 843 844 845
	getContributedNotebookProvider(viewType: string): NotebookProviderInfo | undefined {
		return this.notebookProviderInfoStore.get(viewType);
	}

R
rebornix 已提交
846 847
	getContributedNotebookOutputRenderers(viewType: string): NotebookOutputRendererInfo | undefined {
		return this.notebookRenderersInfoStore.get(viewType);
R
rebornix 已提交
848 849
	}

R
rebornix 已提交
850
	getNotebookProviderResourceRoots(): URI[] {
R
Rob Lourens 已提交
851
		const ret: URI[] = [];
R
rebornix 已提交
852 853 854 855 856 857
		this._notebookProviders.forEach(val => {
			ret.push(URI.revive(val.extensionData.location));
		});

		return ret;
	}
R
rebornix 已提交
858

859 860 861 862 863 864 865
	async resolveNotebookEditor(viewType: string, uri: URI, editorId: string): Promise<void> {
		const entry = this._notebookProviders.get(viewType);
		if (entry) {
			entry.controller.resolveNotebookEditor(viewType, uri, editorId);
		}
	}

R
rebornix 已提交
866
	removeNotebookEditor(editor: INotebookEditor) {
R
Rob Lourens 已提交
867
		const editorCache = this._notebookEditors.get(editor.getId());
868 869 870 871

		if (editorCache) {
			this._notebookEditors.delete(editor.getId());
			this._onNotebookEditorsRemove.fire([editor]);
R
rebornix 已提交
872 873 874 875
		}
	}

	addNotebookEditor(editor: INotebookEditor) {
876
		this._notebookEditors.set(editor.getId(), editor);
R
rebornix 已提交
877 878 879
		this._onNotebookEditorAdd.fire(editor);
	}

880 881 882 883
	getNotebookEditor(editorId: string) {
		return this._notebookEditors.get(editorId);
	}

R
rebornix 已提交
884
	listNotebookEditors(): INotebookEditor[] {
885 886 887 888
		return [...this._notebookEditors].map(e => e[1]);
	}

	listVisibleNotebookEditors(): INotebookEditor[] {
R
rebornix 已提交
889
		return this._editorService.visibleEditorPanes
R
rebornix 已提交
890
			.filter(pane => (pane as unknown as { isNotebookEditor?: boolean }).isNotebookEditor)
891 892 893
			.map(pane => pane.getControl() as INotebookEditor)
			.filter(editor => !!editor)
			.filter(editor => this._notebookEditors.has(editor.getId()));
R
rebornix 已提交
894 895 896
	}

	listNotebookDocuments(): NotebookTextModel[] {
897
		return [...this._models].map(e => e[1].model);
R
rebornix 已提交
898 899
	}

900
	destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void {
901
		this._onWillDisposeDocument(notebook);
R
rebornix 已提交
902 903
	}

R
rebornix 已提交
904
	updateActiveNotebookEditor(editor: INotebookEditor | null) {
R
rebornix 已提交
905 906 907 908 909 910 911 912 913 914 915
		this._activeEditorDisposables.clear();

		if (editor) {
			this._activeEditorDisposables.add(editor.onDidChangeKernel(() => {
				this._onDidChangeNotebookActiveKernel.fire({
					uri: editor.uri!,
					providerHandle: editor.activeKernel?.providerHandle,
					kernelId: editor.activeKernel?.id
				});
			}));
		}
R
rebornix 已提交
916 917 918 919
		this._onDidChangeActiveEditor.fire(editor ? editor.getId() : null);
	}

	updateVisibleNotebookEditor(editors: string[]) {
920 921
		const alreadyCreated = editors.filter(editorId => this._notebookEditors.has(editorId));
		this._onDidChangeVisibleEditors.fire(alreadyCreated);
R
rebornix 已提交
922 923
	}

R
rebornix 已提交
924
	setToCopy(items: NotebookCellTextModel[], isCopy: boolean) {
R
rebornix 已提交
925
		this.cutItems = items;
R
rebornix 已提交
926
		this._lastClipboardIsCopy = isCopy;
R
rebornix 已提交
927 928
	}

R
rebornix 已提交
929 930 931 932 933 934
	getToCopy(): { items: NotebookCellTextModel[], isCopy: boolean; } | undefined {
		if (this.cutItems) {
			return { items: this.cutItems, isCopy: this._lastClipboardIsCopy };
		}

		return undefined;
R
rebornix 已提交
935 936
	}

R
rebornix 已提交
937
	async save(viewType: string, resource: URI, token: CancellationToken): Promise<boolean> {
R
Rob Lourens 已提交
938
		const provider = this._notebookProviders.get(viewType);
939 940

		if (provider) {
941 942 943 944 945 946
			const ret = await provider.controller.save(resource, token);
			if (ret) {
				this._onNotebookDocumentSaved.fire(resource);
			}

			return ret;
947 948 949 950 951
		}

		return false;
	}

R
saveAs  
rebornix 已提交
952
	async saveAs(viewType: string, resource: URI, target: URI, token: CancellationToken): Promise<boolean> {
R
Rob Lourens 已提交
953
		const provider = this._notebookProviders.get(viewType);
R
saveAs  
rebornix 已提交
954 955

		if (provider) {
956 957 958 959 960 961
			const ret = await provider.controller.saveAs(resource, target, token);
			if (ret) {
				this._onNotebookDocumentSaved.fire(resource);
			}

			return ret;
R
saveAs  
rebornix 已提交
962 963 964 965 966
		}

		return false;
	}

967
	async backup(viewType: string, uri: URI, token: CancellationToken): Promise<string | undefined> {
R
Rob Lourens 已提交
968
		const provider = this._notebookProviders.get(viewType);
969 970 971 972 973 974 975 976

		if (provider) {
			return provider.controller.backup(uri, token);
		}

		return;
	}

977
	onDidReceiveMessage(viewType: string, editorId: string, rendererType: string | undefined, message: any): void {
R
Rob Lourens 已提交
978
		const provider = this._notebookProviders.get(viewType);
979 980

		if (provider) {
981
			return provider.controller.onDidReceiveMessage(editorId, rendererType, message);
982 983 984
		}
	}

985
	private _onWillDisposeDocument(model: INotebookTextModel): void {
R
rebornix 已提交
986

J
Johannes Rieken 已提交
987 988
		const modelData = this._models.get(model.uri);
		this._models.delete(model.uri);
989 990 991 992 993 994 995 996 997 998

		if (modelData) {
			// delete editors and documents
			const willRemovedEditors: INotebookEditor[] = [];
			this._notebookEditors.forEach(editor => {
				if (editor.textModel === modelData!.model) {
					willRemovedEditors.push(editor);
				}
			});

999 1000
			modelData.model.dispose();
			modelData.dispose();
1001

1002
			willRemovedEditors.forEach(e => this._notebookEditors.delete(e.getId()));
1003
			this._onNotebookEditorsRemove.fire(willRemovedEditors.map(e => e));
1004
			this._onDidRemoveNotebookDocument.fire(modelData.model.uri);
1005
		}
R
rebornix 已提交
1006
	}
R
rebornix 已提交
1007
}