notebookEditor.ts 32.7 KB
Newer Older
P
Peng Lyu 已提交
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 { getZoomLevel } from 'vs/base/browser/browser';
P
Peng Lyu 已提交
7
import * as DOM from 'vs/base/browser/dom';
P
Peng Lyu 已提交
8
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
9 10 11
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Color, RGBA } from 'vs/base/common/color';
import { Emitter, Event } from 'vs/base/common/event';
R
rebornix 已提交
12
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
R
rebornix 已提交
13
import 'vs/css!./notebook';
14
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
15
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
R
rebornix 已提交
16
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
17 18 19
import { Range } from 'vs/editor/common/core/range';
import { ICompositeCodeEditor, IEditor } from 'vs/editor/common/editorCommon';
import * as nls from 'vs/nls';
20
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
R
Rob Lourens 已提交
21
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
22
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
R
rebornix 已提交
23 24 25
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
26
import { contrastBorder, editorBackground, focusBorder, foreground, registerColor, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground } from 'vs/platform/theme/common/colorRegistry';
R
rebornix 已提交
27 28
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
29 30
import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { EditorOptions, IEditorCloseEvent, IEditorMemento } from 'vs/workbench/common/editor';
R
rebornix 已提交
31
import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_TOP_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
32
import { NotebookFindWidget } from 'vs/workbench/contrib/notebook/browser/contrib/notebookFindWidget';
33
import { CellEditState, CellFocusMode, ICellViewModel, INotebookEditor, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
R
rebornix 已提交
34
import { NotebookEditorInput, NotebookEditorModel } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
R
rebornix 已提交
35
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
36
import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
R
rebornix 已提交
37 38 39
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
import { CodeCellRenderer, MarkdownCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
R
rebornix 已提交
40
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
R
rebornix 已提交
41
import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
42 43 44 45
import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { CellKind, CellUri, IOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
P
Peng Lyu 已提交
46 47

const $ = DOM.$;
R
rebornix 已提交
48 49
const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState';

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
export class NotebookEditorOptions extends EditorOptions {

	readonly cellOptions?: IResourceEditorInput;

	constructor(options: Partial<NotebookEditorOptions>) {
		super();
		this.overwrite(options);
		this.cellOptions = options.cellOptions;
	}

	with(options: Partial<NotebookEditorOptions>): NotebookEditorOptions {
		return new NotebookEditorOptions({ ...this, ...options });
	}
}

R
rebornix 已提交
65
export class NotebookCodeEditors implements ICompositeCodeEditor {
66

67
	private readonly _disposables = new DisposableStore();
68 69 70 71
	private readonly _onDidChangeActiveEditor = new Emitter<this>();
	readonly onDidChangeActiveEditor: Event<this> = this._onDidChangeActiveEditor.event;

	constructor(
R
rebornix 已提交
72
		private _list: NotebookCellList,
R
rebornix 已提交
73
		private _renderedEditors: Map<ICellViewModel, ICodeEditor | undefined>
74
	) {
75
		_list.onDidChangeFocus(_e => this._onDidChangeActiveEditor.fire(this), undefined, this._disposables);
76 77 78 79 80 81
	}

	dispose(): void {
		this._onDidChangeActiveEditor.dispose();
		this._disposables.dispose();
	}
82 83 84

	get activeCodeEditor(): IEditor | undefined {
		const [focused] = this._list.getFocusedElements();
R
rebornix 已提交
85
		return this._renderedEditors.get(focused);
86 87 88
	}
}

R
rebornix 已提交
89
export class NotebookEditor extends BaseEditor implements INotebookEditor {
P
Peng Lyu 已提交
90 91 92
	static readonly ID: string = 'workbench.editor.notebook';
	private rootElement!: HTMLElement;
	private body!: HTMLElement;
P
Peng Lyu 已提交
93
	private webview: BackLayerWebView | null = null;
R
rebornix 已提交
94
	private list: NotebookCellList | undefined;
95
	private control: ICompositeCodeEditor | undefined;
R
rebornix 已提交
96
	private renderedEditors: Map<ICellViewModel, ICodeEditor | undefined> = new Map();
R
rebornix 已提交
97
	private eventDispatcher: NotebookEventDispatcher | undefined;
R
rebornix 已提交
98
	private notebookViewModel: NotebookViewModel | undefined;
99
	private localStore: DisposableStore = this._register(new DisposableStore());
R
rebornix 已提交
100
	private editorMemento: IEditorMemento<INotebookEditorViewState>;
R
rebornix 已提交
101
	private readonly groupListener = this._register(new MutableDisposable());
102
	private fontInfo: BareFontInfo | undefined;
103
	private dimension: DOM.Dimension | null = null;
R
rebornix 已提交
104
	private editorFocus: IContextKey<boolean> | null = null;
R
rebornix 已提交
105
	private editorEditable: IContextKey<boolean> | null = null;
106
	private editorExecutingNotebook: IContextKey<boolean> | null = null;
R
rebornix 已提交
107
	private outputRenderer: OutputRenderer;
R
rebornix 已提交
108
	private findWidget: NotebookFindWidget;
P
Peng Lyu 已提交
109 110 111 112 113

	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
		@IThemeService themeService: IThemeService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
P
Peng Lyu 已提交
114
		@IStorageService storageService: IStorageService,
R
rebornix 已提交
115
		@INotebookService private notebookService: INotebookService,
116
		@IEditorGroupsService editorGroupService: IEditorGroupsService,
R
rebornix 已提交
117
		@IConfigurationService private readonly configurationService: IConfigurationService,
R
rebornix 已提交
118
		@IContextKeyService private readonly contextKeyService: IContextKeyService,
119
		// @IEditorProgressService private readonly progressService: IEditorProgressService,
P
Peng Lyu 已提交
120 121
	) {
		super(NotebookEditor.ID, telemetryService, themeService, storageService);
R
rebornix 已提交
122 123

		this.editorMemento = this.getEditorMemento<INotebookEditorViewState>(editorGroupService, NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY);
R
rebornix 已提交
124
		this.outputRenderer = new OutputRenderer(this, this.instantiationService);
R
rebornix 已提交
125
		this.findWidget = this.instantiationService.createInstance(NotebookFindWidget, this);
126
		this.findWidget.updateTheme(this.themeService.getColorTheme());
R
rebornix 已提交
127 128
	}

R
rebornix 已提交
129 130 131 132
	get viewModel() {
		return this.notebookViewModel;
	}

P
Peng Lyu 已提交
133 134 135 136 137 138 139 140
	get minimumWidth(): number { return 375; }
	get maximumWidth(): number { return Number.POSITIVE_INFINITY; }

	// these setters need to exist because this extends from BaseEditor
	set minimumWidth(value: number) { /*noop*/ }
	set maximumWidth(value: number) { /*noop*/ }


R
rebornix 已提交
141
	//#region Editor Core
R
rebornix 已提交
142

R
rebornix 已提交
143 144 145 146 147

	public get isNotebookEditor() {
		return true;
	}

P
Peng Lyu 已提交
148 149 150
	protected createEditor(parent: HTMLElement): void {
		this.rootElement = DOM.append(parent, $('.notebook-editor'));
		this.createBody(this.rootElement);
151
		this.generateFontInfo();
R
rebornix 已提交
152
		this.editorFocus = NOTEBOOK_EDITOR_FOCUSED.bindTo(this.contextKeyService);
153
		this.editorFocus.set(true);
R
rebornix 已提交
154 155 156 157 158 159 160
		this._register(this.onDidFocus(() => {
			this.editorFocus?.set(true);
		}));

		this._register(this.onDidBlur(() => {
			this.editorFocus?.set(false);
		}));
R
rebornix 已提交
161 162 163

		this.editorEditable = NOTEBOOK_EDITOR_EDITABLE.bindTo(this.contextKeyService);
		this.editorEditable.set(true);
164
		this.editorExecutingNotebook = NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.bindTo(this.contextKeyService);
165 166 167 168 169
	}

	private generateFontInfo(): void {
		const editorOptions = this.configurationService.getValue<IEditorOptions>('editor');
		this.fontInfo = BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel());
P
Peng Lyu 已提交
170 171 172
	}

	private createBody(parent: HTMLElement): void {
P
Peng Lyu 已提交
173
		this.body = document.createElement('div');
P
Peng Lyu 已提交
174 175 176
		DOM.addClass(this.body, 'cell-list-container');
		this.createCellList();
		DOM.append(parent, this.body);
R
rebornix 已提交
177
		DOM.append(parent, this.findWidget.getDomNode());
P
Peng Lyu 已提交
178 179
	}

P
Peng Lyu 已提交
180 181
	private createCellList(): void {
		DOM.addClass(this.body, 'cell-list-container');
P
Peng Lyu 已提交
182

P
Peng Lyu 已提交
183
		const renders = [
R
rebornix 已提交
184 185
			this.instantiationService.createInstance(CodeCellRenderer, this, this.contextKeyService, this.renderedEditors),
			this.instantiationService.createInstance(MarkdownCellRenderer, this.contextKeyService, this),
P
Peng Lyu 已提交
186 187
		];

R
rebornix 已提交
188
		this.list = <NotebookCellList>this.instantiationService.createInstance(
R
rebornix 已提交
189
			NotebookCellList,
P
Peng Lyu 已提交
190 191 192 193
			'NotebookCellList',
			this.body,
			this.instantiationService.createInstance(NotebookCellListDelegate),
			renders,
R
rebornix 已提交
194
			this.contextKeyService,
P
Peng Lyu 已提交
195 196
			{
				setRowLineHeight: false,
R
rebornix 已提交
197
				setRowHeight: false,
P
Peng Lyu 已提交
198 199 200
				supportDynamicHeights: true,
				horizontalScrolling: false,
				keyboardSupport: false,
R
rebornix 已提交
201
				mouseSupport: true,
P
Peng Lyu 已提交
202
				multipleSelectionSupport: false,
R
rebornix 已提交
203
				enableKeyboardNavigation: true,
204
				additionalScrollHeight: 0,
205
				styleController: (_suffix: string) => { return this.list!; },
P
Peng Lyu 已提交
206 207 208 209 210 211 212 213 214 215
				overrideStyles: {
					listBackground: editorBackground,
					listActiveSelectionBackground: editorBackground,
					listActiveSelectionForeground: foreground,
					listFocusAndSelectionBackground: editorBackground,
					listFocusAndSelectionForeground: foreground,
					listFocusBackground: editorBackground,
					listFocusForeground: foreground,
					listHoverForeground: foreground,
					listHoverBackground: editorBackground,
216 217
					listHoverOutline: focusBorder,
					listFocusOutline: focusBorder,
218 219 220 221
					listInactiveSelectionBackground: editorBackground,
					listInactiveSelectionForeground: foreground,
					listInactiveFocusBackground: editorBackground,
					listInactiveFocusOutline: editorBackground,
P
Peng Lyu 已提交
222
				}
R
rebornix 已提交
223
			},
P
Peng Lyu 已提交
224
		);
P
Peng Lyu 已提交
225

226
		this.control = new NotebookCodeEditors(this.list, this.renderedEditors);
227
		this.webview = this.instantiationService.createInstance(BackLayerWebView, this);
228 229 230 231 232
		this._register(this.webview.onMessage(message => {
			if (this.viewModel) {
				this.notebookService.onDidReceiveMessage(this.viewModel.viewType, this.viewModel.uri, message);
			}
		}));
R
rebornix 已提交
233
		this.list.rowsContainer.appendChild(this.webview.element);
P
Peng Lyu 已提交
234 235 236
		this._register(this.list);
	}

237 238 239 240
	getControl() {
		return this.control;
	}

P
Peng Lyu 已提交
241
	onHide() {
R
rebornix 已提交
242
		this.editorFocus?.set(false);
243 244
		if (this.webview) {
			this.localStore.clear();
R
rebornix 已提交
245
			this.list?.rowsContainer.removeChild(this.webview?.element);
246 247 248 249 250 251
			this.webview?.dispose();
			this.webview = null;
		}

		this.list?.splice(0, this.list?.length);
		super.onHide();
P
Peng Lyu 已提交
252 253
	}

R
rebornix 已提交
254 255 256 257 258 259 260 261 262 263 264 265 266
	setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void {
		super.setEditorVisible(visible, group);
		this.groupListener.value = ((group as IEditorGroupView).onWillCloseEditor(e => this.onWillCloseEditorInGroup(e)));
	}

	private onWillCloseEditorInGroup(e: IEditorCloseEvent): void {
		const editor = e.editor;
		if (!(editor instanceof NotebookEditorInput)) {
			return; // only handle files
		}

		if (editor === this.input) {
			this.saveTextEditorViewState(editor);
267 268 269
		}
	}

R
rebornix 已提交
270 271 272 273 274
	focus() {
		super.focus();
		this.editorFocus?.set(true);
	}

R
rebornix 已提交
275
	async setInput(input: NotebookEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
R
rebornix 已提交
276 277 278 279
		if (this.input instanceof NotebookEditorInput) {
			this.saveTextEditorViewState(this.input);
		}

R
rebornix 已提交
280 281
		await super.setInput(input, options, token);
		const model = await input.resolve();
P
Peng Lyu 已提交
282

283 284 285
		if (this.notebookViewModel === undefined || !this.notebookViewModel.equal(model) || this.webview === null) {
			this.detachModel();
			await this.attachModel(input, model);
R
rebornix 已提交
286
		}
P
Peng Lyu 已提交
287

288 289 290
		// reveal cell if editor options tell to do so
		if (options instanceof NotebookEditorOptions && options.cellOptions) {
			const cellOptions = options.cellOptions;
R
rebornix 已提交
291
			const cell = this.notebookViewModel!.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString());
292
			if (cell) {
293
				this.selectElement(cell);
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
				this.revealInCenterIfOutsideViewport(cell);
				const editor = this.renderedEditors.get(cell)!;
				if (editor) {
					if (cellOptions.options?.selection) {
						const { selection } = cellOptions.options;
						editor.setSelection({
							...selection,
							endLineNumber: selection.endLineNumber || selection.startLineNumber,
							endColumn: selection.endColumn || selection.startColumn
						});
					}
					if (!cellOptions.options?.preserveFocus) {
						editor.focus();
					}
				}
309 310
			}
		}
R
rebornix 已提交
311
	}
312

R
rebornix 已提交
313 314 315 316 317 318 319 320
	clearInput(): void {
		if (this.input && this.input instanceof NotebookEditorInput && !this.input.isDisposed()) {
			this.saveTextEditorViewState(this.input);
		}

		super.clearInput();
	}

R
rebornix 已提交
321 322 323 324 325 326
	private detachModel() {
		this.localStore.clear();
		this.notebookViewModel?.dispose();
		this.notebookViewModel = undefined;
		this.webview?.clearInsets();
		this.webview?.clearPreloadsCache();
327
		this.findWidget.clear();
328
		this.list?.splice(0, this.list?.length || 0);
R
rebornix 已提交
329
	}
R
rebornix 已提交
330

R
rebornix 已提交
331 332
	private async attachModel(input: NotebookEditorInput, model: NotebookEditorModel) {
		if (!this.webview) {
333
			this.webview = this.instantiationService.createInstance(BackLayerWebView, this);
R
rebornix 已提交
334 335
			this.list?.rowsContainer.insertAdjacentElement('afterbegin', this.webview!.element);
		}
336

R
rebornix 已提交
337
		this.eventDispatcher = new NotebookEventDispatcher();
338
		this.notebookViewModel = this.instantiationService.createInstance(NotebookViewModel, input.viewType!, model, this.eventDispatcher, this.getLayoutInfo());
339
		this.editorEditable?.set(!!this.notebookViewModel.metadata?.editable);
R
rebornix 已提交
340
		this.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
R
rebornix 已提交
341
		const viewState = this.loadTextEditorViewState(input);
R
rebornix 已提交
342
		this.notebookViewModel.restoreEditorViewState(viewState);
343

R
rebornix 已提交
344 345 346 347 348 349 350 351
		if (viewState?.scrollPosition !== undefined) {
			this.list!.scrollTop = viewState!.scrollPosition.top;
			this.list!.scrollLeft = viewState!.scrollPosition.left;
		} else {
			this.list!.scrollTop = 0;
			this.list!.scrollLeft = 0;
		}

R
rebornix 已提交
352 353 354 355
		this.localStore.add(this.eventDispatcher.onDidChangeMetadata((e) => {
			this.editorEditable?.set(e.source.editable);
		}));

R
rebornix 已提交
356 357 358
		this.localStore.add(this.notebookViewModel.onDidChangeViewCells((e) => {
			if (e.synchronous) {
				e.splices.reverse().forEach((diff) => {
359 360 361 362 363 364 365 366
					// remove output in the webview
					for (let i = diff[0]; i < diff[0] + diff[1]; i++) {
						const cell = this.list?.element(i);
						cell?.cell.outputs.forEach(output => {
							this.removeInset(output);
						});
					}

R
rebornix 已提交
367 368 369 370 371
					this.list?.splice(diff[0], diff[1], diff[2]);
				});
			} else {
				DOM.scheduleAtNextAnimationFrame(() => {
					e.splices.reverse().forEach((diff) => {
372 373 374 375 376 377 378 379
						// remove output in the webview
						for (let i = diff[0]; i < diff[0] + diff[1]; i++) {
							const cell = this.list?.element(i);
							cell?.cell.outputs.forEach(output => {
								this.removeInset(output);
							});
						}

R
rebornix 已提交
380 381 382 383
						this.list?.splice(diff[0], diff[1], diff[2]);
					});
				});
			}
R
rebornix 已提交
384 385 386 387 388 389 390 391 392 393 394 395
		}));

		this.webview?.updateRendererPreloads(this.notebookViewModel.renderers);

		this.localStore.add(this.list!.onWillScroll(e => {
			this.webview!.updateViewScrollTop(-e.scrollTop, []);
		}));

		this.localStore.add(this.list!.onDidChangeContentHeight(() => {
			const scrollTop = this.list?.scrollTop || 0;
			const scrollHeight = this.list?.scrollHeight || 0;
			this.webview!.element.style.height = `${scrollHeight}px`;
R
rebornix 已提交
396
			let updateItems: { cell: CodeCellViewModel, output: IOutput, cellTop: number }[] = [];
R
rebornix 已提交
397 398 399 400 401 402 403 404 405 406 407 408

			if (this.webview?.insetMapping) {
				this.webview?.insetMapping.forEach((value, key) => {
					let cell = value.cell;
					let index = this.notebookViewModel!.getViewCellIndex(cell);
					let cellTop = this.list?.getAbsoluteTop(index) || 0;
					if (this.webview!.shouldUpdateInset(cell, key, cellTop)) {
						updateItems.push({
							cell: cell,
							output: key,
							cellTop: cellTop
						});
R
rebornix 已提交
409
					}
R
rebornix 已提交
410
				});
411

R
rebornix 已提交
412 413 414 415 416 417
				if (updateItems.length) {
					this.webview?.updateViewScrollTop(-scrollTop, updateItems);
				}
			}
		}));

418
		this.list?.splice(0, 0, this.notebookViewModel!.viewCells as CellViewModel[]);
R
rebornix 已提交
419
		this.list?.layout();
P
Peng Lyu 已提交
420 421
	}

R
rebornix 已提交
422
	private saveTextEditorViewState(input: NotebookEditorInput): void {
R
npe  
rebornix 已提交
423
		if (this.group && this.notebookViewModel) {
R
rebornix 已提交
424
			const state = this.notebookViewModel.saveEditorViewState();
R
rebornix 已提交
425 426 427 428
			if (this.list) {
				state.scrollPosition = { left: this.list.scrollLeft, top: this.list.scrollTop };
			}

R
rebornix 已提交
429
			this.editorMemento.saveEditorState(this.group, input.resource, state);
R
rebornix 已提交
430 431 432 433 434
		}
	}

	private loadTextEditorViewState(input: NotebookEditorInput): INotebookEditorViewState | undefined {
		if (this.group) {
R
rebornix 已提交
435
			return this.editorMemento.loadEditorState(this.group, input.resource);
R
rebornix 已提交
436 437 438 439 440 441 442 443 444 445
		}

		return;
	}

	layout(dimension: DOM.Dimension): void {
		this.dimension = new DOM.Dimension(dimension.width, dimension.height);
		DOM.toggleClass(this.rootElement, 'mid-width', dimension.width < 1000 && dimension.width >= 600);
		DOM.toggleClass(this.rootElement, 'narrow-width', dimension.width < 600);
		DOM.size(this.body, dimension.width, dimension.height);
446
		this.list?.updateOptions({ additionalScrollHeight: dimension.height });
R
rebornix 已提交
447
		this.list?.layout(dimension.height, dimension.width);
R
rebornix 已提交
448
		this.eventDispatcher?.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
R
rebornix 已提交
449 450 451 452 453 454 455 456 457 458 459 460
	}

	protected saveState(): void {
		if (this.input instanceof NotebookEditorInput) {
			this.saveTextEditorViewState(this.input);
		}

		super.saveState();
	}

	//#endregion

R
rebornix 已提交
461 462
	//#region Editor Features

R
rebornix 已提交
463
	selectElement(cell: ICellViewModel) {
R
rebornix 已提交
464 465 466 467 468 469 470 471
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
			this.list?.setSelection([index]);
			this.list?.setFocus([index]);
		}
	}

R
rebornix 已提交
472
	revealInView(cell: ICellViewModel) {
R
rebornix 已提交
473 474 475
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
476
			this.list?.revealInView(index);
R
rebornix 已提交
477 478 479
		}
	}

R
rebornix 已提交
480
	revealInCenterIfOutsideViewport(cell: ICellViewModel) {
R
rebornix 已提交
481 482 483
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
484
			this.list?.revealInCenterIfOutsideViewport(index);
R
rebornix 已提交
485 486 487
		}
	}

R
rebornix 已提交
488
	revealInCenter(cell: ICellViewModel) {
R
rebornix 已提交
489 490 491
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
492 493 494 495
			this.list?.revealInCenter(index);
		}
	}

R
rebornix 已提交
496
	revealLineInView(cell: ICellViewModel, line: number): void {
R
rebornix 已提交
497 498 499 500
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
			this.list?.revealLineInView(index, line);
R
rebornix 已提交
501 502
		}
	}
R
rebornix 已提交
503

R
rebornix 已提交
504
	revealLineInCenter(cell: ICellViewModel, line: number) {
R
rebornix 已提交
505 506 507
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
508
			this.list?.revealLineInCenter(index, line);
R
rebornix 已提交
509 510 511
		}
	}

R
rebornix 已提交
512
	revealLineInCenterIfOutsideViewport(cell: ICellViewModel, line: number) {
R
rebornix 已提交
513 514 515 516 517 518 519
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
			this.list?.revealLineInCenterIfOutsideViewport(index, line);
		}
	}

R
rebornix 已提交
520
	revealRangeInView(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
521 522 523 524 525 526 527
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
			this.list?.revealRangeInView(index, range);
		}
	}

R
rebornix 已提交
528
	revealRangeInCenter(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
529 530 531 532 533 534 535
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
			this.list?.revealRangeInCenter(index, range);
		}
	}

R
rebornix 已提交
536
	revealRangeInCenterIfOutsideViewport(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
537 538 539 540 541 542 543
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
			this.list?.revealRangeInCenterIfOutsideViewport(index, range);
		}
	}

R
rebornix 已提交
544
	setCellSelection(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
545 546 547 548 549 550 551
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
			this.list?.setCellSelection(index, range);
		}
	}

R
rebornix 已提交
552 553 554 555 556 557
	changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any {
		return this.notebookViewModel?.changeDecorations(callback);
	}

	//#endregion

R
rebornix 已提交
558 559 560 561 562 563 564 565
	//#region Find Delegate

	public showFind() {
		this.findWidget.reveal();
	}

	public hideFind() {
		this.findWidget.hide();
R
rebornix 已提交
566
		this.focus();
R
rebornix 已提交
567 568 569 570
	}

	//#endregion

R
rebornix 已提交
571
	//#region Cell operations
572
	async layoutNotebookCell(cell: ICellViewModel, height: number): Promise<void> {
R
rebornix 已提交
573
		let relayout = (cell: ICellViewModel, height: number) => {
R
rebornix 已提交
574
			let index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
575
			if (index >= 0) {
R
rebornix 已提交
576
				this.list?.updateElementHeight(index, height);
R
rebornix 已提交
577
			}
R
rebornix 已提交
578 579
		};

580
		let r: () => void;
R
rebornix 已提交
581
		DOM.scheduleAtNextAnimationFrame(() => {
R
rebornix 已提交
582
			relayout(cell, height);
583
			r();
R
rebornix 已提交
584
		});
585 586

		return new Promise(resolve => { r = resolve; });
587 588
	}

R
rebornix 已提交
589
	async insertNotebookCell(cell: ICellViewModel, type: CellKind, direction: 'above' | 'below', initialText: string = ''): Promise<void> {
R
rebornix 已提交
590 591 592
		const newLanguages = this.notebookViewModel!.languages;
		const language = newLanguages && newLanguages.length ? newLanguages[0] : 'markdown';
		const index = this.notebookViewModel!.getViewCellIndex(cell);
P
Peng Lyu 已提交
593
		const insertIndex = direction === 'above' ? index : index + 1;
R
rebornix 已提交
594
		const newCell = this.notebookViewModel!.createCell(insertIndex, initialText.split(/\r?\n/g), language, type, true);
R
rebornix 已提交
595
		this.list?.setFocus([insertIndex]);
P
Peng Lyu 已提交
596

R
rebornix 已提交
597
		if (type === CellKind.Markdown) {
598
			newCell.editState = CellEditState.Editing;
P
Peng Lyu 已提交
599
		}
R
rebornix 已提交
600

601
		let r: () => void;
R
rebornix 已提交
602
		DOM.scheduleAtNextAnimationFrame(() => {
R
rebornix 已提交
603
			this.list?.revealInCenterIfOutsideViewport(insertIndex);
604
			r();
R
rebornix 已提交
605
		});
606 607

		return new Promise(resolve => { r = resolve; });
P
Peng Lyu 已提交
608 609
	}

R
rebornix 已提交
610
	async deleteNotebookCell(cell: ICellViewModel): Promise<void> {
611
		(cell as CellViewModel).save();
R
rebornix 已提交
612
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
613
		this.notebookViewModel!.deleteCell(index, true);
R
rebornix 已提交
614 615
	}

616
	async moveCellDown(cell: ICellViewModel): Promise<void> {
617
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
618 619 620 621
		if (index === this.notebookViewModel!.viewCells.length - 1) {
			return;
		}

622
		const newIdx = index + 1;
623
		return this.moveCellToIndex(index, newIdx);
624 625
	}

626
	async moveCellUp(cell: ICellViewModel): Promise<void> {
627
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
628 629 630 631
		if (index === 0) {
			return;
		}

632
		const newIdx = index - 1;
633
		return this.moveCellToIndex(index, newIdx);
634 635
	}

636
	private async moveCellToIndex(index: number, newIdx: number): Promise<void> {
R
rebornix 已提交
637
		if (!this.notebookViewModel!.moveCellToIdx(index, newIdx, true)) {
638 639 640
			return;
		}

641
		let r: () => void;
642 643
		DOM.scheduleAtNextAnimationFrame(() => {
			this.list?.revealInCenterIfOutsideViewport(index + 1);
644
			r();
645
		});
646 647

		return new Promise(resolve => { r = resolve; });
648 649
	}

R
rebornix 已提交
650
	editNotebookCell(cell: CellViewModel): void {
651
		cell.editState = CellEditState.Editing;
R
rebornix 已提交
652 653

		this.renderedEditors.get(cell)?.focus();
P
Peng Lyu 已提交
654 655
	}

R
rebornix 已提交
656
	saveNotebookCell(cell: ICellViewModel): void {
657
		cell.editState = CellEditState.Preview;
P
Peng Lyu 已提交
658 659
	}

R
rebornix 已提交
660 661 662 663 664 665 666 667 668 669
	getActiveCell() {
		let elements = this.list?.getFocusedElements();

		if (elements && elements.length) {
			return elements[0];
		}

		return undefined;
	}

670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
	cancelNotebookExecution(): void {
		if (!this.notebookViewModel!.currentTokenSource) {
			throw new Error('Notebook is not executing');
		}


		this.notebookViewModel!.currentTokenSource.cancel();
		this.notebookViewModel!.currentTokenSource = undefined;
	}

	async executeNotebook(): Promise<void> {
		// return this.progressService.showWhile(this._executeNotebook());
		return this._executeNotebook();
	}

	async _executeNotebook(): Promise<void> {
		if (this.notebookViewModel!.currentTokenSource) {
			return;
		}

		const tokenSource = new CancellationTokenSource();
		try {
			this.editorExecutingNotebook!.set(true);
			this.notebookViewModel!.currentTokenSource = tokenSource;

			for (let cell of this.notebookViewModel!.viewCells) {
				if (cell.cellKind === CellKind.Code) {
					await this._executeNotebookCell(cell, tokenSource);
				}
			}
		} finally {
			this.editorExecutingNotebook!.set(false);
			this.notebookViewModel!.currentTokenSource = undefined;
			tokenSource.dispose();
		}
	}

	cancelNotebookCellExecution(cell: ICellViewModel): void {
		if (!cell.currentTokenSource) {
			throw new Error('Cell is not executing');
		}

		cell.currentTokenSource.cancel();
		cell.currentTokenSource = undefined;
	}

716
	async executeNotebookCell(cell: ICellViewModel): Promise<void> {
717
		const tokenSource = new CancellationTokenSource();
718 719 720 721 722 723 724 725
		try {
			this._executeNotebookCell(cell, tokenSource);
		} finally {
			tokenSource.dispose();
		}
	}

	async _executeNotebookCell(cell: ICellViewModel, tokenSource: CancellationTokenSource): Promise<void> {
726
		try {
727
			cell.currentTokenSource = tokenSource;
R
rebornix 已提交
728
			const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0];
729 730 731 732
			if (provider) {
				const viewType = provider.id;
				const notebookUri = CellUri.parse(cell.uri)?.notebook;
				if (notebookUri) {
733
					return await this.notebookService.executeNotebookCell(viewType, notebookUri, cell.handle, tokenSource.token);
734 735 736
				}
			}
		} finally {
737
			cell.currentTokenSource = undefined;
738 739 740
		}
	}

R
rebornix 已提交
741
	focusNotebookCell(cell: ICellViewModel, focusEditor: boolean) {
R
rebornix 已提交
742
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
743 744

		if (focusEditor) {
R
rebornix 已提交
745 746 747
			this.list?.setFocus([index]);
			this.list?.setSelection([index]);
			this.list?.focusView();
R
rebornix 已提交
748

749
			cell.editState = CellEditState.Editing;
R
rebornix 已提交
750
			cell.focusMode = CellFocusMode.Editor;
751
			this.revealInCenterIfOutsideViewport(cell);
R
rebornix 已提交
752
		} else {
R
rebornix 已提交
753
			let itemDOM = this.list?.domElementAtIndex(index);
R
rebornix 已提交
754 755 756
			if (document.activeElement && itemDOM && itemDOM.contains(document.activeElement)) {
				(document.activeElement as HTMLElement).blur();
			}
757

758
			cell.editState = CellEditState.Preview;
759
			cell.focusMode = CellFocusMode.Editor;
R
rebornix 已提交
760

R
rebornix 已提交
761 762
			this.list?.setFocus([index]);
			this.list?.setSelection([index]);
763
			this.revealInCenterIfOutsideViewport(cell);
R
rebornix 已提交
764
			this.list?.focusView();
R
rebornix 已提交
765 766 767
		}
	}

R
rebornix 已提交
768 769 770 771
	//#endregion

	//#region MISC

R
rebornix 已提交
772 773 774 775 776 777 778 779 780 781 782
	getLayoutInfo(): NotebookLayoutInfo {
		if (!this.list) {
			throw new Error('Editor is not initalized successfully');
		}

		return {
			width: this.dimension!.width,
			height: this.dimension!.height,
			fontInfo: this.fontInfo!
		};
	}
R
rebornix 已提交
783

R
rebornix 已提交
784 785
	triggerScroll(event: IMouseWheelEvent) {
		this.list?.triggerScrollFromMouseWheelEvent(event);
R
rebornix 已提交
786 787
	}

R
rebornix 已提交
788
	createInset(cell: CodeCellViewModel, output: IOutput, shadowContent: string, offset: number) {
R
rebornix 已提交
789 790
		if (!this.webview) {
			return;
R
rebornix 已提交
791 792
		}

R
rebornix 已提交
793
		let preloads = this.notebookViewModel!.renderers;
R
rebornix 已提交
794

795
		if (!this.webview!.insetMapping.has(output)) {
R
rebornix 已提交
796
			let index = this.notebookViewModel!.getViewCellIndex(cell);
797 798 799
			let cellTop = this.list?.getAbsoluteTop(index) || 0;

			this.webview!.createInset(cell, output, cellTop, offset, shadowContent, preloads);
R
rebornix 已提交
800
		} else {
R
rebornix 已提交
801
			let index = this.notebookViewModel!.getViewCellIndex(cell);
802
			let cellTop = this.list?.getAbsoluteTop(index) || 0;
R
rebornix 已提交
803 804
			let scrollTop = this.list?.scrollTop || 0;

805
			this.webview!.updateViewScrollTop(-scrollTop, [{ cell: cell, output: output, cellTop: cellTop }]);
R
rebornix 已提交
806
		}
R
rebornix 已提交
807
	}
R
rebornix 已提交
808

R
rebornix 已提交
809 810 811 812 813 814 815 816
	removeInset(output: IOutput) {
		if (!this.webview) {
			return;
		}

		this.webview!.removeInset(output);
	}

R
rebornix 已提交
817 818
	getOutputRenderer(): OutputRenderer {
		return this.outputRenderer;
R
rebornix 已提交
819
	}
820

821 822 823 824
	postMessage(message: any) {
		this.webview?.webview.sendMessage(message);
	}

R
rebornix 已提交
825
	//#endregion
826 827 828 829 830 831

	toJSON(): any {
		return {
			notebookHandle: this.viewModel?.handle
		};
	}
P
Peng Lyu 已提交
832 833 834 835
}

const embeddedEditorBackground = 'walkThrough.embeddedEditorBackground';

836 837 838 839 840 841
export const focusedCellIndicator = registerColor('notebook.focusedCellIndicator', {
	light: new Color(new RGBA(102, 175, 224)),
	dark: new Color(new RGBA(12, 125, 157)),
	hc: new Color(new RGBA(0, 73, 122))
}, nls.localize('notebook.focusedCellIndicator', "The color of the focused notebook cell indicator."));

842 843 844 845 846 847 848
export const notebookOutputContainerColor = registerColor('notebook.outputContainerBackgroundColor', {
	dark: new Color(new RGBA(255, 255, 255, 0.06)),
	light: new Color(new RGBA(228, 230, 241)),
	hc: null
}
	, nls.localize('notebook.outputContainerBackgroundColor', "The Color of the notebook output container background."));

R
rebornix 已提交
849 850 851 852 853 854
export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeperator', {
	dark: Color.fromHex('#808080').transparent(0.35),
	light: Color.fromHex('#808080').transparent(0.35),
	hc: contrastBorder
}, nls.localize('cellToolbarSeperator', "The color of seperator in Cell bottom toolbar"));

855

P
Peng Lyu 已提交
856 857 858
registerThemingParticipant((theme, collector) => {
	const color = getExtraColor(theme, embeddedEditorBackground, { dark: 'rgba(0, 0, 0, .4)', extra_dark: 'rgba(200, 235, 255, .064)', light: '#f4f4f4', hc: null });
	if (color) {
R
rebornix 已提交
859 860
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .monaco-editor-background,
			.monaco-workbench .part.editor > .content .notebook-editor .cell .margin-view-overlays { background: ${color}; }`);
P
Peng Lyu 已提交
861 862 863
	}
	const link = theme.getColor(textLinkForeground);
	if (link) {
R
Rob Lourens 已提交
864
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .output a { color: ${link}; }`);
P
Peng Lyu 已提交
865 866 867
	}
	const activeLink = theme.getColor(textLinkActiveForeground);
	if (activeLink) {
R
Rob Lourens 已提交
868 869
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .output a:hover,
			.monaco-workbench .part.editor > .content .notebook-editor .cell .output a:active { color: ${activeLink}; }`);
P
Peng Lyu 已提交
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
	}
	const shortcut = theme.getColor(textPreformatForeground);
	if (shortcut) {
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor code,
			.monaco-workbench .part.editor > .content .notebook-editor .shortcut { color: ${shortcut}; }`);
	}
	const border = theme.getColor(contrastBorder);
	if (border) {
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-editor { border-color: ${border}; }`);
	}
	const quoteBackground = theme.getColor(textBlockQuoteBackground);
	if (quoteBackground) {
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor blockquote { background: ${quoteBackground}; }`);
	}
	const quoteBorder = theme.getColor(textBlockQuoteBorder);
	if (quoteBorder) {
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor blockquote { border-color: ${quoteBorder}; }`);
	}
888

889
	const containerBackground = theme.getColor(notebookOutputContainerColor);
890

891 892
	if (containerBackground) {
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .output { background-color: ${containerBackground}; }`);
893
	}
894

895 896 897 898 899 900
	const focusedCellIndicatorColor = theme.getColor(focusedCellIndicator);
	if (focusedCellIndicatorColor) {
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row.focused .notebook-cell-focus-indicator { border-color: ${focusedCellIndicatorColor}; }`);
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row.selected .notebook-cell-focus-indicator { border-color: ${focusedCellIndicatorColor}; }`);
	}

R
rebornix 已提交
901 902 903 904 905 906
	const cellToolbarSeperator = theme.getColor(CELL_TOOLBAR_SEPERATOR);
	if (cellToolbarSeperator) {
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell-bottom-toolbar-container .seperator { background-color: ${cellToolbarSeperator} }`);
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell-bottom-toolbar-container .seperator-short { background-color: ${cellToolbarSeperator} }`);
	}

907
	// Cell Margin
R
rebornix 已提交
908
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row > div.cell { margin: ${EDITOR_TOP_MARGIN}px ${CELL_MARGIN}px 0px ${CELL_MARGIN}px; }`);
909
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .output { margin: 0px ${CELL_MARGIN}px 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px }`);
R
rebornix 已提交
910
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell-bottom-toolbar-container { width: calc(100% - ${CELL_MARGIN * 2 + CELL_RUN_GUTTER}px); margin: 0px ${CELL_MARGIN}px 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px }`);
R
Rob Lourens 已提交
911

R
Rob Lourens 已提交
912
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .cell-editor-container { width: calc(100% - ${CELL_RUN_GUTTER}px); }`);
913
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .markdown-editor-container { margin-left: ${CELL_RUN_GUTTER}px; }`);
R
Rob Lourens 已提交
914 915
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row > div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`);
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .run-button-container { width: ${CELL_RUN_GUTTER}px; }`);
P
Peng Lyu 已提交
916
});