notebookEditor.ts 34.2 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';
R
rebornix 已提交
8
import { IMouseWheelEvent, StandardMouseEvent } 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';
R
rebornix 已提交
46
import { Webview } from 'vs/workbench/contrib/webview/browser/webview';
P
Peng Lyu 已提交
47 48

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

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
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 已提交
66
export class NotebookCodeEditors implements ICompositeCodeEditor {
67

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

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

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

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

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

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

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

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

P
Peng Lyu 已提交
135 136 137 138 139 140 141 142
	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 已提交
143
	//#region Editor Core
R
rebornix 已提交
144

R
rebornix 已提交
145 146 147 148 149

	public get isNotebookEditor() {
		return true;
	}

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

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

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

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

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

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

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

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

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

P
Peng Lyu 已提交
237
		this._register(this.list);
R
rebornix 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255

		// transparent cover
		this.webviewTransparentCover = DOM.append(this.list.rowsContainer, $('.webview-cover'));
		this.webviewTransparentCover.style.display = 'none';

		this._register(DOM.addStandardDisposableGenericMouseDownListner(this.rootElement, (e: StandardMouseEvent) => {
			if (DOM.hasClass(e.target, 'slider') && this.webviewTransparentCover) {
				this.webviewTransparentCover.style.display = 'block';
			}
		}));

		this._register(DOM.addStandardDisposableGenericMouseUpListner(this.rootElement, (e: StandardMouseEvent) => {
			if (this.webviewTransparentCover) {
				// no matter when
				this.webviewTransparentCover.style.display = 'none';
			}
		}));

P
Peng Lyu 已提交
256 257
	}

258 259 260 261
	getControl() {
		return this.control;
	}

R
rebornix 已提交
262 263 264 265
	getInnerWebview(): Webview | undefined {
		return this.webview?.webview;
	}

P
Peng Lyu 已提交
266
	onHide() {
R
rebornix 已提交
267
		this.editorFocus?.set(false);
268 269
		if (this.webview) {
			this.localStore.clear();
R
rebornix 已提交
270
			this.list?.rowsContainer.removeChild(this.webview?.element);
271 272 273 274 275 276
			this.webview?.dispose();
			this.webview = null;
		}

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

R
rebornix 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291
	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);
292 293 294
		}
	}

R
rebornix 已提交
295 296 297 298 299
	focus() {
		super.focus();
		this.editorFocus?.set(true);
	}

R
rebornix 已提交
300
	async setInput(input: NotebookEditorInput, options: EditorOptions | undefined, token: CancellationToken): Promise<void> {
R
rebornix 已提交
301 302 303 304
		if (this.input instanceof NotebookEditorInput) {
			this.saveTextEditorViewState(this.input);
		}

R
rebornix 已提交
305 306
		await super.setInput(input, options, token);
		const model = await input.resolve();
P
Peng Lyu 已提交
307

308 309 310
		if (this.notebookViewModel === undefined || !this.notebookViewModel.equal(model) || this.webview === null) {
			this.detachModel();
			await this.attachModel(input, model);
R
rebornix 已提交
311
		}
P
Peng Lyu 已提交
312

313 314 315
		// reveal cell if editor options tell to do so
		if (options instanceof NotebookEditorOptions && options.cellOptions) {
			const cellOptions = options.cellOptions;
R
rebornix 已提交
316
			const cell = this.notebookViewModel!.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString());
317
			if (cell) {
318
				this.selectElement(cell);
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
				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();
					}
				}
334 335
			}
		}
R
rebornix 已提交
336
	}
337

R
rebornix 已提交
338 339 340 341 342 343 344 345
	clearInput(): void {
		if (this.input && this.input instanceof NotebookEditorInput && !this.input.isDisposed()) {
			this.saveTextEditorViewState(this.input);
		}

		super.clearInput();
	}

R
rebornix 已提交
346 347 348 349 350 351
	private detachModel() {
		this.localStore.clear();
		this.notebookViewModel?.dispose();
		this.notebookViewModel = undefined;
		this.webview?.clearInsets();
		this.webview?.clearPreloadsCache();
352
		this.findWidget.clear();
353
		this.list?.splice(0, this.list?.length || 0);
R
rebornix 已提交
354
	}
R
rebornix 已提交
355

R
rebornix 已提交
356 357
	private async attachModel(input: NotebookEditorInput, model: NotebookEditorModel) {
		if (!this.webview) {
358
			this.webview = this.instantiationService.createInstance(BackLayerWebView, this);
R
rebornix 已提交
359 360
			this.list?.rowsContainer.insertAdjacentElement('afterbegin', this.webview!.element);
		}
361

R
rebornix 已提交
362
		this.eventDispatcher = new NotebookEventDispatcher();
363
		this.notebookViewModel = this.instantiationService.createInstance(NotebookViewModel, input.viewType!, model, this.eventDispatcher, this.getLayoutInfo());
364
		this.editorEditable?.set(!!this.notebookViewModel.metadata?.editable);
R
rebornix 已提交
365
		this.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
R
rebornix 已提交
366
		const viewState = this.loadTextEditorViewState(input);
R
rebornix 已提交
367
		this.notebookViewModel.restoreEditorViewState(viewState);
368

R
rebornix 已提交
369 370 371 372
		this.localStore.add(this.eventDispatcher.onDidChangeMetadata((e) => {
			this.editorEditable?.set(e.source.editable);
		}));

R
rebornix 已提交
373 374 375
		this.localStore.add(this.notebookViewModel.onDidChangeViewCells((e) => {
			if (e.synchronous) {
				e.splices.reverse().forEach((diff) => {
376 377 378 379 380 381 382 383
					// 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 已提交
384 385 386 387 388
					this.list?.splice(diff[0], diff[1], diff[2]);
				});
			} else {
				DOM.scheduleAtNextAnimationFrame(() => {
					e.splices.reverse().forEach((diff) => {
389 390 391 392 393 394 395 396
						// 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 已提交
397 398 399 400
						this.list?.splice(diff[0], diff[1], diff[2]);
					});
				});
			}
R
rebornix 已提交
401 402 403 404 405 406
		}));

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

		this.localStore.add(this.list!.onWillScroll(e => {
			this.webview!.updateViewScrollTop(-e.scrollTop, []);
R
rebornix 已提交
407
			this.webviewTransparentCover!.style.top = `${e.scrollTop}px`;
R
rebornix 已提交
408 409 410 411 412 413
		}));

		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 已提交
414
			let updateItems: { cell: CodeCellViewModel, output: IOutput, cellTop: number }[] = [];
R
rebornix 已提交
415 416 417 418 419 420 421 422 423 424 425 426

			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 已提交
427
					}
R
rebornix 已提交
428
				});
429

R
rebornix 已提交
430 431 432 433 434 435
				if (updateItems.length) {
					this.webview?.updateViewScrollTop(-scrollTop, updateItems);
				}
			}
		}));

436
		this.list?.splice(0, 0, this.notebookViewModel!.viewCells as CellViewModel[]);
R
rebornix 已提交
437
		this.list?.layout();
R
rebornix 已提交
438 439 440 441 442 443 444 445

		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;
		}
P
Peng Lyu 已提交
446 447
	}

R
rebornix 已提交
448
	private saveTextEditorViewState(input: NotebookEditorInput): void {
R
npe  
rebornix 已提交
449
		if (this.group && this.notebookViewModel) {
R
rebornix 已提交
450
			const state = this.notebookViewModel.saveEditorViewState();
R
rebornix 已提交
451 452
			if (this.list) {
				state.scrollPosition = { left: this.list.scrollLeft, top: this.list.scrollTop };
453 454 455 456 457 458 459 460 461 462 463
				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 已提交
464 465
			}

R
rebornix 已提交
466
			this.editorMemento.saveEditorState(this.group, input.resource, state);
R
rebornix 已提交
467 468 469 470 471
		}
	}

	private loadTextEditorViewState(input: NotebookEditorInput): INotebookEditorViewState | undefined {
		if (this.group) {
R
rebornix 已提交
472
			return this.editorMemento.loadEditorState(this.group, input.resource);
R
rebornix 已提交
473 474 475 476 477 478 479 480 481 482
		}

		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);
483
		this.list?.updateOptions({ additionalScrollHeight: dimension.height });
R
rebornix 已提交
484
		this.list?.layout(dimension.height, dimension.width);
R
rebornix 已提交
485 486 487 488 489 490

		if (this.webviewTransparentCover) {
			this.webviewTransparentCover.style.height = `${dimension.height}px`;
			this.webviewTransparentCover.style.width = `${dimension.width}px`;
		}

R
rebornix 已提交
491
		this.eventDispatcher?.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);
R
rebornix 已提交
492 493 494 495 496 497 498 499 500 501 502 503
	}

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

		super.saveState();
	}

	//#endregion

R
rebornix 已提交
504 505
	//#region Editor Features

R
rebornix 已提交
506
	selectElement(cell: ICellViewModel) {
R
rebornix 已提交
507 508 509 510 511 512 513 514
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

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

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

R
rebornix 已提交
523
	revealInCenterIfOutsideViewport(cell: ICellViewModel) {
R
rebornix 已提交
524 525 526
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
527
			this.list?.revealInCenterIfOutsideViewport(index);
R
rebornix 已提交
528 529 530
		}
	}

R
rebornix 已提交
531
	revealInCenter(cell: ICellViewModel) {
R
rebornix 已提交
532 533 534
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
535 536 537 538
			this.list?.revealInCenter(index);
		}
	}

R
rebornix 已提交
539
	revealLineInView(cell: ICellViewModel, line: number): void {
R
rebornix 已提交
540 541 542 543
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
			this.list?.revealLineInView(index, line);
R
rebornix 已提交
544 545
		}
	}
R
rebornix 已提交
546

R
rebornix 已提交
547
	revealLineInCenter(cell: ICellViewModel, line: number) {
R
rebornix 已提交
548 549 550
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
551
			this.list?.revealLineInCenter(index, line);
R
rebornix 已提交
552 553 554
		}
	}

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

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

R
rebornix 已提交
563
	revealRangeInView(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
564 565 566 567 568 569 570
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
571
	revealRangeInCenter(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
572 573 574 575 576 577 578
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
579
	revealRangeInCenterIfOutsideViewport(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
580 581 582 583 584 585 586
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
587
	setCellSelection(cell: ICellViewModel, range: Range): void {
R
rebornix 已提交
588 589 590 591 592 593 594
		const index = this.notebookViewModel?.getViewCellIndex(cell);

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

R
rebornix 已提交
595 596 597 598 599 600
	changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any {
		return this.notebookViewModel?.changeDecorations(callback);
	}

	//#endregion

R
rebornix 已提交
601 602 603 604 605 606 607 608
	//#region Find Delegate

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

	public hideFind() {
		this.findWidget.hide();
R
rebornix 已提交
609
		this.focus();
R
rebornix 已提交
610 611 612 613
	}

	//#endregion

R
rebornix 已提交
614
	//#region Cell operations
615
	async layoutNotebookCell(cell: ICellViewModel, height: number): Promise<void> {
R
rebornix 已提交
616
		let relayout = (cell: ICellViewModel, height: number) => {
R
rebornix 已提交
617
			let index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
618
			if (index >= 0) {
R
rebornix 已提交
619
				this.list?.updateElementHeight(index, height);
R
rebornix 已提交
620
			}
R
rebornix 已提交
621 622
		};

623
		let r: () => void;
R
rebornix 已提交
624
		DOM.scheduleAtNextAnimationFrame(() => {
R
rebornix 已提交
625
			relayout(cell, height);
626
			r();
R
rebornix 已提交
627
		});
628 629

		return new Promise(resolve => { r = resolve; });
630 631
	}

R
rebornix 已提交
632
	async insertNotebookCell(cell: ICellViewModel, type: CellKind, direction: 'above' | 'below', initialText: string = ''): Promise<void> {
R
rebornix 已提交
633 634 635
		const newLanguages = this.notebookViewModel!.languages;
		const language = newLanguages && newLanguages.length ? newLanguages[0] : 'markdown';
		const index = this.notebookViewModel!.getViewCellIndex(cell);
P
Peng Lyu 已提交
636
		const insertIndex = direction === 'above' ? index : index + 1;
R
rebornix 已提交
637
		const newCell = this.notebookViewModel!.createCell(insertIndex, initialText.split(/\r?\n/g), language, type, true);
R
rebornix 已提交
638
		this.list?.setFocus([insertIndex]);
P
Peng Lyu 已提交
639

R
rebornix 已提交
640
		if (type === CellKind.Markdown) {
641
			newCell.editState = CellEditState.Editing;
P
Peng Lyu 已提交
642
		}
R
rebornix 已提交
643

644
		let r: () => void;
R
rebornix 已提交
645
		DOM.scheduleAtNextAnimationFrame(() => {
R
rebornix 已提交
646
			this.list?.revealInCenterIfOutsideViewport(insertIndex);
647
			r();
R
rebornix 已提交
648
		});
649 650

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

R
rebornix 已提交
653
	async deleteNotebookCell(cell: ICellViewModel): Promise<void> {
654
		(cell as CellViewModel).save();
R
rebornix 已提交
655
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
656
		this.notebookViewModel!.deleteCell(index, true);
R
rebornix 已提交
657 658
	}

659
	async moveCellDown(cell: ICellViewModel): Promise<void> {
660
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
661 662 663 664
		if (index === this.notebookViewModel!.viewCells.length - 1) {
			return;
		}

665
		const newIdx = index + 1;
666
		return this.moveCellToIndex(index, newIdx);
667 668
	}

669
	async moveCellUp(cell: ICellViewModel): Promise<void> {
670
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
671 672 673 674
		if (index === 0) {
			return;
		}

675
		const newIdx = index - 1;
676
		return this.moveCellToIndex(index, newIdx);
677 678
	}

679
	private async moveCellToIndex(index: number, newIdx: number): Promise<void> {
R
rebornix 已提交
680
		if (!this.notebookViewModel!.moveCellToIdx(index, newIdx, true)) {
681 682 683
			return;
		}

684
		let r: () => void;
685 686
		DOM.scheduleAtNextAnimationFrame(() => {
			this.list?.revealInCenterIfOutsideViewport(index + 1);
687
			r();
688
		});
689 690

		return new Promise(resolve => { r = resolve; });
691 692
	}

R
rebornix 已提交
693
	editNotebookCell(cell: CellViewModel): void {
694
		cell.editState = CellEditState.Editing;
R
rebornix 已提交
695 696

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

R
rebornix 已提交
699
	saveNotebookCell(cell: ICellViewModel): void {
700
		cell.editState = CellEditState.Preview;
P
Peng Lyu 已提交
701 702
	}

R
rebornix 已提交
703 704 705 706 707 708 709 710 711 712
	getActiveCell() {
		let elements = this.list?.getFocusedElements();

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

		return undefined;
	}

713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
	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;
	}

759
	async executeNotebookCell(cell: ICellViewModel): Promise<void> {
760
		const tokenSource = new CancellationTokenSource();
761 762 763 764 765 766 767 768
		try {
			this._executeNotebookCell(cell, tokenSource);
		} finally {
			tokenSource.dispose();
		}
	}

	async _executeNotebookCell(cell: ICellViewModel, tokenSource: CancellationTokenSource): Promise<void> {
769
		try {
770
			cell.currentTokenSource = tokenSource;
R
rebornix 已提交
771
			const provider = this.notebookService.getContributedNotebookProviders(this.viewModel!.uri)[0];
772 773 774 775
			if (provider) {
				const viewType = provider.id;
				const notebookUri = CellUri.parse(cell.uri)?.notebook;
				if (notebookUri) {
776
					return await this.notebookService.executeNotebookCell(viewType, notebookUri, cell.handle, tokenSource.token);
777 778 779
				}
			}
		} finally {
780
			cell.currentTokenSource = undefined;
781 782 783
		}
	}

R
rebornix 已提交
784
	focusNotebookCell(cell: ICellViewModel, focusEditor: boolean) {
R
rebornix 已提交
785
		const index = this.notebookViewModel!.getViewCellIndex(cell);
R
rebornix 已提交
786 787

		if (focusEditor) {
R
rebornix 已提交
788 789 790
			this.list?.setFocus([index]);
			this.list?.setSelection([index]);
			this.list?.focusView();
R
rebornix 已提交
791

792
			cell.editState = CellEditState.Editing;
R
rebornix 已提交
793
			cell.focusMode = CellFocusMode.Editor;
794
			this.revealInCenterIfOutsideViewport(cell);
R
rebornix 已提交
795
		} else {
R
rebornix 已提交
796
			let itemDOM = this.list?.domElementAtIndex(index);
R
rebornix 已提交
797 798 799
			if (document.activeElement && itemDOM && itemDOM.contains(document.activeElement)) {
				(document.activeElement as HTMLElement).blur();
			}
800

801
			cell.editState = CellEditState.Preview;
802
			cell.focusMode = CellFocusMode.Editor;
R
rebornix 已提交
803

R
rebornix 已提交
804 805
			this.list?.setFocus([index]);
			this.list?.setSelection([index]);
806
			this.revealInCenterIfOutsideViewport(cell);
R
rebornix 已提交
807
			this.list?.focusView();
R
rebornix 已提交
808 809 810
		}
	}

R
rebornix 已提交
811 812 813 814
	//#endregion

	//#region MISC

R
rebornix 已提交
815 816 817 818 819 820 821 822 823 824 825
	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 已提交
826

R
rebornix 已提交
827 828
	triggerScroll(event: IMouseWheelEvent) {
		this.list?.triggerScrollFromMouseWheelEvent(event);
R
rebornix 已提交
829 830
	}

R
rebornix 已提交
831
	createInset(cell: CodeCellViewModel, output: IOutput, shadowContent: string, offset: number) {
R
rebornix 已提交
832 833
		if (!this.webview) {
			return;
R
rebornix 已提交
834 835
		}

R
rebornix 已提交
836
		let preloads = this.notebookViewModel!.renderers;
R
rebornix 已提交
837

838
		if (!this.webview!.insetMapping.has(output)) {
R
rebornix 已提交
839
			let index = this.notebookViewModel!.getViewCellIndex(cell);
840 841 842
			let cellTop = this.list?.getAbsoluteTop(index) || 0;

			this.webview!.createInset(cell, output, cellTop, offset, shadowContent, preloads);
R
rebornix 已提交
843
		} else {
R
rebornix 已提交
844
			let index = this.notebookViewModel!.getViewCellIndex(cell);
845
			let cellTop = this.list?.getAbsoluteTop(index) || 0;
R
rebornix 已提交
846 847
			let scrollTop = this.list?.scrollTop || 0;

848
			this.webview!.updateViewScrollTop(-scrollTop, [{ cell: cell, output: output, cellTop: cellTop }]);
R
rebornix 已提交
849
		}
R
rebornix 已提交
850
	}
R
rebornix 已提交
851

R
rebornix 已提交
852 853 854 855 856 857 858 859
	removeInset(output: IOutput) {
		if (!this.webview) {
			return;
		}

		this.webview!.removeInset(output);
	}

R
rebornix 已提交
860 861
	getOutputRenderer(): OutputRenderer {
		return this.outputRenderer;
R
rebornix 已提交
862
	}
863

864 865 866 867
	postMessage(message: any) {
		this.webview?.webview.sendMessage(message);
	}

R
rebornix 已提交
868
	//#endregion
869 870 871 872 873 874

	toJSON(): any {
		return {
			notebookHandle: this.viewModel?.handle
		};
	}
P
Peng Lyu 已提交
875 876 877 878
}

const embeddedEditorBackground = 'walkThrough.embeddedEditorBackground';

879 880 881 882 883 884
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."));

885 886 887 888 889 890 891
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 已提交
892 893 894 895 896 897
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"));

898

P
Peng Lyu 已提交
899 900 901
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 已提交
902 903
		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 已提交
904 905 906
	}
	const link = theme.getColor(textLinkForeground);
	if (link) {
R
Rob Lourens 已提交
907
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .output a { color: ${link}; }`);
P
Peng Lyu 已提交
908 909 910
	}
	const activeLink = theme.getColor(textLinkActiveForeground);
	if (activeLink) {
R
Rob Lourens 已提交
911 912
		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 已提交
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930
	}
	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}; }`);
	}
931

932
	const containerBackground = theme.getColor(notebookOutputContainerColor);
933

934 935
	if (containerBackground) {
		collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .output { background-color: ${containerBackground}; }`);
936
	}
937

938 939 940 941 942 943
	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 已提交
944 945 946 947 948 949
	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} }`);
	}

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

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