notebookEditor.ts 33.1 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
			if (this.list) {
				state.scrollPosition = { left: this.list.scrollLeft, top: this.list.scrollTop };
427 428 429 430 431 432 433 434 435 436 437
				let cellHeights: { [key: number]: number } = {};
				for (let i = 0; i < this.list.length; i++) {
					const elm = this.list.element(i)!;
					if (elm.cellKind === CellKind.Code) {
						cellHeights[i] = elm.layoutInfo.totalHeight;
					} else {
						cellHeights[i] = 0;
					}
				}

				state.cellTotalHeights = cellHeights;
R
rebornix 已提交
438 439
			}

R
rebornix 已提交
440
			this.editorMemento.saveEditorState(this.group, input.resource, state);
R
rebornix 已提交
441 442 443 444 445
		}
	}

	private loadTextEditorViewState(input: NotebookEditorInput): INotebookEditorViewState | undefined {
		if (this.group) {
R
rebornix 已提交
446
			return this.editorMemento.loadEditorState(this.group, input.resource);
R
rebornix 已提交
447 448 449 450 451 452 453 454 455 456
		}

		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);
457
		this.list?.updateOptions({ additionalScrollHeight: dimension.height });
R
rebornix 已提交
458
		this.list?.layout(dimension.height, dimension.width);
R
rebornix 已提交
459
		this.eventDispatcher?.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
R
rebornix 已提交
460 461 462 463 464 465 466 467 468 469 470 471
	}

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

		super.saveState();
	}

	//#endregion

R
rebornix 已提交
472 473
	//#region Editor Features

R
rebornix 已提交
474
	selectElement(cell: ICellViewModel) {
R
rebornix 已提交
475 476 477 478 479 480 481 482
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
483
	revealInView(cell: ICellViewModel) {
R
rebornix 已提交
484 485 486
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
487
			this.list?.revealInView(index);
R
rebornix 已提交
488 489 490
		}
	}

R
rebornix 已提交
491
	revealInCenterIfOutsideViewport(cell: ICellViewModel) {
R
rebornix 已提交
492 493 494
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
495
			this.list?.revealInCenterIfOutsideViewport(index);
R
rebornix 已提交
496 497 498
		}
	}

R
rebornix 已提交
499
	revealInCenter(cell: ICellViewModel) {
R
rebornix 已提交
500 501 502
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
503 504 505 506
			this.list?.revealInCenter(index);
		}
	}

R
rebornix 已提交
507
	revealLineInView(cell: ICellViewModel, line: number): void {
R
rebornix 已提交
508 509 510 511
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
			this.list?.revealLineInView(index, line);
R
rebornix 已提交
512 513
		}
	}
R
rebornix 已提交
514

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

		if (index !== undefined) {
R
rebornix 已提交
519
			this.list?.revealLineInCenter(index, line);
R
rebornix 已提交
520 521 522
		}
	}

R
rebornix 已提交
523
	revealLineInCenterIfOutsideViewport(cell: ICellViewModel, line: number) {
R
rebornix 已提交
524 525 526 527 528 529 530
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
531
	revealRangeInView(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
532 533 534 535 536 537 538
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
539
	revealRangeInCenter(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
540 541 542 543 544 545 546
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
547
	revealRangeInCenterIfOutsideViewport(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
548 549 550 551 552 553 554
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
555
	setCellSelection(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
556 557 558 559 560 561 562
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
563 564 565 566 567 568
	changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any {
		return this.notebookViewModel?.changeDecorations(callback);
	}

	//#endregion

R
rebornix 已提交
569 570 571 572 573 574 575 576
	//#region Find Delegate

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

	public hideFind() {
		this.findWidget.hide();
R
rebornix 已提交
577
		this.focus();
R
rebornix 已提交
578 579 580 581
	}

	//#endregion

R
rebornix 已提交
582
	//#region Cell operations
583
	async layoutNotebookCell(cell: ICellViewModel, height: number): Promise<void> {
R
rebornix 已提交
584
		let relayout = (cell: ICellViewModel, height: number) => {
R
rebornix 已提交
585
			let index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
586
			if (index >= 0) {
R
rebornix 已提交
587
				this.list?.updateElementHeight(index, height);
R
rebornix 已提交
588
			}
R
rebornix 已提交
589 590
		};

591
		let r: () => void;
R
rebornix 已提交
592
		DOM.scheduleAtNextAnimationFrame(() => {
R
rebornix 已提交
593
			relayout(cell, height);
594
			r();
R
rebornix 已提交
595
		});
596 597

		return new Promise(resolve => { r = resolve; });
598 599
	}

R
rebornix 已提交
600
	async insertNotebookCell(cell: ICellViewModel, type: CellKind, direction: 'above' | 'below', initialText: string = ''): Promise<void> {
R
rebornix 已提交
601 602 603
		const newLanguages = this.notebookViewModel!.languages;
		const language = newLanguages && newLanguages.length ? newLanguages[0] : 'markdown';
		const index = this.notebookViewModel!.getViewCellIndex(cell);
P
Peng Lyu 已提交
604
		const insertIndex = direction === 'above' ? index : index + 1;
R
rebornix 已提交
605
		const newCell = this.notebookViewModel!.createCell(insertIndex, initialText.split(/\r?\n/g), language, type, true);
R
rebornix 已提交
606
		this.list?.setFocus([insertIndex]);
P
Peng Lyu 已提交
607

R
rebornix 已提交
608
		if (type === CellKind.Markdown) {
609
			newCell.editState = CellEditState.Editing;
P
Peng Lyu 已提交
610
		}
R
rebornix 已提交
611

612
		let r: () => void;
R
rebornix 已提交
613
		DOM.scheduleAtNextAnimationFrame(() => {
R
rebornix 已提交
614
			this.list?.revealInCenterIfOutsideViewport(insertIndex);
615
			r();
R
rebornix 已提交
616
		});
617 618

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

R
rebornix 已提交
621
	async deleteNotebookCell(cell: ICellViewModel): Promise<void> {
622
		(cell as CellViewModel).save();
R
rebornix 已提交
623
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
624
		this.notebookViewModel!.deleteCell(index, true);
R
rebornix 已提交
625 626
	}

627
	async moveCellDown(cell: ICellViewModel): Promise<void> {
628
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
629 630 631 632
		if (index === this.notebookViewModel!.viewCells.length - 1) {
			return;
		}

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

637
	async moveCellUp(cell: ICellViewModel): Promise<void> {
638
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
639 640 641 642
		if (index === 0) {
			return;
		}

643
		const newIdx = index - 1;
644
		return this.moveCellToIndex(index, newIdx);
645 646
	}

647
	private async moveCellToIndex(index: number, newIdx: number): Promise<void> {
R
rebornix 已提交
648
		if (!this.notebookViewModel!.moveCellToIdx(index, newIdx, true)) {
649 650 651
			return;
		}

652
		let r: () => void;
653 654
		DOM.scheduleAtNextAnimationFrame(() => {
			this.list?.revealInCenterIfOutsideViewport(index + 1);
655
			r();
656
		});
657 658

		return new Promise(resolve => { r = resolve; });
659 660
	}

R
rebornix 已提交
661
	editNotebookCell(cell: CellViewModel): void {
662
		cell.editState = CellEditState.Editing;
R
rebornix 已提交
663 664

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

R
rebornix 已提交
667
	saveNotebookCell(cell: ICellViewModel): void {
668
		cell.editState = CellEditState.Preview;
P
Peng Lyu 已提交
669 670
	}

R
rebornix 已提交
671 672 673 674 675 676 677 678 679 680
	getActiveCell() {
		let elements = this.list?.getFocusedElements();

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

		return undefined;
	}

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 716 717 718 719 720 721 722 723 724 725 726
	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;
	}

727
	async executeNotebookCell(cell: ICellViewModel): Promise<void> {
728
		const tokenSource = new CancellationTokenSource();
729 730 731 732 733 734 735 736
		try {
			this._executeNotebookCell(cell, tokenSource);
		} finally {
			tokenSource.dispose();
		}
	}

	async _executeNotebookCell(cell: ICellViewModel, tokenSource: CancellationTokenSource): Promise<void> {
737
		try {
738
			cell.currentTokenSource = tokenSource;
R
rebornix 已提交
739
			const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0];
740 741 742 743
			if (provider) {
				const viewType = provider.id;
				const notebookUri = CellUri.parse(cell.uri)?.notebook;
				if (notebookUri) {
744
					return await this.notebookService.executeNotebookCell(viewType, notebookUri, cell.handle, tokenSource.token);
745 746 747
				}
			}
		} finally {
748
			cell.currentTokenSource = undefined;
749 750 751
		}
	}

R
rebornix 已提交
752
	focusNotebookCell(cell: ICellViewModel, focusEditor: boolean) {
R
rebornix 已提交
753
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
754 755

		if (focusEditor) {
R
rebornix 已提交
756 757 758
			this.list?.setFocus([index]);
			this.list?.setSelection([index]);
			this.list?.focusView();
R
rebornix 已提交
759

760
			cell.editState = CellEditState.Editing;
R
rebornix 已提交
761
			cell.focusMode = CellFocusMode.Editor;
762
			this.revealInCenterIfOutsideViewport(cell);
R
rebornix 已提交
763
		} else {
R
rebornix 已提交
764
			let itemDOM = this.list?.domElementAtIndex(index);
R
rebornix 已提交
765 766 767
			if (document.activeElement && itemDOM && itemDOM.contains(document.activeElement)) {
				(document.activeElement as HTMLElement).blur();
			}
768

769
			cell.editState = CellEditState.Preview;
770
			cell.focusMode = CellFocusMode.Editor;
R
rebornix 已提交
771

R
rebornix 已提交
772 773
			this.list?.setFocus([index]);
			this.list?.setSelection([index]);
774
			this.revealInCenterIfOutsideViewport(cell);
R
rebornix 已提交
775
			this.list?.focusView();
R
rebornix 已提交
776 777 778
		}
	}

R
rebornix 已提交
779 780 781 782
	//#endregion

	//#region MISC

R
rebornix 已提交
783 784 785 786 787 788 789 790 791 792 793
	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 已提交
794

R
rebornix 已提交
795 796
	triggerScroll(event: IMouseWheelEvent) {
		this.list?.triggerScrollFromMouseWheelEvent(event);
R
rebornix 已提交
797 798
	}

R
rebornix 已提交
799
	createInset(cell: CodeCellViewModel, output: IOutput, shadowContent: string, offset: number) {
R
rebornix 已提交
800 801
		if (!this.webview) {
			return;
R
rebornix 已提交
802 803
		}

R
rebornix 已提交
804
		let preloads = this.notebookViewModel!.renderers;
R
rebornix 已提交
805

806
		if (!this.webview!.insetMapping.has(output)) {
R
rebornix 已提交
807
			let index = this.notebookViewModel!.getViewCellIndex(cell);
808 809 810
			let cellTop = this.list?.getAbsoluteTop(index) || 0;

			this.webview!.createInset(cell, output, cellTop, offset, shadowContent, preloads);
R
rebornix 已提交
811
		} else {
R
rebornix 已提交
812
			let index = this.notebookViewModel!.getViewCellIndex(cell);
813
			let cellTop = this.list?.getAbsoluteTop(index) || 0;
R
rebornix 已提交
814 815
			let scrollTop = this.list?.scrollTop || 0;

816
			this.webview!.updateViewScrollTop(-scrollTop, [{ cell: cell, output: output, cellTop: cellTop }]);
R
rebornix 已提交
817
		}
R
rebornix 已提交
818
	}
R
rebornix 已提交
819

R
rebornix 已提交
820 821 822 823 824 825 826 827
	removeInset(output: IOutput) {
		if (!this.webview) {
			return;
		}

		this.webview!.removeInset(output);
	}

R
rebornix 已提交
828 829
	getOutputRenderer(): OutputRenderer {
		return this.outputRenderer;
R
rebornix 已提交
830
	}
831

832 833 834 835
	postMessage(message: any) {
		this.webview?.webview.sendMessage(message);
	}

R
rebornix 已提交
836
	//#endregion
837 838 839 840 841 842

	toJSON(): any {
		return {
			notebookHandle: this.viewModel?.handle
		};
	}
P
Peng Lyu 已提交
843 844 845 846
}

const embeddedEditorBackground = 'walkThrough.embeddedEditorBackground';

847 848 849 850 851 852
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."));

853 854 855 856 857 858 859
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 已提交
860 861 862 863 864 865
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"));

866

P
Peng Lyu 已提交
867 868 869
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 已提交
870 871
		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 已提交
872 873 874
	}
	const link = theme.getColor(textLinkForeground);
	if (link) {
R
Rob Lourens 已提交
875
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .output a { color: ${link}; }`);
P
Peng Lyu 已提交
876 877 878
	}
	const activeLink = theme.getColor(textLinkActiveForeground);
	if (activeLink) {
R
Rob Lourens 已提交
879 880
		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 已提交
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898
	}
	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}; }`);
	}
899

900
	const containerBackground = theme.getColor(notebookOutputContainerColor);
901

902 903
	if (containerBackground) {
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .output { background-color: ${containerBackground}; }`);
904
	}
905

906 907 908 909 910 911
	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 已提交
912 913 914 915 916 917
	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} }`);
	}

918
	// Cell Margin
R
rebornix 已提交
919
	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; }`);
920
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .output { margin: 0px ${CELL_MARGIN}px 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px }`);
R
rebornix 已提交
921
	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 已提交
922

R
Rob Lourens 已提交
923
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .cell-editor-container { width: calc(100% - ${CELL_RUN_GUTTER}px); }`);
924
	collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .markdown-editor-container { margin-left: ${CELL_RUN_GUTTER}px; }`);
R
Rob Lourens 已提交
925 926
	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 已提交
927
});