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.
 *--------------------------------------------------------------------------------------------*/

B
Benjamin Pasero 已提交
6
import 'vs/css!./media/notebook';
R
rebornix 已提交
7
import { getZoomLevel } from 'vs/base/browser/browser';
P
Peng Lyu 已提交
8
import * as DOM from 'vs/base/browser/dom';
R
rebornix 已提交
9
import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
10 11 12
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 已提交
13
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
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
		];

J
João Moreno 已提交
190
		this.list = 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,
J
João Moreno 已提交
224 225 226
				},
				accessibilityProvider: {
					getAriaLabel() { return null; }
P
Peng Lyu 已提交
227
				}
R
rebornix 已提交
228
			},
P
Peng Lyu 已提交
229
		);
P
Peng Lyu 已提交
230

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

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

		// 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 已提交
259 260
	}

261 262 263 264
	getControl() {
		return this.control;
	}

R
rebornix 已提交
265 266 267 268
	getInnerWebview(): Webview | undefined {
		return this.webview?.webview;
	}

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

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

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

R
rebornix 已提交
298 299 300 301 302
	focus() {
		super.focus();
		this.editorFocus?.set(true);
	}

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

R
rebornix 已提交
308 309
		await super.setInput(input, options, token);
		const model = await input.resolve();
P
Peng Lyu 已提交
310

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

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

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

		super.clearInput();
	}

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

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

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

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

R
rebornix 已提交
376 377 378
		this.localStore.add(this.notebookViewModel.onDidChangeViewCells((e) => {
			if (e.synchronous) {
				e.splices.reverse().forEach((diff) => {
379 380 381
					// remove output in the webview
					for (let i = diff[0]; i < diff[0] + diff[1]; i++) {
						const cell = this.list?.element(i);
R
rebornix 已提交
382
						cell?.model.outputs.forEach(output => {
383 384 385 386
							this.removeInset(output);
						});
					}

R
rebornix 已提交
387 388 389 390 391
					this.list?.splice(diff[0], diff[1], diff[2]);
				});
			} else {
				DOM.scheduleAtNextAnimationFrame(() => {
					e.splices.reverse().forEach((diff) => {
392 393 394
						// remove output in the webview
						for (let i = diff[0]; i < diff[0] + diff[1]; i++) {
							const cell = this.list?.element(i);
R
rebornix 已提交
395
							cell?.model.outputs.forEach(output => {
396 397 398 399
								this.removeInset(output);
							});
						}

R
rebornix 已提交
400 401 402 403
						this.list?.splice(diff[0], diff[1], diff[2]);
					});
				});
			}
R
rebornix 已提交
404 405 406 407 408 409
		}));

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

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

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

			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 已提交
430
					}
R
rebornix 已提交
431
				});
432

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

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

		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 已提交
449 450
	}

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

R
rebornix 已提交
469
			this.editorMemento.saveEditorState(this.group, input.resource, state);
R
rebornix 已提交
470 471 472 473 474
		}
	}

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

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

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

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

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

		super.saveState();
	}

	//#endregion

R
rebornix 已提交
507 508
	//#region Editor Features

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

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

R
rebornix 已提交
518
	revealInView(cell: ICellViewModel) {
R
rebornix 已提交
519 520 521
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
522
			this.list?.revealInView(index);
R
rebornix 已提交
523 524 525
		}
	}

R
rebornix 已提交
526
	revealInCenterIfOutsideViewport(cell: ICellViewModel) {
R
rebornix 已提交
527 528 529
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
530
			this.list?.revealInCenterIfOutsideViewport(index);
R
rebornix 已提交
531 532 533
		}
	}

R
rebornix 已提交
534
	revealInCenter(cell: ICellViewModel) {
R
rebornix 已提交
535 536 537
		const index = this.notebookViewModel?.getViewCellIndex(cell);

		if (index !== undefined) {
R
rebornix 已提交
538 539 540 541
			this.list?.revealInCenter(index);
		}
	}

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

		if (index !== undefined) {
			this.list?.revealLineInView(index, line);
R
rebornix 已提交
547 548
		}
	}
R
rebornix 已提交
549

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

		if (index !== undefined) {
R
rebornix 已提交
554
			this.list?.revealLineInCenter(index, line);
R
rebornix 已提交
555 556 557
		}
	}

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

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

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

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

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

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

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

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

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

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

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

	//#endregion

R
rebornix 已提交
604 605 606 607 608 609 610 611
	//#region Find Delegate

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

	public hideFind() {
		this.findWidget.hide();
R
rebornix 已提交
612
		this.focus();
R
rebornix 已提交
613 614 615 616
	}

	//#endregion

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

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

		return new Promise(resolve => { r = resolve; });
633 634
	}

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

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

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

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

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

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

668
		const newIdx = index + 1;
669
		return this.moveCellToIndex(index, newIdx);
670 671
	}

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

678
		const newIdx = index - 1;
679
		return this.moveCellToIndex(index, newIdx);
680 681
	}

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

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

		return new Promise(resolve => { r = resolve; });
694 695
	}

R
rebornix 已提交
696
	editNotebookCell(cell: CellViewModel): void {
697
		cell.editState = CellEditState.Editing;
R
rebornix 已提交
698 699

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

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

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

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

		return undefined;
	}

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 759 760 761
	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;
	}

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

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

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

		if (focusEditor) {
R
rebornix 已提交
791 792 793
			this.list?.setFocus([index]);
			this.list?.setSelection([index]);
			this.list?.focusView();
R
rebornix 已提交
794

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

804
			cell.editState = CellEditState.Preview;
805
			cell.focusMode = CellFocusMode.Editor;
R
rebornix 已提交
806

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

R
rebornix 已提交
814 815 816 817
	//#endregion

	//#region MISC

R
rebornix 已提交
818 819 820 821 822 823 824 825 826 827 828
	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 已提交
829

R
rebornix 已提交
830 831
	triggerScroll(event: IMouseWheelEvent) {
		this.list?.triggerScrollFromMouseWheelEvent(event);
R
rebornix 已提交
832 833
	}

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

R
rebornix 已提交
839
		let preloads = this.notebookViewModel!.renderers;
R
rebornix 已提交
840

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

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

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

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

		this.webview!.removeInset(output);
	}

R
rebornix 已提交
863 864
	getOutputRenderer(): OutputRenderer {
		return this.outputRenderer;
R
rebornix 已提交
865
	}
866

867 868 869 870
	postMessage(message: any) {
		this.webview?.webview.sendMessage(message);
	}

R
rebornix 已提交
871
	//#endregion
872 873 874 875 876 877

	toJSON(): any {
		return {
			notebookHandle: this.viewModel?.handle
		};
	}
P
Peng Lyu 已提交
878 879 880 881
}

const embeddedEditorBackground = 'walkThrough.embeddedEditorBackground';

882 883 884 885 886 887
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."));

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

901

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

935
	const containerBackground = theme.getColor(notebookOutputContainerColor);
936

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

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

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

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