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
3296 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
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
};
147
148
this._register(this.onDidChangeState(e => {
149
this.viewContext.eventDispatcher.emit([new NotebookCellStateChangedEvent(e, this.model)]);
150
151
if (e.foldingStateChanged) {
152
this._updateTotalHeight(this._computeTotalHeight(), CellLayoutContext.Fold);
153
}
154
}));
155
}
156
157
private _computeTotalHeight(): number {
158
const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
159
const { bottomToolbarGap } = this.viewContext.notebookOptions.computeBottomToolbarDimensions(this.viewType);
160
const foldHintHeight = this._computeFoldHintHeight();
161
162
if (this.getEditState() === CellEditState.Editing) {
163
return this._editorHeight
164
+ layoutConfiguration.markdownCellTopMargin
165
+ layoutConfiguration.markdownCellBottomMargin
166
+ bottomToolbarGap
167
+ this._statusBarHeight
168
+ this._commentHeight;
169
} else {
170
// @rebornix
171
// On file open, the previewHeight + bottomToolbarGap for a cell out of viewport can be 0
172
// When it's 0, the list view will never try to render it anymore even if we scroll the cell into view.
173
// Thus we make sure it's greater than 0
174
return Math.max(1, this._previewHeight + bottomToolbarGap + foldHintHeight + this._commentHeight);
175
}
176
}
177
178
private _computeFoldHintHeight(): number {
179
return (this.getEditState() === CellEditState.Editing || this.foldingState !== CellFoldingState.Collapsed) ?
180
0 : this.viewContext.notebookOptions.getLayoutConfiguration().markdownFoldHintHeight;
181
}
182
183
override updateOptions(e: NotebookOptionsChangeEvent) {
184
super.updateOptions(e);
185
if (e.cellStatusBarVisibility || e.insertToolbarPosition || e.cellToolbarLocation) {
186
this._updateTotalHeight(this._computeTotalHeight());
187
}
188
}
189
190
/**
191
* we put outputs stuff here to make compiler happy
192
*/
193
outputsViewModels: ICellOutputViewModel[] = [];
194
getOutputOffset(index: number): number {
195
// throw new Error('Method not implemented.');
196
return -1;
197
}
198
updateOutputHeight(index: number, height: number): void {
199
// throw new Error('Method not implemented.');
200
}
201
202
triggerFoldingStateChange() {
203
this._onDidChangeState.fire({ foldingStateChanged: true });
204
}
205
206
private _updateTotalHeight(newHeight: number, context?: CellLayoutContext) {
207
if (newHeight !== this.layoutInfo.totalHeight) {
208
this.layoutChange({ totalHeight: newHeight, context });
209
}
210
}
211
212
layoutChange(state: MarkupCellLayoutChangeEvent) {
213
let totalHeight: number;
214
let foldHintHeight: number;
215
if (!this.isInputCollapsed) {
216
totalHeight = state.totalHeight === undefined ?
217
(this._layoutInfo.layoutState ===
218
CellLayoutState.Uninitialized ?
219
100 :
220
this._layoutInfo.totalHeight) :
221
state.totalHeight;
222
// recompute
223
foldHintHeight = this._computeFoldHintHeight();
224
} else {
225
totalHeight =
226
this.viewContext.notebookOptions
227
.computeCollapsedMarkdownCellHeight(this.viewType);
228
state.totalHeight = totalHeight;
229
230
foldHintHeight = 0;
231
}
232
let commentOffset: number;
233
if (this.getEditState() === CellEditState.Editing) {
234
const notebookLayoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration();
235
commentOffset = notebookLayoutConfiguration.editorToolbarHeight
236
+ notebookLayoutConfiguration.cellTopMargin // CELL_TOP_MARGIN
237
+ this._chatHeight
238
+ this._editorHeight
239
+ this._statusBarHeight;
240
} else {
241
commentOffset = this._previewHeight;
242
}
243
244
this._layoutInfo = {
245
fontInfo: state.font || this._layoutInfo.fontInfo,
246
editorWidth: state.outerWidth !== undefined ?
247
this.viewContext.notebookOptions
248
.computeMarkdownCellEditorWidth(state.outerWidth) :
249
this._layoutInfo.editorWidth,
250
chatHeight: this._chatHeight,
251
editorHeight: this._editorHeight,
252
statusBarHeight: this._statusBarHeight,
253
previewHeight: this._previewHeight,
254
bottomToolbarOffset: this.viewContext.notebookOptions
255
.computeBottomToolbarOffset(
256
totalHeight, this.viewType),
257
totalHeight,
258
layoutState: CellLayoutState.Measured,
259
foldHintHeight,
260
commentOffset,
261
commentHeight: state.commentHeight ?
262
this._commentHeight :
263
this._layoutInfo.commentHeight,
264
};
265
266
this._onDidChangeLayout.fire(state);
267
}
268
269
override restoreEditorViewState(editorViewStates: editorCommon.ICodeEditorViewState | null, totalHeight?: number) {
270
super.restoreEditorViewState(editorViewStates);
271
// we might already warmup the viewport so the cell has a total height computed
272
if (totalHeight !== undefined && this.layoutInfo.layoutState === CellLayoutState.Uninitialized) {
273
this._layoutInfo = {
274
...this.layoutInfo,
275
totalHeight: totalHeight,
276
chatHeight: this._chatHeight,
277
editorHeight: this._editorHeight,
278
statusBarHeight: this._statusBarHeight,
279
layoutState: CellLayoutState.FromCache,
280
};
281
this.layoutChange({});
282
}
283
}
284
285
getDynamicHeight() {
286
return null;
287
}
288
289
getHeight(lineHeight: number) {
290
if (this._layoutInfo.layoutState === CellLayoutState.Uninitialized) {
291
return 100;
292
} else {
293
return this._layoutInfo.totalHeight;
294
}
295
}
296
297
protected onDidChangeTextModelContent(): void {
298
this._onDidChangeState.fire({ contentChanged: true });
299
}
300
301
onDeselect() {
302
}
303
304
305
private readonly _hasFindResult = this._register(new Emitter<boolean>());
306
public readonly hasFindResult: Event<boolean> = this._hasFindResult.event;
307
308
startFind(value: string, options: INotebookFindOptions): CellFindMatch | null {
309
const matches = super.cellStartFind(value, options);
310
311
if (matches === null) {
312
return null;
313
}
314
315
return {
316
cell: this,
317
contentMatches: matches
318
};
319
}
320
321
override dispose() {
322
super.dispose();
323
(this.foldingDelegate as any) = null;
324
}
325
}
326
327