Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts
5240 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Emitter, Event } from '../../../../../base/common/event.js';
7
import * as UUID from '../../../../../base/common/uuid.js';
8
import * as editorCommon from '../../../../../editor/common/editorCommon.js';
9
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
10
import { CellEditState, CellFindMatch, CellFoldingState, CellLayoutContext, CellLayoutState, EditorFoldingStateDelegate, ICellOutputViewModel, ICellViewModel, MarkupCellLayoutChangeEvent, MarkupCellLayoutInfo } from '../notebookBrowser.js';
11
import { BaseCellViewModel } from './baseCellViewModel.js';
12
import { NotebookCellTextModel } from '../../common/model/notebookCellTextModel.js';
13
import { CellKind, INotebookFindOptions } from '../../common/notebookCommon.js';
14
import { ITextModelService } from '../../../../../editor/common/services/resolverService.js';
15
import { ViewContext } from './viewContext.js';
16
import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js';
17
import { NotebookOptionsChangeEvent } from '../notebookOptions.js';
18
import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js';
19
import { NotebookCellStateChangedEvent, NotebookLayoutInfo } from '../notebookViewEvents.js';
20
import { IInlineChatSessionService } from '../../../inlineChat/browser/inlineChatSessionService.js';
21
22
export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewModel {
23
24
readonly cellKind = CellKind.Markup;
25
26
private _layoutInfo: MarkupCellLayoutInfo;
27
28
private _renderedHtml?: string;
29
30
public get renderedHtml(): string | undefined { return this._renderedHtml; }
31
public set renderedHtml(value: string | undefined) {
32
if (this._renderedHtml !== value) {
33
this._renderedHtml = value;
34
this._onDidChangeState.fire({ contentChanged: true });
35
}
36
}
37
38
get layoutInfo() {
39
return this._layoutInfo;
40
}
41
42
private _previewHeight = 0;
43
44
set renderedMarkdownHeight(newHeight: number) {
45
this._previewHeight = newHeight;
46
this._updateTotalHeight(this._computeTotalHeight());
47
}
48
49
private _chatHeight = 0;
50
51
set chatHeight(newHeight: number) {
52
this._chatHeight = newHeight;
53
this._updateTotalHeight(this._computeTotalHeight());
54
}
55
56
get chatHeight() {
57
return this._chatHeight;
58
}
59
60
private _editorHeight = 0;
61
private _statusBarHeight = 0;
62
set editorHeight(newHeight: number) {
63
this._editorHeight = newHeight;
64
this._statusBarHeight = this.viewContext.notebookOptions.computeStatusBarHeight();
65
this._updateTotalHeight(this._computeTotalHeight());
66
}
67
68
get editorHeight() {
69
throw new Error('MarkdownCellViewModel.editorHeight is write only');
70
}
71
72
protected readonly _onDidChangeLayout = this._register(new Emitter<MarkupCellLayoutChangeEvent>());
73
readonly onDidChangeLayout = this._onDidChangeLayout.event;
74
75
get foldingState() {
76
return this.foldingDelegate.getFoldingState(this.foldingDelegate.getCellIndex(this));
77
}
78
79
private _hoveringOutput: boolean = false;
80
public get outputIsHovered(): boolean {
81
return this._hoveringOutput;
82
}
83
84
public set outputIsHovered(v: boolean) {
85
this._hoveringOutput = v;
86
}
87
88
private _focusOnOutput: boolean = false;
89
public get outputIsFocused(): boolean {
90
return this._focusOnOutput;
91
}
92
93
public set outputIsFocused(v: boolean) {
94
this._focusOnOutput = v;
95
}
96
97
public get inputInOutputIsFocused(): boolean {
98
return false;
99
}
100
101
public set inputInOutputIsFocused(_: boolean) {
102
//
103
}
104
105
private _hoveringCell = false;
106
public get cellIsHovered(): boolean {
107
return this._hoveringCell;
108
}
109
110
public set cellIsHovered(v: boolean) {
111
this._hoveringCell = v;
112
this._onDidChangeState.fire({ cellIsHoveredChanged: true });
113
}
114
115
constructor(
116
viewType: string,
117
model: NotebookCellTextModel,
118
initialNotebookLayoutInfo: NotebookLayoutInfo | null,
119
readonly foldingDelegate: EditorFoldingStateDelegate,
120
readonly viewContext: ViewContext,
121
@IConfigurationService configurationService: IConfigurationService,
122
@ITextModelService textModelService: ITextModelService,
123
@IUndoRedoService undoRedoService: IUndoRedoService,
124
@ICodeEditorService codeEditorService: ICodeEditorService,
125
@IInlineChatSessionService inlineChatSessionService: IInlineChatSessionService
126
) {
127
super(viewType, model, UUID.generateUuid(), viewContext, configurationService, textModelService, undoRedoService, codeEditorService, inlineChatSessionService);
128
129
const { bottomToolbarGap } = this.viewContext.notebookOptions.computeBottomToolbarDimensions(this.viewType);
130
const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
131
this._layoutInfo = {
132
chatHeight: 0,
133
editorHeight: 0,
134
previewHeight: 0,
135
fontInfo: initialNotebookLayoutInfo?.fontInfo || null,
136
editorWidth: initialNotebookLayoutInfo?.width
137
? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(initialNotebookLayoutInfo.width)
138
: 0,
139
commentOffset: 0,
140
commentHeight: 0,
141
bottomToolbarOffset: bottomToolbarGap,
142
totalHeight: 100,
143
layoutState: CellLayoutState.Uninitialized,
144
foldHintHeight: 0,
145
statusBarHeight: 0,
146
outlineWidth: 1,
147
bottomMargin: layoutConfiguration.markdownCellBottomMargin,
148
topMargin: layoutConfiguration.markdownCellTopMargin,
149
};
150
151
this._register(this.onDidChangeState(e => {
152
this.viewContext.eventDispatcher.emit([new NotebookCellStateChangedEvent(e, this.model)]);
153
154
if (e.foldingStateChanged) {
155
this._updateTotalHeight(this._computeTotalHeight(), CellLayoutContext.Fold);
156
}
157
}));
158
}
159
160
private _computeTotalHeight(): number {
161
const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
162
const { bottomToolbarGap } = this.viewContext.notebookOptions.computeBottomToolbarDimensions(this.viewType);
163
const foldHintHeight = this._computeFoldHintHeight();
164
165
if (this.getEditState() === CellEditState.Editing) {
166
return this._editorHeight
167
+ layoutConfiguration.markdownCellTopMargin
168
+ layoutConfiguration.markdownCellBottomMargin
169
+ bottomToolbarGap
170
+ this._statusBarHeight
171
+ this._commentHeight;
172
} else {
173
// @rebornix
174
// On file open, the previewHeight + bottomToolbarGap for a cell out of viewport can be 0
175
// When it's 0, the list view will never try to render it anymore even if we scroll the cell into view.
176
// Thus we make sure it's greater than 0
177
return Math.max(1, this._previewHeight + bottomToolbarGap + foldHintHeight + this._commentHeight);
178
}
179
}
180
181
private _computeFoldHintHeight(): number {
182
return (this.getEditState() === CellEditState.Editing || this.foldingState !== CellFoldingState.Collapsed) ?
183
0 : this.viewContext.notebookOptions.getLayoutConfiguration().markdownFoldHintHeight;
184
}
185
186
override updateOptions(e: NotebookOptionsChangeEvent) {
187
super.updateOptions(e);
188
if (e.cellStatusBarVisibility || e.insertToolbarPosition || e.cellToolbarLocation) {
189
this._updateTotalHeight(this._computeTotalHeight());
190
}
191
}
192
193
/**
194
* we put outputs stuff here to make compiler happy
195
*/
196
outputsViewModels: ICellOutputViewModel[] = [];
197
getOutputOffset(index: number): number {
198
// throw new Error('Method not implemented.');
199
return -1;
200
}
201
updateOutputHeight(index: number, height: number): void {
202
// throw new Error('Method not implemented.');
203
}
204
205
triggerFoldingStateChange() {
206
this._onDidChangeState.fire({ foldingStateChanged: true });
207
}
208
209
private _updateTotalHeight(newHeight: number, context?: CellLayoutContext) {
210
if (newHeight !== this.layoutInfo.totalHeight) {
211
this.layoutChange({ totalHeight: newHeight, context });
212
}
213
}
214
215
layoutChange(state: MarkupCellLayoutChangeEvent) {
216
let totalHeight: number;
217
let foldHintHeight: number;
218
if (!this.isInputCollapsed) {
219
totalHeight = state.totalHeight === undefined ?
220
(this._layoutInfo.layoutState ===
221
CellLayoutState.Uninitialized ?
222
100 :
223
this._layoutInfo.totalHeight) :
224
state.totalHeight;
225
// recompute
226
foldHintHeight = this._computeFoldHintHeight();
227
} else {
228
totalHeight =
229
this.viewContext.notebookOptions
230
.computeCollapsedMarkdownCellHeight(this.viewType);
231
state.totalHeight = totalHeight;
232
233
foldHintHeight = 0;
234
}
235
let commentOffset: number;
236
const notebookLayoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
237
if (this.getEditState() === CellEditState.Editing) {
238
commentOffset = notebookLayoutConfiguration.editorToolbarHeight
239
+ notebookLayoutConfiguration.cellTopMargin // CELL_TOP_MARGIN
240
+ this._chatHeight
241
+ this._editorHeight
242
+ this._statusBarHeight;
243
} else {
244
commentOffset = this._previewHeight;
245
}
246
247
this._layoutInfo = {
248
fontInfo: state.font || this._layoutInfo.fontInfo,
249
editorWidth: state.outerWidth !== undefined ?
250
this.viewContext.notebookOptions
251
.computeMarkdownCellEditorWidth(state.outerWidth) :
252
this._layoutInfo.editorWidth,
253
chatHeight: this._chatHeight,
254
editorHeight: this._editorHeight,
255
statusBarHeight: this._statusBarHeight,
256
previewHeight: this._previewHeight,
257
bottomToolbarOffset: this.viewContext.notebookOptions
258
.computeBottomToolbarOffset(
259
totalHeight, this.viewType),
260
totalHeight,
261
layoutState: CellLayoutState.Measured,
262
foldHintHeight,
263
commentOffset,
264
commentHeight: state.commentHeight ?
265
this._commentHeight :
266
this._layoutInfo.commentHeight,
267
outlineWidth: 1,
268
bottomMargin: notebookLayoutConfiguration.markdownCellBottomMargin,
269
topMargin: notebookLayoutConfiguration.markdownCellTopMargin,
270
};
271
272
this._onDidChangeLayout.fire(state);
273
}
274
275
override restoreEditorViewState(editorViewStates: editorCommon.ICodeEditorViewState | null, totalHeight?: number) {
276
super.restoreEditorViewState(editorViewStates);
277
// we might already warmup the viewport so the cell has a total height computed
278
if (totalHeight !== undefined && this.layoutInfo.layoutState === CellLayoutState.Uninitialized) {
279
this._layoutInfo = {
280
...this.layoutInfo,
281
totalHeight: totalHeight,
282
chatHeight: this._chatHeight,
283
editorHeight: this._editorHeight,
284
statusBarHeight: this._statusBarHeight,
285
layoutState: CellLayoutState.FromCache,
286
};
287
this.layoutChange({});
288
}
289
}
290
291
getDynamicHeight() {
292
return null;
293
}
294
295
getHeight(lineHeight: number) {
296
if (this._layoutInfo.layoutState === CellLayoutState.Uninitialized) {
297
return 100;
298
} else {
299
return this._layoutInfo.totalHeight;
300
}
301
}
302
303
protected onDidChangeTextModelContent(): void {
304
this._onDidChangeState.fire({ contentChanged: true });
305
}
306
307
onDeselect() {
308
}
309
310
311
private readonly _hasFindResult = this._register(new Emitter<boolean>());
312
public readonly hasFindResult: Event<boolean> = this._hasFindResult.event;
313
314
startFind(value: string, options: INotebookFindOptions): CellFindMatch | null {
315
const matches = super.cellStartFind(value, options);
316
317
if (matches === null) {
318
return null;
319
}
320
321
return {
322
cell: this,
323
contentMatches: matches
324
};
325
}
326
327
override dispose() {
328
super.dispose();
329
(this.foldingDelegate as unknown) = null;
330
}
331
}
332
333