Path: blob/main/src/vs/editor/common/model/textModel.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { ArrayQueue, pushMany } from '../../../base/common/arrays.js';6import { VSBuffer, VSBufferReadableStream } from '../../../base/common/buffer.js';7import { Color } from '../../../base/common/color.js';8import { BugIndicatingError, illegalArgument, onUnexpectedError } from '../../../base/common/errors.js';9import { Emitter, Event } from '../../../base/common/event.js';10import { IMarkdownString } from '../../../base/common/htmlContent.js';11import { Disposable, IDisposable, MutableDisposable, combinedDisposable } from '../../../base/common/lifecycle.js';12import { listenStream } from '../../../base/common/stream.js';13import * as strings from '../../../base/common/strings.js';14import { ThemeColor } from '../../../base/common/themables.js';15import { Constants } from '../../../base/common/uint.js';16import { URI } from '../../../base/common/uri.js';17import { ISingleEditOperation } from '../core/editOperation.js';18import { countEOL } from '../core/misc/eolCounter.js';19import { normalizeIndentation } from '../core/misc/indentation.js';20import { IPosition, Position } from '../core/position.js';21import { IRange, Range } from '../core/range.js';22import { Selection } from '../core/selection.js';23import { TextChange } from '../core/textChange.js';24import { EDITOR_MODEL_DEFAULTS } from '../core/misc/textModelDefaults.js';25import { IWordAtPosition } from '../core/wordHelper.js';26import { FormattingOptions } from '../languages.js';27import { ILanguageSelection, ILanguageService } from '../languages/language.js';28import { ILanguageConfigurationService } from '../languages/languageConfigurationRegistry.js';29import * as model from '../model.js';30import { BracketPairsTextModelPart } from './bracketPairsTextModelPart/bracketPairsImpl.js';31import { ColorizedBracketPairsDecorationProvider } from './bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.js';32import { EditStack } from './editStack.js';33import { GuidesTextModelPart } from './guidesTextModelPart.js';34import { guessIndentation } from './indentationGuesser.js';35import { IntervalNode, IntervalTree, recomputeMaxEnd } from './intervalTree.js';36import { PieceTreeTextBuffer } from './pieceTreeTextBuffer/pieceTreeTextBuffer.js';37import { PieceTreeTextBufferBuilder } from './pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js';38import { SearchParams, TextModelSearch } from './textModelSearch.js';39import { TokenizationTextModelPart } from './tokens/tokenizationTextModelPart.js';40import { AttachedViews } from './tokens/abstractSyntaxTokenBackend.js';41import { IBracketPairsTextModelPart } from '../textModelBracketPairs.js';42import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelOptionsChangedEvent, InternalModelContentChangeEvent, ModelInjectedTextChangedEvent, ModelRawChange, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted, ModelLineHeightChangedEvent, ModelLineHeightChanged, ModelFontChangedEvent, ModelFontChanged, LineInjectedText } from '../textModelEvents.js';43import { IGuidesTextModelPart } from '../textModelGuides.js';44import { ITokenizationTextModelPart } from '../tokenizationTextModelPart.js';45import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';46import { IColorTheme } from '../../../platform/theme/common/themeService.js';47import { IUndoRedoService, ResourceEditStackSnapshot, UndoRedoGroup } from '../../../platform/undoRedo/common/undoRedo.js';48import { TokenArray } from '../tokens/lineTokens.js';49import { SetWithKey } from '../../../base/common/collections.js';50import { EditSources, TextModelEditSource } from '../textModelEditSource.js';51import { TextEdit } from '../core/edits/textEdit.js';5253export function createTextBufferFactory(text: string): model.ITextBufferFactory {54const builder = new PieceTreeTextBufferBuilder();55builder.acceptChunk(text);56return builder.finish();57}5859interface ITextStream {60on(event: 'data', callback: (data: string) => void): void;61on(event: 'error', callback: (err: Error) => void): void;62on(event: 'end', callback: () => void): void;63on(event: string, callback: any): void;64}6566export function createTextBufferFactoryFromStream(stream: ITextStream): Promise<model.ITextBufferFactory>;67export function createTextBufferFactoryFromStream(stream: VSBufferReadableStream): Promise<model.ITextBufferFactory>;68export function createTextBufferFactoryFromStream(stream: ITextStream | VSBufferReadableStream): Promise<model.ITextBufferFactory> {69return new Promise<model.ITextBufferFactory>((resolve, reject) => {70const builder = new PieceTreeTextBufferBuilder();7172let done = false;7374listenStream<string | VSBuffer>(stream, {75onData: chunk => {76builder.acceptChunk((typeof chunk === 'string') ? chunk : chunk.toString());77},78onError: error => {79if (!done) {80done = true;81reject(error);82}83},84onEnd: () => {85if (!done) {86done = true;87resolve(builder.finish());88}89}90});91});92}9394export function createTextBufferFactoryFromSnapshot(snapshot: model.ITextSnapshot): model.ITextBufferFactory {95const builder = new PieceTreeTextBufferBuilder();9697let chunk: string | null;98while (typeof (chunk = snapshot.read()) === 'string') {99builder.acceptChunk(chunk);100}101102return builder.finish();103}104105export function createTextBuffer(value: string | model.ITextBufferFactory | model.ITextSnapshot, defaultEOL: model.DefaultEndOfLine): { textBuffer: model.ITextBuffer; disposable: IDisposable } {106let factory: model.ITextBufferFactory;107if (typeof value === 'string') {108factory = createTextBufferFactory(value);109} else if (model.isITextSnapshot(value)) {110factory = createTextBufferFactoryFromSnapshot(value);111} else {112factory = value;113}114return factory.create(defaultEOL);115}116117let MODEL_ID = 0;118119const LIMIT_FIND_COUNT = 999;120const LONG_LINE_BOUNDARY = 10000;121const LINE_HEIGHT_CEILING = 300;122123class TextModelSnapshot implements model.ITextSnapshot {124125private readonly _source: model.ITextSnapshot;126private _eos: boolean;127128constructor(source: model.ITextSnapshot) {129this._source = source;130this._eos = false;131}132133public read(): string | null {134if (this._eos) {135return null;136}137138const result: string[] = [];139let resultCnt = 0;140let resultLength = 0;141142do {143const tmp = this._source.read();144145if (tmp === null) {146// end-of-stream147this._eos = true;148if (resultCnt === 0) {149return null;150} else {151return result.join('');152}153}154155if (tmp.length > 0) {156result[resultCnt++] = tmp;157resultLength += tmp.length;158}159160if (resultLength >= 64 * 1024) {161return result.join('');162}163} while (true);164}165}166167const invalidFunc = () => { throw new Error(`Invalid change accessor`); };168169const enum StringOffsetValidationType {170/**171* Even allowed in surrogate pairs172*/173Relaxed = 0,174/**175* Not allowed in surrogate pairs176*/177SurrogatePairs = 1,178}179180export class TextModel extends Disposable implements model.ITextModel, IDecorationsTreesHost {181182static _MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB, // used in tests183private static readonly LARGE_FILE_SIZE_THRESHOLD = 20 * 1024 * 1024; // 20 MB;184private static readonly LARGE_FILE_LINE_COUNT_THRESHOLD = 300 * 1000; // 300K lines185private static readonly LARGE_FILE_HEAP_OPERATION_THRESHOLD = 256 * 1024 * 1024; // 256M characters, usually ~> 512MB memory usage186187public static DEFAULT_CREATION_OPTIONS: model.ITextModelCreationOptions = {188isForSimpleWidget: false,189tabSize: EDITOR_MODEL_DEFAULTS.tabSize,190indentSize: EDITOR_MODEL_DEFAULTS.indentSize,191insertSpaces: EDITOR_MODEL_DEFAULTS.insertSpaces,192detectIndentation: false,193defaultEOL: model.DefaultEndOfLine.LF,194trimAutoWhitespace: EDITOR_MODEL_DEFAULTS.trimAutoWhitespace,195largeFileOptimizations: EDITOR_MODEL_DEFAULTS.largeFileOptimizations,196bracketPairColorizationOptions: EDITOR_MODEL_DEFAULTS.bracketPairColorizationOptions,197};198199public static resolveOptions(textBuffer: model.ITextBuffer, options: model.ITextModelCreationOptions): model.TextModelResolvedOptions {200if (options.detectIndentation) {201const guessedIndentation = guessIndentation(textBuffer, options.tabSize, options.insertSpaces);202return new model.TextModelResolvedOptions({203tabSize: guessedIndentation.tabSize,204indentSize: 'tabSize', // TODO@Alex: guess indentSize independent of tabSize205insertSpaces: guessedIndentation.insertSpaces,206trimAutoWhitespace: options.trimAutoWhitespace,207defaultEOL: options.defaultEOL,208bracketPairColorizationOptions: options.bracketPairColorizationOptions,209});210}211212return new model.TextModelResolvedOptions(options);213}214215//#region Events216private readonly _onWillDispose: Emitter<void> = this._register(new Emitter<void>());217public readonly onWillDispose: Event<void> = this._onWillDispose.event;218219private readonly _onDidChangeDecorations: DidChangeDecorationsEmitter = this._register(new DidChangeDecorationsEmitter((affectedInjectedTextLines, affectedLineHeights, affectedFontLines) => this.handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines, affectedLineHeights, affectedFontLines)));220public readonly onDidChangeDecorations: Event<IModelDecorationsChangedEvent> = this._onDidChangeDecorations.event;221222public get onDidChangeLanguage() { return this._tokenizationTextModelPart.onDidChangeLanguage; }223public get onDidChangeLanguageConfiguration() { return this._tokenizationTextModelPart.onDidChangeLanguageConfiguration; }224public get onDidChangeTokens() { return this._tokenizationTextModelPart.onDidChangeTokens; }225226private readonly _onDidChangeOptions: Emitter<IModelOptionsChangedEvent> = this._register(new Emitter<IModelOptionsChangedEvent>());227public get onDidChangeOptions(): Event<IModelOptionsChangedEvent> { return this._onDidChangeOptions.event; }228229private readonly _onDidChangeAttached: Emitter<void> = this._register(new Emitter<void>());230public get onDidChangeAttached(): Event<void> { return this._onDidChangeAttached.event; }231232private readonly _onDidChangeInjectedText: Emitter<ModelInjectedTextChangedEvent> = this._register(new Emitter<ModelInjectedTextChangedEvent>());233234private readonly _onDidChangeLineHeight: Emitter<ModelLineHeightChangedEvent> = this._register(new Emitter<ModelLineHeightChangedEvent>());235public get onDidChangeLineHeight(): Event<ModelLineHeightChangedEvent> { return this._onDidChangeLineHeight.event; }236237private readonly _onDidChangeFont: Emitter<ModelFontChangedEvent> = this._register(new Emitter<ModelFontChangedEvent>());238public get onDidChangeFont(): Event<ModelFontChangedEvent> { return this._onDidChangeFont.event; }239240private readonly _eventEmitter: DidChangeContentEmitter = this._register(new DidChangeContentEmitter());241public onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable {242return this._eventEmitter.slowEvent((e: InternalModelContentChangeEvent) => listener(e.contentChangedEvent));243}244public onDidChangeContentOrInjectedText(listener: (e: InternalModelContentChangeEvent | ModelInjectedTextChangedEvent) => void): IDisposable {245return combinedDisposable(246this._eventEmitter.fastEvent(e => listener(e)),247this._onDidChangeInjectedText.event(e => listener(e))248);249}250//#endregion251252public readonly id: string;253public readonly isForSimpleWidget: boolean;254private readonly _associatedResource: URI;255private _attachedEditorCount: number;256private _buffer: model.ITextBuffer;257private _bufferDisposable: IDisposable;258private _options: model.TextModelResolvedOptions;259private readonly _languageSelectionListener = this._register(new MutableDisposable<IDisposable>());260261private _isDisposed: boolean;262private __isDisposing: boolean;263public _isDisposing(): boolean { return this.__isDisposing; }264private _versionId: number;265/**266* Unlike, versionId, this can go down (via undo) or go to previous values (via redo)267*/268private _alternativeVersionId: number;269private _initialUndoRedoSnapshot: ResourceEditStackSnapshot | null;270private readonly _isTooLargeForSyncing: boolean;271private readonly _isTooLargeForTokenization: boolean;272private readonly _isTooLargeForHeapOperation: boolean;273274//#region Editing275private readonly _commandManager: EditStack;276private _isUndoing: boolean;277private _isRedoing: boolean;278private _trimAutoWhitespaceLines: number[] | null;279//#endregion280281//#region Decorations282/**283* Used to workaround broken clients that might attempt using a decoration id generated by a different model.284* It is not globally unique in order to limit it to one character.285*/286private readonly _instanceId: string;287private _deltaDecorationCallCnt: number = 0;288private _lastDecorationId: number;289private _decorations: { [decorationId: string]: IntervalNode };290private _decorationsTree: DecorationsTrees;291private readonly _decorationProvider: ColorizedBracketPairsDecorationProvider;292//#endregion293294private readonly _tokenizationTextModelPart: TokenizationTextModelPart;295public get tokenization(): ITokenizationTextModelPart { return this._tokenizationTextModelPart; }296297private readonly _bracketPairs: BracketPairsTextModelPart;298public get bracketPairs(): IBracketPairsTextModelPart { return this._bracketPairs; }299300private readonly _guidesTextModelPart: GuidesTextModelPart;301public get guides(): IGuidesTextModelPart { return this._guidesTextModelPart; }302303private readonly _attachedViews = new AttachedViews();304305constructor(306source: string | model.ITextBufferFactory,307languageIdOrSelection: string | ILanguageSelection,308creationOptions: model.ITextModelCreationOptions,309associatedResource: 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: IInstantiationService314) {315super();316317// Generate a new unique model id318MODEL_ID++;319this.id = '$model' + MODEL_ID;320this.isForSimpleWidget = creationOptions.isForSimpleWidget;321if (typeof associatedResource === 'undefined' || associatedResource === null) {322this._associatedResource = URI.parse('inmemory://model/' + MODEL_ID);323} else {324this._associatedResource = associatedResource;325}326this._attachedEditorCount = 0;327328const { textBuffer, disposable } = createTextBuffer(source, creationOptions.defaultEOL);329this._buffer = textBuffer;330this._bufferDisposable = disposable;331332const bufferLineCount = this._buffer.getLineCount();333const bufferTextLength = this._buffer.getValueLengthInRange(new Range(1, 1, bufferLineCount, this._buffer.getLineLength(bufferLineCount) + 1), model.EndOfLinePreference.TextDefined);334335// !!! 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.338if (creationOptions.largeFileOptimizations) {339this._isTooLargeForTokenization = (340(bufferTextLength > TextModel.LARGE_FILE_SIZE_THRESHOLD)341|| (bufferLineCount > TextModel.LARGE_FILE_LINE_COUNT_THRESHOLD)342);343344this._isTooLargeForHeapOperation = bufferTextLength > TextModel.LARGE_FILE_HEAP_OPERATION_THRESHOLD;345} else {346this._isTooLargeForTokenization = false;347this._isTooLargeForHeapOperation = false;348}349350this._options = TextModel.resolveOptions(this._buffer, creationOptions);351352const languageId = (typeof languageIdOrSelection === 'string' ? languageIdOrSelection : languageIdOrSelection.languageId);353if (typeof languageIdOrSelection !== 'string') {354this._languageSelectionListener.value = languageIdOrSelection.onDidChange(() => this._setLanguage(languageIdOrSelection.languageId));355}356357this._bracketPairs = this._register(new BracketPairsTextModelPart(this, this._languageConfigurationService));358this._guidesTextModelPart = this._register(new GuidesTextModelPart(this, this._languageConfigurationService));359this._decorationProvider = this._register(new ColorizedBracketPairsDecorationProvider(this));360this._tokenizationTextModelPart = this.instantiationService.createInstance(TokenizationTextModelPart,361this,362this._bracketPairs,363languageId,364this._attachedViews365);366367this._isTooLargeForSyncing = (bufferTextLength > TextModel._MODEL_SYNC_LIMIT);368369this._versionId = 1;370this._alternativeVersionId = 1;371this._initialUndoRedoSnapshot = null;372373this._isDisposed = false;374this.__isDisposing = false;375376this._instanceId = strings.singleLetterHash(MODEL_ID);377this._lastDecorationId = 0;378this._decorations = Object.create(null);379this._decorationsTree = new DecorationsTrees();380381this._commandManager = new EditStack(this, this._undoRedoService);382this._isUndoing = false;383this._isRedoing = false;384this._trimAutoWhitespaceLines = null;385386387this._register(this._decorationProvider.onDidChange(() => {388this._onDidChangeDecorations.beginDeferredEmit();389this._onDidChangeDecorations.fire();390this._onDidChangeDecorations.endDeferredEmit();391}));392393this._languageService.requestRichLanguageFeatures(languageId);394395this._register(this._languageConfigurationService.onDidChange(e => {396this._bracketPairs.handleLanguageConfigurationServiceChange(e);397this._tokenizationTextModelPart.handleLanguageConfigurationServiceChange(e);398}));399}400401public override dispose(): void {402this.__isDisposing = true;403this._onWillDispose.fire();404this._tokenizationTextModelPart.dispose();405this._isDisposed = true;406super.dispose();407this._bufferDisposable.dispose();408this.__isDisposing = false;409// Manually release reference to previous text buffer to avoid large leaks410// in case someone leaks a TextModel reference411const emptyDisposedTextBuffer = new PieceTreeTextBuffer([], '', '\n', false, false, true, true);412emptyDisposedTextBuffer.dispose();413this._buffer = emptyDisposedTextBuffer;414this._bufferDisposable = Disposable.None;415}416417_hasListeners(): boolean {418return (419this._onWillDispose.hasListeners()420|| this._onDidChangeDecorations.hasListeners()421|| this._tokenizationTextModelPart._hasListeners()422|| this._onDidChangeOptions.hasListeners()423|| this._onDidChangeAttached.hasListeners()424|| this._onDidChangeInjectedText.hasListeners()425|| this._onDidChangeLineHeight.hasListeners()426|| this._onDidChangeFont.hasListeners()427|| this._eventEmitter.hasListeners()428);429}430431private _assertNotDisposed(): void {432if (this._isDisposed) {433throw new BugIndicatingError('Model is disposed!');434}435}436437public equalsTextBuffer(other: model.ITextBuffer): boolean {438this._assertNotDisposed();439return this._buffer.equals(other);440}441442public getTextBuffer(): model.ITextBuffer {443this._assertNotDisposed();444return this._buffer;445}446447private _emitContentChangedEvent(rawChange: ModelRawContentChangedEvent, change: IModelContentChangedEvent): void {448if (this.__isDisposing) {449// Do not confuse listeners by emitting any event after disposing450return;451}452this._tokenizationTextModelPart.handleDidChangeContent(change);453this._bracketPairs.handleDidChangeContent(change);454this._eventEmitter.fire(new InternalModelContentChangeEvent(rawChange, change));455}456457public setValue(value: string | model.ITextSnapshot, reason = EditSources.setValue()): void {458this._assertNotDisposed();459460if (value === null || value === undefined) {461throw illegalArgument();462}463464const { textBuffer, disposable } = createTextBuffer(value, this._options.defaultEOL);465this._setValueFromTextBuffer(textBuffer, disposable, reason);466}467468private _createContentChanged2(range: Range, rangeOffset: number, rangeLength: number, rangeEndPosition: Position, text: string, isUndoing: boolean, isRedoing: boolean, isFlush: boolean, isEolChange: boolean, reason: TextModelEditSource): IModelContentChangedEvent {469return {470changes: [{471range: range,472rangeOffset: rangeOffset,473rangeLength: rangeLength,474text: text,475}],476eol: this._buffer.getEOL(),477isEolChange: isEolChange,478versionId: this.getVersionId(),479isUndoing: isUndoing,480isRedoing: isRedoing,481isFlush: isFlush,482detailedReasons: [reason],483detailedReasonsChangeLengths: [1],484};485}486487private _setValueFromTextBuffer(textBuffer: model.ITextBuffer, textBufferDisposable: IDisposable, reason: TextModelEditSource): void {488this._assertNotDisposed();489const oldFullModelRange = this.getFullModelRange();490const oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);491const endLineNumber = this.getLineCount();492const endColumn = this.getLineMaxColumn(endLineNumber);493494this._buffer = textBuffer;495this._bufferDisposable.dispose();496this._bufferDisposable = textBufferDisposable;497this._increaseVersionId();498499// Destroy all my decorations500this._decorations = Object.create(null);501this._decorationsTree = new DecorationsTrees();502503// Destroy my edit history and settings504this._commandManager.clear();505this._trimAutoWhitespaceLines = null;506507this._emitContentChangedEvent(508new ModelRawContentChangedEvent(509[510new ModelRawFlush()511],512this._versionId,513false,514false515),516this._createContentChanged2(new Range(1, 1, endLineNumber, endColumn), 0, oldModelValueLength, new Position(endLineNumber, endColumn), this.getValue(), false, false, true, false, reason)517);518}519520public setEOL(eol: model.EndOfLineSequence): void {521this._assertNotDisposed();522const newEOL = (eol === model.EndOfLineSequence.CRLF ? '\r\n' : '\n');523if (this._buffer.getEOL() === newEOL) {524// Nothing to do525return;526}527528const oldFullModelRange = this.getFullModelRange();529const oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);530const endLineNumber = this.getLineCount();531const endColumn = this.getLineMaxColumn(endLineNumber);532533this._onBeforeEOLChange();534this._buffer.setEOL(newEOL);535this._increaseVersionId();536this._onAfterEOLChange();537538this._emitContentChangedEvent(539new ModelRawContentChangedEvent(540[541new ModelRawEOLChanged()542],543this._versionId,544false,545false546),547this._createContentChanged2(new Range(1, 1, endLineNumber, endColumn), 0, oldModelValueLength, new Position(endLineNumber, endColumn), this.getValue(), false, false, false, true, EditSources.eolChange())548);549}550551private _onBeforeEOLChange(): void {552// Ensure all decorations get their `range` set.553this._decorationsTree.ensureAllNodesHaveRanges(this);554}555556private _onAfterEOLChange(): void {557// Transform back `range` to offsets558const versionId = this.getVersionId();559const allDecorations = this._decorationsTree.collectNodesPostOrder();560for (let i = 0, len = allDecorations.length; i < len; i++) {561const node = allDecorations[i];562const range = node.range!; // the range is defined due to `_onBeforeEOLChange`563564const delta = node.cachedAbsoluteStart - node.start;565566const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);567const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);568569node.cachedAbsoluteStart = startOffset;570node.cachedAbsoluteEnd = endOffset;571node.cachedVersionId = versionId;572573node.start = startOffset - delta;574node.end = endOffset - delta;575576recomputeMaxEnd(node);577}578}579580public onBeforeAttached(): model.IAttachedView {581this._attachedEditorCount++;582if (this._attachedEditorCount === 1) {583this._tokenizationTextModelPart.handleDidChangeAttached();584this._onDidChangeAttached.fire(undefined);585}586return this._attachedViews.attachView();587}588589public onBeforeDetached(view: model.IAttachedView): void {590this._attachedEditorCount--;591if (this._attachedEditorCount === 0) {592this._tokenizationTextModelPart.handleDidChangeAttached();593this._onDidChangeAttached.fire(undefined);594}595this._attachedViews.detachView(view);596}597598public isAttachedToEditor(): boolean {599return this._attachedEditorCount > 0;600}601602public getAttachedEditorCount(): number {603return this._attachedEditorCount;604}605606public isTooLargeForSyncing(): boolean {607return this._isTooLargeForSyncing;608}609610public isTooLargeForTokenization(): boolean {611return this._isTooLargeForTokenization;612}613614public isTooLargeForHeapOperation(): boolean {615return this._isTooLargeForHeapOperation;616}617618public isDisposed(): boolean {619return this._isDisposed;620}621622public isDominatedByLongLines(): boolean {623this._assertNotDisposed();624if (this.isTooLargeForTokenization()) {625// Cannot word wrap huge files anyways, so it doesn't really matter626return false;627}628let smallLineCharCount = 0;629let longLineCharCount = 0;630631const lineCount = this._buffer.getLineCount();632for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) {633const lineLength = this._buffer.getLineLength(lineNumber);634if (lineLength >= LONG_LINE_BOUNDARY) {635longLineCharCount += lineLength;636} else {637smallLineCharCount += lineLength;638}639}640641return (longLineCharCount > smallLineCharCount);642}643644public get uri(): URI {645return this._associatedResource;646}647648//#region Options649650public getOptions(): model.TextModelResolvedOptions {651this._assertNotDisposed();652return this._options;653}654655public getFormattingOptions(): FormattingOptions {656return {657tabSize: this._options.indentSize,658insertSpaces: this._options.insertSpaces659};660}661662public updateOptions(_newOpts: model.ITextModelUpdateOptions): void {663this._assertNotDisposed();664const tabSize = (typeof _newOpts.tabSize !== 'undefined') ? _newOpts.tabSize : this._options.tabSize;665const indentSize = (typeof _newOpts.indentSize !== 'undefined') ? _newOpts.indentSize : this._options.originalIndentSize;666const insertSpaces = (typeof _newOpts.insertSpaces !== 'undefined') ? _newOpts.insertSpaces : this._options.insertSpaces;667const trimAutoWhitespace = (typeof _newOpts.trimAutoWhitespace !== 'undefined') ? _newOpts.trimAutoWhitespace : this._options.trimAutoWhitespace;668const bracketPairColorizationOptions = (typeof _newOpts.bracketColorizationOptions !== 'undefined') ? _newOpts.bracketColorizationOptions : this._options.bracketPairColorizationOptions;669670const newOpts = new model.TextModelResolvedOptions({671tabSize: tabSize,672indentSize: indentSize,673insertSpaces: insertSpaces,674defaultEOL: this._options.defaultEOL,675trimAutoWhitespace: trimAutoWhitespace,676bracketPairColorizationOptions,677});678679if (this._options.equals(newOpts)) {680return;681}682683const e = this._options.createChangeEvent(newOpts);684this._options = newOpts;685686this._bracketPairs.handleDidChangeOptions(e);687this._decorationProvider.handleDidChangeOptions(e);688this._onDidChangeOptions.fire(e);689}690691public detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void {692this._assertNotDisposed();693const guessedIndentation = guessIndentation(this._buffer, defaultTabSize, defaultInsertSpaces);694this.updateOptions({695insertSpaces: guessedIndentation.insertSpaces,696tabSize: guessedIndentation.tabSize,697indentSize: guessedIndentation.tabSize, // TODO@Alex: guess indentSize independent of tabSize698});699}700701public normalizeIndentation(str: string): string {702this._assertNotDisposed();703return normalizeIndentation(str, this._options.indentSize, this._options.insertSpaces);704}705706//#endregion707708//#region Reading709710public getVersionId(): number {711this._assertNotDisposed();712return this._versionId;713}714715public mightContainRTL(): boolean {716return this._buffer.mightContainRTL();717}718719public mightContainUnusualLineTerminators(): boolean {720return this._buffer.mightContainUnusualLineTerminators();721}722723public removeUnusualLineTerminators(selections: Selection[] | null = null): void {724const matches = this.findMatches(strings.UNUSUAL_LINE_TERMINATORS.source, false, true, false, null, false, Constants.MAX_SAFE_SMALL_INTEGER);725this._buffer.resetMightContainUnusualLineTerminators();726this.pushEditOperations(selections, matches.map(m => ({ range: m.range, text: null })), () => null);727}728729public mightContainNonBasicASCII(): boolean {730return this._buffer.mightContainNonBasicASCII();731}732733public getAlternativeVersionId(): number {734this._assertNotDisposed();735return this._alternativeVersionId;736}737738public getInitialUndoRedoSnapshot(): ResourceEditStackSnapshot | null {739this._assertNotDisposed();740return this._initialUndoRedoSnapshot;741}742743public getOffsetAt(rawPosition: IPosition): number {744this._assertNotDisposed();745const position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, StringOffsetValidationType.Relaxed);746return this._buffer.getOffsetAt(position.lineNumber, position.column);747}748749public getPositionAt(rawOffset: number): Position {750this._assertNotDisposed();751const offset = (Math.min(this._buffer.getLength(), Math.max(0, rawOffset)));752return this._buffer.getPositionAt(offset);753}754755private _increaseVersionId(): void {756this._versionId = this._versionId + 1;757this._alternativeVersionId = this._versionId;758}759760public _overwriteVersionId(versionId: number): void {761this._versionId = versionId;762}763764public _overwriteAlternativeVersionId(newAlternativeVersionId: number): void {765this._alternativeVersionId = newAlternativeVersionId;766}767768public _overwriteInitialUndoRedoSnapshot(newInitialUndoRedoSnapshot: ResourceEditStackSnapshot | null): void {769this._initialUndoRedoSnapshot = newInitialUndoRedoSnapshot;770}771772public getValue(eol?: model.EndOfLinePreference, preserveBOM: boolean = false): string {773this._assertNotDisposed();774if (this.isTooLargeForHeapOperation()) {775throw new BugIndicatingError('Operation would exceed heap memory limits');776}777778const fullModelRange = this.getFullModelRange();779const fullModelValue = this.getValueInRange(fullModelRange, eol);780781if (preserveBOM) {782return this._buffer.getBOM() + fullModelValue;783}784785return fullModelValue;786}787788public createSnapshot(preserveBOM: boolean = false): model.ITextSnapshot {789return new TextModelSnapshot(this._buffer.createSnapshot(preserveBOM));790}791792public getValueLength(eol?: model.EndOfLinePreference, preserveBOM: boolean = false): number {793this._assertNotDisposed();794const fullModelRange = this.getFullModelRange();795const fullModelValue = this.getValueLengthInRange(fullModelRange, eol);796797if (preserveBOM) {798return this._buffer.getBOM().length + fullModelValue;799}800801return fullModelValue;802}803804public getValueInRange(rawRange: IRange, eol: model.EndOfLinePreference = model.EndOfLinePreference.TextDefined): string {805this._assertNotDisposed();806return this._buffer.getValueInRange(this.validateRange(rawRange), eol);807}808809public getValueLengthInRange(rawRange: IRange, eol: model.EndOfLinePreference = model.EndOfLinePreference.TextDefined): number {810this._assertNotDisposed();811return this._buffer.getValueLengthInRange(this.validateRange(rawRange), eol);812}813814public getCharacterCountInRange(rawRange: IRange, eol: model.EndOfLinePreference = model.EndOfLinePreference.TextDefined): number {815this._assertNotDisposed();816return this._buffer.getCharacterCountInRange(this.validateRange(rawRange), eol);817}818819public getLineCount(): number {820this._assertNotDisposed();821return this._buffer.getLineCount();822}823824public getLineContent(lineNumber: number): string {825this._assertNotDisposed();826if (lineNumber < 1 || lineNumber > this.getLineCount()) {827throw new BugIndicatingError('Illegal value for lineNumber');828}829830return this._buffer.getLineContent(lineNumber);831}832833public getLineLength(lineNumber: number): number {834this._assertNotDisposed();835if (lineNumber < 1 || lineNumber > this.getLineCount()) {836throw new BugIndicatingError('Illegal value for lineNumber');837}838839return this._buffer.getLineLength(lineNumber);840}841842public getLinesContent(): string[] {843this._assertNotDisposed();844if (this.isTooLargeForHeapOperation()) {845throw new BugIndicatingError('Operation would exceed heap memory limits');846}847848return this._buffer.getLinesContent();849}850851public getEOL(): string {852this._assertNotDisposed();853return this._buffer.getEOL();854}855856public getEndOfLineSequence(): model.EndOfLineSequence {857this._assertNotDisposed();858return (859this._buffer.getEOL() === '\n'860? model.EndOfLineSequence.LF861: model.EndOfLineSequence.CRLF862);863}864865public getLineMinColumn(lineNumber: number): number {866this._assertNotDisposed();867return 1;868}869870public getLineMaxColumn(lineNumber: number): number {871this._assertNotDisposed();872if (lineNumber < 1 || lineNumber > this.getLineCount()) {873throw new BugIndicatingError('Illegal value for lineNumber');874}875return this._buffer.getLineLength(lineNumber) + 1;876}877878public getLineFirstNonWhitespaceColumn(lineNumber: number): number {879this._assertNotDisposed();880if (lineNumber < 1 || lineNumber > this.getLineCount()) {881throw new BugIndicatingError('Illegal value for lineNumber');882}883return this._buffer.getLineFirstNonWhitespaceColumn(lineNumber);884}885886public getLineLastNonWhitespaceColumn(lineNumber: number): number {887this._assertNotDisposed();888if (lineNumber < 1 || lineNumber > this.getLineCount()) {889throw new BugIndicatingError('Illegal value for lineNumber');890}891return this._buffer.getLineLastNonWhitespaceColumn(lineNumber);892}893894/**895* Validates `range` is within buffer bounds, but allows it to sit in between surrogate pairs, etc.896* Will try to not allocate if possible.897*/898public _validateRangeRelaxedNoAllocations(range: IRange): Range {899const linesCount = this._buffer.getLineCount();900901const initialStartLineNumber = range.startLineNumber;902const initialStartColumn = range.startColumn;903let startLineNumber = Math.floor((typeof initialStartLineNumber === 'number' && !isNaN(initialStartLineNumber)) ? initialStartLineNumber : 1);904let startColumn = Math.floor((typeof initialStartColumn === 'number' && !isNaN(initialStartColumn)) ? initialStartColumn : 1);905906if (startLineNumber < 1) {907startLineNumber = 1;908startColumn = 1;909} else if (startLineNumber > linesCount) {910startLineNumber = linesCount;911startColumn = this.getLineMaxColumn(startLineNumber);912} else {913if (startColumn <= 1) {914startColumn = 1;915} else {916const maxColumn = this.getLineMaxColumn(startLineNumber);917if (startColumn >= maxColumn) {918startColumn = maxColumn;919}920}921}922923const initialEndLineNumber = range.endLineNumber;924const initialEndColumn = range.endColumn;925let endLineNumber = Math.floor((typeof initialEndLineNumber === 'number' && !isNaN(initialEndLineNumber)) ? initialEndLineNumber : 1);926let endColumn = Math.floor((typeof initialEndColumn === 'number' && !isNaN(initialEndColumn)) ? initialEndColumn : 1);927928if (endLineNumber < 1) {929endLineNumber = 1;930endColumn = 1;931} else if (endLineNumber > linesCount) {932endLineNumber = linesCount;933endColumn = this.getLineMaxColumn(endLineNumber);934} else {935if (endColumn <= 1) {936endColumn = 1;937} else {938const maxColumn = this.getLineMaxColumn(endLineNumber);939if (endColumn >= maxColumn) {940endColumn = maxColumn;941}942}943}944945if (946initialStartLineNumber === startLineNumber947&& initialStartColumn === startColumn948&& initialEndLineNumber === endLineNumber949&& initialEndColumn === endColumn950&& range instanceof Range951&& !(range instanceof Selection)952) {953return range;954}955956return new Range(startLineNumber, startColumn, endLineNumber, endColumn);957}958959private _isValidPosition(lineNumber: number, column: number, validationType: StringOffsetValidationType): boolean {960if (typeof lineNumber !== 'number' || typeof column !== 'number') {961return false;962}963964if (isNaN(lineNumber) || isNaN(column)) {965return false;966}967968if (lineNumber < 1 || column < 1) {969return false;970}971972if ((lineNumber | 0) !== lineNumber || (column | 0) !== column) {973return false;974}975976const lineCount = this._buffer.getLineCount();977if (lineNumber > lineCount) {978return false;979}980981if (column === 1) {982return true;983}984985const maxColumn = this.getLineMaxColumn(lineNumber);986if (column > maxColumn) {987return false;988}989990if (validationType === StringOffsetValidationType.SurrogatePairs) {991// !!At this point, column > 1992const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2);993if (strings.isHighSurrogate(charCodeBefore)) {994return false;995}996}997998return true;999}10001001private _validatePosition(_lineNumber: number, _column: number, validationType: StringOffsetValidationType): Position {1002const lineNumber = Math.floor((typeof _lineNumber === 'number' && !isNaN(_lineNumber)) ? _lineNumber : 1);1003const column = Math.floor((typeof _column === 'number' && !isNaN(_column)) ? _column : 1);1004const lineCount = this._buffer.getLineCount();10051006if (lineNumber < 1) {1007return new Position(1, 1);1008}10091010if (lineNumber > lineCount) {1011return new Position(lineCount, this.getLineMaxColumn(lineCount));1012}10131014if (column <= 1) {1015return new Position(lineNumber, 1);1016}10171018const maxColumn = this.getLineMaxColumn(lineNumber);1019if (column >= maxColumn) {1020return new Position(lineNumber, maxColumn);1021}10221023if (validationType === StringOffsetValidationType.SurrogatePairs) {1024// If the position would end up in the middle of a high-low surrogate pair,1025// we move it to before the pair1026// !!At this point, column > 11027const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2);1028if (strings.isHighSurrogate(charCodeBefore)) {1029return new Position(lineNumber, column - 1);1030}1031}10321033return new Position(lineNumber, column);1034}10351036public validatePosition(position: IPosition): Position {1037const validationType = StringOffsetValidationType.SurrogatePairs;1038this._assertNotDisposed();10391040// Avoid object allocation and cover most likely case1041if (position instanceof Position) {1042if (this._isValidPosition(position.lineNumber, position.column, validationType)) {1043return position;1044}1045}10461047return this._validatePosition(position.lineNumber, position.column, validationType);1048}10491050public isValidRange(range: Range): boolean {1051return this._isValidRange(range, StringOffsetValidationType.SurrogatePairs);1052}10531054private _isValidRange(range: Range, validationType: StringOffsetValidationType): boolean {1055const startLineNumber = range.startLineNumber;1056const startColumn = range.startColumn;1057const endLineNumber = range.endLineNumber;1058const endColumn = range.endColumn;10591060if (!this._isValidPosition(startLineNumber, startColumn, StringOffsetValidationType.Relaxed)) {1061return false;1062}1063if (!this._isValidPosition(endLineNumber, endColumn, StringOffsetValidationType.Relaxed)) {1064return false;1065}10661067if (validationType === StringOffsetValidationType.SurrogatePairs) {1068const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0);1069const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0);10701071const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart);1072const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd);10731074if (!startInsideSurrogatePair && !endInsideSurrogatePair) {1075return true;1076}1077return false;1078}10791080return true;1081}10821083public validateRange(_range: IRange): Range {1084const validationType = StringOffsetValidationType.SurrogatePairs;1085this._assertNotDisposed();10861087// Avoid object allocation and cover most likely case1088if ((_range instanceof Range) && !(_range instanceof Selection)) {1089if (this._isValidRange(_range, validationType)) {1090return _range;1091}1092}10931094const start = this._validatePosition(_range.startLineNumber, _range.startColumn, StringOffsetValidationType.Relaxed);1095const end = this._validatePosition(_range.endLineNumber, _range.endColumn, StringOffsetValidationType.Relaxed);10961097const startLineNumber = start.lineNumber;1098const startColumn = start.column;1099const endLineNumber = end.lineNumber;1100const endColumn = end.column;11011102if (validationType === StringOffsetValidationType.SurrogatePairs) {1103const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0);1104const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0);11051106const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart);1107const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd);11081109if (!startInsideSurrogatePair && !endInsideSurrogatePair) {1110return new Range(startLineNumber, startColumn, endLineNumber, endColumn);1111}11121113if (startLineNumber === endLineNumber && startColumn === endColumn) {1114// do not expand a collapsed range, simply move it to a valid location1115return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn - 1);1116}11171118if (startInsideSurrogatePair && endInsideSurrogatePair) {1119// expand range at both ends1120return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn + 1);1121}11221123if (startInsideSurrogatePair) {1124// only expand range at the start1125return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn);1126}11271128// only expand range at the end1129return new Range(startLineNumber, startColumn, endLineNumber, endColumn + 1);1130}11311132return new Range(startLineNumber, startColumn, endLineNumber, endColumn);1133}11341135public modifyPosition(rawPosition: IPosition, offset: number): Position {1136this._assertNotDisposed();1137const candidate = this.getOffsetAt(rawPosition) + offset;1138return this.getPositionAt(Math.min(this._buffer.getLength(), Math.max(0, candidate)));1139}11401141public getFullModelRange(): Range {1142this._assertNotDisposed();1143const lineCount = this.getLineCount();1144return new Range(1, 1, lineCount, this.getLineMaxColumn(lineCount));1145}11461147private findMatchesLineByLine(searchRange: Range, searchData: model.SearchData, captureMatches: boolean, limitResultCount: number): model.FindMatch[] {1148return this._buffer.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);1149}11501151public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount: number = LIMIT_FIND_COUNT): model.FindMatch[] {1152this._assertNotDisposed();11531154let searchRanges: Range[] | null = null;11551156if (rawSearchScope !== null) {1157if (!Array.isArray(rawSearchScope)) {1158rawSearchScope = [rawSearchScope];1159}11601161if (rawSearchScope.every((searchScope: Range) => Range.isIRange(searchScope))) {1162searchRanges = rawSearchScope.map((searchScope: Range) => this.validateRange(searchScope));1163}1164}11651166if (searchRanges === null) {1167searchRanges = [this.getFullModelRange()];1168}11691170searchRanges = searchRanges.sort((d1, d2) => d1.startLineNumber - d2.startLineNumber || d1.startColumn - d2.startColumn);11711172const uniqueSearchRanges: Range[] = [];1173uniqueSearchRanges.push(searchRanges.reduce((prev, curr) => {1174if (Range.areIntersecting(prev, curr)) {1175return prev.plusRange(curr);1176}11771178uniqueSearchRanges.push(prev);1179return curr;1180}));11811182let matchMapper: (value: Range, index: number, array: Range[]) => model.FindMatch[];1183if (!isRegex && searchString.indexOf('\n') < 0) {1184// not regex, not multi line1185const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);1186const searchData = searchParams.parseSearchRequest();11871188if (!searchData) {1189return [];1190}11911192matchMapper = (searchRange: Range) => this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);1193} else {1194matchMapper = (searchRange: Range) => TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount);1195}11961197return uniqueSearchRanges.map(matchMapper).reduce((arr, matches: model.FindMatch[]) => arr.concat(matches), []);1198}11991200public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null {1201this._assertNotDisposed();1202const searchStart = this.validatePosition(rawSearchStart);12031204if (!isRegex && searchString.indexOf('\n') < 0) {1205const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);1206const searchData = searchParams.parseSearchRequest();1207if (!searchData) {1208return null;1209}12101211const lineCount = this.getLineCount();1212let searchRange = new Range(searchStart.lineNumber, searchStart.column, lineCount, this.getLineMaxColumn(lineCount));1213let ret = this.findMatchesLineByLine(searchRange, searchData, captureMatches, 1);1214TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);1215if (ret.length > 0) {1216return ret[0];1217}12181219searchRange = new Range(1, 1, searchStart.lineNumber, this.getLineMaxColumn(searchStart.lineNumber));1220ret = this.findMatchesLineByLine(searchRange, searchData, captureMatches, 1);12211222if (ret.length > 0) {1223return ret[0];1224}12251226return null;1227}12281229return TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);1230}12311232public findPreviousMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null {1233this._assertNotDisposed();1234const searchStart = this.validatePosition(rawSearchStart);1235return TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);1236}12371238//#endregion12391240//#region Editing12411242public pushStackElement(): void {1243this._commandManager.pushStackElement();1244}12451246public popStackElement(): void {1247this._commandManager.popStackElement();1248}12491250public pushEOL(eol: model.EndOfLineSequence): void {1251const currentEOL = (this.getEOL() === '\n' ? model.EndOfLineSequence.LF : model.EndOfLineSequence.CRLF);1252if (currentEOL === eol) {1253return;1254}1255try {1256this._onDidChangeDecorations.beginDeferredEmit();1257this._eventEmitter.beginDeferredEmit();1258if (this._initialUndoRedoSnapshot === null) {1259this._initialUndoRedoSnapshot = this._undoRedoService.createSnapshot(this.uri);1260}1261this._commandManager.pushEOL(eol);1262} finally {1263this._eventEmitter.endDeferredEmit();1264this._onDidChangeDecorations.endDeferredEmit();1265}1266}12671268private _validateEditOperation(rawOperation: model.IIdentifiedSingleEditOperation): model.ValidAnnotatedEditOperation {1269if (rawOperation instanceof model.ValidAnnotatedEditOperation) {1270return rawOperation;1271}1272return new model.ValidAnnotatedEditOperation(1273rawOperation.identifier || null,1274this.validateRange(rawOperation.range),1275rawOperation.text,1276rawOperation.forceMoveMarkers || false,1277rawOperation.isAutoWhitespaceEdit || false,1278rawOperation._isTracked || false1279);1280}12811282private _validateEditOperations(rawOperations: readonly model.IIdentifiedSingleEditOperation[]): model.ValidAnnotatedEditOperation[] {1283const result: model.ValidAnnotatedEditOperation[] = [];1284for (let i = 0, len = rawOperations.length; i < len; i++) {1285result[i] = this._validateEditOperation(rawOperations[i]);1286}1287return result;1288}12891290public edit(edit: TextEdit, options?: { reason?: TextModelEditSource }): void {1291this.pushEditOperations(null, edit.replacements.map(r => ({ range: r.range, text: r.text })), null);1292}12931294public pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null, group?: UndoRedoGroup, reason?: TextModelEditSource): Selection[] | null {1295try {1296this._onDidChangeDecorations.beginDeferredEmit();1297this._eventEmitter.beginDeferredEmit();1298return this._pushEditOperations(beforeCursorState, this._validateEditOperations(editOperations), cursorStateComputer, group, reason);1299} finally {1300this._eventEmitter.endDeferredEmit();1301this._onDidChangeDecorations.endDeferredEmit();1302}1303}13041305private _pushEditOperations(beforeCursorState: Selection[] | null, editOperations: model.ValidAnnotatedEditOperation[], cursorStateComputer: model.ICursorStateComputer | null, group?: UndoRedoGroup, reason?: TextModelEditSource): Selection[] | null {1306if (this._options.trimAutoWhitespace && this._trimAutoWhitespaceLines) {1307// Go through each saved line number and insert a trim whitespace edit1308// if it is safe to do so (no conflicts with other edits).13091310const incomingEdits = editOperations.map((op) => {1311return {1312range: this.validateRange(op.range),1313text: op.text1314};1315});13161317// Sometimes, auto-formatters change ranges automatically which can cause undesired auto whitespace trimming near the cursor1318// We'll use the following heuristic: if the edits occur near the cursor, then it's ok to trim auto whitespace1319let editsAreNearCursors = true;1320if (beforeCursorState) {1321for (let i = 0, len = beforeCursorState.length; i < len; i++) {1322const sel = beforeCursorState[i];1323let foundEditNearSel = false;1324for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {1325const editRange = incomingEdits[j].range;1326const selIsAbove = editRange.startLineNumber > sel.endLineNumber;1327const selIsBelow = sel.startLineNumber > editRange.endLineNumber;1328if (!selIsAbove && !selIsBelow) {1329foundEditNearSel = true;1330break;1331}1332}1333if (!foundEditNearSel) {1334editsAreNearCursors = false;1335break;1336}1337}1338}13391340if (editsAreNearCursors) {1341for (let i = 0, len = this._trimAutoWhitespaceLines.length; i < len; i++) {1342const trimLineNumber = this._trimAutoWhitespaceLines[i];1343const maxLineColumn = this.getLineMaxColumn(trimLineNumber);13441345let allowTrimLine = true;1346for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {1347const editRange = incomingEdits[j].range;1348const editText = incomingEdits[j].text;13491350if (trimLineNumber < editRange.startLineNumber || trimLineNumber > editRange.endLineNumber) {1351// `trimLine` is completely outside this edit1352continue;1353}13541355// At this point:1356// editRange.startLineNumber <= trimLine <= editRange.endLineNumber13571358if (1359trimLineNumber === editRange.startLineNumber && editRange.startColumn === maxLineColumn1360&& editRange.isEmpty() && editText && editText.length > 0 && editText.charAt(0) === '\n'1361) {1362// This edit inserts a new line (and maybe other text) after `trimLine`1363continue;1364}13651366if (1367trimLineNumber === editRange.startLineNumber && editRange.startColumn === 11368&& editRange.isEmpty() && editText && editText.length > 0 && editText.charAt(editText.length - 1) === '\n'1369) {1370// This edit inserts a new line (and maybe other text) before `trimLine`1371continue;1372}13731374// Looks like we can't trim this line as it would interfere with an incoming edit1375allowTrimLine = false;1376break;1377}13781379if (allowTrimLine) {1380const trimRange = new Range(trimLineNumber, 1, trimLineNumber, maxLineColumn);1381editOperations.push(new model.ValidAnnotatedEditOperation(null, trimRange, null, false, false, false));1382}13831384}1385}13861387this._trimAutoWhitespaceLines = null;1388}1389if (this._initialUndoRedoSnapshot === null) {1390this._initialUndoRedoSnapshot = this._undoRedoService.createSnapshot(this.uri);1391}1392return this._commandManager.pushEditOperation(beforeCursorState, editOperations, cursorStateComputer, group, reason);1393}13941395_applyUndo(changes: TextChange[], eol: model.EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void {1396const edits = changes.map<ISingleEditOperation>((change) => {1397const rangeStart = this.getPositionAt(change.newPosition);1398const rangeEnd = this.getPositionAt(change.newEnd);1399return {1400range: new Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column),1401text: change.oldText1402};1403});1404this._applyUndoRedoEdits(edits, eol, true, false, resultingAlternativeVersionId, resultingSelection);1405}14061407_applyRedo(changes: TextChange[], eol: model.EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void {1408const edits = changes.map<ISingleEditOperation>((change) => {1409const rangeStart = this.getPositionAt(change.oldPosition);1410const rangeEnd = this.getPositionAt(change.oldEnd);1411return {1412range: new Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column),1413text: change.newText1414};1415});1416this._applyUndoRedoEdits(edits, eol, false, true, resultingAlternativeVersionId, resultingSelection);1417}14181419private _applyUndoRedoEdits(edits: ISingleEditOperation[], eol: model.EndOfLineSequence, isUndoing: boolean, isRedoing: boolean, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void {1420try {1421this._onDidChangeDecorations.beginDeferredEmit();1422this._eventEmitter.beginDeferredEmit();1423this._isUndoing = isUndoing;1424this._isRedoing = isRedoing;1425this.applyEdits(edits, false);1426this.setEOL(eol);1427this._overwriteAlternativeVersionId(resultingAlternativeVersionId);1428} finally {1429this._isUndoing = false;1430this._isRedoing = false;1431this._eventEmitter.endDeferredEmit(resultingSelection);1432this._onDidChangeDecorations.endDeferredEmit();1433}1434}14351436public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[]): void;1437public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits: false): void;1438public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits: true): model.IValidEditOperation[];1439/** @internal */1440public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits: false, reason: TextModelEditSource): void;1441/** @internal */1442public applyEdits(operations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits: true, reason: TextModelEditSource): model.IValidEditOperation[];1443public applyEdits(rawOperations: readonly model.IIdentifiedSingleEditOperation[], computeUndoEdits?: boolean, reason?: TextModelEditSource): void | model.IValidEditOperation[] {1444try {1445this._onDidChangeDecorations.beginDeferredEmit();1446this._eventEmitter.beginDeferredEmit();1447const operations = this._validateEditOperations(rawOperations);14481449return this._doApplyEdits(operations, computeUndoEdits ?? false, reason ?? EditSources.applyEdits());1450} finally {1451this._eventEmitter.endDeferredEmit();1452this._onDidChangeDecorations.endDeferredEmit();1453}1454}14551456private _doApplyEdits(rawOperations: model.ValidAnnotatedEditOperation[], computeUndoEdits: boolean, reason: TextModelEditSource): void | model.IValidEditOperation[] {14571458const oldLineCount = this._buffer.getLineCount();1459const result = this._buffer.applyEdits(rawOperations, this._options.trimAutoWhitespace, computeUndoEdits);1460const newLineCount = this._buffer.getLineCount();14611462const contentChanges = result.changes;1463this._trimAutoWhitespaceLines = result.trimAutoWhitespaceLineNumbers;14641465if (contentChanges.length !== 0) {1466// We do a first pass to update decorations1467// because we want to read decorations in the second pass1468// where we will emit content change events1469// and we want to read the final decorations1470for (let i = 0, len = contentChanges.length; i < len; i++) {1471const change = contentChanges[i];1472this._decorationsTree.acceptReplace(change.rangeOffset, change.rangeLength, change.text.length, change.forceMoveMarkers);1473}14741475const rawContentChanges: ModelRawChange[] = [];14761477this._increaseVersionId();14781479let lineCount = oldLineCount;1480for (let i = 0, len = contentChanges.length; i < len; i++) {1481const change = contentChanges[i];1482const [eolCount] = countEOL(change.text);1483this._onDidChangeDecorations.fire();14841485const startLineNumber = change.range.startLineNumber;1486const endLineNumber = change.range.endLineNumber;14871488const deletingLinesCnt = endLineNumber - startLineNumber;1489const insertingLinesCnt = eolCount;1490const editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt);14911492const changeLineCountDelta = (insertingLinesCnt - deletingLinesCnt);14931494const currentEditStartLineNumber = newLineCount - lineCount - changeLineCountDelta + startLineNumber;1495const firstEditLineNumber = currentEditStartLineNumber;1496const lastInsertedLineNumber = currentEditStartLineNumber + insertingLinesCnt;14971498const decorationsWithInjectedTextInEditedRange = this._decorationsTree.getInjectedTextInInterval(1499this,1500this.getOffsetAt(new Position(firstEditLineNumber, 1)),1501this.getOffsetAt(new Position(lastInsertedLineNumber, this.getLineMaxColumn(lastInsertedLineNumber))),150201503);150415051506const injectedTextInEditedRange = LineInjectedText.fromDecorations(decorationsWithInjectedTextInEditedRange);1507const injectedTextInEditedRangeQueue = new ArrayQueue(injectedTextInEditedRange);15081509for (let j = editingLinesCnt; j >= 0; j--) {1510const editLineNumber = startLineNumber + j;1511const currentEditLineNumber = currentEditStartLineNumber + j;15121513injectedTextInEditedRangeQueue.takeFromEndWhile(r => r.lineNumber > currentEditLineNumber);1514const decorationsInCurrentLine = injectedTextInEditedRangeQueue.takeFromEndWhile(r => r.lineNumber === currentEditLineNumber);15151516rawContentChanges.push(1517new ModelRawLineChanged(1518editLineNumber,1519this.getLineContent(currentEditLineNumber),1520decorationsInCurrentLine1521));1522}15231524if (editingLinesCnt < deletingLinesCnt) {1525// Must delete some lines1526const spliceStartLineNumber = startLineNumber + editingLinesCnt;1527rawContentChanges.push(new ModelRawLinesDeleted(spliceStartLineNumber + 1, endLineNumber));1528}15291530if (editingLinesCnt < insertingLinesCnt) {1531const injectedTextInEditedRangeQueue = new ArrayQueue(injectedTextInEditedRange);1532// Must insert some lines1533const spliceLineNumber = startLineNumber + editingLinesCnt;1534const cnt = insertingLinesCnt - editingLinesCnt;1535const fromLineNumber = newLineCount - lineCount - cnt + spliceLineNumber + 1;1536const injectedTexts: (LineInjectedText[] | null)[] = [];1537const newLines: string[] = [];1538for (let i = 0; i < cnt; i++) {1539const lineNumber = fromLineNumber + i;1540newLines[i] = this.getLineContent(lineNumber);15411542injectedTextInEditedRangeQueue.takeWhile(r => r.lineNumber < lineNumber);1543injectedTexts[i] = injectedTextInEditedRangeQueue.takeWhile(r => r.lineNumber === lineNumber);1544}15451546rawContentChanges.push(1547new ModelRawLinesInserted(1548spliceLineNumber + 1,1549startLineNumber + insertingLinesCnt,1550newLines,1551injectedTexts1552)1553);1554}15551556lineCount += changeLineCountDelta;1557}15581559this._emitContentChangedEvent(1560new ModelRawContentChangedEvent(1561rawContentChanges,1562this.getVersionId(),1563this._isUndoing,1564this._isRedoing1565),1566{1567changes: contentChanges,1568eol: this._buffer.getEOL(),1569isEolChange: false,1570versionId: this.getVersionId(),1571isUndoing: this._isUndoing,1572isRedoing: this._isRedoing,1573isFlush: false,1574detailedReasons: [reason],1575detailedReasonsChangeLengths: [contentChanges.length],1576}1577);1578}15791580return (result.reverseEdits === null ? undefined : result.reverseEdits);1581}15821583public undo(): void | Promise<void> {1584return this._undoRedoService.undo(this.uri);1585}15861587public canUndo(): boolean {1588return this._undoRedoService.canUndo(this.uri);1589}15901591public redo(): void | Promise<void> {1592return this._undoRedoService.redo(this.uri);1593}15941595public canRedo(): boolean {1596return this._undoRedoService.canRedo(this.uri);1597}15981599//#endregion16001601//#region Decorations16021603private handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines: Set<number> | null, affectedLineHeights: Set<LineHeightChangingDecoration> | null, affectedFontLines: Set<LineFontChangingDecoration> | null): void {1604// This is called before the decoration changed event is fired.16051606if (affectedInjectedTextLines && affectedInjectedTextLines.size > 0) {1607const affectedLines = Array.from(affectedInjectedTextLines);1608const lineChangeEvents = affectedLines.map(lineNumber => new ModelRawLineChanged(lineNumber, this.getLineContent(lineNumber), this._getInjectedTextInLine(lineNumber)));1609this._onDidChangeInjectedText.fire(new ModelInjectedTextChangedEvent(lineChangeEvents));1610}1611if (affectedLineHeights && affectedLineHeights.size > 0) {1612const affectedLines = Array.from(affectedLineHeights);1613const lineHeightChangeEvent = affectedLines.map(specialLineHeightChange => new ModelLineHeightChanged(specialLineHeightChange.ownerId, specialLineHeightChange.decorationId, specialLineHeightChange.lineNumber, specialLineHeightChange.lineHeight));1614this._onDidChangeLineHeight.fire(new ModelLineHeightChangedEvent(lineHeightChangeEvent));1615}1616if (affectedFontLines && affectedFontLines.size > 0) {1617const affectedLines = Array.from(affectedFontLines);1618const fontChangeEvent = affectedLines.map(fontChange => new ModelFontChanged(fontChange.ownerId, fontChange.lineNumber));1619this._onDidChangeFont.fire(new ModelFontChangedEvent(fontChangeEvent));1620}1621}16221623public changeDecorations<T>(callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T, ownerId: number = 0): T | null {1624this._assertNotDisposed();16251626try {1627this._onDidChangeDecorations.beginDeferredEmit();1628return this._changeDecorations(ownerId, callback);1629} finally {1630this._onDidChangeDecorations.endDeferredEmit();1631}1632}16331634private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null {1635const changeAccessor: model.IModelDecorationsChangeAccessor = {1636addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => {1637return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0];1638},1639changeDecoration: (id: string, newRange: IRange): void => {1640this._changeDecorationImpl(ownerId, id, newRange);1641},1642changeDecorationOptions: (id: string, options: model.IModelDecorationOptions) => {1643this._changeDecorationOptionsImpl(ownerId, id, _normalizeOptions(options));1644},1645removeDecoration: (id: string): void => {1646this._deltaDecorationsImpl(ownerId, [id], []);1647},1648deltaDecorations: (oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] => {1649if (oldDecorations.length === 0 && newDecorations.length === 0) {1650// nothing to do1651return [];1652}1653return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);1654}1655};1656let result: T | null = null;1657try {1658result = callback(changeAccessor);1659} catch (e) {1660onUnexpectedError(e);1661}1662// Invalidate change accessor1663changeAccessor.addDecoration = invalidFunc;1664changeAccessor.changeDecoration = invalidFunc;1665changeAccessor.changeDecorationOptions = invalidFunc;1666changeAccessor.removeDecoration = invalidFunc;1667changeAccessor.deltaDecorations = invalidFunc;1668return result;1669}16701671public deltaDecorations(oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[], ownerId: number = 0): string[] {1672this._assertNotDisposed();1673if (!oldDecorations) {1674oldDecorations = [];1675}1676if (oldDecorations.length === 0 && newDecorations.length === 0) {1677// nothing to do1678return [];1679}16801681try {1682this._deltaDecorationCallCnt++;1683if (this._deltaDecorationCallCnt > 1) {1684console.warn(`Invoking deltaDecorations recursively could lead to leaking decorations.`);1685onUnexpectedError(new Error(`Invoking deltaDecorations recursively could lead to leaking decorations.`));1686}1687this._onDidChangeDecorations.beginDeferredEmit();1688return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);1689} finally {1690this._onDidChangeDecorations.endDeferredEmit();1691this._deltaDecorationCallCnt--;1692}1693}16941695_getTrackedRange(id: string): Range | null {1696return this.getDecorationRange(id);1697}16981699_setTrackedRange(id: string | null, newRange: null, newStickiness: model.TrackedRangeStickiness): null;1700_setTrackedRange(id: string | null, newRange: Range, newStickiness: model.TrackedRangeStickiness): string;1701_setTrackedRange(id: string | null, newRange: Range | null, newStickiness: model.TrackedRangeStickiness): string | null {1702const node = (id ? this._decorations[id] : null);17031704if (!node) {1705if (!newRange) {1706// node doesn't exist, the request is to delete => nothing to do1707return null;1708}1709// node doesn't exist, the request is to set => add the tracked range1710return this._deltaDecorationsImpl(0, [], [{ range: newRange, options: TRACKED_RANGE_OPTIONS[newStickiness] }], true)[0];1711}17121713if (!newRange) {1714// node exists, the request is to delete => delete node1715this._decorationsTree.delete(node);1716delete this._decorations[node.id];1717return null;1718}17191720// node exists, the request is to set => change the tracked range and its options1721const range = this._validateRangeRelaxedNoAllocations(newRange);1722const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);1723const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);1724this._decorationsTree.delete(node);1725node.reset(this.getVersionId(), startOffset, endOffset, range);1726node.setOptions(TRACKED_RANGE_OPTIONS[newStickiness]);1727this._decorationsTree.insert(node);1728return node.id;1729}17301731public removeAllDecorationsWithOwnerId(ownerId: number): void {1732if (this._isDisposed) {1733return;1734}1735const nodes = this._decorationsTree.collectNodesFromOwner(ownerId);1736for (let i = 0, len = nodes.length; i < len; i++) {1737const node = nodes[i];17381739this._decorationsTree.delete(node);1740delete this._decorations[node.id];1741}1742}17431744public getDecorationOptions(decorationId: string): model.IModelDecorationOptions | null {1745const node = this._decorations[decorationId];1746if (!node) {1747return null;1748}1749return node.options;1750}17511752public getDecorationRange(decorationId: string): Range | null {1753const node = this._decorations[decorationId];1754if (!node) {1755return null;1756}1757return this._decorationsTree.getNodeRange(this, node);1758}17591760public getLineDecorations(lineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false): model.IModelDecoration[] {1761if (lineNumber < 1 || lineNumber > this.getLineCount()) {1762return [];1763}1764return this.getLinesDecorations(lineNumber, lineNumber, ownerId, filterOutValidation, filterFontDecorations);1765}17661767public getLinesDecorations(_startLineNumber: number, _endLineNumber: number, ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false, onlyMarginDecorations: boolean = false): model.IModelDecoration[] {1768const lineCount = this.getLineCount();1769const startLineNumber = Math.min(lineCount, Math.max(1, _startLineNumber));1770const endLineNumber = Math.min(lineCount, Math.max(1, _endLineNumber));1771const endColumn = this.getLineMaxColumn(endLineNumber);1772const range = new Range(startLineNumber, 1, endLineNumber, endColumn);17731774const decorations = this._getDecorationsInRange(range, ownerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations);1775pushMany(decorations, this._decorationProvider.getDecorationsInRange(range, ownerId, filterOutValidation));1776return decorations;1777}17781779public getDecorationsInRange(range: IRange, ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false, onlyMinimapDecorations: boolean = false, onlyMarginDecorations: boolean = false): model.IModelDecoration[] {1780const validatedRange = this.validateRange(range);17811782const decorations = this._getDecorationsInRange(validatedRange, ownerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations);1783pushMany(decorations, this._decorationProvider.getDecorationsInRange(validatedRange, ownerId, filterOutValidation, onlyMinimapDecorations));1784return decorations;1785}17861787public getOverviewRulerDecorations(ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false): model.IModelDecoration[] {1788return this._decorationsTree.getAll(this, ownerId, filterOutValidation, filterFontDecorations, true, false);1789}17901791public getInjectedTextDecorations(ownerId: number = 0): model.IModelDecoration[] {1792return this._decorationsTree.getAllInjectedText(this, ownerId);1793}17941795public getCustomLineHeightsDecorations(ownerId: number = 0): model.IModelDecoration[] {1796return this._decorationsTree.getAllCustomLineHeights(this, ownerId);1797}17981799private _getInjectedTextInLine(lineNumber: number): LineInjectedText[] {1800const startOffset = this._buffer.getOffsetAt(lineNumber, 1);1801const endOffset = startOffset + this._buffer.getLineLength(lineNumber);18021803const result = this._decorationsTree.getInjectedTextInInterval(this, startOffset, endOffset, 0);1804return LineInjectedText.fromDecorations(result).filter(t => t.lineNumber === lineNumber);1805}18061807public getFontDecorationsInRange(range: IRange, ownerId: number = 0): model.IModelDecoration[] {1808const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);1809const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);1810return this._decorationsTree.getFontDecorationsInInterval(this, startOffset, endOffset, ownerId);1811}18121813public getAllDecorations(ownerId: number = 0, filterOutValidation: boolean = false, filterFontDecorations: boolean = false): model.IModelDecoration[] {1814let result = this._decorationsTree.getAll(this, ownerId, filterOutValidation, filterFontDecorations, false, false);1815result = result.concat(this._decorationProvider.getAllDecorations(ownerId, filterOutValidation));1816return result;1817}18181819public getAllMarginDecorations(ownerId: number = 0): model.IModelDecoration[] {1820return this._decorationsTree.getAll(this, ownerId, false, false, false, true);1821}18221823private _getDecorationsInRange(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] {1824const startOffset = this._buffer.getOffsetAt(filterRange.startLineNumber, filterRange.startColumn);1825const endOffset = this._buffer.getOffsetAt(filterRange.endLineNumber, filterRange.endColumn);1826return this._decorationsTree.getAllInInterval(this, startOffset, endOffset, filterOwnerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations);1827}18281829public getRangeAt(start: number, end: number): Range {1830return this._buffer.getRangeAt(start, end - start);1831}18321833private _changeDecorationImpl(ownerId: number, decorationId: string, _range: IRange): void {1834const node = this._decorations[decorationId];1835if (!node) {1836return;1837}18381839if (node.options.after) {1840const oldRange = this.getDecorationRange(decorationId);1841this._onDidChangeDecorations.recordLineAffectedByInjectedText(oldRange!.endLineNumber);1842}1843if (node.options.before) {1844const oldRange = this.getDecorationRange(decorationId);1845this._onDidChangeDecorations.recordLineAffectedByInjectedText(oldRange!.startLineNumber);1846}1847if (node.options.lineHeight !== null) {1848const oldRange = this.getDecorationRange(decorationId);1849this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, oldRange!.startLineNumber, null);1850}1851if (node.options.affectsFont) {1852const oldRange = this.getDecorationRange(decorationId);1853this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, oldRange!.startLineNumber);1854}18551856const range = this._validateRangeRelaxedNoAllocations(_range);1857const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);1858const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);18591860this._decorationsTree.delete(node);1861node.reset(this.getVersionId(), startOffset, endOffset, range);1862this._decorationsTree.insert(node);1863this._onDidChangeDecorations.checkAffectedAndFire(node.options);18641865if (node.options.after) {1866this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.endLineNumber);1867}1868if (node.options.before) {1869this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.startLineNumber);1870}1871if (node.options.lineHeight !== null) {1872this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, range.startLineNumber, node.options.lineHeight);1873}1874if (node.options.affectsFont) {1875this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, range.startLineNumber);1876}1877}18781879private _changeDecorationOptionsImpl(ownerId: number, decorationId: string, options: ModelDecorationOptions): void {1880const node = this._decorations[decorationId];1881if (!node) {1882return;1883}18841885const nodeWasInOverviewRuler = (node.options.overviewRuler && node.options.overviewRuler.color ? true : false);1886const nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false);18871888this._onDidChangeDecorations.checkAffectedAndFire(node.options);1889this._onDidChangeDecorations.checkAffectedAndFire(options);18901891if (node.options.after || options.after) {1892const nodeRange = this._decorationsTree.getNodeRange(this, node);1893this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.endLineNumber);1894}1895if (node.options.before || options.before) {1896const nodeRange = this._decorationsTree.getNodeRange(this, node);1897this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.startLineNumber);1898}1899if (node.options.lineHeight !== null || options.lineHeight !== null) {1900const nodeRange = this._decorationsTree.getNodeRange(this, node);1901this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, nodeRange.startLineNumber, options.lineHeight);1902}1903if (node.options.affectsFont || options.affectsFont) {1904const nodeRange = this._decorationsTree.getNodeRange(this, node);1905this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, decorationId, nodeRange.startLineNumber);1906}19071908const movedInOverviewRuler = nodeWasInOverviewRuler !== nodeIsInOverviewRuler;1909const changedWhetherInjectedText = isOptionsInjectedText(options) !== isNodeInjectedText(node);1910if (movedInOverviewRuler || changedWhetherInjectedText) {1911this._decorationsTree.delete(node);1912node.setOptions(options);1913this._decorationsTree.insert(node);1914} else {1915node.setOptions(options);1916}1917}19181919private _deltaDecorationsImpl(ownerId: number, oldDecorationsIds: string[], newDecorations: model.IModelDeltaDecoration[], suppressEvents: boolean = false): string[] {1920const versionId = this.getVersionId();19211922const oldDecorationsLen = oldDecorationsIds.length;1923let oldDecorationIndex = 0;19241925const newDecorationsLen = newDecorations.length;1926let newDecorationIndex = 0;19271928this._onDidChangeDecorations.beginDeferredEmit();1929try {1930const result = new Array<string>(newDecorationsLen);1931while (oldDecorationIndex < oldDecorationsLen || newDecorationIndex < newDecorationsLen) {19321933let node: IntervalNode | null = null;19341935if (oldDecorationIndex < oldDecorationsLen) {1936// (1) get ourselves an old node1937let decorationId: string;1938do {1939decorationId = oldDecorationsIds[oldDecorationIndex++];1940node = this._decorations[decorationId];1941} while (!node && oldDecorationIndex < oldDecorationsLen);19421943// (2) remove the node from the tree (if it exists)1944if (node) {1945if (node.options.after) {1946const nodeRange = this._decorationsTree.getNodeRange(this, node);1947this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.endLineNumber);1948}1949if (node.options.before) {1950const nodeRange = this._decorationsTree.getNodeRange(this, node);1951this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.startLineNumber);1952}1953if (node.options.lineHeight !== null) {1954const nodeRange = this._decorationsTree.getNodeRange(this, node);1955this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, nodeRange.startLineNumber, null);1956}1957if (node.options.affectsFont) {1958const nodeRange = this._decorationsTree.getNodeRange(this, node);1959this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, decorationId, nodeRange.startLineNumber);1960}1961this._decorationsTree.delete(node);19621963if (!suppressEvents) {1964this._onDidChangeDecorations.checkAffectedAndFire(node.options);1965}1966}1967}19681969if (newDecorationIndex < newDecorationsLen) {1970// (3) create a new node if necessary1971if (!node) {1972const internalDecorationId = (++this._lastDecorationId);1973const decorationId = `${this._instanceId};${internalDecorationId}`;1974node = new IntervalNode(decorationId, 0, 0);1975this._decorations[decorationId] = node;1976}19771978// (4) initialize node1979const newDecoration = newDecorations[newDecorationIndex];1980const range = this._validateRangeRelaxedNoAllocations(newDecoration.range);1981const options = _normalizeOptions(newDecoration.options);1982const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);1983const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);19841985node.ownerId = ownerId;1986node.reset(versionId, startOffset, endOffset, range);1987node.setOptions(options);19881989if (node.options.after) {1990this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.endLineNumber);1991}1992if (node.options.before) {1993this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.startLineNumber);1994}1995if (node.options.lineHeight !== null) {1996this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, node.id, range.startLineNumber, node.options.lineHeight);1997}1998if (node.options.affectsFont) {1999this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, range.startLineNumber);2000}2001if (!suppressEvents) {2002this._onDidChangeDecorations.checkAffectedAndFire(options);2003}20042005this._decorationsTree.insert(node);20062007result[newDecorationIndex] = node.id;20082009newDecorationIndex++;2010} else {2011if (node) {2012delete this._decorations[node.id];2013}2014}2015}20162017return result;2018} finally {2019this._onDidChangeDecorations.endDeferredEmit();2020}2021}20222023//#endregion20242025//#region Tokenization20262027// TODO move them to the tokenization part.2028public getLanguageId(): string {2029return this.tokenization.getLanguageId();2030}20312032public setLanguage(languageIdOrSelection: string | ILanguageSelection, source?: string): void {2033if (typeof languageIdOrSelection === 'string') {2034this._languageSelectionListener.clear();2035this._setLanguage(languageIdOrSelection, source);2036} else {2037this._languageSelectionListener.value = languageIdOrSelection.onDidChange(() => this._setLanguage(languageIdOrSelection.languageId, source));2038this._setLanguage(languageIdOrSelection.languageId, source);2039}2040}20412042private _setLanguage(languageId: string, source?: string): void {2043this.tokenization.setLanguageId(languageId, source);2044this._languageService.requestRichLanguageFeatures(languageId);2045}20462047public getLanguageIdAtPosition(lineNumber: number, column: number): string {2048return this.tokenization.getLanguageIdAtPosition(lineNumber, column);2049}20502051public getWordAtPosition(position: IPosition): IWordAtPosition | null {2052return this._tokenizationTextModelPart.getWordAtPosition(position);2053}20542055public getWordUntilPosition(position: IPosition): IWordAtPosition {2056return this._tokenizationTextModelPart.getWordUntilPosition(position);2057}20582059//#endregion2060normalizePosition(position: Position, affinity: model.PositionAffinity): Position {2061return position;2062}20632064/**2065* Gets the column at which indentation stops at a given line.2066* @internal2067*/2068public getLineIndentColumn(lineNumber: number): number {2069// Columns start with 1.2070return indentOfLine(this.getLineContent(lineNumber)) + 1;2071}20722073public override toString(): string {2074return `TextModel(${this.uri.toString()})`;2075}2076}20772078export function indentOfLine(line: string): number {2079let indent = 0;2080for (const c of line) {2081if (c === ' ' || c === '\t') {2082indent++;2083} else {2084break;2085}2086}2087return indent;2088}20892090//#region Decorations20912092function isNodeInOverviewRuler(node: IntervalNode): boolean {2093return (node.options.overviewRuler && node.options.overviewRuler.color ? true : false);2094}20952096function isOptionsInjectedText(options: ModelDecorationOptions): boolean {2097return !!options.after || !!options.before;2098}20992100function isNodeInjectedText(node: IntervalNode): boolean {2101return !!node.options.after || !!node.options.before;2102}21032104export interface IDecorationsTreesHost {2105getVersionId(): number;2106getRangeAt(start: number, end: number): Range;2107}21082109class DecorationsTrees {21102111/**2112* This tree holds decorations that do not show up in the overview ruler.2113*/2114private readonly _decorationsTree0: IntervalTree;21152116/**2117* This tree holds decorations that show up in the overview ruler.2118*/2119private readonly _decorationsTree1: IntervalTree;21202121/**2122* This tree holds decorations that contain injected text.2123*/2124private readonly _injectedTextDecorationsTree: IntervalTree;21252126constructor() {2127this._decorationsTree0 = new IntervalTree();2128this._decorationsTree1 = new IntervalTree();2129this._injectedTextDecorationsTree = new IntervalTree();2130}21312132public ensureAllNodesHaveRanges(host: IDecorationsTreesHost): void {2133this.getAll(host, 0, false, false, false, false);2134}21352136private _ensureNodesHaveRanges(host: IDecorationsTreesHost, nodes: IntervalNode[]): model.IModelDecoration[] {2137for (const node of nodes) {2138if (node.range === null) {2139node.range = host.getRangeAt(node.cachedAbsoluteStart, node.cachedAbsoluteEnd);2140}2141}2142return <model.IModelDecoration[]>nodes;2143}21442145public getAllInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] {2146const versionId = host.getVersionId();2147const result = this._intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, versionId, onlyMarginDecorations);2148return this._ensureNodesHaveRanges(host, result);2149}21502151private _intervalSearch(start: number, end: number, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] {2152const r0 = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);2153const r1 = this._decorationsTree1.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);2154const r2 = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);2155return r0.concat(r1).concat(r2);2156}21572158public getInjectedTextInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number): model.IModelDecoration[] {2159const versionId = host.getVersionId();2160const result = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, false, false, versionId, false);2161return this._ensureNodesHaveRanges(host, result).filter((i) => i.options.showIfCollapsed || !i.range.isEmpty());2162}21632164public getFontDecorationsInInterval(host: IDecorationsTreesHost, start: number, end: number, filterOwnerId: number): model.IModelDecoration[] {2165const versionId = host.getVersionId();2166const decorations = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, false, false, versionId, false);2167return this._ensureNodesHaveRanges(host, decorations).filter((i) => i.options.affectsFont);2168}21692170public getAllInjectedText(host: IDecorationsTreesHost, filterOwnerId: number): model.IModelDecoration[] {2171const versionId = host.getVersionId();2172const result = this._injectedTextDecorationsTree.search(filterOwnerId, false, false, versionId, false);2173return this._ensureNodesHaveRanges(host, result).filter((i) => i.options.showIfCollapsed || !i.range.isEmpty());2174}21752176public getAllCustomLineHeights(host: IDecorationsTreesHost, filterOwnerId: number): model.IModelDecoration[] {2177const versionId = host.getVersionId();2178const result = this._search(filterOwnerId, false, false, false, versionId, false);2179return this._ensureNodesHaveRanges(host, result).filter((i) => typeof i.options.lineHeight === 'number');2180}21812182public getAll(host: IDecorationsTreesHost, filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, overviewRulerOnly: boolean, onlyMarginDecorations: boolean): model.IModelDecoration[] {2183const versionId = host.getVersionId();2184const result = this._search(filterOwnerId, filterOutValidation, filterFontDecorations, overviewRulerOnly, versionId, onlyMarginDecorations);2185return this._ensureNodesHaveRanges(host, result);2186}21872188private _search(filterOwnerId: number, filterOutValidation: boolean, filterFontDecorations: boolean, overviewRulerOnly: boolean, cachedVersionId: number, onlyMarginDecorations: boolean): IntervalNode[] {2189if (overviewRulerOnly) {2190return this._decorationsTree1.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);2191} else {2192const r0 = this._decorationsTree0.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);2193const r1 = this._decorationsTree1.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);2194const r2 = this._injectedTextDecorationsTree.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);2195return r0.concat(r1).concat(r2);2196}2197}21982199public collectNodesFromOwner(ownerId: number): IntervalNode[] {2200const r0 = this._decorationsTree0.collectNodesFromOwner(ownerId);2201const r1 = this._decorationsTree1.collectNodesFromOwner(ownerId);2202const r2 = this._injectedTextDecorationsTree.collectNodesFromOwner(ownerId);2203return r0.concat(r1).concat(r2);2204}22052206public collectNodesPostOrder(): IntervalNode[] {2207const r0 = this._decorationsTree0.collectNodesPostOrder();2208const r1 = this._decorationsTree1.collectNodesPostOrder();2209const r2 = this._injectedTextDecorationsTree.collectNodesPostOrder();2210return r0.concat(r1).concat(r2);2211}22122213public insert(node: IntervalNode): void {2214if (isNodeInjectedText(node)) {2215this._injectedTextDecorationsTree.insert(node);2216} else if (isNodeInOverviewRuler(node)) {2217this._decorationsTree1.insert(node);2218} else {2219this._decorationsTree0.insert(node);2220}2221}22222223public delete(node: IntervalNode): void {2224if (isNodeInjectedText(node)) {2225this._injectedTextDecorationsTree.delete(node);2226} else if (isNodeInOverviewRuler(node)) {2227this._decorationsTree1.delete(node);2228} else {2229this._decorationsTree0.delete(node);2230}2231}22322233public getNodeRange(host: IDecorationsTreesHost, node: IntervalNode): Range {2234const versionId = host.getVersionId();2235if (node.cachedVersionId !== versionId) {2236this._resolveNode(node, versionId);2237}2238if (node.range === null) {2239node.range = host.getRangeAt(node.cachedAbsoluteStart, node.cachedAbsoluteEnd);2240}2241return node.range;2242}22432244private _resolveNode(node: IntervalNode, cachedVersionId: number): void {2245if (isNodeInjectedText(node)) {2246this._injectedTextDecorationsTree.resolveNode(node, cachedVersionId);2247} else if (isNodeInOverviewRuler(node)) {2248this._decorationsTree1.resolveNode(node, cachedVersionId);2249} else {2250this._decorationsTree0.resolveNode(node, cachedVersionId);2251}2252}22532254public acceptReplace(offset: number, length: number, textLength: number, forceMoveMarkers: boolean): void {2255this._decorationsTree0.acceptReplace(offset, length, textLength, forceMoveMarkers);2256this._decorationsTree1.acceptReplace(offset, length, textLength, forceMoveMarkers);2257this._injectedTextDecorationsTree.acceptReplace(offset, length, textLength, forceMoveMarkers);2258}2259}22602261function cleanClassName(className: string): string {2262return className.replace(/[^a-z0-9\-_]/gi, ' ');2263}22642265class DecorationOptions implements model.IDecorationOptions {2266readonly color: string | ThemeColor;2267readonly darkColor: string | ThemeColor;22682269constructor(options: model.IDecorationOptions) {2270this.color = options.color || '';2271this.darkColor = options.darkColor || '';22722273}2274}22752276export class ModelDecorationOverviewRulerOptions extends DecorationOptions {2277readonly position: model.OverviewRulerLane;2278private _resolvedColor: string | null;22792280constructor(options: model.IModelDecorationOverviewRulerOptions) {2281super(options);2282this._resolvedColor = null;2283this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center);2284}22852286public getColor(theme: IColorTheme): string {2287if (!this._resolvedColor) {2288if (theme.type !== 'light' && this.darkColor) {2289this._resolvedColor = this._resolveColor(this.darkColor, theme);2290} else {2291this._resolvedColor = this._resolveColor(this.color, theme);2292}2293}2294return this._resolvedColor;2295}22962297public invalidateCachedColor(): void {2298this._resolvedColor = null;2299}23002301private _resolveColor(color: string | ThemeColor, theme: IColorTheme): string {2302if (typeof color === 'string') {2303return color;2304}2305const c = color ? theme.getColor(color.id) : null;2306if (!c) {2307return '';2308}2309return c.toString();2310}2311}23122313export class ModelDecorationGlyphMarginOptions {2314readonly position: model.GlyphMarginLane;2315readonly persistLane: boolean | undefined;23162317constructor(options: model.IModelDecorationGlyphMarginOptions | null | undefined) {2318this.position = options?.position ?? model.GlyphMarginLane.Center;2319this.persistLane = options?.persistLane;2320}2321}23222323export class ModelDecorationMinimapOptions extends DecorationOptions {2324readonly position: model.MinimapPosition;2325readonly sectionHeaderStyle: model.MinimapSectionHeaderStyle | null;2326readonly sectionHeaderText: string | null;2327private _resolvedColor: Color | undefined;23282329constructor(options: model.IModelDecorationMinimapOptions) {2330super(options);2331this.position = options.position;2332this.sectionHeaderStyle = options.sectionHeaderStyle ?? null;2333this.sectionHeaderText = options.sectionHeaderText ?? null;2334}23352336public getColor(theme: IColorTheme): Color | undefined {2337if (!this._resolvedColor) {2338if (theme.type !== 'light' && this.darkColor) {2339this._resolvedColor = this._resolveColor(this.darkColor, theme);2340} else {2341this._resolvedColor = this._resolveColor(this.color, theme);2342}2343}23442345return this._resolvedColor;2346}23472348public invalidateCachedColor(): void {2349this._resolvedColor = undefined;2350}23512352private _resolveColor(color: string | ThemeColor, theme: IColorTheme): Color | undefined {2353if (typeof color === 'string') {2354return Color.fromHex(color);2355}2356return theme.getColor(color.id);2357}2358}23592360export class ModelDecorationInjectedTextOptions implements model.InjectedTextOptions {2361public static from(options: model.InjectedTextOptions): ModelDecorationInjectedTextOptions {2362if (options instanceof ModelDecorationInjectedTextOptions) {2363return options;2364}2365return new ModelDecorationInjectedTextOptions(options);2366}23672368public readonly content: string;2369public readonly tokens: TokenArray | null;2370readonly inlineClassName: string | null;2371readonly inlineClassNameAffectsLetterSpacing: boolean;2372readonly attachedData: unknown | null;2373readonly cursorStops: model.InjectedTextCursorStops | null;23742375private constructor(options: model.InjectedTextOptions) {2376this.content = options.content || '';2377this.tokens = options.tokens ?? null;2378this.inlineClassName = options.inlineClassName || null;2379this.inlineClassNameAffectsLetterSpacing = options.inlineClassNameAffectsLetterSpacing || false;2380this.attachedData = options.attachedData || null;2381this.cursorStops = options.cursorStops || null;2382}2383}23842385export class ModelDecorationOptions implements model.IModelDecorationOptions {23862387public static EMPTY: ModelDecorationOptions;23882389public static register(options: model.IModelDecorationOptions): ModelDecorationOptions {2390return new ModelDecorationOptions(options);2391}23922393public static createDynamic(options: model.IModelDecorationOptions): ModelDecorationOptions {2394return new ModelDecorationOptions(options);2395}2396readonly description: string;2397readonly blockClassName: string | null;2398readonly blockIsAfterEnd: boolean | null;2399readonly blockDoesNotCollapse?: boolean | null;2400readonly blockPadding: [top: number, right: number, bottom: number, left: number] | null;2401readonly stickiness: model.TrackedRangeStickiness;2402readonly zIndex: number;2403readonly className: string | null;2404readonly shouldFillLineOnLineBreak: boolean | null;2405readonly hoverMessage: IMarkdownString | IMarkdownString[] | null;2406readonly glyphMarginHoverMessage: IMarkdownString | IMarkdownString[] | null;2407readonly isWholeLine: boolean;2408readonly lineHeight: number | null;2409readonly fontSize: string | null;2410readonly showIfCollapsed: boolean;2411readonly collapseOnReplaceEdit: boolean;2412readonly overviewRuler: ModelDecorationOverviewRulerOptions | null;2413readonly minimap: ModelDecorationMinimapOptions | null;2414readonly glyphMargin?: model.IModelDecorationGlyphMarginOptions | null | undefined;2415readonly glyphMarginClassName: string | null;2416readonly linesDecorationsClassName: string | null;2417readonly lineNumberClassName: string | null;2418readonly lineNumberHoverMessage: IMarkdownString | IMarkdownString[] | null;2419readonly linesDecorationsTooltip: string | null;2420readonly firstLineDecorationClassName: string | null;2421readonly marginClassName: string | null;2422readonly inlineClassName: string | null;2423readonly inlineClassNameAffectsLetterSpacing: boolean;2424readonly beforeContentClassName: string | null;2425readonly afterContentClassName: string | null;2426readonly after: ModelDecorationInjectedTextOptions | null;2427readonly before: ModelDecorationInjectedTextOptions | null;2428readonly hideInCommentTokens: boolean | null;2429readonly hideInStringTokens: boolean | null;2430readonly affectsFont: boolean | null;2431readonly textDirection?: model.TextDirection | null | undefined;24322433private constructor(options: model.IModelDecorationOptions) {2434this.description = options.description;2435this.blockClassName = options.blockClassName ? cleanClassName(options.blockClassName) : null;2436this.blockDoesNotCollapse = options.blockDoesNotCollapse ?? null;2437this.blockIsAfterEnd = options.blockIsAfterEnd ?? null;2438this.blockPadding = options.blockPadding ?? null;2439this.stickiness = options.stickiness || model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges;2440this.zIndex = options.zIndex || 0;2441this.className = options.className ? cleanClassName(options.className) : null;2442this.shouldFillLineOnLineBreak = options.shouldFillLineOnLineBreak ?? null;2443this.hoverMessage = options.hoverMessage || null;2444this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || null;2445this.lineNumberHoverMessage = options.lineNumberHoverMessage || null;2446this.isWholeLine = options.isWholeLine || false;2447this.lineHeight = options.lineHeight ? Math.min(options.lineHeight, LINE_HEIGHT_CEILING) : null;2448this.fontSize = options.fontSize || null;2449this.affectsFont = !!options.fontSize || !!options.fontFamily || !!options.fontWeight || !!options.fontStyle;2450this.showIfCollapsed = options.showIfCollapsed || false;2451this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false;2452this.overviewRuler = options.overviewRuler ? new ModelDecorationOverviewRulerOptions(options.overviewRuler) : null;2453this.minimap = options.minimap ? new ModelDecorationMinimapOptions(options.minimap) : null;2454this.glyphMargin = options.glyphMarginClassName ? new ModelDecorationGlyphMarginOptions(options.glyphMargin) : null;2455this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : null;2456this.linesDecorationsClassName = options.linesDecorationsClassName ? cleanClassName(options.linesDecorationsClassName) : null;2457this.lineNumberClassName = options.lineNumberClassName ? cleanClassName(options.lineNumberClassName) : null;2458this.linesDecorationsTooltip = options.linesDecorationsTooltip ? strings.htmlAttributeEncodeValue(options.linesDecorationsTooltip) : null;2459this.firstLineDecorationClassName = options.firstLineDecorationClassName ? cleanClassName(options.firstLineDecorationClassName) : null;2460this.marginClassName = options.marginClassName ? cleanClassName(options.marginClassName) : null;2461this.inlineClassName = options.inlineClassName ? cleanClassName(options.inlineClassName) : null;2462this.inlineClassNameAffectsLetterSpacing = options.inlineClassNameAffectsLetterSpacing || false;2463this.beforeContentClassName = options.beforeContentClassName ? cleanClassName(options.beforeContentClassName) : null;2464this.afterContentClassName = options.afterContentClassName ? cleanClassName(options.afterContentClassName) : null;2465this.after = options.after ? ModelDecorationInjectedTextOptions.from(options.after) : null;2466this.before = options.before ? ModelDecorationInjectedTextOptions.from(options.before) : null;2467this.hideInCommentTokens = options.hideInCommentTokens ?? false;2468this.hideInStringTokens = options.hideInStringTokens ?? false;2469this.textDirection = options.textDirection ?? null;2470}2471}2472ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({ description: 'empty' });24732474/**2475* The order carefully matches the values of the enum.2476*/2477const TRACKED_RANGE_OPTIONS = [2478ModelDecorationOptions.register({ description: 'tracked-range-always-grows-when-typing-at-edges', stickiness: model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges }),2479ModelDecorationOptions.register({ description: 'tracked-range-never-grows-when-typing-at-edges', stickiness: model.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges }),2480ModelDecorationOptions.register({ description: 'tracked-range-grows-only-when-typing-before', stickiness: model.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore }),2481ModelDecorationOptions.register({ description: 'tracked-range-grows-only-when-typing-after', stickiness: model.TrackedRangeStickiness.GrowsOnlyWhenTypingAfter }),2482];24832484function _normalizeOptions(options: model.IModelDecorationOptions): ModelDecorationOptions {2485if (options instanceof ModelDecorationOptions) {2486return options;2487}2488return ModelDecorationOptions.createDynamic(options);2489}24902491class LineHeightChangingDecoration {24922493public static toKey(obj: LineHeightChangingDecoration): string {2494return `${obj.ownerId};${obj.decorationId};${obj.lineNumber}`;2495}24962497constructor(2498public readonly ownerId: number,2499public readonly decorationId: string,2500public readonly lineNumber: number,2501public readonly lineHeight: number | null2502) { }2503}25042505class LineFontChangingDecoration {25062507public static toKey(obj: LineFontChangingDecoration): string {2508return `${obj.ownerId};${obj.decorationId};${obj.lineNumber}`;2509}25102511constructor(2512public readonly ownerId: number,2513public readonly decorationId: string,2514public readonly lineNumber: number2515) { }2516}25172518class DidChangeDecorationsEmitter extends Disposable {25192520private readonly _actual: Emitter<IModelDecorationsChangedEvent> = this._register(new Emitter<IModelDecorationsChangedEvent>());2521public readonly event: Event<IModelDecorationsChangedEvent> = this._actual.event;25222523private _deferredCnt: number;2524private _shouldFireDeferred: boolean;2525private _affectsMinimap: boolean;2526private _affectsOverviewRuler: boolean;2527private _affectedInjectedTextLines: Set<number> | null = null;2528private _affectedLineHeights: SetWithKey<LineHeightChangingDecoration> | null = null;2529private _affectedFontLines: SetWithKey<LineFontChangingDecoration> | null = null;2530private _affectsGlyphMargin: boolean;2531private _affectsLineNumber: boolean;25322533constructor(private readonly handleBeforeFire: (affectedInjectedTextLines: Set<number> | null, affectedLineHeights: SetWithKey<LineHeightChangingDecoration> | null, affectedFontLines: SetWithKey<LineFontChangingDecoration> | null) => void) {2534super();2535this._deferredCnt = 0;2536this._shouldFireDeferred = false;2537this._affectsMinimap = false;2538this._affectsOverviewRuler = false;2539this._affectsGlyphMargin = false;2540this._affectsLineNumber = false;2541}25422543hasListeners(): boolean {2544return this._actual.hasListeners();2545}25462547public beginDeferredEmit(): void {2548this._deferredCnt++;2549}25502551public endDeferredEmit(): void {2552this._deferredCnt--;2553if (this._deferredCnt === 0) {2554if (this._shouldFireDeferred) {2555this.doFire();2556}25572558this._affectedInjectedTextLines?.clear();2559this._affectedInjectedTextLines = null;2560this._affectedLineHeights?.clear();2561this._affectedLineHeights = null;2562this._affectedFontLines?.clear();2563this._affectedFontLines = null;2564}2565}25662567public recordLineAffectedByInjectedText(lineNumber: number): void {2568if (!this._affectedInjectedTextLines) {2569this._affectedInjectedTextLines = new Set();2570}2571this._affectedInjectedTextLines.add(lineNumber);2572}25732574public recordLineAffectedByLineHeightChange(ownerId: number, decorationId: string, lineNumber: number, lineHeight: number | null): void {2575if (!this._affectedLineHeights) {2576this._affectedLineHeights = new SetWithKey<LineHeightChangingDecoration>([], LineHeightChangingDecoration.toKey);2577}2578this._affectedLineHeights.add(new LineHeightChangingDecoration(ownerId, decorationId, lineNumber, lineHeight));2579}25802581public recordLineAffectedByFontChange(ownerId: number, decorationId: string, lineNumber: number): void {2582if (!this._affectedFontLines) {2583this._affectedFontLines = new SetWithKey<LineFontChangingDecoration>([], LineFontChangingDecoration.toKey);2584}2585this._affectedFontLines.add(new LineFontChangingDecoration(ownerId, decorationId, lineNumber));2586}25872588public checkAffectedAndFire(options: ModelDecorationOptions): void {2589this._affectsMinimap ||= !!options.minimap?.position;2590this._affectsOverviewRuler ||= !!options.overviewRuler?.color;2591this._affectsGlyphMargin ||= !!options.glyphMarginClassName;2592this._affectsLineNumber ||= !!options.lineNumberClassName;2593this.tryFire();2594}25952596public fire(): void {2597this._affectsMinimap = true;2598this._affectsOverviewRuler = true;2599this._affectsGlyphMargin = true;2600this.tryFire();2601}26022603private tryFire() {2604if (this._deferredCnt === 0) {2605this.doFire();2606} else {2607this._shouldFireDeferred = true;2608}2609}26102611private doFire() {2612this.handleBeforeFire(this._affectedInjectedTextLines, this._affectedLineHeights, this._affectedFontLines);26132614const event: IModelDecorationsChangedEvent = {2615affectsMinimap: this._affectsMinimap,2616affectsOverviewRuler: this._affectsOverviewRuler,2617affectsGlyphMargin: this._affectsGlyphMargin,2618affectsLineNumber: this._affectsLineNumber,2619};2620this._shouldFireDeferred = false;2621this._affectsMinimap = false;2622this._affectsOverviewRuler = false;2623this._affectsGlyphMargin = false;2624this._actual.fire(event);2625}2626}26272628//#endregion26292630class DidChangeContentEmitter extends Disposable {26312632/**2633* Both `fastEvent` and `slowEvent` work the same way and contain the same events, but first we invoke `fastEvent` and then `slowEvent`.2634*/2635private readonly _fastEmitter: Emitter<InternalModelContentChangeEvent> = this._register(new Emitter<InternalModelContentChangeEvent>());2636public readonly fastEvent: Event<InternalModelContentChangeEvent> = this._fastEmitter.event;2637private readonly _slowEmitter: Emitter<InternalModelContentChangeEvent> = this._register(new Emitter<InternalModelContentChangeEvent>());2638public readonly slowEvent: Event<InternalModelContentChangeEvent> = this._slowEmitter.event;26392640private _deferredCnt: number;2641private _deferredEvent: InternalModelContentChangeEvent | null;26422643constructor() {2644super();2645this._deferredCnt = 0;2646this._deferredEvent = null;2647}26482649public hasListeners(): boolean {2650return (2651this._fastEmitter.hasListeners()2652|| this._slowEmitter.hasListeners()2653);2654}26552656public beginDeferredEmit(): void {2657this._deferredCnt++;2658}26592660public endDeferredEmit(resultingSelection: Selection[] | null = null): void {2661this._deferredCnt--;2662if (this._deferredCnt === 0) {2663if (this._deferredEvent !== null) {2664this._deferredEvent.rawContentChangedEvent.resultingSelection = resultingSelection;2665const e = this._deferredEvent;2666this._deferredEvent = null;2667this._fastEmitter.fire(e);2668this._slowEmitter.fire(e);2669}2670}2671}26722673public fire(e: InternalModelContentChangeEvent): void {2674if (this._deferredCnt > 0) {2675if (this._deferredEvent) {2676this._deferredEvent = this._deferredEvent.merge(e);2677} else {2678this._deferredEvent = e;2679}2680return;2681}2682this._fastEmitter.fire(e);2683this._slowEmitter.fire(e);2684}2685}268626872688