Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.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 { Disposable, IDisposable, IReference, MutableDisposable, dispose } from '../../../../../base/common/lifecycle.js';
8
import { Mimes } from '../../../../../base/common/mime.js';
9
import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js';
10
import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js';
11
import { IEditorCommentsOptions } from '../../../../../editor/common/config/editorOptions.js';
12
import { IPosition } from '../../../../../editor/common/core/position.js';
13
import { IRange, Range } from '../../../../../editor/common/core/range.js';
14
import { Selection } from '../../../../../editor/common/core/selection.js';
15
import * as editorCommon from '../../../../../editor/common/editorCommon.js';
16
import * as model from '../../../../../editor/common/model.js';
17
import { SearchParams } from '../../../../../editor/common/model/textModelSearch.js';
18
import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/services/resolverService.js';
19
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
20
import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js';
21
import { IWordWrapTransientState, readTransientState, writeTransientState } from '../../../codeEditor/browser/toggleWordWrap.js';
22
import { CellEditState, CellFocusMode, CellLayoutChangeEvent, CursorAtBoundary, CursorAtLineBoundary, IEditableCellViewModel, INotebookCellDecorationOptions } from '../notebookBrowser.js';
23
import { NotebookOptionsChangeEvent } from '../notebookOptions.js';
24
import { CellViewModelStateChangeEvent } from '../notebookViewEvents.js';
25
import { ViewContext } from './viewContext.js';
26
import { NotebookCellTextModel } from '../../common/model/notebookCellTextModel.js';
27
import { CellKind, INotebookCellStatusBarItem, INotebookFindOptions } from '../../common/notebookCommon.js';
28
import { IInlineChatSessionService } from '../../../inlineChat/browser/inlineChatSessionService.js';
29
30
export abstract class BaseCellViewModel extends Disposable {
31
32
protected readonly _onDidChangeEditorAttachState = this._register(new Emitter<void>());
33
// Do not merge this event with `onDidChangeState` as we are using `Event.once(onDidChangeEditorAttachState)` elsewhere.
34
readonly onDidChangeEditorAttachState = this._onDidChangeEditorAttachState.event;
35
protected readonly _onDidChangeState = this._register(new Emitter<CellViewModelStateChangeEvent>());
36
public readonly onDidChangeState: Event<CellViewModelStateChangeEvent> = this._onDidChangeState.event;
37
38
get handle() {
39
return this.model.handle;
40
}
41
get uri() {
42
return this.model.uri;
43
}
44
get lineCount() {
45
return this.model.textBuffer.getLineCount();
46
}
47
get metadata() {
48
return this.model.metadata;
49
}
50
get internalMetadata() {
51
return this.model.internalMetadata;
52
}
53
get language() {
54
return this.model.language;
55
}
56
57
get mime(): string {
58
if (typeof this.model.mime === 'string') {
59
return this.model.mime;
60
}
61
62
switch (this.language) {
63
case 'markdown':
64
return Mimes.markdown;
65
66
default:
67
return Mimes.text;
68
}
69
}
70
71
abstract cellKind: CellKind;
72
73
private _editState: CellEditState = CellEditState.Preview;
74
75
private _lineNumbers: 'on' | 'off' | 'inherit' = 'inherit';
76
get lineNumbers(): 'on' | 'off' | 'inherit' {
77
return this._lineNumbers;
78
}
79
80
set lineNumbers(lineNumbers: 'on' | 'off' | 'inherit') {
81
if (lineNumbers === this._lineNumbers) {
82
return;
83
}
84
85
this._lineNumbers = lineNumbers;
86
this._onDidChangeState.fire({ cellLineNumberChanged: true });
87
}
88
89
private _commentOptions: IEditorCommentsOptions;
90
public get commentOptions(): IEditorCommentsOptions {
91
return this._commentOptions;
92
}
93
94
public set commentOptions(newOptions: IEditorCommentsOptions) {
95
this._commentOptions = newOptions;
96
}
97
98
private _focusMode: CellFocusMode = CellFocusMode.Container;
99
get focusMode() {
100
return this._focusMode;
101
}
102
set focusMode(newMode: CellFocusMode) {
103
if (this._focusMode !== newMode) {
104
this._focusMode = newMode;
105
this._onDidChangeState.fire({ focusModeChanged: true });
106
}
107
}
108
109
protected _textEditor?: ICodeEditor;
110
get editorAttached(): boolean {
111
return !!this._textEditor;
112
}
113
private _editorListeners: IDisposable[] = [];
114
private _editorViewStates: editorCommon.ICodeEditorViewState | null = null;
115
private _editorTransientState: IWordWrapTransientState | null = null;
116
private _resolvedCellDecorations = new Map<string, INotebookCellDecorationOptions>();
117
private readonly _textModelRefChangeDisposable = this._register(new MutableDisposable());
118
119
private readonly _cellDecorationsChanged = this._register(new Emitter<{ added: INotebookCellDecorationOptions[]; removed: INotebookCellDecorationOptions[] }>());
120
onCellDecorationsChanged: Event<{ added: INotebookCellDecorationOptions[]; removed: INotebookCellDecorationOptions[] }> = this._cellDecorationsChanged.event;
121
122
private _resolvedDecorations = new Map<string, {
123
id?: string;
124
options: model.IModelDeltaDecoration;
125
}>();
126
private _lastDecorationId: number = 0;
127
128
private _cellStatusBarItems = new Map<string, INotebookCellStatusBarItem>();
129
private readonly _onDidChangeCellStatusBarItems = this._register(new Emitter<void>());
130
readonly onDidChangeCellStatusBarItems: Event<void> = this._onDidChangeCellStatusBarItems.event;
131
private _lastStatusBarId: number = 0;
132
133
get textModel(): model.ITextModel | undefined {
134
return this.model.textModel;
135
}
136
137
hasModel(): this is IEditableCellViewModel {
138
return !!this.textModel;
139
}
140
141
private _dragging: boolean = false;
142
get dragging(): boolean {
143
return this._dragging;
144
}
145
146
set dragging(v: boolean) {
147
this._dragging = v;
148
this._onDidChangeState.fire({ dragStateChanged: true });
149
}
150
151
protected _textModelRef: IReference<IResolvedTextEditorModel> | undefined;
152
153
private _inputCollapsed: boolean = false;
154
get isInputCollapsed(): boolean {
155
return this._inputCollapsed;
156
}
157
set isInputCollapsed(v: boolean) {
158
this._inputCollapsed = v;
159
this._onDidChangeState.fire({ inputCollapsedChanged: true });
160
}
161
162
private _outputCollapsed: boolean = false;
163
get isOutputCollapsed(): boolean {
164
return this._outputCollapsed;
165
}
166
set isOutputCollapsed(v: boolean) {
167
this._outputCollapsed = v;
168
this._onDidChangeState.fire({ outputCollapsedChanged: true });
169
}
170
171
protected _commentHeight = 0;
172
173
set commentHeight(height: number) {
174
if (this._commentHeight === height) {
175
return;
176
}
177
this._commentHeight = height;
178
this.layoutChange({ commentHeight: true }, 'BaseCellViewModel#commentHeight');
179
}
180
181
private _isDisposed = false;
182
private _isReadonly = false;
183
184
constructor(
185
readonly viewType: string,
186
readonly model: NotebookCellTextModel,
187
public id: string,
188
private readonly _viewContext: ViewContext,
189
private readonly _configurationService: IConfigurationService,
190
private readonly _modelService: ITextModelService,
191
private readonly _undoRedoService: IUndoRedoService,
192
private readonly _codeEditorService: ICodeEditorService,
193
private readonly _inlineChatSessionService: IInlineChatSessionService
194
// private readonly _keymapService: INotebookKeymapService
195
) {
196
super();
197
198
this._register(model.onDidChangeMetadata(() => {
199
this._onDidChangeState.fire({ metadataChanged: true });
200
}));
201
202
this._register(model.onDidChangeInternalMetadata(e => {
203
this._onDidChangeState.fire({ internalMetadataChanged: true });
204
if (e.lastRunSuccessChanged) {
205
// Statusbar visibility may change
206
this.layoutChange({});
207
}
208
}));
209
210
this._register(this._configurationService.onDidChangeConfiguration(e => {
211
if (e.affectsConfiguration('notebook.lineNumbers')) {
212
this.lineNumbers = 'inherit';
213
}
214
}));
215
216
if (this.model.collapseState?.inputCollapsed) {
217
this._inputCollapsed = true;
218
}
219
220
if (this.model.collapseState?.outputCollapsed) {
221
this._outputCollapsed = true;
222
}
223
224
this._commentOptions = this._configurationService.getValue<IEditorCommentsOptions>('editor.comments', { overrideIdentifier: this.language });
225
this._register(this._configurationService.onDidChangeConfiguration(e => {
226
if (e.affectsConfiguration('editor.comments')) {
227
this._commentOptions = this._configurationService.getValue<IEditorCommentsOptions>('editor.comments', { overrideIdentifier: this.language });
228
}
229
}));
230
}
231
232
233
updateOptions(e: NotebookOptionsChangeEvent): void {
234
if (this._textEditor && typeof e.readonly === 'boolean') {
235
this._textEditor.updateOptions({ readOnly: e.readonly });
236
}
237
if (typeof e.readonly === 'boolean') {
238
this._isReadonly = e.readonly;
239
}
240
}
241
abstract getHeight(lineHeight: number): number;
242
abstract onDeselect(): void;
243
abstract layoutChange(change: CellLayoutChangeEvent, source?: string): void;
244
245
assertTextModelAttached(): boolean {
246
if (this.textModel && this._textEditor && this._textEditor.getModel() === this.textModel) {
247
return true;
248
}
249
250
return false;
251
}
252
253
// private handleKeyDown(e: IKeyboardEvent) {
254
// if (this.viewType === IPYNB_VIEW_TYPE && isWindows && e.ctrlKey && e.keyCode === KeyCode.Enter) {
255
// this._keymapService.promptKeymapRecommendation();
256
// }
257
// }
258
259
attachTextEditor(editor: ICodeEditor, estimatedHasHorizontalScrolling?: boolean) {
260
if (!editor.hasModel()) {
261
throw new Error('Invalid editor: model is missing');
262
}
263
264
if (this._textEditor === editor) {
265
if (this._editorListeners.length === 0) {
266
this._editorListeners.push(this._textEditor.onDidChangeCursorSelection(() => { this._onDidChangeState.fire({ selectionChanged: true }); }));
267
// this._editorListeners.push(this._textEditor.onKeyDown(e => this.handleKeyDown(e)));
268
this._onDidChangeState.fire({ selectionChanged: true });
269
}
270
return;
271
}
272
273
this._textEditor = editor;
274
if (this._isReadonly) {
275
editor.updateOptions({ readOnly: this._isReadonly });
276
}
277
if (this._editorViewStates) {
278
this._restoreViewState(this._editorViewStates);
279
} else {
280
// If no real editor view state was persisted, restore a default state.
281
// This forces the editor to measure its content width immediately.
282
if (estimatedHasHorizontalScrolling) {
283
this._restoreViewState({
284
contributionsState: {},
285
cursorState: [],
286
viewState: {
287
scrollLeft: 0,
288
firstPosition: { lineNumber: 1, column: 1 },
289
firstPositionDeltaTop: this._viewContext.notebookOptions.getLayoutConfiguration().editorTopPadding
290
}
291
});
292
}
293
}
294
295
if (this._editorTransientState) {
296
writeTransientState(editor.getModel(), this._editorTransientState, this._codeEditorService);
297
}
298
299
if (this._isDisposed) {
300
// Restore View State could adjust the editor layout and trigger a list view update. The list view update might then dispose this view model.
301
return;
302
}
303
304
editor.changeDecorations((accessor) => {
305
this._resolvedDecorations.forEach((value, key) => {
306
if (key.startsWith('_lazy_')) {
307
// lazy ones
308
const ret = accessor.addDecoration(value.options.range, value.options.options);
309
this._resolvedDecorations.get(key)!.id = ret;
310
}
311
else {
312
const ret = accessor.addDecoration(value.options.range, value.options.options);
313
this._resolvedDecorations.get(key)!.id = ret;
314
}
315
});
316
});
317
318
this._editorListeners.push(editor.onDidChangeCursorSelection(() => { this._onDidChangeState.fire({ selectionChanged: true }); }));
319
this._editorListeners.push(this._inlineChatSessionService.onWillStartSession((e) => {
320
if (e === this._textEditor && this.textBuffer.getLength() === 0) {
321
this.enableAutoLanguageDetection();
322
}
323
}));
324
325
this._onDidChangeState.fire({ selectionChanged: true });
326
this._onDidChangeEditorAttachState.fire();
327
}
328
329
detachTextEditor() {
330
this.saveViewState();
331
this.saveTransientState();
332
// decorations need to be cleared first as editors can be resued.
333
this._textEditor?.changeDecorations((accessor) => {
334
this._resolvedDecorations.forEach(value => {
335
const resolvedid = value.id;
336
337
if (resolvedid) {
338
accessor.removeDecoration(resolvedid);
339
}
340
});
341
});
342
343
this._textEditor = undefined;
344
dispose(this._editorListeners);
345
this._editorListeners = [];
346
this._onDidChangeEditorAttachState.fire();
347
348
if (this._textModelRef) {
349
this._textModelRef.dispose();
350
this._textModelRef = undefined;
351
}
352
this._textModelRefChangeDisposable.clear();
353
}
354
355
getText(): string {
356
return this.model.getValue();
357
}
358
359
getAlternativeId(): number {
360
return this.model.alternativeId;
361
}
362
363
getTextLength(): number {
364
return this.model.getTextLength();
365
}
366
367
enableAutoLanguageDetection() {
368
this.model.enableAutoLanguageDetection();
369
}
370
371
private saveViewState(): void {
372
if (!this._textEditor) {
373
return;
374
}
375
376
this._editorViewStates = this._textEditor.saveViewState();
377
}
378
379
private saveTransientState() {
380
if (!this._textEditor || !this._textEditor.hasModel()) {
381
return;
382
}
383
384
this._editorTransientState = readTransientState(this._textEditor.getModel(), this._codeEditorService);
385
}
386
387
saveEditorViewState() {
388
if (this._textEditor) {
389
this._editorViewStates = this._textEditor.saveViewState();
390
}
391
392
return this._editorViewStates;
393
}
394
395
restoreEditorViewState(editorViewStates: editorCommon.ICodeEditorViewState | null, totalHeight?: number) {
396
this._editorViewStates = editorViewStates;
397
}
398
399
private _restoreViewState(state: editorCommon.ICodeEditorViewState | null): void {
400
if (state) {
401
this._textEditor?.restoreViewState(state);
402
}
403
}
404
405
addModelDecoration(decoration: model.IModelDeltaDecoration): string {
406
if (!this._textEditor) {
407
const id = ++this._lastDecorationId;
408
const decorationId = `_lazy_${this.id};${id}`;
409
this._resolvedDecorations.set(decorationId, { options: decoration });
410
return decorationId;
411
}
412
413
let id: string;
414
this._textEditor.changeDecorations((accessor) => {
415
id = accessor.addDecoration(decoration.range, decoration.options);
416
this._resolvedDecorations.set(id, { id, options: decoration });
417
});
418
return id!;
419
}
420
421
removeModelDecoration(decorationId: string) {
422
const realDecorationId = this._resolvedDecorations.get(decorationId);
423
424
if (this._textEditor && realDecorationId && realDecorationId.id !== undefined) {
425
this._textEditor.changeDecorations((accessor) => {
426
accessor.removeDecoration(realDecorationId.id!);
427
});
428
}
429
430
// lastly, remove all the cache
431
this._resolvedDecorations.delete(decorationId);
432
}
433
434
deltaModelDecorations(oldDecorations: readonly string[], newDecorations: readonly model.IModelDeltaDecoration[]): string[] {
435
oldDecorations.forEach(id => {
436
this.removeModelDecoration(id);
437
});
438
439
const ret = newDecorations.map(option => {
440
return this.addModelDecoration(option);
441
});
442
443
return ret;
444
}
445
446
private _removeCellDecoration(decorationId: string) {
447
const options = this._resolvedCellDecorations.get(decorationId);
448
this._resolvedCellDecorations.delete(decorationId);
449
450
if (options) {
451
for (const existingOptions of this._resolvedCellDecorations.values()) {
452
// don't remove decorations that are applied from other entries
453
if (options.className === existingOptions.className) {
454
options.className = undefined;
455
}
456
if (options.outputClassName === existingOptions.outputClassName) {
457
options.outputClassName = undefined;
458
}
459
if (options.gutterClassName === existingOptions.gutterClassName) {
460
options.gutterClassName = undefined;
461
}
462
if (options.topClassName === existingOptions.topClassName) {
463
options.topClassName = undefined;
464
}
465
}
466
467
this._cellDecorationsChanged.fire({ added: [], removed: [options] });
468
}
469
}
470
471
private _addCellDecoration(options: INotebookCellDecorationOptions): string {
472
const id = ++this._lastDecorationId;
473
const decorationId = `_cell_${this.id};${id}`;
474
this._resolvedCellDecorations.set(decorationId, options);
475
this._cellDecorationsChanged.fire({ added: [options], removed: [] });
476
return decorationId;
477
}
478
479
getCellDecorations() {
480
return [...this._resolvedCellDecorations.values()];
481
}
482
483
getCellDecorationRange(decorationId: string): Range | null {
484
if (this._textEditor) {
485
// (this._textEditor as CodeEditorWidget).decora
486
return this._textEditor.getModel()?.getDecorationRange(decorationId) ?? null;
487
}
488
489
return null;
490
}
491
492
deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookCellDecorationOptions[]): string[] {
493
oldDecorations.forEach(id => {
494
this._removeCellDecoration(id);
495
});
496
497
const ret = newDecorations.map(option => {
498
return this._addCellDecoration(option);
499
});
500
501
return ret;
502
}
503
504
deltaCellStatusBarItems(oldItems: readonly string[], newItems: readonly INotebookCellStatusBarItem[]): string[] {
505
oldItems.forEach(id => {
506
const item = this._cellStatusBarItems.get(id);
507
if (item) {
508
this._cellStatusBarItems.delete(id);
509
}
510
});
511
512
const newIds = newItems.map(item => {
513
const id = ++this._lastStatusBarId;
514
const itemId = `_cell_${this.id};${id}`;
515
this._cellStatusBarItems.set(itemId, item);
516
return itemId;
517
});
518
519
this._onDidChangeCellStatusBarItems.fire();
520
521
return newIds;
522
}
523
524
getCellStatusBarItems(): INotebookCellStatusBarItem[] {
525
return Array.from(this._cellStatusBarItems.values());
526
}
527
528
revealRangeInCenter(range: Range) {
529
this._textEditor?.revealRangeInCenter(range, editorCommon.ScrollType.Immediate);
530
}
531
532
setSelection(range: Range) {
533
this._textEditor?.setSelection(range);
534
}
535
536
setSelections(selections: Selection[]) {
537
if (selections.length) {
538
if (this._textEditor) {
539
this._textEditor?.setSelections(selections);
540
} else if (this._editorViewStates) {
541
this._editorViewStates.cursorState = selections.map(selection => {
542
return {
543
inSelectionMode: !selection.isEmpty(),
544
selectionStart: selection.getStartPosition(),
545
position: selection.getEndPosition(),
546
};
547
});
548
}
549
}
550
}
551
552
getSelections() {
553
return this._textEditor?.getSelections()
554
?? this._editorViewStates?.cursorState.map(state => new Selection(state.selectionStart.lineNumber, state.selectionStart.column, state.position.lineNumber, state.position.column))
555
?? [];
556
}
557
558
getSelectionsStartPosition(): IPosition[] | undefined {
559
if (this._textEditor) {
560
const selections = this._textEditor.getSelections();
561
return selections?.map(s => s.getStartPosition());
562
} else {
563
const selections = this._editorViewStates?.cursorState;
564
return selections?.map(s => s.selectionStart);
565
}
566
}
567
568
getLineScrollTopOffset(line: number): number {
569
if (!this._textEditor) {
570
return 0;
571
}
572
573
const editorPadding = this._viewContext.notebookOptions.computeEditorPadding(this.internalMetadata, this.uri);
574
return this._textEditor.getTopForLineNumber(line) + editorPadding.top;
575
}
576
577
getPositionScrollTopOffset(range: Selection | Range): number {
578
if (!this._textEditor) {
579
return 0;
580
}
581
582
583
const position = range instanceof Selection ? range.getPosition() : range.getStartPosition();
584
585
const editorPadding = this._viewContext.notebookOptions.computeEditorPadding(this.internalMetadata, this.uri);
586
return this._textEditor.getTopForPosition(position.lineNumber, position.column) + editorPadding.top;
587
}
588
589
cursorAtLineBoundary(): CursorAtLineBoundary {
590
if (!this._textEditor || !this.textModel || !this._textEditor.hasTextFocus()) {
591
return CursorAtLineBoundary.None;
592
}
593
594
const selection = this._textEditor.getSelection();
595
596
if (!selection || !selection.isEmpty()) {
597
return CursorAtLineBoundary.None;
598
}
599
600
const currentLineLength = this.textModel.getLineLength(selection.startLineNumber);
601
602
if (currentLineLength === 0) {
603
return CursorAtLineBoundary.Both;
604
}
605
606
switch (selection.startColumn) {
607
case 1:
608
return CursorAtLineBoundary.Start;
609
case currentLineLength + 1:
610
return CursorAtLineBoundary.End;
611
default:
612
return CursorAtLineBoundary.None;
613
}
614
}
615
616
cursorAtBoundary(): CursorAtBoundary {
617
if (!this._textEditor) {
618
return CursorAtBoundary.None;
619
}
620
621
if (!this.textModel) {
622
return CursorAtBoundary.None;
623
}
624
625
// only validate primary cursor
626
const selection = this._textEditor.getSelection();
627
628
// only validate empty cursor
629
if (!selection || !selection.isEmpty()) {
630
return CursorAtBoundary.None;
631
}
632
633
const firstViewLineTop = this._textEditor.getTopForPosition(1, 1);
634
const lastViewLineTop = this._textEditor.getTopForPosition(this.textModel.getLineCount(), this.textModel.getLineLength(this.textModel.getLineCount()));
635
const selectionTop = this._textEditor.getTopForPosition(selection.startLineNumber, selection.startColumn);
636
637
if (selectionTop === lastViewLineTop) {
638
if (selectionTop === firstViewLineTop) {
639
return CursorAtBoundary.Both;
640
} else {
641
return CursorAtBoundary.Bottom;
642
}
643
} else {
644
if (selectionTop === firstViewLineTop) {
645
return CursorAtBoundary.Top;
646
} else {
647
return CursorAtBoundary.None;
648
}
649
}
650
}
651
652
private _editStateSource: string = '';
653
654
get editStateSource(): string {
655
return this._editStateSource;
656
}
657
658
updateEditState(newState: CellEditState, source: string) {
659
if (newState === this._editState) {
660
return;
661
}
662
663
this._editStateSource = source;
664
this._editState = newState;
665
this._onDidChangeState.fire({ editStateChanged: true });
666
if (this._editState === CellEditState.Preview) {
667
this.focusMode = CellFocusMode.Container;
668
}
669
}
670
671
getEditState() {
672
return this._editState;
673
}
674
675
get textBuffer() {
676
return this.model.textBuffer;
677
}
678
679
/**
680
* Text model is used for editing.
681
*/
682
async resolveTextModel(): Promise<model.ITextModel> {
683
if (!this._textModelRef || !this.textModel) {
684
this._textModelRef = await this._modelService.createModelReference(this.uri);
685
if (this._isDisposed) {
686
return this.textModel!;
687
}
688
689
if (!this._textModelRef) {
690
throw new Error(`Cannot resolve text model for ${this.uri}`);
691
}
692
this._textModelRefChangeDisposable.value = this.textModel!.onDidChangeContent(() => this.onDidChangeTextModelContent());
693
}
694
695
return this.textModel!;
696
}
697
698
protected abstract onDidChangeTextModelContent(): void;
699
700
protected cellStartFind(value: string, options: INotebookFindOptions): model.FindMatch[] | null {
701
let cellMatches: model.FindMatch[] = [];
702
703
const lineCount = this.textBuffer.getLineCount();
704
const findRange: IRange[] = options.findScope?.selectedTextRanges ?? [new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1)];
705
706
if (this.assertTextModelAttached()) {
707
cellMatches = this.textModel!.findMatches(
708
value,
709
findRange,
710
options.regex || false,
711
options.caseSensitive || false,
712
options.wholeWord ? options.wordSeparators || null : null,
713
options.regex || false);
714
} else {
715
const searchParams = new SearchParams(value, options.regex || false, options.caseSensitive || false, options.wholeWord ? options.wordSeparators || null : null,);
716
const searchData = searchParams.parseSearchRequest();
717
718
if (!searchData) {
719
return null;
720
}
721
722
findRange.forEach(range => {
723
cellMatches.push(...this.textBuffer.findMatchesLineByLine(new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn), searchData, options.regex || false, 1000));
724
});
725
}
726
727
return cellMatches;
728
}
729
730
override dispose() {
731
this._isDisposed = true;
732
super.dispose();
733
734
dispose(this._editorListeners);
735
736
// Only remove the undo redo stack if we map this cell uri to itself
737
// If we are not in perCell mode, it will map to the full NotebookDocument and
738
// we don't want to remove that entire document undo / redo stack when a cell is deleted
739
if (this._undoRedoService.getUriComparisonKey(this.uri) === this.uri.toString()) {
740
this._undoRedoService.removeElements(this.uri);
741
}
742
743
this._textModelRef?.dispose();
744
}
745
746
toJSON(): object {
747
return {
748
handle: this.handle
749
};
750
}
751
}
752
753