src/vs/editor/common/model/textModel.ts

2743 LOC · 2073 covered · 670 uncovered · 546 ranges · 1708 concepts · 151 introducers · 861 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- textModel.ts ×190
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 { pushMany } from '../../../base/common/arrays.js';
7 > import { VSBuffer, VSBufferReadableStream } from '../../../base/common/buffer.js';
8 > import { CharCode } from '../../../base/common/charCode.js';
9 > import { SetWithKey } from '../../../base/common/collections.js';
10 > import { Color } from '../../../base/common/color.js';
11 > import { BugIndicatingError, illegalArgument, onUnexpectedError } from '../../../base/common/errors.js';
12 > import { Emitter, Event } from '../../../base/common/event.js';
13 > import { IMarkdownString } from '../../../base/common/htmlContent.js';
14 > import { Disposable, IDisposable, MutableDisposable } from '../../../base/common/lifecycle.js';
15 > import { listenStream } from '../../../base/common/stream.js';
16 > import * as strings from '../../../base/common/strings.js';
17 > import { ThemeColor } from '../../../base/common/themables.js';
18 > import { Constants } from '../../../base/common/uint.js';
19 > import { URI } from '../../../base/common/uri.js';
20 > import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';
21 > import { isDark } from '../../../platform/theme/common/theme.js';
22 > import { IColorTheme } from '../../../platform/theme/common/themeService.js';
23 > import { IUndoRedoService, ResourceEditStackSnapshot, UndoRedoGroup } from '../../../platform/undoRedo/common/undoRedo.js';
24 > import { ISingleEditOperation } from '../core/editOperation.js';
25 > import { TextEdit } from '../core/edits/textEdit.js';
26 > import { countEOL } from '../core/misc/eolCounter.js';
27 > import { normalizeIndentation } from '../core/misc/indentation.js';
28 > import { EDITOR_MODEL_DEFAULTS } from '../core/misc/textModelDefaults.js';
29 > import { IPosition, Position } from '../core/position.js';
30 > import { IRange, Range } from '../core/range.js';
31 > import { Selection } from '../core/selection.js';
32 > import { TextChange } from '../core/textChange.js';
33 > import { IWordAtPosition } from '../core/wordHelper.js';
34 > import { FormattingOptions } from '../languages.js';
35 > import { ILanguageSelection, ILanguageService } from '../languages/language.js';
36 > import { ILanguageConfigurationService } from '../languages/languageConfigurationRegistry.js';
37 > import * as model from '../model.js';
38 > import { IBracketPairsTextModelPart } from '../textModelBracketPairs.js';
39 > import { EditSources, TextModelEditSource } from '../textModelEditSource.js';
40 > import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelOptionsChangedEvent, InternalModelContentChangeEvent, LineInjectedText, ModelFontChanged, ModelFontChangedEvent, ModelInjectedTextChangedEvent, ModelLineHeightChanged, ModelLineHeightChangedEvent, ModelRawChange, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from '../textModelEvents.js';
41 > import { IGuidesTextModelPart } from '../textModelGuides.js';
42 > import { ITokenizationTextModelPart } from '../tokenizationTextModelPart.js';
43 > import { LineTokens, TokenArray } from '../tokens/lineTokens.js';
44 > import { BracketPairsTextModelPart } from './bracketPairsTextModelPart/bracketPairsImpl.js';
45 > import { ColorizedBracketPairsDecorationProvider } from './bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.js';
46 > import { EditStack } from './editStack.js';
47 > import { GuidesTextModelPart } from './guidesTextModelPart.js';
48 > import { guessIndentation } from './indentationGuesser.js';
49 > import { IntervalNode, IntervalTree, recomputeMaxEnd } from './intervalTree.js';
50 > import { PieceTreeTextBuffer } from './pieceTreeTextBuffer/pieceTreeTextBuffer.js';
51 > import { PieceTreeTextBufferBuilder } from './pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js';
52 > import { SearchParams, TextModelSearch } from './textModelSearch.js';
53 > import { AttachedViews } from './tokens/abstractSyntaxTokenBackend.js';
54 > import { TokenizationFontDecorationProvider } from './tokens/tokenizationFontDecorationsProvider.js';
55 > import { LineFontChangingDecoration, LineHeightChangingDecoration } from './decorationProvider.js';
56 > import { TokenizationTextModelPart } from './tokens/tokenizationTextModelPart.js';
57 > import { IViewModel } from '../viewModel.js';
58 >
59 > export function createTextBufferFactory(text: string): model.ITextBufferFactory {
60 > const builder = new PieceTreeTextBufferBuilder(); textModel.ts ×1
61 > builder.acceptChunk(text);
62 > return builder.finish();
63 > }
65 > interface ITextStream {
66 > on(event: 'data', callback: (data: string) => void): void;
67 > on(event: 'error', callback: (err: Error) => void): void;
68 > on(event: 'end', callback: () => void): void;
69 > on(event: string, callback: (...args: unknown[]) => void): void;
70 > }
71 >
72 > export function createTextBufferFactoryFromStream(stream: ITextStream): Promise<model.ITextBufferFactory>;
73 > export function createTextBufferFactoryFromStream(stream: VSBufferReadableStream): Promise<model.ITextBufferFactory>;
74 > export function createTextBufferFactoryFromStream(stream: ITextStream | VSBufferReadableStream): Promise<model.ITextBufferFactory> {
75 return new Promise<model.ITextBufferFactory>((resolve, reject) => {
76 const builder = new PieceTreeTextBufferBuilder();
77
78 let done = false;
79
80 listenStream<string | VSBuffer>(stream, {
81 onData: chunk => {
82 builder.acceptChunk((typeof chunk === 'string') ? chunk : chunk.toString());
83 },
84 onError: error => {
85 if (!done) {
86 done = true;
87 reject(error);
88 }
89 },
90 onEnd: () => {
91 if (!done) {
92 done = true;
93 resolve(builder.finish());
94 }
95 }
96 });
97 });
98 }
100 > export function createTextBufferFactoryFromSnapshot(snapshot: model.ITextSnapshot): model.ITextBufferFactory {
101 const builder = new PieceTreeTextBufferBuilder();
102
103 let chunk: string | null;
104 while (typeof (chunk = snapshot.read()) === 'string') {
105 builder.acceptChunk(chunk);
106 }
107
108 return builder.finish();
109 }
111 > export function createTextBuffer(value: string | model.ITextBufferFactory | model.ITextSnapshot, defaultEOL: model.DefaultEndOfLine): { textBuffer: model.ITextBuffer; disposable: IDisposable } {
112 > let factory: model.ITextBufferFactory; textModel.ts ×2
113 > if (typeof value === 'string') {
114 > factory = createTextBufferFactory(value);
115 > } else if (model.isITextSnapshot(value)) {
116 factory = createTextBufferFactoryFromSnapshot(value);
117 } else {
118 factory = value;
119 }
120 > return factory.create(defaultEOL); textModel.ts ×2
121 > }
123 > let MODEL_ID = 0;
124 >
125 > const LIMIT_FIND_COUNT = 999;
126 > const LONG_LINE_BOUNDARY = 10000;
127 > const LINE_HEIGHT_CEILING = 300;
128 >
129 > class TextModelSnapshot implements model.ITextSnapshot {
130 >
131 > private readonly _source: model.ITextSnapshot;
132 > private _eos: boolean;
133 >
134 > constructor(source: model.ITextSnapshot) {
135 > this._source = source; textModel.ts ×8
136 > this._eos = false;
137 > }
139 > public read(): string | null {
140 > if (this._eos) { textModel.ts ×8
141 > return null; textModel.ts ×2
142 > }
144 > const result: string[] = [];
145 > let resultCnt = 0;
146 > let resultLength = 0;
147 >
148 > do {
149 > const tmp = this._source.read();
150 >
151 > if (tmp === null) {
152 > // end-of-stream
153 > this._eos = true;
154 > if (resultCnt === 0) {
155 > return null; textModel.ts ×1
156 > } else { textModel.ts ×8
157 > return result.join(''); textModel.ts ×2
158 > }
160 >
161 > if (tmp.length > 0) {
162 > result[resultCnt++] = tmp; pieceTreeBase.ts ×3
163 > resultLength += tmp.length;
164 > }
166 > if (resultLength >= 64 * 1024) {
167 > return result.join(''); pieceTreeBase.ts ×1
168 > }
169 > } while (true); textModel.ts ×8
170 > }
172 >
173 > const invalidFunc = () => { throw new Error(`Invalid change accessor`); };
174 >
175 > const enum StringOffsetValidationType {
176 > /**
177 > * Even allowed in surrogate pairs
178 > */
179 > Relaxed = 0,
180 > /**
181 > * Not allowed in surrogate pairs
182 > */
183 > SurrogatePairs = 1,
184 > }
185 >
186 > export class TextModel extends Disposable implements model.ITextModel, IDecorationsTreesHost {
187 >
188 > static _MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB, // used in tests
189 > private static readonly LARGE_FILE_SIZE_THRESHOLD = 20 * 1024 * 1024; // 20 MB;
190 > private static readonly LARGE_FILE_LINE_COUNT_THRESHOLD = 300 * 1000; // 300K lines
191 > private static readonly LARGE_FILE_HEAP_OPERATION_THRESHOLD = 256 * 1024 * 1024; // 256M characters, usually ~> 512MB memory usage
192 >
193 > public static DEFAULT_CREATION_OPTIONS: model.ITextModelCreationOptions = {
194 > isForSimpleWidget: false,
195 > tabSize: EDITOR_MODEL_DEFAULTS.tabSize,
196 > indentSize: EDITOR_MODEL_DEFAULTS.indentSize,
197 > insertSpaces: EDITOR_MODEL_DEFAULTS.insertSpaces,
198 > detectIndentation: false,
199 > defaultEOL: model.DefaultEndOfLine.LF,
200 > trimAutoWhitespace: EDITOR_MODEL_DEFAULTS.trimAutoWhitespace,
201 > largeFileOptimizations: EDITOR_MODEL_DEFAULTS.largeFileOptimizations,
202 > bracketPairColorizationOptions: EDITOR_MODEL_DEFAULTS.bracketPairColorizationOptions,
203 > };
204 >
205 > public static resolveOptions(textBuffer: model.ITextBuffer, options: model.ITextModelCreationOptions): model.TextModelResolvedOptions {
206 > if (options.detectIndentation) { textModel.ts ×20
207 > const guessedIndentation = guessIndentation(textBuffer, options.tabSize, options.insertSpaces); indentationGuesser.ts ×18
208 > return new model.TextModelResolvedOptions({
209 > tabSize: guessedIndentation.tabSize,
210 > indentSize: 'tabSize', // TODO@Alex: guess indentSize independent of tabSize
211 > insertSpaces: guessedIndentation.insertSpaces,
212 > trimAutoWhitespace: options.trimAutoWhitespace,
213 > defaultEOL: options.defaultEOL,
214 > bracketPairColorizationOptions: options.bracketPairColorizationOptions,
215 > });
216 > }
218 > return new model.TextModelResolvedOptions(options);
221 > //#region Events
222 > private readonly _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
223 > public readonly onWillDispose: Event<void> = this._onWillDispose.event;
224 >
225 > private readonly _onDidChangeDecorations: DidChangeDecorationsEmitter = this._register(new DidChangeDecorationsEmitter((affectedInjectedTextLines, affectedLineHeights, affectedFontLines) => this.handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines, affectedLineHeights, affectedFontLines)));
226 > public readonly onDidChangeDecorations: Event<IModelDecorationsChangedEvent> = this._onDidChangeDecorations.event;
227 >
228 > public get onDidChangeLanguage() { return this._tokenizationTextModelPart.onDidChangeLanguage; }
229 > public get onDidChangeLanguageConfiguration() { return this._tokenizationTextModelPart.onDidChangeLanguageConfiguration; }
230 > public get onDidChangeTokens() { return this._tokenizationTextModelPart.onDidChangeTokens; }
231 >
232 > private readonly _onDidChangeOptions: Emitter<IModelOptionsChangedEvent> = this._register(new Emitter<IModelOptionsChangedEvent>());
233 > public get onDidChangeOptions(): Event<IModelOptionsChangedEvent> { return this._onDidChangeOptions.event; }
234 >
235 > private readonly _onDidChangeAttached: Emitter<void> = this._register(new Emitter<void>());
236 > public get onDidChangeAttached(): Event<void> { return this._onDidChangeAttached.event; }
237 >
238 > private readonly _onDidChangeLineHeight: Emitter<ModelLineHeightChangedEvent> = this._register(new Emitter<ModelLineHeightChangedEvent>());
239 > public get onDidChangeLineHeight(): Event<ModelLineHeightChangedEvent> { return this._onDidChangeLineHeight.event; }
240 >
241 > private readonly _onDidChangeFont: Emitter<ModelFontChangedEvent> = this._register(new Emitter<ModelFontChangedEvent>());
242 > public get onDidChangeFont(): Event<ModelFontChangedEvent> { return this._onDidChangeFont.event; }
243 >
244 > private readonly _eventEmitter: DidChangeContentEmitter = this._register(new DidChangeContentEmitter());
245 > public onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable {
246 > return this._eventEmitter.event((e: InternalModelContentChangeEvent) => listener(e.contentChangedEvent)); mirrorTextModel.ts ×2
247 > }
248 > //#endregion textModel.ts ×190
249 >
250 > public readonly id: string;
251 > public readonly isForSimpleWidget: boolean;
252 > private readonly _associatedResource: URI;
253 > private _attachedEditorCount: number;
254 > private _buffer: model.ITextBuffer;
255 > private _bufferDisposable: IDisposable;
256 > private _options: model.TextModelResolvedOptions;
257 > private readonly _languageSelectionListener = this._register(new MutableDisposable<IDisposable>());
258 >
259 > private _isDisposed: boolean;
260 > private __isDisposing: boolean;
261 > public _isDisposing(): boolean { return this.__isDisposing; }
262 > private _versionId: number;
263 > /**
264 > * Unlike, versionId, this can go down (via undo) or go to previous values (via redo)
265 > */
266 > private _alternativeVersionId: number;
267 > private _initialUndoRedoSnapshot: ResourceEditStackSnapshot | null;
268 > private readonly _isTooLargeForSyncing: boolean;
269 > private readonly _isTooLargeForTokenization: boolean;
270 > private readonly _isTooLargeForHeapOperation: boolean;
271 >
272 > //#region Editing
273 > private readonly _commandManager: EditStack;
274 > private _isUndoing: boolean;
275 > private _isRedoing: boolean;
276 > private _trimAutoWhitespaceLines: number[] | null;
277 > //#endregion
278 >
279 > //#region Decorations
280 > /**
281 > * Used to workaround broken clients that might attempt using a decoration id generated by a different model.
282 > * It is not globally unique in order to limit it to one character.
283 > */
284 > private readonly _instanceId: string;
285 > private _deltaDecorationCallCnt: number = 0;
286 > private _lastDecorationId: number;
287 > private _decorations: { [decorationId: string]: IntervalNode };
288 > private _decorationsTree: DecorationsTrees;
289 > private readonly _decorationProvider: ColorizedBracketPairsDecorationProvider;
290 > private readonly _fontTokenDecorationsProvider: TokenizationFontDecorationProvider;
291 > //#endregion
292 >
293 > private readonly _tokenizationTextModelPart: TokenizationTextModelPart;
294 > public get tokenization(): ITokenizationTextModelPart { return this._tokenizationTextModelPart; }
295 >
296 > private readonly _bracketPairs: BracketPairsTextModelPart;
297 > public get bracketPairs(): IBracketPairsTextModelPart { return this._bracketPairs; }
298 >
299 > private readonly _guidesTextModelPart: GuidesTextModelPart;
300 > public get guides(): IGuidesTextModelPart { return this._guidesTextModelPart; }
301 >
302 > private readonly _attachedViews = this._register(new AttachedViews());
303 > private readonly _viewModels = new Set<IViewModel>();
304 >
305 > constructor(
306 > source: string | model.ITextBufferFactory, textModel.ts ×20
307 > languageIdOrSelection: string | ILanguageSelection,
308 > creationOptions: model.ITextModelCreationOptions,
309 > associatedResource: URI | null = null,
310 > @IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
311 > @ILanguageService private readonly _languageService: ILanguageService,
312 > @ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService,
313 > @IInstantiationService private readonly instantiationService: IInstantiationService
314 > ) {
315 > super();
316 >
317 > // Generate a new unique model id
318 > MODEL_ID++;
319 > this.id = '$model' + MODEL_ID;
320 > this.isForSimpleWidget = creationOptions.isForSimpleWidget;
321 > if (typeof associatedResource === 'undefined' || associatedResource === null) {
322 > this._associatedResource = URI.parse('inmemory://model/' + MODEL_ID); textModel.ts ×1
323 > } else { textModel.ts ×20
324 > this._associatedResource = associatedResource; modelService.ts ×29
325 > }
326 > this._attachedEditorCount = 0; textModel.ts ×20
327 >
328 > const { textBuffer, disposable } = createTextBuffer(source, creationOptions.defaultEOL);
329 > this._buffer = textBuffer;
330 > this._bufferDisposable = disposable;
331 >
332 > const bufferLineCount = this._buffer.getLineCount();
333 > const bufferTextLength = this._buffer.getValueLengthInRange(new Range(1, 1, bufferLineCount, this._buffer.getLineLength(bufferLineCount) + 1), model.EndOfLinePreference.TextDefined);
334 >
335 > // !!! Make a decision in the ctor and permanently respect this decision !!!
336 > // If a model is too large at construction time, it will never get tokenized,
337 > // under no circumstances.
338 > if (creationOptions.largeFileOptimizations) {
339 > this._isTooLargeForTokenization = (
340 > (bufferTextLength > TextModel.LARGE_FILE_SIZE_THRESHOLD)
341 > || (bufferLineCount > TextModel.LARGE_FILE_LINE_COUNT_THRESHOLD)
342 > );
343 >
344 > this._isTooLargeForHeapOperation = bufferTextLength > TextModel.LARGE_FILE_HEAP_OPERATION_THRESHOLD;
345 > } else {
346 this._isTooLargeForTokenization = false;
347 this._isTooLargeForHeapOperation = false;
348 }
350 > this._options = TextModel.resolveOptions(this._buffer, creationOptions);
351 >
352 > const languageId = (typeof languageIdOrSelection === 'string' ? languageIdOrSelection : languageIdOrSelection.languageId);
353 > if (typeof languageIdOrSelection !== 'string') {
354 this._languageSelectionListener.value = languageIdOrSelection.onDidChange(() => this._setLanguage(languageIdOrSelection.languageId));
355 }
357 > this._bracketPairs = this._register(new BracketPairsTextModelPart(this, this._languageConfigurationService));
358 > this._guidesTextModelPart = this._register(new GuidesTextModelPart(this, this._languageConfigurationService));
359 > this._decorationProvider = this._register(new ColorizedBracketPairsDecorationProvider(this));
360 > this._tokenizationTextModelPart = this.instantiationService.createInstance(TokenizationTextModelPart,
361 > this,
362 > this._bracketPairs,
363 > languageId,
364 > this._attachedViews
365 > );
366 > this._fontTokenDecorationsProvider = this._register(new TokenizationFontDecorationProvider(this, this._tokenizationTextModelPart));
367 >
368 > this._isTooLargeForSyncing = (bufferTextLength > TextModel._MODEL_SYNC_LIMIT);
369 >
370 > this._versionId = 1;
371 > this._alternativeVersionId = 1;
372 > this._initialUndoRedoSnapshot = null;
373 >
374 > this._isDisposed = false;
375 > this.__isDisposing = false;
376 >
377 > this._instanceId = strings.singleLetterHash(MODEL_ID);
378 > this._lastDecorationId = 0;
379 > this._decorations = Object.create(null);
380 > this._decorationsTree = new DecorationsTrees();
381 >
382 > this._commandManager = new EditStack(this, this._undoRedoService);
383 > this._isUndoing = false;
384 > this._isRedoing = false;
385 > this._trimAutoWhitespaceLines = null;
386 >
387 >
388 > this._register(this._decorationProvider.onDidChange(() => {
389 > this._onDidChangeDecorations.beginDeferredEmit(); parser.ts ×14
390 > this._onDidChangeDecorations.fire();
391 > this._onDidChangeDecorations.endDeferredEmit();
392 > })); textModel.ts ×20
393 > this._register(this._fontTokenDecorationsProvider.onDidChangeLineHeight((affectedLineHeights) => {
394 this._onDidChangeDecorations.beginDeferredEmit();
395 this._onDidChangeDecorations.fire();
396 this._fireOnDidChangeLineHeight(affectedLineHeights);
397 this._onDidChangeDecorations.endDeferredEmit();
398 > })); textModel.ts ×20
399 > this._register(this._fontTokenDecorationsProvider.onDidChangeFont((affectedFontLines) => {
400 this._onDidChangeDecorations.beginDeferredEmit();
401 this._onDidChangeDecorations.fire();
402 this._fireOnDidChangeFont(affectedFontLines);
403 this._onDidChangeDecorations.endDeferredEmit();
404 > })); textModel.ts ×20
405 >
406 > this._languageService.requestRichLanguageFeatures(languageId);
407 >
408 > this._register(this._languageConfigurationService.onDidChange(e => {
409 this._bracketPairs.handleLanguageConfigurationServiceChange(e);
410 this._tokenizationTextModelPart.handleLanguageConfigurationServiceChange(e);
411 > })); textModel.ts ×20
412 > }
414 > public override dispose(): void {
415 > this.__isDisposing = true; textModel.ts ×20
416 > this._onWillDispose.fire();
417 > this._tokenizationTextModelPart.dispose();
418 > this._isDisposed = true;
419 > super.dispose();
420 > this._bufferDisposable.dispose();
421 > this.__isDisposing = false;
422 > // Manually release reference to previous text buffer to avoid large leaks
423 > // in case someone leaks a TextModel reference
424 > const emptyDisposedTextBuffer = new PieceTreeTextBuffer([], '', '\n', false, false, true, true);
425 > emptyDisposedTextBuffer.dispose();
426 > this._buffer = emptyDisposedTextBuffer;
427 > this._bufferDisposable = Disposable.None;
428 > }
430 > _hasListeners(): boolean {
431 > return ( textModel.ts ×3
432 > this._onWillDispose.hasListeners()
433 > || this._onDidChangeDecorations.hasListeners()
434 > || this._tokenizationTextModelPart._hasListeners()
435 > || this._onDidChangeOptions.hasListeners()
436 > || this._onDidChangeAttached.hasListeners()
437 > || this._onDidChangeLineHeight.hasListeners()
438 > || this._onDidChangeFont.hasListeners()
439 > || this._eventEmitter.hasListeners()
440 > );
441 > }
443 > private _assertNotDisposed(): void {
444 > if (this._isDisposed) { textModel.ts ×20
445 throw new BugIndicatingError('Model is disposed!');
446 }
449 > public registerViewModel(viewModel: IViewModel): void {
450 > this._viewModels.add(viewModel); textModel.ts ×2
451 > }
453 > public unregisterViewModel(viewModel: IViewModel): void {
454 > this._viewModels.delete(viewModel); textModel.ts ×2
455 > }
457 > public equalsTextBuffer(other: model.ITextBuffer): boolean {
458 this._assertNotDisposed();
459 return this._buffer.equals(other);
460 }
462 > public getTextBuffer(): model.ITextBuffer {
463 this._assertNotDisposed();
464 return this._buffer;
465 }
467 > private _emitContentChangedEvent(rawChange: ModelRawContentChangedEvent, change: IModelContentChangedEvent, resultingSelection: Selection[] | null = null): void {
468 > if (this.__isDisposing) { textModel.ts ×9
469 // Do not confuse listeners by emitting any event after disposing
470 return;
471 }
472 > this._tokenizationTextModelPart.handleDidChangeContent(change); textModel.ts ×9
473 > this._bracketPairs.handleDidChangeContent(change);
474 > this._fontTokenDecorationsProvider.handleDidChangeContent(change);
475 > const contentChangeEvent = new InternalModelContentChangeEvent(rawChange, change);
476 > // Set resultingSelection early so viewModels can use it for cursor positioning
477 > if (resultingSelection) {
478 > contentChangeEvent.rawContentChangedEvent.resultingSelection = resultingSelection; editStack.ts ×3
479 > }
480 > this._onDidChangeContentOrInjectedText(contentChangeEvent); textModel.ts ×9
481 > this._eventEmitter.fire(contentChangeEvent);
482 > }
484 > public setValue(value: string | model.ITextSnapshot, reason = EditSources.setValue()): void {
485 > this._assertNotDisposed(); textModel.ts ×3
486 >
487 > if (value === null || value === undefined) {
488 throw illegalArgument();
489 }
491 > const { textBuffer, disposable } = createTextBuffer(value, this._options.defaultEOL);
492 > this._setValueFromTextBuffer(textBuffer, disposable, reason);
493 > }
495 > private _createContentChanged2(range: Range, rangeOffset: number, rangeLength: number, rangeEndPosition: Position, text: string, isUndoing: boolean, isRedoing: boolean, isFlush: boolean, isEolChange: boolean, reason: TextModelEditSource): IModelContentChangedEvent {
496 > return { textModel.ts ×2
497 > changes: [{
498 > range: range,
499 > rangeOffset: rangeOffset,
500 > rangeLength: rangeLength,
501 > text: text,
502 > }],
503 > eol: this._buffer.getEOL(),
504 > isEolChange: isEolChange,
505 > versionId: this.getVersionId(),
506 > isUndoing: isUndoing,
507 > isRedoing: isRedoing,
508 > isFlush: isFlush,
509 > detailedReasons: [reason],
510 > detailedReasonsChangeLengths: [1],
511 > };
512 > }
514 > private _setValueFromTextBuffer(textBuffer: model.ITextBuffer, textBufferDisposable: IDisposable, reason: TextModelEditSource): void {
515 > this._assertNotDisposed(); textModel.ts ×3
516 > const oldFullModelRange = this.getFullModelRange();
517 > const oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);
518 > const endLineNumber = this.getLineCount();
519 > const endColumn = this.getLineMaxColumn(endLineNumber);
520 >
521 > this._buffer = textBuffer;
522 > this._bufferDisposable.dispose();
523 > this._bufferDisposable = textBufferDisposable;
524 > this._increaseVersionId();
525 >
526 > // Destroy all my decorations
527 > this._decorations = Object.create(null);
528 > this._decorationsTree = new DecorationsTrees();
529 >
530 > // Destroy my edit history and settings
531 > this._commandManager.clear();
532 > this._trimAutoWhitespaceLines = null;
533 >
534 > this._emitContentChangedEvent(
535 > new ModelRawContentChangedEvent(
536 > [
537 > new ModelRawFlush()
538 > ],
539 > this._versionId,
540 > false,
541 > false
542 > ),
543 > this._createContentChanged2(new Range(1, 1, endLineNumber, endColumn), 0, oldModelValueLength, new Position(endLineNumber, endColumn), this.getValue(), false, false, true, false, reason)
544 > );
545 > }
547 > public setEOL(eol: model.EndOfLineSequence): void {
548 > this._assertNotDisposed(); textModel.ts ×2
549 > const newEOL = (eol === model.EndOfLineSequence.CRLF ? '\r\n' : '\n');
550 > if (this._buffer.getEOL() === newEOL) {
551 > // Nothing to do textModel.ts ×1
552 > return;
553 > }
555 > const oldFullModelRange = this.getFullModelRange();
556 > const oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);
557 > const endLineNumber = this.getLineCount();
558 > const endColumn = this.getLineMaxColumn(endLineNumber);
559 >
560 > this._onBeforeEOLChange();
561 > this._buffer.setEOL(newEOL);
562 > this._increaseVersionId();
563 > this._onAfterEOLChange();
564 >
565 > this._emitContentChangedEvent(
566 > new ModelRawContentChangedEvent(
567 > [
568 > new ModelRawEOLChanged()
569 > ],
570 > this._versionId,
571 > false,
572 > false
573 > ),
574 > this._createContentChanged2(new Range(1, 1, endLineNumber, endColumn), 0, oldModelValueLength, new Position(endLineNumber, endColumn), this.getValue(), false, false, false, true, EditSources.eolChange())
575 > );
578 > private _onBeforeEOLChange(): void {
579 > // Ensure all decorations get their `range` set. textModel.ts ×6
580 > this._decorationsTree.ensureAllNodesHaveRanges(this);
581 > }
583 > private _onAfterEOLChange(): void {
584 > // Transform back `range` to offsets textModel.ts ×6
585 > const versionId = this.getVersionId();
586 > const allDecorations = this._decorationsTree.collectNodesPostOrder();
587 > for (let i = 0, len = allDecorations.length; i < len; i++) {
588 > const node = allDecorations[i]; intervalTree.ts ×3
589 > const range = node.range!; // the range is defined due to `_onBeforeEOLChange`
590 >
591 > const delta = node.cachedAbsoluteStart - node.start;
592 >
593 > const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
594 > const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
595 >
596 > node.cachedAbsoluteStart = startOffset;
597 > node.cachedAbsoluteEnd = endOffset;
598 > node.cachedVersionId = versionId;
599 >
600 > node.start = startOffset - delta;
601 > node.end = endOffset - delta;
602 >
603 > recomputeMaxEnd(node);
604 > }
607 > public onBeforeAttached(): model.IAttachedView {
608 > this._attachedEditorCount++; abstractSyntaxTokenBackend.ts ×3
609 > if (this._attachedEditorCount === 1) {
610 > this._tokenizationTextModelPart.handleDidChangeAttached();
611 > this._onDidChangeAttached.fire(undefined);
612 > }
613 > return this._attachedViews.attachView();
614 > }
616 > public onBeforeDetached(view: model.IAttachedView): void {
617 this._attachedEditorCount--;
618 if (this._attachedEditorCount === 0) {
619 this._tokenizationTextModelPart.handleDidChangeAttached();
620 this._onDidChangeAttached.fire(undefined);
621 }
622 this._attachedViews.detachView(view);
623 }
625 > public isAttachedToEditor(): boolean {
626 > return this._attachedEditorCount > 0; textModelTokens.ts ×3
627 > }
629 > public getAttachedEditorCount(): number {
630 return this._attachedEditorCount;
631 }
633 > public isTooLargeForSyncing(): boolean {
634 return this._isTooLargeForSyncing;
635 }
637 > public isTooLargeForTokenization(): boolean {
638 > return this._isTooLargeForTokenization; textModel.ts ×20
639 > }
641 > public isTooLargeForHeapOperation(): boolean {
642 > return this._isTooLargeForHeapOperation; textModel.ts ×1
643 > }
645 > public isDisposed(): boolean {
646 return this._isDisposed;
647 }
649 > public isDominatedByLongLines(): boolean {
650 this._assertNotDisposed();
651 if (this.isTooLargeForTokenization()) {
652 // Cannot word wrap huge files anyways, so it doesn't really matter
653 return false;
654 }
655 let smallLineCharCount = 0;
656 let longLineCharCount = 0;
657
658 const lineCount = this._buffer.getLineCount();
659 for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) {
660 const lineLength = this._buffer.getLineLength(lineNumber);
661 if (lineLength >= LONG_LINE_BOUNDARY) {
662 longLineCharCount += lineLength;
663 } else {
664 smallLineCharCount += lineLength;
665 }
666 }
667
668 return (longLineCharCount > smallLineCharCount);
669 }
671 > public get uri(): URI {
672 > return this._associatedResource; textModel.ts ×1
673 > }
675 > //#region Options
676 >
677 > public getOptions(): model.TextModelResolvedOptions {
678 > this._assertNotDisposed(); textModel.ts ×20
679 > return this._options;
680 > }
682 > public getFormattingOptions(): FormattingOptions {
683 return {
684 tabSize: this._options.indentSize,
685 insertSpaces: this._options.insertSpaces
686 };
687 }
689 > public updateOptions(_newOpts: model.ITextModelUpdateOptions): void {
690 > this._assertNotDisposed(); model.ts ×2
691 > const tabSize = (typeof _newOpts.tabSize !== 'undefined') ? _newOpts.tabSize : this._options.tabSize;
692 > const indentSize = (typeof _newOpts.indentSize !== 'undefined') ? _newOpts.indentSize : this._options.originalIndentSize;
693 > const insertSpaces = (typeof _newOpts.insertSpaces !== 'undefined') ? _newOpts.insertSpaces : this._options.insertSpaces;
694 > const trimAutoWhitespace = (typeof _newOpts.trimAutoWhitespace !== 'undefined') ? _newOpts.trimAutoWhitespace : this._options.trimAutoWhitespace;
695 > const bracketPairColorizationOptions = (typeof _newOpts.bracketColorizationOptions !== 'undefined') ? _newOpts.bracketColorizationOptions : this._options.bracketPairColorizationOptions;
696 >
697 > const newOpts = new model.TextModelResolvedOptions({
698 > tabSize: tabSize,
699 > indentSize: indentSize,
700 > insertSpaces: insertSpaces,
701 > defaultEOL: this._options.defaultEOL,
702 > trimAutoWhitespace: trimAutoWhitespace,
703 > bracketPairColorizationOptions,
704 > });
705 >
706 > if (this._options.equals(newOpts)) {
707 > return; model.ts ×1
708 > }
709 > model.ts ×1
710 > const e = this._options.createChangeEvent(newOpts);
711 > this._options = newOpts;
712 >
713 > this._bracketPairs.handleDidChangeOptions(e);
714 > this._decorationProvider.handleDidChangeOptions(e);
715 > this._onDidChangeOptions.fire(e);
716 > } model.ts ×2
718 > public detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void {
719 this._assertNotDisposed();
720 const guessedIndentation = guessIndentation(this._buffer, defaultTabSize, defaultInsertSpaces);
721 this.updateOptions({
722 insertSpaces: guessedIndentation.insertSpaces,
723 tabSize: guessedIndentation.tabSize,
724 indentSize: guessedIndentation.tabSize, // TODO@Alex: guess indentSize independent of tabSize
725 });
726 }
728 > public normalizeIndentation(str: string): string {
729 > this._assertNotDisposed(); indentation.ts ×1
730 > return normalizeIndentation(str, this._options.indentSize, this._options.insertSpaces);
731 > }
733 > //#endregion
734 >
735 > //#region Reading
736 >
737 > public getVersionId(): number {
738 > this._assertNotDisposed(); textModel.ts ×1
739 > return this._versionId;
740 > }
742 > public mightContainRTL(): boolean {
743 > return this._buffer.mightContainRTL(); textModel.ts ×1
744 > }
746 > public mightContainUnusualLineTerminators(): boolean {
747 return this._buffer.mightContainUnusualLineTerminators();
748 }
750 > public removeUnusualLineTerminators(selections: Selection[] | null = null): void {
751 const matches = this.findMatches(strings.UNUSUAL_LINE_TERMINATORS.source, false, true, false, null, false, Constants.MAX_SAFE_SMALL_INTEGER);
752 this._buffer.resetMightContainUnusualLineTerminators();
753 this.pushEditOperations(selections, matches.map(m => ({ range: m.range, text: null })), () => null);
754 }
756 > public mightContainNonBasicASCII(): boolean {
757 > return this._buffer.mightContainNonBasicASCII(); textModel.ts ×1
758 > }
760 > public getAlternativeVersionId(): number {
761 > this._assertNotDisposed(); editStack.ts ×15
762 > return this._alternativeVersionId;
763 > }
765 > public getInitialUndoRedoSnapshot(): ResourceEditStackSnapshot | null {
766 > this._assertNotDisposed(); modelService.ts ×29
767 > return this._initialUndoRedoSnapshot;
768 > }
770 > public getOffsetAt(rawPosition: IPosition): number {
771 > this._assertNotDisposed(); textModel.ts ×1
772 > const position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, StringOffsetValidationType.Relaxed);
773 > return this._buffer.getOffsetAt(position.lineNumber, position.column);
774 > }
776 > public getPositionAt(rawOffset: number): Position {
777 > this._assertNotDisposed(); pieceTreeTextBuffer.ts ×1
778 > const offset = (Math.min(this._buffer.getLength(), Math.max(0, rawOffset)));
779 > return this._buffer.getPositionAt(offset);
780 > }
782 > private _increaseVersionId(): void {
783 > this._versionId = this._versionId + 1; textModel.ts ×9
784 > this._alternativeVersionId = this._versionId;
785 > }
787 > public _overwriteVersionId(versionId: number): void {
788 > this._versionId = versionId; textModel.ts ×2
789 > }
791 > public _overwriteAlternativeVersionId(newAlternativeVersionId: number): void {
792 > this._alternativeVersionId = newAlternativeVersionId; textModel.ts ×1
793 > }
795 > public _overwriteInitialUndoRedoSnapshot(newInitialUndoRedoSnapshot: ResourceEditStackSnapshot | null): void {
796 > this._initialUndoRedoSnapshot = newInitialUndoRedoSnapshot; textModel.ts ×2
797 > }
799 > public getValue(eol?: model.EndOfLinePreference, preserveBOM: boolean = false): string {
800 > this._assertNotDisposed(); textModel.ts ×3
801 > if (this.isTooLargeForHeapOperation()) {
802 throw new BugIndicatingError('Operation would exceed heap memory limits');
803 }
805 > const fullModelRange = this.getFullModelRange();
806 > const fullModelValue = this.getValueInRange(fullModelRange, eol);
807 >
808 > if (preserveBOM) {
809 return this._buffer.getBOM() + fullModelValue;
810 }
812 > return fullModelValue;
813 > }
815 > public createSnapshot(preserveBOM: boolean = false): model.ITextSnapshot {
816 > return new TextModelSnapshot(this._buffer.createSnapshot(preserveBOM)); textModel.ts ×8
817 > }
819 > public getValueLength(eol?: model.EndOfLinePreference, preserveBOM: boolean = false): number {
820 > this._assertNotDisposed(); textModel.ts ×2
821 > const fullModelRange = this.getFullModelRange();
822 > const fullModelValue = this.getValueLengthInRange(fullModelRange, eol);
823 >
824 > if (preserveBOM) {
825 return this._buffer.getBOM().length + fullModelValue;
826 }
828 > return fullModelValue;
829 > }
831 > public getValueInRange(rawRange: IRange, eol: model.EndOfLinePreference = model.EndOfLinePreference.TextDefined): string {
832 > this._assertNotDisposed(); textModel.ts ×1
833 > return this._buffer.getValueInRange(this.validateRange(rawRange), eol);
834 > }
836 > public getValueLengthInRange(rawRange: IRange, eol: model.EndOfLinePreference = model.EndOfLinePreference.TextDefined): number {
837 > this._assertNotDisposed(); textModel.ts ×1
838 > return this._buffer.getValueLengthInRange(this.validateRange(rawRange), eol);
839 > }
841 > public getCharacterCountInRange(rawRange: IRange, eol: model.EndOfLinePreference = model.EndOfLinePreference.TextDefined): number {
842 this._assertNotDisposed();
843 return this._buffer.getCharacterCountInRange(this.validateRange(rawRange), eol);
844 }
846 > public getLineCount(): number {
847 > this._assertNotDisposed(); textModel.ts ×20
848 > return this._buffer.getLineCount();
849 > }
851 > public getLineContent(lineNumber: number): string {
852 > this._assertNotDisposed(); textModel.ts ×2
853 > if (lineNumber < 1 || lineNumber > this.getLineCount()) {
854 throw new BugIndicatingError('Illegal value for lineNumber');
855 }
857 > return this._buffer.getLineContent(lineNumber);
858 > }
860 > public getLineLength(lineNumber: number): number {
861 > this._assertNotDisposed(); textModel.ts ×2
862 > if (lineNumber < 1 || lineNumber > this.getLineCount()) {
863 throw new BugIndicatingError('Illegal value for lineNumber');
864 }
866 > return this._buffer.getLineLength(lineNumber);
867 > }
869 > public getLinesContent(): string[] {
870 > this._assertNotDisposed(); textModel.ts ×2
871 > if (this.isTooLargeForHeapOperation()) {
872 throw new BugIndicatingError('Operation would exceed heap memory limits');
873 }
875 > return this._buffer.getLinesContent();
876 > }
878 > public getEOL(): string {
879 > this._assertNotDisposed(); textModel.ts ×1
880 > return this._buffer.getEOL();
881 > }
883 > public getEndOfLineSequence(): model.EndOfLineSequence {
884 this._assertNotDisposed();
885 return (
886 this._buffer.getEOL() === '\n'
887 ? model.EndOfLineSequence.LF
888 : model.EndOfLineSequence.CRLF
889 );
890 }
892 > public getLineMinColumn(lineNumber: number): number {
893 this._assertNotDisposed();
894 return 1;
895 }
897 > public getLineMaxColumn(lineNumber: number): number {
898 > this._assertNotDisposed(); textModel.ts ×2
899 > if (lineNumber < 1 || lineNumber > this.getLineCount()) {
900 throw new BugIndicatingError('Illegal value for lineNumber');
901 }
902 > return this._buffer.getLineLength(lineNumber) + 1; textModel.ts ×2
903 > }
905 > public getLineFirstNonWhitespaceColumn(lineNumber: number): number {
906 > this._assertNotDisposed(); textModel.ts ×2
907 > if (lineNumber < 1 || lineNumber > this.getLineCount()) {
908 throw new BugIndicatingError('Illegal value for lineNumber');
909 }
910 > return this._buffer.getLineFirstNonWhitespaceColumn(lineNumber); textModel.ts ×2
911 > }
913 > public getLineLastNonWhitespaceColumn(lineNumber: number): number {
914 > this._assertNotDisposed(); textModel.ts ×2
915 > if (lineNumber < 1 || lineNumber > this.getLineCount()) {
916 throw new BugIndicatingError('Illegal value for lineNumber');
917 }
918 > return this._buffer.getLineLastNonWhitespaceColumn(lineNumber); textModel.ts ×2
919 > }
921 > /**
922 > * Validates `range` is within buffer bounds, but allows it to sit in between surrogate pairs, etc.
923 > * Will try to not allocate if possible.
924 > */
925 > public _validateRangeRelaxedNoAllocations(range: IRange): Range {
926 > const linesCount = this._buffer.getLineCount(); textModel.ts ×10
927 >
928 > const initialStartLineNumber = range.startLineNumber;
929 > const initialStartColumn = range.startColumn;
930 > let startLineNumber = Math.floor((typeof initialStartLineNumber === 'number' && !isNaN(initialStartLineNumber)) ? initialStartLineNumber : 1);
931 > let startColumn = Math.floor((typeof initialStartColumn === 'number' && !isNaN(initialStartColumn)) ? initialStartColumn : 1);
932 >
933 > if (startLineNumber < 1) {
934 startLineNumber = 1;
935 startColumn = 1;
936 > } else if (startLineNumber > linesCount) { textModel.ts ×10
937 startLineNumber = linesCount;
938 startColumn = this.getLineMaxColumn(startLineNumber);
939 > } else { textModel.ts ×10
940 > if (startColumn <= 1) {
941 > startColumn = 1; textModel.ts ×1
942 > } else { textModel.ts ×10
943 > const maxColumn = this.getLineMaxColumn(startLineNumber); textModel.ts ×2
944 > if (startColumn >= maxColumn) {
945 > startColumn = maxColumn; textModel.ts ×2
946 > }
949 >
950 > const initialEndLineNumber = range.endLineNumber;
951 > const initialEndColumn = range.endColumn;
952 > let endLineNumber = Math.floor((typeof initialEndLineNumber === 'number' && !isNaN(initialEndLineNumber)) ? initialEndLineNumber : 1);
953 > let endColumn = Math.floor((typeof initialEndColumn === 'number' && !isNaN(initialEndColumn)) ? initialEndColumn : 1);
954 >
955 > if (endLineNumber < 1) {
956 endLineNumber = 1;
957 endColumn = 1;
958 > } else if (endLineNumber > linesCount) { textModel.ts ×10
959 > endLineNumber = linesCount; textModel.ts ×1
960 > endColumn = this.getLineMaxColumn(endLineNumber);
961 > } else { textModel.ts ×10
962 > if (endColumn <= 1) { textModel.ts ×3
963 > endColumn = 1; textModel.ts ×1
964 > } else { textModel.ts ×3
965 > const maxColumn = this.getLineMaxColumn(endLineNumber); textModel.ts ×2
966 > if (endColumn >= maxColumn) {
967 > endColumn = maxColumn; textModel.ts ×1
968 > }
972 > if (
973 > initialStartLineNumber === startLineNumber
974 > && initialStartColumn === startColumn textModel.ts ×16
975 > && initialEndLineNumber === endLineNumber
976 > && initialEndColumn === endColumn textModel.ts ×2
977 > && range instanceof Range
978 > && !(range instanceof Selection)
979 > ) { textModel.ts ×10
980 > return range; textModel.ts ×2
981 > }
983 > return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
986 > private _isValidPosition(lineNumber: number, column: number, validationType: StringOffsetValidationType): boolean {
987 > if (typeof lineNumber !== 'number' || typeof column !== 'number') { textModel.ts ×5
988 return false;
989 }
991 > if (isNaN(lineNumber) || isNaN(column)) {
992 > return false; textModel.ts ×1
993 > }
995 > if (lineNumber < 1 || column < 1) { textModel.ts ×5
996 > return false; textModel.ts ×1
997 > }
999 > if ((lineNumber | 0) !== lineNumber || (column | 0) !== column) { textModel.ts ×5
1000 > return false; textModel.ts ×1
1001 > }
1003 > const lineCount = this._buffer.getLineCount();
1004 > if (lineNumber > lineCount) {
1005 > return false; textModel.ts ×1
1006 > }
1008 > if (column === 1) {
1009 > return true; textModel.ts ×1
1010 > }
1012 > const maxColumn = this.getLineMaxColumn(lineNumber);
1013 > if (column > maxColumn) {
1014 > return false; textModel.ts ×1
1015 > }
1017 > if (validationType === StringOffsetValidationType.SurrogatePairs) {
1018 > // !!At this point, column > 1 textModel.ts ×2
1019 > const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2);
1020 > if (strings.isHighSurrogate(charCodeBefore)) {
1021 > return false; textModel.ts ×2
1022 > }
1023 > } textModel.ts ×2
1025 > return true;
1026 > } textModel.ts ×5
1028 > private _validatePosition(_lineNumber: number, _column: number, validationType: StringOffsetValidationType): Position {
1029 > const lineNumber = Math.floor((typeof _lineNumber === 'number' && !isNaN(_lineNumber)) ? _lineNumber : 1); textModel.ts ×4
1030 > const column = Math.floor((typeof _column === 'number' && !isNaN(_column)) ? _column : 1);
1031 > const lineCount = this._buffer.getLineCount();
1032 >
1033 > if (lineNumber < 1) {
1034 > return new Position(1, 1); textModel.ts ×1
1035 > }
1037 > if (lineNumber > lineCount) {
1038 > return new Position(lineCount, this.getLineMaxColumn(lineCount)); textModel.ts ×1
1039 > }
1041 > if (column <= 1) {
1042 > return new Position(lineNumber, 1); textModel.ts ×1
1043 > }
1045 > const maxColumn = this.getLineMaxColumn(lineNumber);
1046 > if (column >= maxColumn) {
1047 > return new Position(lineNumber, maxColumn); textModel.ts ×1
1048 > }
1050 > if (validationType === StringOffsetValidationType.SurrogatePairs) {
1051 > // If the position would end up in the middle of a high-low surrogate pair, textModel.ts ×2
1052 > // we move it to before the pair
1053 > // !!At this point, column > 1
1054 > const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2);
1055 > if (strings.isHighSurrogate(charCodeBefore)) {
1056 > return new Position(lineNumber, column - 1); textModel.ts ×2
1057 > }
1058 > } textModel.ts ×2
1060 > return new Position(lineNumber, column);
1061 > } textModel.ts ×4
1063 > public validatePosition(position: IPosition): Position {
1064 > const validationType = StringOffsetValidationType.SurrogatePairs; textModel.ts ×2
1065 > this._assertNotDisposed();
1066 >
1067 > // Avoid object allocation and cover most likely case
1068 > if (position instanceof Position) {
1069 > if (this._isValidPosition(position.lineNumber, position.column, validationType)) { textModel.ts ×2
1070 > return position; textModel.ts ×1
1071 > }
1072 > } textModel.ts ×2
1074 > return this._validatePosition(position.lineNumber, position.column, validationType);
1075 > } textModel.ts ×2
1077 > public isValidRange(range: Range): boolean {
1078 return this._isValidRange(range, StringOffsetValidationType.SurrogatePairs);
1079 }
1081 > private _isValidRange(range: Range, validationType: StringOffsetValidationType): boolean {
1082 > const startLineNumber = range.startLineNumber; textModel.ts ×5
1083 > const startColumn = range.startColumn;
1084 > const endLineNumber = range.endLineNumber;
1085 > const endColumn = range.endColumn;
1086 >
1087 > if (!this._isValidPosition(startLineNumber, startColumn, StringOffsetValidationType.Relaxed)) {
1088 > return false; textModel.ts ×1
1089 > }
1090 > if (!this._isValidPosition(endLineNumber, endColumn, StringOffsetValidationType.Relaxed)) { textModel.ts ×1
1091 > return false; textModel.ts ×1
1092 > }
1094 > if (validationType === StringOffsetValidationType.SurrogatePairs) {
1095 > const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0);
1096 > const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0);
1097 >
1098 > const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart);
1099 > const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd);
1100 >
1101 > if (!startInsideSurrogatePair && !endInsideSurrogatePair) {
1102 > return true;
1103 > }
1104 > return false; textModel.ts ×2
1105 > }
1106
1107 return true;
1108 > } textModel.ts ×5
1110 > public validateRange(_range: IRange): Range {
1111 > const validationType = StringOffsetValidationType.SurrogatePairs; textModel.ts ×5
1112 > this._assertNotDisposed();
1113 >
1114 > // Avoid object allocation and cover most likely case
1115 > if ((_range instanceof Range) && !(_range instanceof Selection)) {
1116 > if (this._isValidRange(_range, validationType)) {
1117 > return _range; textModel.ts ×2
1118 > }
1119 > } textModel.ts ×5
1121 > const start = this._validatePosition(_range.startLineNumber, _range.startColumn, StringOffsetValidationType.Relaxed);
1122 > const end = this._validatePosition(_range.endLineNumber, _range.endColumn, StringOffsetValidationType.Relaxed);
1123 >
1124 > const startLineNumber = start.lineNumber;
1125 > const startColumn = start.column;
1126 > const endLineNumber = end.lineNumber;
1127 > const endColumn = end.column;
1128 >
1129 > if (validationType === StringOffsetValidationType.SurrogatePairs) {
1130 > const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0);
1131 > const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0);
1132 >
1133 > const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart);
1134 > const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd);
1135 >
1136 > if (!startInsideSurrogatePair && !endInsideSurrogatePair) {
1137 > return new Range(startLineNumber, startColumn, endLineNumber, endColumn); textModel.ts ×1
1138 > }
1140 > if (startLineNumber === endLineNumber && startColumn === endColumn) { textModel.ts ×3
1141 > // do not expand a collapsed range, simply move it to a valid location textModel.ts ×1
1142 > return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn - 1);
1143 > }
1145 > if (startInsideSurrogatePair && endInsideSurrogatePair) { textModel.ts ×3
1146 > // expand range at both ends textModel.ts ×1
1147 > return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn + 1);
1148 > }
1150 > if (startInsideSurrogatePair) {
1151 > // only expand range at the start textModel.ts ×1
1152 > return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn);
1153 > }
1155 > // only expand range at the end
1156 > return new Range(startLineNumber, startColumn, endLineNumber, endColumn + 1);
1157 > }
1158
1159 return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
1160 > } textModel.ts ×5
1162 > public modifyPosition(rawPosition: IPosition, offset: number): Position {
1163 > this._assertNotDisposed(); textModel.ts ×1
1164 > const candidate = this.getOffsetAt(rawPosition) + offset;
1165 > return this.getPositionAt(Math.min(this._buffer.getLength(), Math.max(0, candidate)));
1166 > }
1168 > public getFullModelRange(): Range {
1169 > this._assertNotDisposed(); textModel.ts ×1
1170 > const lineCount = this.getLineCount();
1171 > return new Range(1, 1, lineCount, this.getLineMaxColumn(lineCount));
1172 > }
1174 > private findMatchesLineByLine(searchRange: Range, searchData: model.SearchData, captureMatches: boolean, limitResultCount: number): model.FindMatch[] {
1175 return this._buffer.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
1176 }
1178 > public findMatches(searchString: string, rawSearchScope: boolean | IRange | IRange[] | null, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount: number = LIMIT_FIND_COUNT): model.FindMatch[] {
1179 this._assertNotDisposed();
1180
1181 let searchRanges: Range[] | null = null;
1182
1183 if (rawSearchScope !== null && typeof rawSearchScope !== 'boolean') {
1184 if (!Array.isArray(rawSearchScope)) {
1185 rawSearchScope = [rawSearchScope];
1186 }
1187
1188 if (rawSearchScope.every((searchScope: IRange) => Range.isIRange(searchScope))) {
1189 searchRanges = rawSearchScope.map((searchScope: IRange) => this.validateRange(searchScope));
1190 }
1191 }
1192
1193 if (searchRanges === null) {
1194 searchRanges = [this.getFullModelRange()];
1195 }
1196
1197 searchRanges = searchRanges.sort((d1, d2) => d1.startLineNumber - d2.startLineNumber || d1.startColumn - d2.startColumn);
1198
1199 const uniqueSearchRanges: Range[] = [];
1200 uniqueSearchRanges.push(searchRanges.reduce((prev, curr) => {
1201 if (Range.areIntersecting(prev, curr)) {
1202 return prev.plusRange(curr);
1203 }
1204
1205 uniqueSearchRanges.push(prev);
1206 return curr;
1207 }));
1208
1209 let matchMapper: (value: Range, index: number, array: Range[]) => model.FindMatch[];
1210 if (!isRegex && searchString.indexOf('\n') < 0) {
1211 // not regex, not multi line
1212 const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
1213 const searchData = searchParams.parseSearchRequest();
1214
1215 if (!searchData) {
1216 return [];
1217 }
1218
1219 matchMapper = (searchRange: Range) => this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
1220 } else {
1221 matchMapper = (searchRange: Range) => TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount);
1222 }
1223
1224 return uniqueSearchRanges.map(matchMapper).reduce((arr, matches: model.FindMatch[]) => arr.concat(matches), []);
1225 }
1227 > public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null {
1228 this._assertNotDisposed();
1229 const searchStart = this.validatePosition(rawSearchStart);
1230
1231 if (!isRegex && searchString.indexOf('\n') < 0) {
1232 const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
1233 const searchData = searchParams.parseSearchRequest();
1234 if (!searchData) {
1235 return null;
1236 }
1237
1238 const lineCount = this.getLineCount();
1239 let searchRange = new Range(searchStart.lineNumber, searchStart.column, lineCount, this.getLineMaxColumn(lineCount));
1240 let ret = this.findMatchesLineByLine(searchRange, searchData, captureMatches, 1);
1241 TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
1242 if (ret.length > 0) {
1243 return ret[0];
1244 }
1245
1246 searchRange = new Range(1, 1, searchStart.lineNumber, this.getLineMaxColumn(searchStart.lineNumber));
1247 ret = this.findMatchesLineByLine(searchRange, searchData, captureMatches, 1);
1248
1249 if (ret.length > 0) {
1250 return ret[0];
1251 }
1252
1253 return null;
1254 }
1255
1256 return TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
1257 }
1259 > public findPreviousMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null {
1260 this._assertNotDisposed();
1261 const searchStart = this.validatePosition(rawSearchStart);
1262 return TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
1263 }
1265 > //#endregion
1266 >
1267 > //#region Editing
1268 >
1269 > public pushStackElement(): void {
1270 this._commandManager.pushStackElement();
1271 }
1273 > public popStackElement(): void {
1274 this._commandManager.popStackElement();
1275 }
1277 > public pushEOL(eol: model.EndOfLineSequence): void {
1278 const currentEOL = (this.getEOL() === '\n' ? model.EndOfLineSequence.LF : model.EndOfLineSequence.CRLF);
1279 if (currentEOL === eol) {
1280 return;
1281 }
1282 try {
1283 this._onDidChangeDecorations.beginDeferredEmit();
1284 this._eventEmitter.beginDeferredEmit();
1285 if (this._initialUndoRedoSnapshot === null) {
1286 this._initialUndoRedoSnapshot = this._undoRedoService.createSnapshot(this.uri);
1287 }
1288 this._commandManager.pushEOL(eol);
1289 } finally {
1290 this._eventEmitter.endDeferredEmit();
1291 this._onDidChangeDecorations.endDeferredEmit();
1292 }
1293 }
1295 > private _validateEditOperation(rawOperation: model.IIdentifiedSingleEditOperation): model.ValidAnnotatedEditOperation {
1296 > if (rawOperation instanceof model.ValidAnnotatedEditOperation) { textModel.ts ×10
1297 > return rawOperation; editStack.ts ×15
1298 > }
1300 > const validatedRange = this.validateRange(rawOperation.range);
1301 >
1302 > // Normalize edit when replacement text ends with lone CR
1303 > // and the range ends right before a CRLF in the buffer.
1304 > // We strip the trailing CR from the replacement text.
1305 > let opText = rawOperation.text;
1306 > if (opText) {
1307 > const endsWithLoneCR = ( pieceTreeTextBuffer.ts ×7
1308 > opText.length > 0 && opText.charCodeAt(opText.length - 1) === CharCode.CarriageReturn
1309 > );
1310 > const removeTrailingCR = (
1311 > this.getEOL() === '\r\n' && endsWithLoneCR && validatedRange.endColumn === this.getLineMaxColumn(validatedRange.endLineNumber)
1312 > );
1313 > if (removeTrailingCR) {
1314 > opText = opText.substring(0, opText.length - 1); textModel.ts ×1
1315 > }
1318 > return new model.ValidAnnotatedEditOperation(
1319 > rawOperation.identifier || null,
1320 > validatedRange,
1321 > opText,
1322 > rawOperation.forceMoveMarkers || false,
1323 > rawOperation.isAutoWhitespaceEdit || false,
1324 > rawOperation._isTracked || false
1325 > );
1326 > }
1328 > private _validateEditOperations(rawOperations: readonly model.IIdentifiedSingleEditOperation[]): model.ValidAnnotatedEditOperation[] {
1329 > const result: model.ValidAnnotatedEditOperation[] = []; textModel.ts ×10
1330 > for (let i = 0, len = rawOperations.length; i < len; i++) {
1331 > result[i] = this._validateEditOperation(rawOperations[i]);
1332 > }
1333 > return result;
1334 > }
1336 > public edit(edit: TextEdit, options?: { reason?: TextModelEditSource }): void {
1337 this.pushEditOperations(null, edit.replacements.map(r => ({ range: r.range, text: r.text })), null);
1338 }
1340 > public pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null, group?: UndoRedoGroup, reason?: TextModelEditSource): Selection[] | null {
1341 > try { editStack.ts ×15
1342 > this._onDidChangeDecorations.beginDeferredEmit();
1343 > this._eventEmitter.beginDeferredEmit();
1344 > return this._pushEditOperations(beforeCursorState, this._validateEditOperations(editOperations), cursorStateComputer, group, reason);
1345 > } finally {
1346 > this._eventEmitter.endDeferredEmit();
1347 > this._onDidChangeDecorations.endDeferredEmit();
1348 > }
1349 > }
1351 > private _pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.ValidAnnotatedEditOperation[], cursorStateComputer: model.ICursorStateComputer | null, group?: UndoRedoGroup, reason?: TextModelEditSource): Selection[] | null {
1352 > if (this._options.trimAutoWhitespace && this._trimAutoWhitespaceLines) { editStack.ts ×15
1353 // Go through each saved line number and insert a trim whitespace edit
1354 // if it is safe to do so (no conflicts with other edits).
1355
1356 const incomingEdits = editOperations.map((op) => {
1357 return {
1358 range: this.validateRange(op.range),
1359 text: op.text
1360 };
1361 });
1362
1363 // Sometimes, auto-formatters change ranges automatically which can cause undesired auto whitespace trimming near the cursor
1364 // We'll use the following heuristic: if the edits occur near the cursor, then it's ok to trim auto whitespace
1365 let editsAreNearCursors = true;
1366 if (beforeCursorState) {
1367 for (let i = 0, len = beforeCursorState.length; i < len; i++) {
1368 const sel = beforeCursorState[i];
1369 let foundEditNearSel = false;
1370 for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {
1371 const editRange = incomingEdits[j].range;
1372 const selIsAbove = editRange.startLineNumber > sel.endLineNumber;
1373 const selIsBelow = sel.startLineNumber > editRange.endLineNumber;
1374 if (!selIsAbove && !selIsBelow) {
1375 foundEditNearSel = true;
1376 break;
1377 }
1378 }
1379 if (!foundEditNearSel) {
1380 editsAreNearCursors = false;
1381 break;
1382 }
1383 }
1384 }
1385
1386 if (editsAreNearCursors) {
1387 for (let i = 0, len = this._trimAutoWhitespaceLines.length; i < len; i++) {
1388 const trimLineNumber = this._trimAutoWhitespaceLines[i];
1389 const maxLineColumn = this.getLineMaxColumn(trimLineNumber);
1390
1391 let allowTrimLine = true;
1392 for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {
1393 const editRange = incomingEdits[j].range;
1394 const editText = incomingEdits[j].text;
1395
1396 if (trimLineNumber < editRange.startLineNumber || trimLineNumber > editRange.endLineNumber) {
1397 // `trimLine` is completely outside this edit
1398 continue;
1399 }
1400
1401 // At this point:
1402 // editRange.startLineNumber <= trimLine <= editRange.endLineNumber
1403
1404 if (
1405 trimLineNumber === editRange.startLineNumber && editRange.startColumn === maxLineColumn
1406 && editRange.isEmpty() && editText && editText.length > 0 && editText.charAt(0) === '\n'
1407 ) {
1408 // This edit inserts a new line (and maybe other text) after `trimLine`
1409 continue;
1410 }
1411
1412 if (
1413 trimLineNumber === editRange.startLineNumber && editRange.startColumn === 1
1414 && editRange.isEmpty() && editText && editText.length > 0 && editText.charAt(editText.length - 1) === '\n'
1415 ) {
1416 // This edit inserts a new line (and maybe other text) before `trimLine`
1417 continue;
1418 }
1419
1420 // Looks like we can't trim this line as it would interfere with an incoming edit
1421 allowTrimLine = false;
1422 break;
1423 }
1424
1425 if (allowTrimLine) {
1426 const trimRange = new Range(trimLineNumber, 1, trimLineNumber, maxLineColumn);
1427 editOperations.push(new model.ValidAnnotatedEditOperation(null, trimRange, null, false, false, false));
1428 }
1429
1430 }
1431 }
1432
1433 this._trimAutoWhitespaceLines = null;
1434 }
1435 > if (this._initialUndoRedoSnapshot === null) { editStack.ts ×15
1436 > this._initialUndoRedoSnapshot = this._undoRedoService.createSnapshot(this.uri);
1437 > }
1438 > return this._commandManager.pushEditOperation(beforeCursorState, editOperations, cursorStateComputer, group, reason);
1439 > }
1441 > _applyUndo(changes: TextChange[], eol: model.EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void {
1442 > const edits = changes.map<ISingleEditOperation>((change) => { editStack.ts ×3
1443 > const rangeStart = this.getPositionAt(change.newPosition);
1444 > const rangeEnd = this.getPositionAt(change.newEnd);
1445 > return {
1446 > range: new Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column),
1447 > text: change.oldText
1448 > };
1449 > });
1450 > this._applyUndoRedoEdits(edits, eol, true, false, resultingAlternativeVersionId, resultingSelection);
1451 > }
1453 > _applyRedo(changes: TextChange[], eol: model.EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void {
1454 const edits = changes.map<ISingleEditOperation>((change) => {
1455 const rangeStart = this.getPositionAt(change.oldPosition);
1456 const rangeEnd = this.getPositionAt(change.oldEnd);
1457 return {
1458 range: new Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column),
1459 text: change.newText
1460 };
1461 });
1462 this._applyUndoRedoEdits(edits, eol, false, true, resultingAlternativeVersionId, resultingSelection);
1463 }
1465 > private _applyUndoRedoEdits(edits: ISingleEditOperation[], eol: model.EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void {
1466 > try { editStack.ts ×3
1467 > this._onDidChangeDecorations.beginDeferredEmit();
1468 > this._eventEmitter.beginDeferredEmit();
1469 > this._isUndoing = isUndoing;
1470 > this._isRedoing = isRedoing;
1471 > const operations = this._validateEditOperations(edits);
1472 > this._doApplyEdits(operations, false, EditSources.applyEdits(), resultingSelection);
1473 > this.setEOL(eol);
1474 > this._overwriteAlternativeVersionId(resultingAlternativeVersionId);
1475 > } finally {
1476 > this._isUndoing = false;
1477 > this._isRedoing = false;
1478 > this._eventEmitter.endDeferredEmit(resultingSelection);
1479 > this._onDidChangeDecorations.endDeferredEmit();
1480 > }
1481 > }
1483 > public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[]): void;
1484 > public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits: false): void;
1485 > public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits: true): model.IValidEditOperation[];
1486 > /** @internal */
1487 > public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits: false, reason: TextModelEditSource): void;
1488 > /** @internal */
1489 > public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits: true, reason: TextModelEditSource): model.IValidEditOperation[];
1490 > public applyEdits(rawOperations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits?: boolean, reason?: TextModelEditSource): void | model.IValidEditOperation[] {
1491 > try { textModel.ts ×10
1492 > this._onDidChangeDecorations.beginDeferredEmit();
1493 > this._eventEmitter.beginDeferredEmit();
1494 > const operations = this._validateEditOperations(rawOperations);
1495 >
1496 > return this._doApplyEdits(operations, computeUndoEdits ?? false, reason ?? EditSources.applyEdits());
1497 > } finally {
1498 > this._eventEmitter.endDeferredEmit();
1499 > this._onDidChangeDecorations.endDeferredEmit();
1500 > }
1501 > }
1503 > private _doApplyEdits(rawOperations: model.ValidAnnotatedEditOperation[], computeUndoEdits: boolean, reason: TextModelEditSource, resultingSelection: Selection[] | null = null): void | model.IValidEditOperation[] {
1505 > const oldLineCount = this._buffer.getLineCount();
1506 > const result = this._buffer.applyEdits(rawOperations, this._options.trimAutoWhitespace, computeUndoEdits);
1507 > const newLineCount = this._buffer.getLineCount();
1508 >
1509 > const contentChanges = result.changes;
1510 > this._trimAutoWhitespaceLines = result.trimAutoWhitespaceLineNumbers;
1511 >
1512 > if (contentChanges.length !== 0) {
1513 > // We do a first pass to update decorations intervalTree.ts ×7
1514 > // because we want to read decorations in the second pass
1515 > // where we will emit content change events
1516 > // and we want to read the final decorations
1517 > for (let i = 0, len = contentChanges.length; i < len; i++) {
1518 > const change = contentChanges[i];
1519 > this._decorationsTree.acceptReplace(change.rangeOffset, change.rangeLength, change.text.length, change.forceMoveMarkers);
1520 > }
1521 >
1522 > const rawContentChanges: ModelRawChange[] = [];
1523 >
1524 > this._increaseVersionId();
1525 >
1526 > let lineCount = oldLineCount;
1527 > for (let i = 0, len = contentChanges.length; i < len; i++) {
1528 > const change = contentChanges[i];
1529 > const [eolCount] = countEOL(change.text);
1530 > this._onDidChangeDecorations.fire();
1531 >
1532 > const startLineNumber = change.range.startLineNumber;
1533 > const endLineNumber = change.range.endLineNumber;
1534 >
1535 > const deletingLinesCnt = endLineNumber - startLineNumber;
1536 > const insertingLinesCnt = eolCount;
1537 > const editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt);
1538 >
1539 > const changeLineCountDelta = (insertingLinesCnt - deletingLinesCnt);
1540 >
1541 > const currentEditStartLineNumber = newLineCount - lineCount - changeLineCountDelta + startLineNumber;
1542 >
1543 > for (let j = editingLinesCnt; j >= 0; j--) {
1544 > const editLineNumber = startLineNumber + j;
1545 > const currentEditLineNumber = currentEditStartLineNumber + j;
1546 >
1547 > rawContentChanges.push(
1548 > new ModelRawLineChanged(
1549 > editLineNumber,
1550 > currentEditLineNumber
1551 > ));
1552 > }
1553 >
1554 > if (editingLinesCnt < deletingLinesCnt) {
1555 > // Must delete some lines textModel.ts ×1
1556 > const spliceStartLineNumber = startLineNumber + editingLinesCnt;
1557 > const cnt = insertingLinesCnt - deletingLinesCnt;
1558 > const lastUntouchedLinePostEdit = newLineCount - lineCount - cnt + spliceStartLineNumber;
1559 > rawContentChanges.push(new ModelRawLinesDeleted(spliceStartLineNumber + 1, endLineNumber, lastUntouchedLinePostEdit));
1560 > }
1562 > if (editingLinesCnt < insertingLinesCnt) {
1563 > // Must insert some lines textModel.ts ×1
1564 > const spliceLineNumber = startLineNumber + editingLinesCnt;
1565 > const cnt = insertingLinesCnt - editingLinesCnt;
1566 > const fromLineNumber = newLineCount - lineCount - cnt + spliceLineNumber + 1;
1567 > rawContentChanges.push(
1568 > new ModelRawLinesInserted(
1569 > spliceLineNumber + 1,
1570 > fromLineNumber,
1571 > cnt
1572 > )
1573 > );
1574 > }
1576 > lineCount += changeLineCountDelta;
1577 > }
1578 >
1579 > this._emitContentChangedEvent(
1580 > new ModelRawContentChangedEvent(
1581 > rawContentChanges,
1582 > this.getVersionId(),
1583 > this._isUndoing,
1584 > this._isRedoing
1585 > ),
1586 > {
1587 > changes: contentChanges,
1588 > eol: this._buffer.getEOL(),
1589 > isEolChange: false,
1590 > versionId: this.getVersionId(),
1591 > isUndoing: this._isUndoing,
1592 > isRedoing: this._isRedoing,
1593 > isFlush: false,
1594 > detailedReasons: [reason],
1595 > detailedReasonsChangeLengths: [contentChanges.length],
1596 > },
1597 > resultingSelection
1598 > );
1599 > }
1601 > return (result.reverseEdits === null ? undefined : result.reverseEdits); textModel.ts ×10
1602 > }
1604 > public undo(): void | Promise<void> {
1605 > return this._undoRedoService.undo(this.uri); textModel.ts ×1
1606 > }
1608 > public canUndo(): boolean {
1609 return this._undoRedoService.canUndo(this.uri);
1610 }
1612 > public redo(): void | Promise<void> {
1613 return this._undoRedoService.redo(this.uri);
1614 }
1616 > public canRedo(): boolean {
1617 return this._undoRedoService.canRedo(this.uri);
1618 }
1620 > //#endregion
1621 >
1622 > //#region Decorations
1623 >
1624 > private handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines: Set<number> | null, affectedLineHeights: Set<LineHeightChangingDecoration> | null, affectedFontLines: Set<LineFontChangingDecoration> | null): void {
1625 > // This is called before the decoration changed event is fired. textModel.ts ×10
1626 >
1627 > if (affectedInjectedTextLines && affectedInjectedTextLines.size > 0) {
1628 > const affectedLines = Array.from(affectedInjectedTextLines); textModel.ts ×6
1629 > const lineChangeEvents = affectedLines.map(lineNumber => new ModelRawLineChanged(lineNumber, lineNumber));
1630 > this._onDidChangeContentOrInjectedText(new ModelInjectedTextChangedEvent(lineChangeEvents));
1631 > }
1632 > this._fireOnDidChangeLineHeight(affectedLineHeights); textModel.ts ×10
1633 > this._fireOnDidChangeFont(affectedFontLines);
1634 > }
1636 > private _fireOnDidChangeLineHeight(affectedLineHeights: Set<LineHeightChangingDecoration> | null): void {
1637 > if (affectedLineHeights && affectedLineHeights.size > 0) { textModel.ts ×10
1638 const affectedLines = Array.from(affectedLineHeights);
1639 const lineHeightChangeEvent = affectedLines.map(specialLineHeightChange => new ModelLineHeightChanged(specialLineHeightChange.ownerId, specialLineHeightChange.decorationId, specialLineHeightChange.lineNumber, specialLineHeightChange.lineHeight));
1640 this._onDidChangeLineHeight.fire(new ModelLineHeightChangedEvent(lineHeightChangeEvent));
1641 }
1644 > private _fireOnDidChangeFont(affectedFontLines: Set<LineFontChangingDecoration> | null): void {
1645 > if (affectedFontLines && affectedFontLines.size > 0) { textModel.ts ×10
1646 const affectedLines = Array.from(affectedFontLines);
1647 const fontChangeEvent = affectedLines.map(fontChange => new ModelFontChanged(fontChange.ownerId, fontChange.lineNumber));
1648 this._onDidChangeFont.fire(new ModelFontChangedEvent(fontChangeEvent));
1649 }
1652 > private _onDidChangeContentOrInjectedText(e: InternalModelContentChangeEvent | ModelInjectedTextChangedEvent): void {
1653 > for (const viewModel of this._viewModels) { textModel.ts ×9
1654 > try { textModel.ts ×4
1655 > viewModel.onDidChangeContentOrInjectedText(e);
1656 > } catch (error) {
1657 onUnexpectedError(error);
1658 }
1659 > } textModel.ts ×4
1660 > for (const viewModel of this._viewModels) { textModel.ts ×9
1661 > try { textModel.ts ×4
1662 > viewModel.emitContentChangeEvent(e);
1663 > } catch (error) {
1664 onUnexpectedError(error);
1665 }
1666 > } textModel.ts ×4
1667 > } textModel.ts ×9
1669 > public changeDecorations<T>(callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T, ownerId: number = 0): T | null {
1670 > this._assertNotDisposed(); textModel.ts ×8
1671 >
1672 > try {
1673 > this._onDidChangeDecorations.beginDeferredEmit();
1674 > return this._changeDecorations(ownerId, callback);
1675 > } finally {
1676 > this._onDidChangeDecorations.endDeferredEmit();
1677 > }
1678 > }
1680 > private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null {
1681 > const changeAccessor: model.IModelDecorationsChangeAccessor = { textModel.ts ×8
1682 > addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => {
1683 > return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0]; textModel.ts ×1
1684 > },
1685 > changeDecoration: (id: string, newRange: IRange): void => { textModel.ts ×8
1686 > this._changeDecorationImpl(ownerId, id, newRange); textModel.ts ×11
1687 > },
1688 > changeDecorationOptions: (id: string, options: model.IModelDecorationOptions) => { textModel.ts ×8
1689 this._changeDecorationOptionsImpl(ownerId, id, _normalizeOptions(options));
1690 },
1691 > removeDecoration: (id: string): void => { textModel.ts ×8
1692 > this._deltaDecorationsImpl(ownerId, [id], []); textModel.ts ×1
1693 > },
1694 > deltaDecorations: (oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] => { textModel.ts ×8
1695 > if (oldDecorations.length === 0 && newDecorations.length === 0) { textModel.ts ×3
1696 > // nothing to do
1697 > return [];
1698 > }
1699 return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
1700 > } textModel.ts ×3
1701 > }; textModel.ts ×8
1702 > let result: T | null = null;
1703 > try {
1704 > result = callback(changeAccessor);
1705 > } catch (e) {
1706 onUnexpectedError(e);
1707 }
1708 > // Invalidate change accessor textModel.ts ×8
1709 > changeAccessor.addDecoration = invalidFunc;
1710 > changeAccessor.changeDecoration = invalidFunc;
1711 > changeAccessor.changeDecorationOptions = invalidFunc;
1712 > changeAccessor.removeDecoration = invalidFunc;
1713 > changeAccessor.deltaDecorations = invalidFunc;
1714 > return result;
1715 > }
1717 > public deltaDecorations(oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[], ownerId: number = 0): string[] {
1718 > this._assertNotDisposed(); textModel.ts ×3
1719 > if (!oldDecorations) {
1720 oldDecorations = [];
1721 }
1722 > if (oldDecorations.length === 0 && newDecorations.length === 0) { textModel.ts ×3
1723 > // nothing to do textModel.ts ×3
1724 > return [];
1725 > }
1727 > try {
1728 > this._deltaDecorationCallCnt++;
1729 > if (this._deltaDecorationCallCnt > 1) {
1730 console.warn(`Invoking deltaDecorations recursively could lead to leaking decorations.`);
1731 onUnexpectedError(new Error(`Invoking deltaDecorations recursively could lead to leaking decorations.`));
1732 }
1733 > this._onDidChangeDecorations.beginDeferredEmit(); textModel.ts ×2
1734 > return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
1735 > } finally {
1736 > this._onDidChangeDecorations.endDeferredEmit();
1737 > this._deltaDecorationCallCnt--;
1738 > }
1739 > } textModel.ts ×3
1741 > _getTrackedRange(id: string): Range | null {
1742 return this.getDecorationRange(id);
1743 }
1745 > _setTrackedRange(id: string | null, newRange: null, newStickiness: model.TrackedRangeStickiness): null;
1746 > _setTrackedRange(id: string | null, newRange: Range, newStickiness: model.TrackedRangeStickiness): string;
1747 > _setTrackedRange(id: string | null, newRange: Range | null, newStickiness: model.TrackedRangeStickiness): string | null {
1748 const node = (id ? this._decorations[id] : null);
1749
1750 if (!node) {
1751 if (!newRange) {
1752 // node doesn't exist, the request is to delete => nothing to do
1753 return null;
1754 }
1755 // node doesn't exist, the request is to set => add the tracked range
1756 return this._deltaDecorationsImpl(0, [], [{ range: newRange, options: TRACKED_RANGE_OPTIONS[newStickiness] }], true)[0];
1757 }
1758
1759 if (!newRange) {
1760 // node exists, the request is to delete => delete node
1761 this._decorationsTree.delete(node);
1762 delete this._decorations[node.id];
1763 return null;
1764 }
1765
1766 // node exists, the request is to set => change the tracked range and its options
1767 const range = this._validateRangeRelaxedNoAllocations(newRange);
1768 const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
1769 const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
1770 this._decorationsTree.delete(node);
1771 node.reset(this.getVersionId(), startOffset, endOffset, range);
1772 node.setOptions(TRACKED_RANGE_OPTIONS[newStickiness]);
1773 this._decorationsTree.insert(node);
1774 return node.id;
1775 }
1777 > public removeAllDecorationsWithOwnerId(ownerId: number): void {
1778 > if (this._isDisposed) { textModel.ts ×2
1779 > return; textModel.ts ×1
1780 > }
1781 > const nodes = this._decorationsTree.collectNodesFromOwner(ownerId); intervalTree.ts ×4
1782 > for (let i = 0, len = nodes.length; i < len; i++) {
1783 > const node = nodes[i];
1784 >
1785 > this._decorationsTree.delete(node);
1786 > delete this._decorations[node.id];
1787 > }
1788 > } textModel.ts ×2
1790 > public getDecorationOptions(decorationId: string): model.IModelDecorationOptions | null {
1791 > const node = this._decorations[decorationId]; textModel.ts ×2
1792 > if (!node) {
1793 return null;
1794 }
1795 > return node.options; textModel.ts ×2
1796 > }
1798 > public getDecorationRange(decorationId: string): Range | null {
1799 > const node = this._decorations[decorationId]; textModel.ts ×2
1800 > if (!node) {
1801 return null;
1802 }
1803 > return this._decorationsTree.getNodeRange(this, node); textModel.ts ×2
1804 > }
1806 > public getLineDecorations(lineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false): model.IModelDecoration[] {
1807 > if (lineNumber < 1 || lineNumber > this.getLineCount()) { textModel.ts ×3
1808 return [];
1809 }
1810 > return this.getLinesDecorations(lineNumber, lineNumber, ownerId, filterOutValidation, filterFontDecorations); textModel.ts ×3
1811 > }
1813 > public getLinesDecorations(_startLineNumber: number, _endLineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false, onlyMarginDecorations: boolean = false): model.IModelDecoration[] {
1814 > const lineCount = this.getLineCount(); textModel.ts ×3
1815 > const startLineNumber = Math.min(lineCount, Math.max(1, _startLineNumber));
1816 > const endLineNumber = Math.min(lineCount, Math.max(1, _endLineNumber));
1817 > const endColumn = this.getLineMaxColumn(endLineNumber);
1818 > const range = new Range(startLineNumber, 1, endLineNumber, endColumn);
1819 >
1820 > const decorations = this._getDecorationsInRange(range, ownerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations);
1821 > pushMany(decorations, this._decorationProvider.getDecorationsInRange(range, ownerId, filterOutValidation, filterFontDecorations));
1822 > pushMany(decorations, this._fontTokenDecorationsProvider.getDecorationsInRange(range, ownerId, filterOutValidation, filterFontDecorations));
1823 > return decorations;
1824 > }
1826 > public getDecorationsInRange(range: IRange, ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false, onlyMinimapDecorations: boolean = false, onlyMarginDecorations: boolean = false): model.IModelDecoration[] {
1827 > const validatedRange = this.validateRange(range); textModel.ts ×2
1828 >
1829 > const decorations = this._getDecorationsInRange(validatedRange, ownerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations);
1830 > pushMany(decorations, this._decorationProvider.getDecorationsInRange(validatedRange, ownerId, filterOutValidation, filterFontDecorations, onlyMinimapDecorations));
1831 > pushMany(decorations, this._fontTokenDecorationsProvider.getDecorationsInRange(validatedRange, ownerId, filterOutValidation, filterFontDecorations, onlyMinimapDecorations));
1832 > return decorations;
1833 > }
1835 > public getOverviewRulerDecorations(ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false): model.IModelDecoration[] {
1836 return this._decorationsTree.getAll(this, ownerId, filterOutValidation, filterFontDecorations, true, false);
1837 }
1839 > public getInjectedTextDecorations(ownerId: number = 0): model.IModelDecoration[] {
1840 return this._decorationsTree.getAllInjectedText(this, ownerId);
1841 }
1843 > public getCustomLineHeightsDecorations(ownerId: number = 0): model.IModelDecoration[] {
1844 const decs = this._decorationsTree.getAllCustomLineHeights(this, ownerId);
1845 pushMany(decs, this._fontTokenDecorationsProvider.getAllDecorations(ownerId));
1846 return decs;
1847 }
1849 > public getCustomLineHeightsDecorationsInRange(range: Range, ownerId: number = 0): model.IModelDecoration[] {
1850 const decs = this._decorationsTree.getCustomLineHeightsInInterval(this, this.getOffsetAt(range.getStartPosition()), this.getOffsetAt(range.getEndPosition()), ownerId);
1851 pushMany(decs, this._fontTokenDecorationsProvider.getDecorationsInRange(range, ownerId));
1852 return decs;
1853 }
1855 > public getLineInjectedText(lineNumber: number, ownerId: number = 0): LineInjectedText[] {
1856 const startOffset = this._buffer.getOffsetAt(lineNumber, 1);
1857 const endOffset = startOffset + this._buffer.getLineLength(lineNumber);
1858
1859 const result = this._decorationsTree.getInjectedTextInInterval(this, startOffset, endOffset, ownerId);
1860 return LineInjectedText.fromDecorations(result).filter(t => t.lineNumber === lineNumber);
1861 }
1863 > public getFontDecorationsInRange(range: IRange, ownerId: number = 0): model.IModelDecoration[] {
1864 const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
1865 const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
1866 return this._decorationsTree.getFontDecorationsInInterval(this, startOffset, endOffset, ownerId);
1867 }
1869 > public getAllDecorations(ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false): model.IModelDecoration[] {
1870 > let result = this._decorationsTree.getAll(this, ownerId, filterOutValidation, filterFontDecorations, false, false); colorizedBracketPairsDecorationProvider.ts ×3
1871 > result = result.concat(this._decorationProvider.getAllDecorations(ownerId, filterOutValidation));
1872 > result = result.concat(this._fontTokenDecorationsProvider.getAllDecorations(ownerId, filterOutValidation));
1873 > return result;
1874 > }
1876 > public getAllMarginDecorations(ownerId: number = 0): model.IModelDecoration[] {
1877 return this._decorationsTree.getAll(this, ownerId, false, false, false, true);
1878 }
1880 > private _getDecorationsInRange(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] {
1881 > const startOffset = this._buffer.getOffsetAt(filterRange.startLineNumber, filterRange.startColumn); textModel.ts ×3
1882 > const endOffset = this._buffer.getOffsetAt(filterRange.endLineNumber, filterRange.endColumn);
1883 > return this._decorationsTree.getAllInInterval(this, startOffset, endOffset, filterOwnerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations);
1884 > }
1886 > public getRangeAt(start: number, end: number): Range {
1887 > return this._buffer.getRangeAt(start, end - start); intervalTree.ts ×1
1888 > }
1890 > private _changeDecorationImpl(ownerId: number, decorationId: string, _range: IRange): void {
1891 > const node = this._decorations[decorationId]; textModel.ts ×11
1892 > if (!node) {
1893 return;
1894 }
1896 > if (node.options.after) {
1897 const oldRange = this.getDecorationRange(decorationId);
1898 this._onDidChangeDecorations.recordLineAffectedByInjectedText(oldRange!.endLineNumber);
1899 }
1900 > if (node.options.before) { textModel.ts ×11
1901 const oldRange = this.getDecorationRange(decorationId);
1902 this._onDidChangeDecorations.recordLineAffectedByInjectedText(oldRange!.startLineNumber);
1903 }
1904 > if (node.options.lineHeight !== null) { textModel.ts ×11
1905 const oldRange = this.getDecorationRange(decorationId);
1906 this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, oldRange!.startLineNumber, null);
1907 }
1908 > if (node.options.affectsFont) { textModel.ts ×11
1909 const oldRange = this.getDecorationRange(decorationId);
1910 this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, oldRange!.startLineNumber);
1911 }
1913 > const range = this._validateRangeRelaxedNoAllocations(_range);
1914 > const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
1915 > const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
1916 >
1917 > this._decorationsTree.delete(node);
1918 > node.reset(this.getVersionId(), startOffset, endOffset, range);
1919 > this._decorationsTree.insert(node);
1920 > this._onDidChangeDecorations.checkAffectedAndFire(node.options);
1921 >
1922 > if (node.options.after) {
1923 this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.endLineNumber);
1924 }
1925 > if (node.options.before) { textModel.ts ×11
1926 this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.startLineNumber);
1927 }
1928 > if (node.options.lineHeight !== null) { textModel.ts ×11
1929 this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, range.startLineNumber, node.options.lineHeight);
1930 }
1931 > if (node.options.affectsFont) { textModel.ts ×11
1932 this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, range.startLineNumber);
1933 }
1936 > private _changeDecorationOptionsImpl(ownerId: number, decorationId: string, options: ModelDecorationOptions): void {
1937 const node = this._decorations[decorationId];
1938 if (!node) {
1939 return;
1940 }
1941
1942 const nodeWasInOverviewRuler = (node.options.overviewRuler && node.options.overviewRuler.color ? true : false);
1943 const nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false);
1944
1945 this._onDidChangeDecorations.checkAffectedAndFire(node.options);
1946 this._onDidChangeDecorations.checkAffectedAndFire(options);
1947
1948 if (node.options.after || options.after) {
1949 const nodeRange = this._decorationsTree.getNodeRange(this, node);
1950 this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.endLineNumber);
1951 }
1952 if (node.options.before || options.before) {
1953 const nodeRange = this._decorationsTree.getNodeRange(this, node);
1954 this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.startLineNumber);
1955 }
1956 if (node.options.lineHeight !== null || options.lineHeight !== null) {
1957 const nodeRange = this._decorationsTree.getNodeRange(this, node);
1958 this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, nodeRange.startLineNumber, options.lineHeight);
1959 }
1960 if (node.options.affectsFont || options.affectsFont) {
1961 const nodeRange = this._decorationsTree.getNodeRange(this, node);
1962 this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, decorationId, nodeRange.startLineNumber);
1963 }
1964
1965 const movedInOverviewRuler = nodeWasInOverviewRuler !== nodeIsInOverviewRuler;
1966 const changedWhetherInjectedText = isOptionsInjectedText(options) !== isNodeInjectedText(node);
1967 if (movedInOverviewRuler || changedWhetherInjectedText) {
1968 this._decorationsTree.delete(node);
1969 node.setOptions(options);
1970 this._decorationsTree.insert(node);
1971 } else {
1972 node.setOptions(options);
1973 }
1974 }
1976 > private _deltaDecorationsImpl(ownerId: number, oldDecorationsIds: string[], newDecorations: model.IModelDeltaDecoration[], suppressEvents: boolean = false): string[] {
1977 > const versionId = this.getVersionId(); textModel.ts ×16
1978 >
1979 > const oldDecorationsLen = oldDecorationsIds.length;
1980 > let oldDecorationIndex = 0;
1981 >
1982 > const newDecorationsLen = newDecorations.length;
1983 > let newDecorationIndex = 0;
1984 >
1985 > this._onDidChangeDecorations.beginDeferredEmit();
1986 > try {
1987 > const result = new Array<string>(newDecorationsLen);
1988 > while (oldDecorationIndex < oldDecorationsLen || newDecorationIndex < newDecorationsLen) {
1989 >
1990 > let node: IntervalNode | null = null;
1991 >
1992 > if (oldDecorationIndex < oldDecorationsLen) {
1993 > // (1) get ourselves an old node textModel.ts ×5
1994 > let decorationId: string;
1995 > do {
1996 > decorationId = oldDecorationsIds[oldDecorationIndex++];
1997 > node = this._decorations[decorationId];
1998 > } while (!node && oldDecorationIndex < oldDecorationsLen);
1999 >
2000 > // (2) remove the node from the tree (if it exists)
2001 > if (node) {
2002 > if (node.options.after) {
2003 > const nodeRange = this._decorationsTree.getNodeRange(this, node); textModel.ts ×6
2004 > this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.endLineNumber);
2005 > }
2006 > if (node.options.before) { textModel.ts ×5
2007 const nodeRange = this._decorationsTree.getNodeRange(this, node);
2008 this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.startLineNumber);
2009 }
2010 > if (node.options.lineHeight !== null) { textModel.ts ×5
2011 const nodeRange = this._decorationsTree.getNodeRange(this, node);
2012 this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, nodeRange.startLineNumber, null);
2013 }
2014 > if (node.options.affectsFont) { textModel.ts ×5
2015 const nodeRange = this._decorationsTree.getNodeRange(this, node);
2016 this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, decorationId, nodeRange.startLineNumber);
2017 }
2018 > this._decorationsTree.delete(node); textModel.ts ×5
2019 >
2020 > if (!suppressEvents) {
2021 > this._onDidChangeDecorations.checkAffectedAndFire(node.options);
2022 > }
2023 > }
2024 > }
2026 > if (newDecorationIndex < newDecorationsLen) {
2027 > // (3) create a new node if necessary
2028 > if (!node) {
2029 > const internalDecorationId = (++this._lastDecorationId);
2030 > const decorationId = `${this._instanceId};${internalDecorationId}`;
2031 > node = new IntervalNode(decorationId, 0, 0);
2032 > this._decorations[decorationId] = node;
2033 > }
2034 >
2035 > // (4) initialize node
2036 > const newDecoration = newDecorations[newDecorationIndex];
2037 > const range = this._validateRangeRelaxedNoAllocations(newDecoration.range);
2038 > const options = _normalizeOptions(newDecoration.options);
2039 > const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
2040 > const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
2041 >
2042 > node.ownerId = ownerId;
2043 > node.reset(versionId, startOffset, endOffset, range);
2044 > node.setOptions(options);
2045 >
2046 > if (node.options.after) {
2047 > this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.endLineNumber); textModel.ts ×6
2048 > }
2049 > if (node.options.before) { textModel.ts ×16
2050 this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.startLineNumber);
2051 }
2052 > if (node.options.lineHeight !== null) { textModel.ts ×16
2053 this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, node.id, range.startLineNumber, node.options.lineHeight);
2054 }
2055 > if (node.options.affectsFont) { textModel.ts ×16
2056 this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, range.startLineNumber);
2057 }
2058 > if (!suppressEvents) { textModel.ts ×16
2059 > this._onDidChangeDecorations.checkAffectedAndFire(options);
2060 > }
2061 >
2062 > this._decorationsTree.insert(node);
2063 >
2064 > result[newDecorationIndex] = node.id;
2065 >
2066 > newDecorationIndex++;
2067 > } else {
2068 > if (node) { textModel.ts ×1
2069 > delete this._decorations[node.id];
2070 > }
2071 > }
2073 >
2074 > return result;
2075 > } finally {
2076 > this._onDidChangeDecorations.endDeferredEmit();
2077 > }
2078 > }
2080 > //#endregion
2081 >
2082 > //#region Tokenization
2083 >
2084 > // TODO move them to the tokenization part.
2085 > public getLanguageId(): string {
2086 > return this.tokenization.getLanguageId(); textModel.ts ×1
2087 > }
2089 > public setLanguage(languageIdOrSelection: string | ILanguageSelection, source?: string): void {
2090 > if (typeof languageIdOrSelection === 'string') { textModel.ts ×3
2091 > this._languageSelectionListener.clear();
2092 > this._setLanguage(languageIdOrSelection, source);
2093 > } else {
2094 this._languageSelectionListener.value = languageIdOrSelection.onDidChange(() => this._setLanguage(languageIdOrSelection.languageId, source));
2095 this._setLanguage(languageIdOrSelection.languageId, source);
2096 }
2097 > } textModel.ts ×3
2099 > private _setLanguage(languageId: string, source?: string): void {
2100 > this.tokenization.setLanguageId(languageId, source); textModel.ts ×3
2101 > this._languageService.requestRichLanguageFeatures(languageId);
2102 > }
2104 > public getLanguageIdAtPosition(lineNumber: number, column: number): string {
2105 > return this.tokenization.getLanguageIdAtPosition(lineNumber, column); textModel.ts ×1
2106 > }
2108 > public getWordAtPosition(position: IPosition): IWordAtPosition | null {
2109 > return this._tokenizationTextModelPart.getWordAtPosition(position); tokenizationTextModelPart.ts ×6
2110 > }
2112 > public getWordUntilPosition(position: IPosition): IWordAtPosition {
2113 return this._tokenizationTextModelPart.getWordUntilPosition(position);
2114 }
2116 > //#endregion
2117 > normalizePosition(position: Position, affinity: model.PositionAffinity): Position {
2118 return position;
2119 }
2121 > /**
2122 > * Gets the column at which indentation stops at a given line.
2123 > * @internal
2124 > */
2125 > public getLineIndentColumn(lineNumber: number): number {
2126 // Columns start with 1.
2127 return indentOfLine(this.getLineContent(lineNumber)) + 1;
2128 }
2130 > public override toString(): string {
2131 return `TextModel(${this.uri.toString()})`;
2132 }
2134 >
2135 > export function getLineTokensWithInjections(tokens: LineTokens, injectionOptions: model.InjectedTextOptions[] | null, injectionOffsets: number[] | null): LineTokens {
2136 let lineTokens: LineTokens;
2137 if (injectionOffsets) {
2138 const tokensToInsert: { offset: number; text: string; tokenMetadata: number }[] = [];
2139
2140 for (let idx = 0; idx < injectionOffsets.length; idx++) {
2141 const offset = injectionOffsets[idx];
2142 const tokens = injectionOptions![idx].tokens;
2143 if (tokens) {
2144 tokens.forEach((range, info) => {
2145 tokensToInsert.push({
2146 offset,
2147 text: range.substring(injectionOptions![idx].content),
2148 tokenMetadata: info.metadata,
2149 });
2150 });
2151 } else {
2152 tokensToInsert.push({
2153 offset,
2154 text: injectionOptions![idx].content,
2155 tokenMetadata: LineTokens.defaultTokenMetadata,
2156 });
2157 }
2158 }
2159 lineTokens = tokens.withInserted(tokensToInsert);
2160 } else {
2161 lineTokens = tokens;
2162 }
2163 return lineTokens;
2164 }
2166 > export function indentOfLine(line: string): number {
2167 let indent = 0;
2168 for (const c of line) {
2169 if (c === ' ' || c === '\t') {
2170 indent++;
2171 } else {
2172 break;
2173 }
2174 }
2175 return indent;
2176 }
2178 > //#region Decorations
2179 >
2180 > function isNodeInOverviewRuler(node: IntervalNode): boolean { textModel.ts ×2
2181 > return (node.options.overviewRuler && node.options.overviewRuler.color ? true : false);
2182 > }
2184 function isOptionsInjectedText(options: ModelDecorationOptions): boolean {
2185 return !!options.after || !!options.before;
2186 }
2188 > function isNodeInjectedText(node: IntervalNode): boolean { textModel.ts ×16
2189 > return !!node.options.after || !!node.options.before;
2190 > }
2192 > export interface IDecorationsTreesHost {
2193 > getVersionId(): number;
2194 > getRangeAt(start: number, end: number): Range;
2195 > }
2196 >
2197 > class DecorationsTrees {
2198 >
2199 > /**
2200 > * This tree holds decorations that do not show up in the overview ruler.
2201 > */
2202 > private readonly _decorationsTree0: IntervalTree;
2203 >
2204 > /**
2205 > * This tree holds decorations that show up in the overview ruler.
2206 > */
2207 > private readonly _decorationsTree1: IntervalTree;
2208 >
2209 > /**
2210 > * This tree holds decorations that contain injected text.
2211 > */
2212 > private readonly _injectedTextDecorationsTree: IntervalTree;
2213 >
2214 > constructor() {
2215 > this._decorationsTree0 = new IntervalTree(); textModel.ts ×20
2216 > this._decorationsTree1 = new IntervalTree();
2217 > this._injectedTextDecorationsTree = new IntervalTree();
2218 > }
2220 > public ensureAllNodesHaveRanges(host: IDecorationsTreesHost): void {
2221 > this.getAll(host, 0, false, false, false, false); textModel.ts ×6
2222 > }
2224 > private _ensureNodesHaveRanges(host: IDecorationsTreesHost, nodes: IntervalNode[]): model.IModelDecoration[] {
2225 > for (const node of nodes) { textModel.ts ×2
2226 > if (node.range === null) { textModel.ts ×2
2227 > node.range = host.getRangeAt(node.cachedAbsoluteStart, node.cachedAbsoluteEnd); textModel.ts ×1
2228 > }
2229 > } textModel.ts ×2
2230 > return <model.IModelDecoration[]>nodes; textModel.ts ×2
2231 > }
2233 > public getAllInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] {
2234 > const versionId = host.getVersionId(); textModel.ts ×3
2235 > const result = this._intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, versionId, onlyMarginDecorations);
2236 > return this._ensureNodesHaveRanges(host, result);
2237 > }
2239 > private _intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] {
2240 > const r0 = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations); textModel.ts ×3
2241 > const r1 = this._decorationsTree1.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
2242 > const r2 = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
2243 > return r0.concat(r1).concat(r2);
2244 > }
2246 > public getInjectedTextInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number): model.IModelDecoration[] {
2247 const versionId = host.getVersionId();
2248 const result = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, false, false, versionId, false);
2249 return this._ensureNodesHaveRanges(host, result).filter((i) => i.options.showIfCollapsed || !i.range.isEmpty());
2250 }
2252 > public getFontDecorationsInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number): model.IModelDecoration[] {
2253 const versionId = host.getVersionId();
2254 const decorations = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, false, false, versionId, false);
2255 return this._ensureNodesHaveRanges(host, decorations).filter((i) => i.options.affectsFont);
2256 }
2258 > public getAllInjectedText(host: IDecorationsTreesHost, filterOwnerId: number): model.IModelDecoration[] {
2259 const versionId = host.getVersionId();
2260 const result = this._injectedTextDecorationsTree.search(filterOwnerId, false, false, versionId, false);
2261 return this._ensureNodesHaveRanges(host, result).filter((i) => i.options.showIfCollapsed || !i.range.isEmpty());
2262 }
2264 > public getAllCustomLineHeights(host: IDecorationsTreesHost, filterOwnerId: number): model.IModelDecoration[] {
2265 const versionId = host.getVersionId();
2266 const result = this._search(filterOwnerId, false, false, false, versionId, false);
2267 return this._ensureNodesHaveRanges(host, result).filter((i) => typeof i.options.lineHeight === 'number');
2268 }
2270 > public getCustomLineHeightsInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number): model.IModelDecoration[] {
2271 const versionId = host.getVersionId();
2272 const result = this._intervalSearch(start, end, filterOwnerId, false, false, versionId, false);
2273 return this._ensureNodesHaveRanges(host, result).filter((i) => typeof i.options.lineHeight === 'number');
2274 }
2276 > public getAll(host: IDecorationsTreesHost, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, overviewRulerOnly: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] {
2277 > const versionId = host.getVersionId(); textModel.ts ×3
2278 > const result = this._search(filterOwnerId, filterOutValidation, filterFontDecorations, overviewRulerOnly, versionId, onlyMarginDecorations);
2279 > return this._ensureNodesHaveRanges(host, result);
2280 > }
2282 > private _search(filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, overviewRulerOnly: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] {
2283 > if (overviewRulerOnly) { textModel.ts ×3
2284 return this._decorationsTree1.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
2285 > } else { textModel.ts ×3
2286 > const r0 = this._decorationsTree0.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
2287 > const r1 = this._decorationsTree1.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
2288 > const r2 = this._injectedTextDecorationsTree.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
2289 > return r0.concat(r1).concat(r2);
2290 > }
2291 > }
2293 > public collectNodesFromOwner(ownerId: number): IntervalNode[] {
2294 > const r0 = this._decorationsTree0.collectNodesFromOwner(ownerId); intervalTree.ts ×4
2295 > const r1 = this._decorationsTree1.collectNodesFromOwner(ownerId);
2296 > const r2 = this._injectedTextDecorationsTree.collectNodesFromOwner(ownerId);
2297 > return r0.concat(r1).concat(r2);
2298 > }
2300 > public collectNodesPostOrder(): IntervalNode[] {
2301 > const r0 = this._decorationsTree0.collectNodesPostOrder(); textModel.ts ×6
2302 > const r1 = this._decorationsTree1.collectNodesPostOrder();
2303 > const r2 = this._injectedTextDecorationsTree.collectNodesPostOrder();
2304 > return r0.concat(r1).concat(r2);
2305 > }
2307 > public insert(node: IntervalNode): void {
2308 > if (isNodeInjectedText(node)) { textModel.ts ×16
2309 > this._injectedTextDecorationsTree.insert(node); textModel.ts ×6
2310 > } else if (isNodeInOverviewRuler(node)) { textModel.ts ×16
2311 this._decorationsTree1.insert(node);
2312 > } else { textModel.ts ×2
2313 > this._decorationsTree0.insert(node);
2314 > }
2317 > public delete(node: IntervalNode): void {
2318 > if (isNodeInjectedText(node)) { textModel.ts ×3
2319 > this._injectedTextDecorationsTree.delete(node); textModel.ts ×6
2320 > } else if (isNodeInOverviewRuler(node)) { textModel.ts ×3
2321 this._decorationsTree1.delete(node);
2322 > } else { textModel.ts ×1
2323 > this._decorationsTree0.delete(node);
2324 > }
2325 > } textModel.ts ×3
2327 > public getNodeRange(host: IDecorationsTreesHost, node: IntervalNode): Range {
2328 > const versionId = host.getVersionId(); textModel.ts ×3
2329 > if (node.cachedVersionId !== versionId) {
2330 > this._resolveNode(node, versionId); textModel.ts ×5
2331 > }
2332 > if (node.range === null) { textModel.ts ×3
2333 > node.range = host.getRangeAt(node.cachedAbsoluteStart, node.cachedAbsoluteEnd); textModel.ts ×5
2334 > }
2335 > return node.range; textModel.ts ×3
2336 > }
2338 > private _resolveNode(node: IntervalNode, cachedVersionId: number): void {
2339 > if (isNodeInjectedText(node)) { textModel.ts ×5
2340 this._injectedTextDecorationsTree.resolveNode(node, cachedVersionId);
2341 > } else if (isNodeInOverviewRuler(node)) { textModel.ts ×5
2342 this._decorationsTree1.resolveNode(node, cachedVersionId);
2343 > } else { textModel.ts ×5
2344 > this._decorationsTree0.resolveNode(node, cachedVersionId);
2345 > }
2346 > }
2348 > public acceptReplace(offset: number, length: number, textLength: number, forceMoveMarkers: boolean): void {
2349 > this._decorationsTree0.acceptReplace(offset, length, textLength, forceMoveMarkers); intervalTree.ts ×7
2350 > this._decorationsTree1.acceptReplace(offset, length, textLength, forceMoveMarkers);
2351 > this._injectedTextDecorationsTree.acceptReplace(offset, length, textLength, forceMoveMarkers);
2352 > }
2354 >
2355 > function cleanClassName(className: string): string { textModel.ts ×1
2356 > return className.replace(/[^a-z0-9\-_]/gi, ' ');
2357 > }
2359 > class DecorationOptions implements model.IDecorationOptions {
2360 > readonly color: string | ThemeColor;
2361 > readonly darkColor: string | ThemeColor;
2362 >
2363 > constructor(options: model.IDecorationOptions) {
2364 this.color = options.color || '';
2365 this.darkColor = options.darkColor || '';
2366
2367 }
2369 >
2370 > export class ModelDecorationOverviewRulerOptions extends DecorationOptions {
2371 > readonly position: model.OverviewRulerLane;
2372 > private _resolvedColor: string | null;
2373 >
2374 > constructor(options: model.IModelDecorationOverviewRulerOptions) {
2375 super(options);
2376 this._resolvedColor = null;
2377 this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center);
2378 }
2380 > public getColor(theme: IColorTheme): string {
2381 if (!this._resolvedColor) {
2382 if (isDark(theme.type) && this.darkColor) {
2383 this._resolvedColor = this._resolveColor(this.darkColor, theme);
2384 } else {
2385 this._resolvedColor = this._resolveColor(this.color, theme);
2386 }
2387 }
2388 return this._resolvedColor;
2389 }
2391 > public invalidateCachedColor(): void {
2392 this._resolvedColor = null;
2393 }
2395 > private _resolveColor(color: string | ThemeColor, theme: IColorTheme): string {
2396 if (typeof color === 'string') {
2397 return color;
2398 }
2399 const c = color ? theme.getColor(color.id) : null;
2400 if (!c) {
2401 return '';
2402 }
2403 return c.toString();
2404 }
2406 >
2407 > export class ModelDecorationGlyphMarginOptions {
2408 > readonly position: model.GlyphMarginLane;
2409 > readonly persistLane: boolean | undefined;
2410 >
2411 > constructor(options: model.IModelDecorationGlyphMarginOptions | null | undefined) {
2412 this.position = options?.position ?? model.GlyphMarginLane.Center;
2413 this.persistLane = options?.persistLane;
2414 }
2416 >
2417 > export class ModelDecorationMinimapOptions extends DecorationOptions {
2418 > readonly position: model.MinimapPosition;
2419 > readonly sectionHeaderStyle: model.MinimapSectionHeaderStyle | null;
2420 > readonly sectionHeaderText: string | null;
2421 > private _resolvedColor: Color | undefined;
2422 >
2423 > constructor(options: model.IModelDecorationMinimapOptions) {
2424 super(options);
2425 this.position = options.position;
2426 this.sectionHeaderStyle = options.sectionHeaderStyle ?? null;
2427 this.sectionHeaderText = options.sectionHeaderText ?? null;
2428 }
2430 > public getColor(theme: IColorTheme): Color | undefined {
2431 if (!this._resolvedColor) {
2432 if (isDark(theme.type) && this.darkColor) {
2433 this._resolvedColor = this._resolveColor(this.darkColor, theme);
2434 } else {
2435 this._resolvedColor = this._resolveColor(this.color, theme);
2436 }
2437 }
2438
2439 return this._resolvedColor;
2440 }
2442 > public invalidateCachedColor(): void {
2443 this._resolvedColor = undefined;
2444 }
2446 > private _resolveColor(color: string | ThemeColor, theme: IColorTheme): Color | undefined {
2447 if (typeof color === 'string') {
2448 return Color.fromHex(color);
2449 }
2450 return theme.getColor(color.id);
2451 }
2453 >
2454 > export class ModelDecorationInjectedTextOptions implements model.InjectedTextOptions {
2455 > public static from(options: model.InjectedTextOptions): ModelDecorationInjectedTextOptions {
2456 > if (options instanceof ModelDecorationInjectedTextOptions) { textModel.ts ×3
2457 return options;
2458 }
2459 > return new ModelDecorationInjectedTextOptions(options); textModel.ts ×3
2460 > }
2462 > public readonly content: string;
2463 > public readonly tokens: TokenArray | null;
2464 > readonly inlineClassName: string | null;
2465 > readonly inlineClassNameAffectsLetterSpacing: boolean;
2466 > readonly attachedData: unknown | null;
2467 > readonly cursorStops: model.InjectedTextCursorStops | null;
2468 >
2469 > private constructor(options: model.InjectedTextOptions) {
2470 > this.content = options.content || ''; textModel.ts ×3
2471 > this.tokens = options.tokens ?? null;
2472 > this.inlineClassName = options.inlineClassName || null;
2473 > this.inlineClassNameAffectsLetterSpacing = options.inlineClassNameAffectsLetterSpacing || false;
2474 > this.attachedData = options.attachedData || null;
2475 > this.cursorStops = options.cursorStops || null;
2476 > }
2478 >
2479 > export class ModelDecorationOptions implements model.IModelDecorationOptions {
2480 >
2481 > public static EMPTY: ModelDecorationOptions;
2482 >
2483 > public static register(options: model.IModelDecorationOptions): ModelDecorationOptions {
2484 > return new ModelDecorationOptions(options);
2485 > }
2486 >
2487 > public static createDynamic(options: model.IModelDecorationOptions): ModelDecorationOptions {
2488 > return new ModelDecorationOptions(options); textModel.ts ×16
2489 > }
2490 > readonly description: string; textModel.ts ×190
2491 > readonly blockClassName: string | null;
2492 > readonly blockIsAfterEnd: boolean | null;
2493 > readonly blockDoesNotCollapse?: boolean | null;
2494 > readonly blockPadding: [top: number, right: number, bottom: number, left: number] | null;
2495 > readonly stickiness: model.TrackedRangeStickiness;
2496 > readonly zIndex: number;
2497 > readonly className: string | null;
2498 > readonly shouldFillLineOnLineBreak: boolean | null;
2499 > readonly hoverMessage: IMarkdownString | IMarkdownString[] | null;
2500 > readonly glyphMarginHoverMessage: IMarkdownString | IMarkdownString[] | null;
2501 > readonly isWholeLine: boolean;
2502 > readonly lineHeight: number | null;
2503 > readonly fontSize: string | null;
2504 > readonly showIfCollapsed: boolean;
2505 > readonly collapseOnReplaceEdit: boolean;
2506 > readonly overviewRuler: ModelDecorationOverviewRulerOptions | null;
2507 > readonly minimap: ModelDecorationMinimapOptions | null;
2508 > readonly glyphMargin?: model.IModelDecorationGlyphMarginOptions | null | undefined;
2509 > readonly glyphMarginClassName: string | null;
2510 > readonly linesDecorationsClassName: string | null;
2511 > readonly lineNumberClassName: string | null;
2512 > readonly lineNumberHoverMessage: IMarkdownString | IMarkdownString[] | null;
2513 > readonly linesDecorationsTooltip: string | null;
2514 > readonly firstLineDecorationClassName: string | null;
2515 > readonly marginClassName: string | null;
2516 > readonly inlineClassName: string | null;
2517 > readonly inlineClassNameAffectsLetterSpacing: boolean;
2518 > readonly beforeContentClassName: string | null;
2519 > readonly afterContentClassName: string | null;
2520 > readonly after: ModelDecorationInjectedTextOptions | null;
2521 > readonly before: ModelDecorationInjectedTextOptions | null;
2522 > readonly hideInCommentTokens: boolean | null;
2523 > readonly hideInStringTokens: boolean | null;
2524 > readonly affectsFont: boolean | null;
2525 > readonly textDirection?: model.TextDirection | null | undefined;
2526 >
2527 > private constructor(options: model.IModelDecorationOptions) {
2528 > this.description = options.description;
2529 > this.blockClassName = options.blockClassName ? cleanClassName(options.blockClassName) : null;
2530 > this.blockDoesNotCollapse = options.blockDoesNotCollapse ?? null;
2531 > this.blockIsAfterEnd = options.blockIsAfterEnd ?? null;
2532 > this.blockPadding = options.blockPadding ?? null;
2533 > this.stickiness = options.stickiness || model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges;
2534 > this.zIndex = options.zIndex || 0;
2535 > this.className = options.className ? cleanClassName(options.className) : null;
2536 > this.shouldFillLineOnLineBreak = options.shouldFillLineOnLineBreak ?? null;
2537 > this.hoverMessage = options.hoverMessage || null;
2538 > this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || null;
2539 > this.lineNumberHoverMessage = options.lineNumberHoverMessage || null;
2540 > this.isWholeLine = options.isWholeLine || false;
2541 > this.lineHeight = options.lineHeight ? Math.min(options.lineHeight, LINE_HEIGHT_CEILING) : null;
2542 > this.fontSize = options.fontSize || null;
2543 > this.affectsFont = !!options.fontSize || !!options.fontFamily || !!options.fontWeight || !!options.fontStyle;
2544 > this.showIfCollapsed = options.showIfCollapsed || false;
2545 > this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false;
2546 > this.overviewRuler = options.overviewRuler ? new ModelDecorationOverviewRulerOptions(options.overviewRuler) : null;
2547 > this.minimap = options.minimap ? new ModelDecorationMinimapOptions(options.minimap) : null;
2548 > this.glyphMargin = options.glyphMarginClassName ? new ModelDecorationGlyphMarginOptions(options.glyphMargin) : null;
2549 > this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : null;
2550 > this.linesDecorationsClassName = options.linesDecorationsClassName ? cleanClassName(options.linesDecorationsClassName) : null;
2551 > this.lineNumberClassName = options.lineNumberClassName ? cleanClassName(options.lineNumberClassName) : null;
2552 > this.linesDecorationsTooltip = options.linesDecorationsTooltip ? strings.htmlAttributeEncodeValue(options.linesDecorationsTooltip) : null;
2553 > this.firstLineDecorationClassName = options.firstLineDecorationClassName ? cleanClassName(options.firstLineDecorationClassName) : null;
2554 > this.marginClassName = options.marginClassName ? cleanClassName(options.marginClassName) : null;
2555 > this.inlineClassName = options.inlineClassName ? cleanClassName(options.inlineClassName) : null;
2556 > this.inlineClassNameAffectsLetterSpacing = options.inlineClassNameAffectsLetterSpacing || false;
2557 > this.beforeContentClassName = options.beforeContentClassName ? cleanClassName(options.beforeContentClassName) : null;
2558 > this.afterContentClassName = options.afterContentClassName ? cleanClassName(options.afterContentClassName) : null;
2559 > this.after = options.after ? ModelDecorationInjectedTextOptions.from(options.after) : null;
2560 > this.before = options.before ? ModelDecorationInjectedTextOptions.from(options.before) : null;
2561 > this.hideInCommentTokens = options.hideInCommentTokens ?? false;
2562 > this.hideInStringTokens = options.hideInStringTokens ?? false;
2563 > this.textDirection = options.textDirection ?? null;
2564 > }
2565 > }
2566 > ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({ description: 'empty' });
2567 >
2568 > /**
2569 > * The order carefully matches the values of the enum.
2570 > */
2571 > const TRACKED_RANGE_OPTIONS = [
2572 > ModelDecorationOptions.register({ description: 'tracked-range-always-grows-when-typing-at-edges', stickiness: model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges }),
2573 > ModelDecorationOptions.register({ description: 'tracked-range-never-grows-when-typing-at-edges', stickiness: model.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }),
2574 > ModelDecorationOptions.register({ description: 'tracked-range-grows-only-when-typing-before', stickiness: model.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore }),
2575 > ModelDecorationOptions.register({ description: 'tracked-range-grows-only-when-typing-after', stickiness: model.TrackedRangeStickiness.GrowsOnlyWhenTypingAfter }),
2576 > ];
2577 >
2578 > function _normalizeOptions(options: model.IModelDecorationOptions): ModelDecorationOptions { textModel.ts ×16
2579 > if (options instanceof ModelDecorationOptions) {
2580 return options;
2581 }
2582 > return ModelDecorationOptions.createDynamic(options); textModel.ts ×16
2583 > }
2585 >
2586 > class DidChangeDecorationsEmitter extends Disposable {
2587 >
2588 > private readonly _actual: Emitter<IModelDecorationsChangedEvent> = this._register(new Emitter<IModelDecorationsChangedEvent>());
2589 > public readonly event: Event<IModelDecorationsChangedEvent> = this._actual.event;
2590 >
2591 > private _deferredCnt: number;
2592 > private _shouldFireDeferred: boolean;
2593 > private _affectsMinimap: boolean;
2594 > private _affectsOverviewRuler: boolean;
2595 > private _affectedInjectedTextLines: Set<number> | null = null;
2596 > private _affectedLineHeights: SetWithKey<LineHeightChangingDecoration> | null = null;
2597 > private _affectedFontLines: SetWithKey<LineFontChangingDecoration> | null = null;
2598 > private _affectsGlyphMargin: boolean;
2599 > private _affectsLineNumber: boolean;
2600 >
2601 > constructor(private readonly handleBeforeFire: (affectedInjectedTextLines: Set<number> | null, affectedLineHeights: SetWithKey<LineHeightChangingDecoration> | null, affectedFontLines: SetWithKey<LineFontChangingDecoration> | null) => void) {
2602 > super(); textModel.ts ×20
2603 > this._deferredCnt = 0;
2604 > this._shouldFireDeferred = false;
2605 > this._affectsMinimap = false;
2606 > this._affectsOverviewRuler = false;
2607 > this._affectsGlyphMargin = false;
2608 > this._affectsLineNumber = false;
2609 > }
2611 > hasListeners(): boolean {
2612 > return this._actual.hasListeners(); textModel.ts ×3
2613 > }
2615 > public beginDeferredEmit(): void {
2616 > this._deferredCnt++; textModel.ts ×3
2617 > }
2619 > public endDeferredEmit(): void {
2620 > this._deferredCnt--; textModel.ts ×3
2621 > if (this._deferredCnt === 0) {
2622 > if (this._shouldFireDeferred) {
2623 > this.doFire(); textModel.ts ×10
2624 > }
2626 > this._affectedInjectedTextLines?.clear();
2627 > this._affectedInjectedTextLines = null;
2628 > this._affectedLineHeights?.clear();
2629 > this._affectedLineHeights = null;
2630 > this._affectedFontLines?.clear();
2631 > this._affectedFontLines = null;
2632 > }
2633 > }
2635 > public recordLineAffectedByInjectedText(lineNumber: number): void {
2636 > if (!this._affectedInjectedTextLines) { textModel.ts ×6
2637 > this._affectedInjectedTextLines = new Set();
2638 > }
2639 > this._affectedInjectedTextLines.add(lineNumber);
2640 > }
2642 > public recordLineAffectedByLineHeightChange(ownerId: number, decorationId: string, lineNumber: number, lineHeight: number | null): void {
2643 if (!this._affectedLineHeights) {
2644 this._affectedLineHeights = new SetWithKey<LineHeightChangingDecoration>([], LineHeightChangingDecoration.toKey);
2645 }
2646 this._affectedLineHeights.add(new LineHeightChangingDecoration(ownerId, decorationId, lineNumber, lineHeight));
2647 }
2649 > public recordLineAffectedByFontChange(ownerId: number, decorationId: string, lineNumber: number): void {
2650 if (!this._affectedFontLines) {
2651 this._affectedFontLines = new SetWithKey<LineFontChangingDecoration>([], LineFontChangingDecoration.toKey);
2652 }
2653 this._affectedFontLines.add(new LineFontChangingDecoration(ownerId, decorationId, lineNumber));
2654 }
2656 > public checkAffectedAndFire(options: ModelDecorationOptions): void {
2657 > this._affectsMinimap ||= !!options.minimap?.position; textModel.ts ×16
2658 > this._affectsOverviewRuler ||= !!options.overviewRuler?.color;
2659 > this._affectsGlyphMargin ||= !!options.glyphMarginClassName;
2660 > this._affectsLineNumber ||= !!options.lineNumberClassName;
2661 > this.tryFire();
2662 > }
2664 > public fire(): void {
2665 > this._affectsMinimap = true; textModel.ts ×1
2666 > this._affectsOverviewRuler = true;
2667 > this._affectsGlyphMargin = true;
2668 > this.tryFire();
2669 > }
2671 > private tryFire() {
2672 > if (this._deferredCnt === 0) { textModel.ts ×10
2673 this.doFire();
2674 > } else { textModel.ts ×10
2675 > this._shouldFireDeferred = true;
2676 > }
2677 > }
2679 > private doFire() {
2680 > this.handleBeforeFire(this._affectedInjectedTextLines, this._affectedLineHeights, this._affectedFontLines); textModel.ts ×10
2681 >
2682 > const event: IModelDecorationsChangedEvent = {
2683 > affectsMinimap: this._affectsMinimap,
2684 > affectsOverviewRuler: this._affectsOverviewRuler,
2685 > affectsGlyphMargin: this._affectsGlyphMargin,
2686 > affectsLineNumber: this._affectsLineNumber,
2687 > };
2688 > this._shouldFireDeferred = false;
2689 > this._affectsMinimap = false;
2690 > this._affectsOverviewRuler = false;
2691 > this._affectsGlyphMargin = false;
2692 > this._actual.fire(event);
2693 > }
2695 >
2696 > //#endregion
2697 >
2698 > class DidChangeContentEmitter extends Disposable {
2699 >
2700 > private readonly _emitter: Emitter<InternalModelContentChangeEvent> = this._register(new Emitter<InternalModelContentChangeEvent>());
2701 > public readonly event: Event<InternalModelContentChangeEvent> = this._emitter.event;
2702 >
2703 > private _deferredCnt: number;
2704 > private _deferredEvent: InternalModelContentChangeEvent | null;
2705 >
2706 > constructor() {
2707 > super(); textModel.ts ×20
2708 > this._deferredCnt = 0;
2709 > this._deferredEvent = null;
2710 > }
2712 > public hasListeners(): boolean {
2713 > return this._emitter.hasListeners(); textModel.ts ×3
2714 > }
2716 > public beginDeferredEmit(): void {
2717 > this._deferredCnt++; textModel.ts ×10
2718 > }
2720 > public endDeferredEmit(resultingSelection: Selection[] | null = null): void {
2721 > this._deferredCnt--; textModel.ts ×10
2722 > if (this._deferredCnt === 0) {
2723 > if (this._deferredEvent !== null) {
2724 > this._deferredEvent.rawContentChangedEvent.resultingSelection = resultingSelection; intervalTree.ts ×7
2725 > const e = this._deferredEvent;
2726 > this._deferredEvent = null;
2727 > this._emitter.fire(e);
2728 > }
2730 > }
2732 > public fire(e: InternalModelContentChangeEvent): void {
2733 > if (this._deferredCnt > 0) { textModel.ts ×9
2734 > if (this._deferredEvent) { intervalTree.ts ×7
2735 this._deferredEvent = this._deferredEvent.merge(e);
2736 > } else { intervalTree.ts ×7
2737 > this._deferredEvent = e;
2738 > }
2739 > return;
2740 > }
2741 > this._emitter.fire(e); textModel.ts ×2
2742 > } textModel.ts ×9