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

6
import { getZoomLevel } from 'vs/base/browser/browser';
7
import { flatten } from 'vs/base/common/arrays';
R
rebornix 已提交
8
import { CancellationToken } from 'vs/base/common/cancellation';
9 10 11
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 已提交
12
import { ResourceMap } from 'vs/base/common/map';
13
import { Schemas } from 'vs/base/common/network';
14
import { URI } from 'vs/base/common/uri';
R
rebornix 已提交
15
import * as UUID from 'vs/base/common/uuid';
R
rebornix 已提交
16
import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions';
17 18 19
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
20
import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/clipboard';
21 22
import * as nls from 'vs/nls';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
23
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
24 25
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
R
rebornix 已提交
26
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
27 28 29
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';
30
import { CellEditState, getActiveNotebookEditor, INotebookEditor, NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
31
import { NotebookKernelProviderAssociationRegistry, NotebookViewTypesExtensionRegistry, updateNotebookKernelProvideAssociationSchema } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation';
32
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
33 34
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
R
rebornix 已提交
35
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellKind, CellOutputKind, DisplayOrderKey, IDisplayOutput, INotebookDecorationRenderOptions, INotebookKernelInfo2, INotebookKernelProvider, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, ITransformedDisplayOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, notebookDocumentFilterMatch, NotebookEditorPriority, NOTEBOOK_DISPLAY_ORDER, RENDERER_NOT_AVAILABLE, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
36 37 38 39 40 41
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';
42

R
rebornix 已提交
43 44 45 46 47 48 49 50 51 52 53 54
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 已提交
55
			const idx = this._notebookKernelProviders.indexOf(provider);
R
rebornix 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
			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();
	}
}

81
export class NotebookProviderInfoStore extends Disposable {
82 83 84 85
	private static readonly CUSTOM_EDITORS_STORAGE_ID = 'notebookEditors';
	private static readonly CUSTOM_EDITORS_ENTRY_ID = 'editors';

	private readonly _memento: Memento;
86 87 88 89 90 91 92
	private _handled: boolean = false;
	constructor(
		storageService: IStorageService,
		extensionService: IExtensionService

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

R
rebornix 已提交
95
		const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
96 97 98
		for (const info of (mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] || []) as NotebookEditorDescriptor[]) {
			this.add(new NotebookProviderInfo(info));
		}
99

R
rebornix 已提交
100 101
		this._updateProviderExtensionsInfo();

102 103 104 105 106 107 108
		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 已提交
109 110

				this._updateProviderExtensionsInfo();
111 112
			}
		}));
113 114
	}

115 116
	setupHandler(extensions: readonly IExtensionPointUser<INotebookEditorContribution[]>[]) {
		this._handled = true;
117 118 119 120 121 122 123
		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 已提交
124
					selectors: notebookContribution.selector || [],
R
rebornix 已提交
125
					priority: this._convertPriority(notebookContribution.priority),
R
rebornix 已提交
126 127
					providerExtensionId: extension.description.identifier.value,
					providerDescription: extension.description.description,
128
					providerDisplayName: extension.description.isBuiltin ? nls.localize('builtinProviderDisplayName', "Built-in") : extension.description.displayName || extension.description.identifier.value,
129 130 131
					providerExtensionLocation: extension.description.extensionLocation,
					dynamicContribution: false,
					exclusive: false
132 133 134 135
				}));
			}
		}

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

		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();
155 156
	}

R
rebornix 已提交
157
	private _convertPriority(priority?: string) {
158 159 160 161 162 163 164 165 166 167 168 169
		if (!priority) {
			return NotebookEditorPriority.default;
		}

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

		return NotebookEditorPriority.option;

	}

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

R
rebornix 已提交
172
	clear() {
R
rebornix 已提交
173
		this._contributedEditors.clear();
R
rebornix 已提交
174 175
	}

R
rebornix 已提交
176
	get(viewType: string): NotebookProviderInfo | undefined {
R
rebornix 已提交
177
		return this._contributedEditors.get(viewType);
R
rebornix 已提交
178 179
	}

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

R
rebornix 已提交
186
		const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
187 188
		mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values());
		this._memento.saveMemento();
R
rebornix 已提交
189 190
	}

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

	public [Symbol.iterator](): Iterator<NotebookProviderInfo> {
R
rebornix 已提交
196
		return this._contributedEditors.values();
197
	}
R
rebornix 已提交
198 199
}

R
rebornix 已提交
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
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 已提交
224 225 226 227
class ModelData implements IDisposable {
	private readonly _modelEventListeners = new DisposableStore();

	constructor(
228 229
		public model: NotebookTextModel,
		onWillDispose: (model: INotebookTextModel) => void
R
rebornix 已提交
230 231 232 233
	) {
		this._modelEventListeners.add(model.onWillDispose(() => onWillDispose(model)));
	}

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

	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;

260 261
	private readonly _onNotebookDocumentSaved: Emitter<URI> = this._register(new Emitter<URI>());
	public readonly onNotebookDocumentSaved: Event<URI> = this._onNotebookDocumentSaved.event;
262
	private readonly _notebookEditors = new Map<string, INotebookEditor>();
R
rebornix 已提交
263

264 265
	private readonly _onDidChangeViewTypes = new Emitter<void>();
	onDidChangeViewTypes: Event<void> = this._onDidChangeViewTypes.event;
R
rebornix 已提交
266

R
rebornix 已提交
267 268
	private readonly _onDidChangeKernels = new Emitter<URI | undefined>();
	onDidChangeKernels: Event<URI | undefined> = this._onDidChangeKernels.event;
R
rebornix 已提交
269 270
	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 已提交
271
	private cutItems: NotebookCellTextModel[] | undefined;
R
rebornix 已提交
272
	private _lastClipboardIsCopy: boolean = true;
273

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

277
	constructor(
R
rebornix 已提交
278 279 280 281
		@IExtensionService private readonly _extensionService: IExtensionService,
		@IEditorService private readonly _editorService: IEditorService,
		@IConfigurationService private readonly _configurationService: IConfigurationService,
		@IAccessibilityService private readonly _accessibilityService: IAccessibilityService,
282
		@IStorageService private readonly _storageService: IStorageService,
283 284 285
		@IInstantiationService private readonly _instantiationService: IInstantiationService,
		@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
		@IConfigurationService private readonly configurationService: IConfigurationService
286
	) {
R
rebornix 已提交
287 288
		super();

289
		this.notebookProviderInfoStore = new NotebookProviderInfoStore(this._storageService, this._extensionService);
290
		this._register(this.notebookProviderInfoStore);
291

R
rebornix 已提交
292
		notebookProviderExtensionPoint.setHandler((extensions) => {
293
			this.notebookProviderInfoStore.setupHandler(extensions);
R
rebornix 已提交
294 295
		});

R
rebornix 已提交
296 297 298 299 300
		notebookRendererExtensionPoint.setHandler((renderers) => {
			this.notebookRenderersInfoStore.clear();

			for (const extension of renderers) {
				for (const notebookContribution of extension.value) {
301 302 303 304 305
					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;
					}

306 307 308 309 310 311
					const id = notebookContribution.id ?? notebookContribution.viewType;
					if (!id) {
						console.error(`Notebook renderer from ${extension.description.identifier.value} is missing an 'id'`);
						continue;
					}

R
rebornix 已提交
312
					this.notebookRenderersInfoStore.add(new NotebookOutputRendererInfo({
313
						id,
314 315
						extension: extension.description,
						entrypoint: notebookContribution.entrypoint,
R
rebornix 已提交
316
						displayName: notebookContribution.displayName,
317
						mimeTypes: notebookContribution.mimeTypes || [],
R
rebornix 已提交
318 319 320 321
					}));
				}
			}
		});
322

R
rebornix 已提交
323
		this._editorService.registerCustomEditorViewTypesHandler('Notebook', this);
324 325

		const updateOrder = () => {
326
			const userOrder = this._configurationService.getValue<string[]>(DisplayOrderKey);
327
			this._displayOrder = {
R
rebornix 已提交
328
				defaultOrder: this._accessibilityService.isScreenReaderOptimized() ? ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER : NOTEBOOK_DISPLAY_ORDER,
329 330 331 332 333 334
				userOrder: userOrder
			};
		};

		updateOrder();

R
rebornix 已提交
335
		this._register(this._configurationService.onDidChangeConfiguration(e => {
336
			if (e.affectedKeys.indexOf(DisplayOrderKey) >= 0) {
337 338 339 340
				updateOrder();
			}
		}));

R
rebornix 已提交
341
		this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => {
342 343
			updateOrder();
		}));
R
rebornix 已提交
344

345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
		let decorationTriggeredAdjustment = false;
		let decorationCheckSet = new Set<string>();
		this._register(this._codeEditorService.onDecorationTypeRegistered(e => {
			if (decorationTriggeredAdjustment) {
				return;
			}

			if (decorationCheckSet.has(e)) {
				return;
			}

			const options = this._codeEditorService.resolveDecorationOptions(e, true);
			if (options.afterContentClassName || options.beforeContentClassName) {
				const cssRules = this._codeEditorService.resolveDecorationCSSRules(e);
				if (cssRules !== null) {
					for (let i = 0; i < cssRules.length; i++) {
						// The following ways to index into the list are equivalent
						if (
							((cssRules[i] as CSSStyleRule).selectorText.endsWith('::after') || (cssRules[i] as CSSStyleRule).selectorText.endsWith('::after'))
							&& (cssRules[i] as CSSStyleRule).cssText.indexOf('top:') > -1
						) {
							// there is a `::before` or `::after` text decoration whose position is above or below current line
							// we at least make sure that the editor top padding is at least one line
							const editorOptions = this.configurationService.getValue<IEditorOptions>('editor');
							updateEditorTopPadding(BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel()).lineHeight + 2);
							decorationTriggeredAdjustment = true;
							break;
						}
					}
				}
			}

			decorationCheckSet.add(e);
		}));

380
		const getContext = () => {
R
rebornix 已提交
381
			const editor = getActiveNotebookEditor(this._editorService);
382 383 384 385 386 387 388 389 390 391 392
			const activeCell = editor?.getActiveCell();

			return {
				editor,
				activeCell
			};
		};

		const PRIORITY = 50;
		this._register(UndoCommand.addImplementation(PRIORITY, () => {
			const { editor } = getContext();
R
rebornix 已提交
393
			if (editor?.viewModel) {
A
Alex Dima 已提交
394
				return editor.viewModel.undo().then(cellResources => {
R
rebornix 已提交
395
					if (cellResources?.length) {
396 397 398 399 400 401 402
						editor?.viewModel?.viewCells.forEach(cell => {
							if (cell.cellKind === CellKind.Markdown && cellResources.find(resource => resource.fragment === cell.model.uri.fragment)) {
								cell.editState = CellEditState.Editing;
							}
						});

						editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] }, preserveFocus: true }));
R
rebornix 已提交
403 404
					}
				});
R
rebornix 已提交
405 406 407 408 409 410
			}

			return false;
		}));

		this._register(RedoCommand.addImplementation(PRIORITY, () => {
411
			const { editor } = getContext();
R
rebornix 已提交
412
			if (editor?.viewModel) {
A
Alex Dima 已提交
413
				return editor.viewModel.redo().then(cellResources => {
R
rebornix 已提交
414
					if (cellResources?.length) {
415 416 417 418 419 420 421
						editor?.viewModel?.viewCells.forEach(cell => {
							if (cell.cellKind === CellKind.Markdown && cellResources.find(resource => resource.fragment === cell.model.uri.fragment)) {
								cell.editState = CellEditState.Editing;
							}
						});

						editor?.setOptions(new NotebookEditorOptions({ cellOptions: { resource: cellResources[0] }, preserveFocus: true }));
R
rebornix 已提交
422 423
					}
				});
R
rebornix 已提交
424 425 426 427
			}

			return false;
		}));
428 429 430

		if (CopyAction) {
			this._register(CopyAction.addImplementation(PRIORITY, accessor => {
R
rebornix 已提交
431 432 433 434 435
				const activeElement = <HTMLElement>document.activeElement;
				if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
					return false;
				}

436 437 438 439 440
				const { editor, activeCell } = getContext();
				if (!editor || !activeCell) {
					return false;
				}

R
rebornix 已提交
441 442 443 444 445
				if (editor.hasOutputTextSelection()) {
					document.execCommand('copy');
					return true;
				}

446 447 448 449 450 451 452 453 454 455 456
				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 已提交
457 458 459 460 461
				const activeElement = <HTMLElement>document.activeElement;
				if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
					return false;
				}

462 463 464 465 466 467 468
				const pasteCells = this.getToCopy();

				if (!pasteCells) {
					return false;
				}

				const { editor, activeCell } = getContext();
R
rebornix 已提交
469
				if (!editor) {
470 471 472 473 474 475 476 477 478
					return false;
				}

				const viewModel = editor.viewModel;

				if (!viewModel) {
					return false;
				}

R
rebornix 已提交
479 480 481 482
				if (!viewModel.metadata.editable) {
					return false;
				}

R
rebornix 已提交
483 484 485 486 487
				if (activeCell) {
					const currCellIndex = viewModel.getCellIndex(activeCell);

					let topPastedCell: CellViewModel | undefined = undefined;
					pasteCells.items.reverse().map(cell => {
488 489 490 491
						return {
							source: cell.getValue(),
							language: cell.language,
							cellKind: cell.cellKind,
R
rebornix 已提交
492 493 494 495 496 497 498 499 500 501
							outputs: cell.outputs.map(output => {
								if (output.outputKind === CellOutputKind.Rich) {
									return {
										...output,
										outputId: UUID.generateUuid()
									};
								}

								return output;
							}),
502 503 504 505 506 507 508 509 510 511
							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 已提交
512 513
					}).forEach(pasteCell => {
						const newIdx = typeof currCellIndex === 'number' ? currCellIndex + 1 : 0;
514
						topPastedCell = viewModel.createCell(newIdx, pasteCell.source, pasteCell.language, pasteCell.cellKind, pasteCell.metadata, pasteCell.outputs, true);
R
rebornix 已提交
515 516 517 518 519 520 521 522
					});

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

R
rebornix 已提交
525 526
					let topPastedCell: CellViewModel | undefined = undefined;
					pasteCells.items.reverse().map(cell => {
527 528 529 530
						return {
							source: cell.getValue(),
							language: cell.language,
							cellKind: cell.cellKind,
R
rebornix 已提交
531 532 533 534 535 536 537 538 539 540
							outputs: cell.outputs.map(output => {
								if (output.outputKind === CellOutputKind.Rich) {
									return {
										...output,
										outputId: UUID.generateUuid()
									};
								}

								return output;
							}),
541 542 543 544 545 546 547 548 549 550
							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 已提交
551
					}).forEach(pasteCell => {
552
						topPastedCell = viewModel.createCell(0, pasteCell.source, pasteCell.language, pasteCell.cellKind, pasteCell.metadata, pasteCell.outputs, true);
R
rebornix 已提交
553 554 555 556 557
					});

					if (topPastedCell) {
						editor.focusNotebookCell(topPastedCell, 'container');
					}
558 559
				}

R
rebornix 已提交
560

561 562 563 564 565 566
				return true;
			});
		}

		if (CutAction) {
			CutAction.addImplementation(PRIORITY, accessor => {
R
rebornix 已提交
567 568 569 570 571
				const activeElement = <HTMLElement>document.activeElement;
				if (activeElement && ['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) >= 0) {
					return false;
				}

572 573 574 575 576 577 578 579 580 581 582
				const { editor, activeCell } = getContext();
				if (!editor || !activeCell) {
					return false;
				}

				const viewModel = editor.viewModel;

				if (!viewModel) {
					return false;
				}

R
rebornix 已提交
583 584 585 586
				if (!viewModel.metadata.editable) {
					return false;
				}

587 588 589 590 591 592 593 594 595 596
				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;
			});
		}

597 598
	}

R
rebornix 已提交
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
	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);
	}

617 618 619 620 621 622
	getViewTypes(): ICustomEditorInfo[] {
		return [...this.notebookProviderInfoStore].map(info => ({
			id: info.id,
			displayName: info.displayName,
			providerDisplayName: info.providerDisplayName
		}));
R
rebornix 已提交
623 624
	}

625 626
	async canResolve(viewType: string): Promise<boolean> {
		if (!this._notebookProviders.has(viewType)) {
R
rebornix 已提交
627
			await this._extensionService.whenInstalledExtensionsRegistered();
628
			// this awaits full activation of all matching extensions
629
			await this._extensionService.activateByEvent(`onNotebook:${viewType}`);
630 631 632 633 634 635 636
			if (this._notebookProviders.has(viewType)) {
				return true;
			} else {
				// notebook providers/kernels/renderers might use `*` as activation event.
				// TODO, only activate by `*` if this._notebookProviders.get(viewType).dynamicContribution === true
				await this._extensionService.activateByEvent(`*`);
			}
R
rebornix 已提交
637
		}
638
		return this._notebookProviders.has(viewType);
639 640
	}

641
	registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable {
R
rebornix 已提交
642
		this._notebookProviders.set(viewType, { extensionData, controller });
643 644 645

		if (controller.viewOptions && !this.notebookProviderInfoStore.get(viewType)) {
			// register this content provider to the static contribution, if it does not exist
646
			const info = new NotebookProviderInfo({
647 648 649
				displayName: controller.viewOptions.displayName,
				id: viewType,
				priority: NotebookEditorPriority.default,
650
				selectors: [],
651 652 653 654 655 656
				providerExtensionId: extensionData.id.value,
				providerDescription: extensionData.description,
				providerDisplayName: extensionData.id.value,
				providerExtensionLocation: URI.revive(extensionData.location),
				dynamicContribution: true,
				exclusive: controller.viewOptions.exclusive
657 658 659 660 661
			});

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

664 665
		this.notebookProviderInfoStore.get(viewType)?.update({ options: controller.options });

666
		this._onDidChangeViewTypes.fire();
667 668 669 670
		return toDisposable(() => {
			this._notebookProviders.delete(viewType);
			this._onDidChangeViewTypes.fire();
		});
R
rebornix 已提交
671 672
	}

R
rebornix 已提交
673
	registerNotebookKernelProvider(provider: INotebookKernelProvider): IDisposable {
R
rebornix 已提交
674
		const d = this.notebookKernelProviderInfoStore.add(provider);
R
rebornix 已提交
675 676
		const kernelChangeEventListener = provider.onDidChangeKernels((e) => {
			this._onDidChangeKernels.fire(e);
R
rebornix 已提交
677
		});
678

R
rebornix 已提交
679
		this._onDidChangeKernels.fire(undefined);
R
rebornix 已提交
680
		return toDisposable(() => {
R
rebornix 已提交
681
			kernelChangeEventListener.dispose();
R
rebornix 已提交
682
			d.dispose();
R
rebornix 已提交
683 684 685
		});
	}

R
rebornix 已提交
686
	async getContributedNotebookKernels(viewType: string, resource: URI, token: CancellationToken): Promise<INotebookKernelInfo2[]> {
R
rebornix 已提交
687
		const filteredProvider = this.notebookKernelProviderInfoStore.get(viewType, resource);
R
rebornix 已提交
688 689 690 691 692 693 694
		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 已提交
695
					extensionLocation: URI.revive(dto.extensionLocation),
R
rebornix 已提交
696 697 698
					id: dto.id,
					label: dto.label,
					description: dto.description,
R
rebornix 已提交
699
					detail: dto.detail,
R
rebornix 已提交
700 701
					isPreferred: dto.isPreferred,
					preloads: dto.preloads,
R
rebornix 已提交
702
					providerHandle: dto.providerHandle,
R
rebornix 已提交
703 704
					resolve: async (uri: URI, editorId: string, token: CancellationToken) => {
						return provider.resolveKernel(editorId, uri, dto.id, token);
R
rebornix 已提交
705
					},
R
Rob Lourens 已提交
706 707
					executeNotebookCell: async (uri: URI, handle: number | undefined) => {
						return provider.executeNotebook(uri, dto.id, handle);
708 709 710
					},
					cancelNotebookCell: (uri: URI, handle: number | undefined): Promise<void> => {
						return provider.cancelNotebook(uri, dto.id, handle);
R
rebornix 已提交
711 712 713 714 715 716 717 718 719
					}
				};
			});
		});

		await Promise.all(promises);

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

721
	getRendererInfo(id: string): INotebookRendererInfo | undefined {
722
		return this.notebookRenderersInfoStore.get(id);
723 724
	}

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

727
		if (!await this.canResolve(viewType)) {
728
			throw new Error(`CANNOT load notebook, no provider for '${viewType}'`);
R
rebornix 已提交
729 730
		}

731 732
		const provider = this._notebookProviders.get(viewType)!;
		let notebookModel: NotebookTextModel;
J
Johannes Rieken 已提交
733
		if (this._models.has(uri)) {
734
			// the model already exists
J
Johannes Rieken 已提交
735
			notebookModel = this._models.get(uri)!.model;
736 737 738
			if (forceReload) {
				await provider.controller.reloadNotebook(notebookModel);
			}
739
			return notebookModel;
740

741
		} else {
742 743 744 745 746 747 748 749 750 751
			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 已提交
752 753
		}

R
rebornix 已提交
754 755
		// new notebook model created
		const modelData = new ModelData(
756
			notebookModel,
757
			(model) => this._onWillDisposeDocument(model),
R
rebornix 已提交
758 759
		);

J
Johannes Rieken 已提交
760
		this._models.set(uri, modelData);
761
		this._onDidAddNotebookDocument.fire(notebookModel);
762

763
		return modelData.model;
R
rebornix 已提交
764 765
	}

766
	getNotebookTextModel(uri: URI): NotebookTextModel | undefined {
J
Johannes Rieken 已提交
767
		return this._models.get(uri)?.model;
768 769
	}

770 771 772 773
	getNotebookTextModels(): Iterable<NotebookTextModel> {
		return Iterable.map(this._models.values(), data => data.model);
	}

774 775
	getMimeTypeInfo(textModel: NotebookTextModel, output: ITransformedDisplayOutputDto): readonly IOrderedMimeType[] {
		// TODO@rebornix no string[] casting
R
rebornix 已提交
776
		return this._getOrderedMimeTypes(textModel, output, textModel.metadata.displayOrder as string[] ?? []);
777 778
	}

R
rebornix 已提交
779
	private _getOrderedMimeTypes(textModel: NotebookTextModel, output: IDisplayOutput, documentDisplayOrder: string[]): IOrderedMimeType[] {
R
Rob Lourens 已提交
780 781
		const mimeTypes = Object.keys(output.data);
		const coreDisplayOrder = this._displayOrder;
782 783
		const sorted = sortMimeTypes(mimeTypes, coreDisplayOrder?.userOrder || [], documentDisplayOrder, coreDisplayOrder?.defaultOrder || []);

R
Rob Lourens 已提交
784
		const orderMimeTypes: IOrderedMimeType[] = [];
785 786

		sorted.forEach(mimeType => {
R
Rob Lourens 已提交
787
			const handlers = this._findBestMatchedRenderer(mimeType);
788 789 790 791 792 793 794

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

				orderMimeTypes.push({
					mimeType: mimeType,
					rendererId: handler.id,
R
rebornix 已提交
795
					isTrusted: textModel.metadata.trusted
796 797 798 799 800
				});

				for (let i = 1; i < handlers.length; i++) {
					orderMimeTypes.push({
						mimeType: mimeType,
R
rebornix 已提交
801 802
						rendererId: handlers[i].id,
						isTrusted: textModel.metadata.trusted
803 804 805 806 807 808
					});
				}

				if (mimeTypeSupportedByCore(mimeType)) {
					orderMimeTypes.push({
						mimeType: mimeType,
R
rebornix 已提交
809 810
						rendererId: BUILTIN_RENDERER_ID,
						isTrusted: mimeTypeIsAlwaysSecure(mimeType) || textModel.metadata.trusted
811 812 813
					});
				}
			} else {
R
rebornix 已提交
814 815 816
				if (mimeTypeSupportedByCore(mimeType)) {
					orderMimeTypes.push({
						mimeType: mimeType,
R
rebornix 已提交
817 818
						rendererId: BUILTIN_RENDERER_ID,
						isTrusted: mimeTypeIsAlwaysSecure(mimeType) || textModel.metadata.trusted
R
rebornix 已提交
819 820 821 822
					});
				} else {
					orderMimeTypes.push({
						mimeType: mimeType,
R
rebornix 已提交
823 824
						rendererId: RENDERER_NOT_AVAILABLE,
						isTrusted: textModel.metadata.trusted
R
rebornix 已提交
825 826
					});
				}
827 828 829
			}
		});

830
		return orderMimeTypes;
831 832
	}

833
	private _findBestMatchedRenderer(mimeType: string): readonly NotebookOutputRendererInfo[] {
834 835 836
		return this.notebookRenderersInfoStore.getContributedRenderer(mimeType);
	}

837 838 839 840 841 842
	getContributedNotebookProviders(resource?: URI): readonly NotebookProviderInfo[] {
		if (resource) {
			return this.notebookProviderInfoStore.getContributedNotebook(resource);
		}

		return [...this.notebookProviderInfoStore];
R
rebornix 已提交
843
	}
R
rebornix 已提交
844

R
rebornix 已提交
845 846 847 848
	getContributedNotebookProvider(viewType: string): NotebookProviderInfo | undefined {
		return this.notebookProviderInfoStore.get(viewType);
	}

R
rebornix 已提交
849 850
	getContributedNotebookOutputRenderers(viewType: string): NotebookOutputRendererInfo | undefined {
		return this.notebookRenderersInfoStore.get(viewType);
R
rebornix 已提交
851 852
	}

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

		return ret;
	}
R
rebornix 已提交
861

862 863 864 865 866 867 868
	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 已提交
869
	removeNotebookEditor(editor: INotebookEditor) {
R
Rob Lourens 已提交
870
		const editorCache = this._notebookEditors.get(editor.getId());
871 872 873 874

		if (editorCache) {
			this._notebookEditors.delete(editor.getId());
			this._onNotebookEditorsRemove.fire([editor]);
R
rebornix 已提交
875 876 877 878
		}
	}

	addNotebookEditor(editor: INotebookEditor) {
879
		this._notebookEditors.set(editor.getId(), editor);
R
rebornix 已提交
880 881 882
		this._onNotebookEditorAdd.fire(editor);
	}

883 884 885 886
	getNotebookEditor(editorId: string) {
		return this._notebookEditors.get(editorId);
	}

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

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

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

903
	destoryNotebookDocument(viewType: string, notebook: INotebookTextModel): void {
904
		this._onWillDisposeDocument(notebook);
R
rebornix 已提交
905 906
	}

R
rebornix 已提交
907
	updateActiveNotebookEditor(editor: INotebookEditor | null) {
R
rebornix 已提交
908 909 910 911 912 913 914 915 916 917 918
		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 已提交
919 920 921 922
		this._onDidChangeActiveEditor.fire(editor ? editor.getId() : null);
	}

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

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

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

		return undefined;
R
rebornix 已提交
938 939
	}

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

		if (provider) {
944 945 946 947 948 949
			const ret = await provider.controller.save(resource, token);
			if (ret) {
				this._onNotebookDocumentSaved.fire(resource);
			}

			return ret;
950 951 952 953 954
		}

		return false;
	}

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

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

			return ret;
R
saveAs  
rebornix 已提交
965 966 967 968 969
		}

		return false;
	}

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

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

		return;
	}

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

		if (provider) {
984
			return provider.controller.onDidReceiveMessage(editorId, rendererType, message);
985 986 987
		}
	}

988
	private _onWillDisposeDocument(model: INotebookTextModel): void {
R
rebornix 已提交
989

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

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

1002 1003
			modelData.model.dispose();
			modelData.dispose();
1004

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