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

import { Emitter, Event } from 'vs/base/common/event';
import * as UUID from 'vs/base/common/uuid';
R
rebornix 已提交
8
import * as editorCommon from 'vs/editor/common/editorCommon';
R
rebornix 已提交
9 10 11
import * as model from 'vs/editor/common/model';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
R
rebornix 已提交
12 13
import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFindMatch, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
R
rebornix 已提交
14 15
import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer';
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
R
rebornix 已提交
16
import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
R
rebornix 已提交
17
import { FoldingRegionDelegate } from 'vs/workbench/contrib/notebook/browser/viewModel/foldingModel';
R
rebornix 已提交
18 19
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
R
rebornix 已提交
20 21 22 23 24

export class MarkdownCellViewModel extends BaseCellViewModel implements ICellViewModel {
	cellKind: CellKind.Markdown = CellKind.Markdown;
	private _mdRenderer: MarkdownRenderer | null = null;
	private _html: HTMLElement | null = null;
R
rebornix 已提交
25 26 27 28 29 30
	private _layoutInfo: MarkdownCellLayoutInfo;

	get layoutInfo() {
		return this._layoutInfo;
	}

R
rebornix 已提交
31 32 33 34 35 36 37 38
	set totalHeight(newHeight: number) {
		this.layoutChange({ totalHeight: newHeight });
	}

	get totalHeight() {
		throw new Error('MarkdownCellViewModel.totalHeight is write only');
	}

R
rebornix 已提交
39 40
	protected readonly _onDidChangeLayout = new Emitter<MarkdownCellLayoutChangeEvent>();
	readonly onDidChangeLayout = this._onDidChangeLayout.event;
R
rebornix 已提交
41

R
rebornix 已提交
42 43 44 45
	get foldingState() {
		return this.foldingDelegate.getFoldingState(this);
	}

R
rebornix 已提交
46 47 48
	constructor(
		readonly viewType: string,
		readonly notebookHandle: number,
R
rebornix 已提交
49
		readonly model: NotebookCellTextModel,
R
rebornix 已提交
50
		readonly eventDispatcher: NotebookEventDispatcher,
51
		initialNotebookLayoutInfo: NotebookLayoutInfo | null,
R
rebornix 已提交
52
		readonly foldingDelegate: FoldingRegionDelegate,
R
rebornix 已提交
53 54
		@IInstantiationService private readonly _instaService: IInstantiationService,
		@ITextModelService private readonly _modelService: ITextModelService) {
R
rebornix 已提交
55
		super(viewType, notebookHandle, model, UUID.generateUuid());
R
rebornix 已提交
56 57

		this._layoutInfo = {
58
			fontInfo: initialNotebookLayoutInfo?.fontInfo || null,
R
rebornix 已提交
59
			editorWidth: initialNotebookLayoutInfo?.width || 0,
R
rebornix 已提交
60 61
			bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_HEIGHT,
			totalHeight: 0
R
rebornix 已提交
62 63
		};

R
rebornix 已提交
64 65 66
		this._register(eventDispatcher.onDidChangeLayout((e) => {
			if (e.source.width || e.source.fontInfo) {
				this.layoutChange({ outerWidth: e.value.width, font: e.value.fontInfo });
R
rebornix 已提交
67 68
			}
		}));
R
rebornix 已提交
69 70

		this._register(foldingDelegate.onDidFoldingRegionChanged(() => {
R
rebornix 已提交
71
			this._onDidChangeState.fire({ foldingStateChanged: true });
R
rebornix 已提交
72
		}));
R
rebornix 已提交
73 74 75 76
	}

	layoutChange(state: MarkdownCellLayoutChangeEvent) {
		// recompute
77
		const editorWidth = state.outerWidth !== undefined ? state.outerWidth - CELL_MARGIN * 2 - CELL_RUN_GUTTER : this._layoutInfo.editorWidth;
R
rebornix 已提交
78 79

		this._layoutInfo = {
80
			fontInfo: state.font || this._layoutInfo.fontInfo,
R
rebornix 已提交
81
			editorWidth,
R
rebornix 已提交
82 83
			bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_HEIGHT,
			totalHeight: state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight
R
rebornix 已提交
84 85 86
		};

		this._onDidChangeLayout.fire(state);
R
rebornix 已提交
87 88
	}

R
rebornix 已提交
89 90 91 92 93 94 95 96 97 98 99 100
	restoreEditorViewState(editorViewStates: editorCommon.ICodeEditorViewState | null, totalHeight?: number) {
		super.restoreEditorViewState(editorViewStates);
		if (totalHeight !== undefined) {
			this._layoutInfo = {
				fontInfo: this._layoutInfo.fontInfo,
				editorWidth: this._layoutInfo.editorWidth,
				bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset,
				totalHeight: totalHeight
			};
		}
	}

R
rebornix 已提交
101 102 103 104 105
	hasDynamicHeight() {
		return true;
	}

	getHeight(lineHeight: number) {
R
rebornix 已提交
106 107 108 109 110
		if (this._layoutInfo.totalHeight === 0) {
			return 100;
		} else {
			return this._layoutInfo.totalHeight;
		}
R
rebornix 已提交
111 112 113
	}

	setText(strs: string[]) {
R
rebornix 已提交
114
		this.model.source = strs;
R
rebornix 已提交
115 116 117 118
		this._html = null;
	}

	save() {
119
		if (this._textModel && !this._textModel.isDisposed() && this.editState === CellEditState.Editing) {
R
rebornix 已提交
120
			let cnt = this._textModel.getLineCount();
R
rebornix 已提交
121
			this.model.source = this._textModel.getLinesContent().map((str, index) => str + (index !== cnt - 1 ? '\n' : ''));
R
rebornix 已提交
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
		}
	}

	getHTML(): HTMLElement | null {
		if (this.cellKind === CellKind.Markdown) {
			if (this._html) {
				return this._html;
			}
			let renderer = this.getMarkdownRenderer();
			this._html = renderer.render({ value: this.getText(), isTrusted: true }).element;
			return this._html;
		}
		return null;
	}

	async resolveTextModel(): Promise<model.ITextModel> {
		if (!this._textModel) {
R
rebornix 已提交
139
			const ref = await this._modelService.createModelReference(this.model.uri);
R
rebornix 已提交
140 141 142 143
			this._textModel = ref.object.textEditorModel;
			this._buffer = this._textModel.getTextBuffer();
			this._register(ref);
			this._register(this._textModel.onDidChangeContent(() => {
R
rebornix 已提交
144
				this.model.contentChange();
R
rebornix 已提交
145
				this._html = null;
R
rebornix 已提交
146
				this._onDidChangeState.fire({ contentChanged: true });
R
rebornix 已提交
147 148 149 150 151 152
			}));
		}
		return this._textModel;
	}

	onDeselect() {
153
		this.editState = CellEditState.Preview;
R
rebornix 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
	}

	getMarkdownRenderer() {
		if (!this._mdRenderer) {
			this._mdRenderer = this._instaService.createInstance(MarkdownRenderer);
		}
		return this._mdRenderer;
	}

	private readonly _hasFindResult = this._register(new Emitter<boolean>());
	public readonly hasFindResult: Event<boolean> = this._hasFindResult.event;

	startFind(value: string): CellFindMatch | null {
		const matches = super.cellStartFind(value);

		if (matches === null) {
			return null;
		}

		return {
			cell: this,
			matches
		};
	}
}