monaco-editor
Version:
A browser based code editor
2,030 lines • 100 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var TextModel_1;
import { ArrayQueue, pushMany } from '../../../base/common/arrays.js';
import { Color } from '../../../base/common/color.js';
import { BugIndicatingError, illegalArgument, onUnexpectedError } from '../../../base/common/errors.js';
import { Emitter } from '../../../base/common/event.js';
import { Disposable, MutableDisposable, combinedDisposable } from '../../../base/common/lifecycle.js';
import * as strings from '../../../base/common/strings.js';
import { URI } from '../../../base/common/uri.js';
import { countEOL } from '../core/misc/eolCounter.js';
import { normalizeIndentation } from '../core/misc/indentation.js';
import { Position } from '../core/position.js';
import { Range } from '../core/range.js';
import { Selection } from '../core/selection.js';
import { EDITOR_MODEL_DEFAULTS } from '../core/misc/textModelDefaults.js';
import { ILanguageService } from '../languages/language.js';
import { ILanguageConfigurationService } from '../languages/languageConfigurationRegistry.js';
import * as model from '../model.js';
import { BracketPairsTextModelPart } from './bracketPairsTextModelPart/bracketPairsImpl.js';
import { ColorizedBracketPairsDecorationProvider } from './bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.js';
import { EditStack } from './editStack.js';
import { GuidesTextModelPart } from './guidesTextModelPart.js';
import { guessIndentation } from './indentationGuesser.js';
import { IntervalNode, IntervalTree, recomputeMaxEnd } from './intervalTree.js';
import { PieceTreeTextBuffer } from './pieceTreeTextBuffer/pieceTreeTextBuffer.js';
import { PieceTreeTextBufferBuilder } from './pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js';
import { SearchParams, TextModelSearch } from './textModelSearch.js';
import { TokenizationTextModelPart } from './tokens/tokenizationTextModelPart.js';
import { AttachedViews } from './tokens/abstractSyntaxTokenBackend.js';
import { InternalModelContentChangeEvent, ModelInjectedTextChangedEvent, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted, ModelLineHeightChangedEvent, ModelLineHeightChanged, ModelFontChangedEvent, ModelFontChanged, LineInjectedText } from '../textModelEvents.js';
import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';
import { IUndoRedoService } from '../../../platform/undoRedo/common/undoRedo.js';
import { SetWithKey } from '../../../base/common/collections.js';
import { EditSources } from '../textModelEditSource.js';
export function createTextBufferFactory(text) {
const builder = new PieceTreeTextBufferBuilder();
builder.acceptChunk(text);
return builder.finish();
}
export function createTextBufferFactoryFromSnapshot(snapshot) {
const builder = new PieceTreeTextBufferBuilder();
let chunk;
while (typeof (chunk = snapshot.read()) === 'string') {
builder.acceptChunk(chunk);
}
return builder.finish();
}
export function createTextBuffer(value, defaultEOL) {
let factory;
if (typeof value === 'string') {
factory = createTextBufferFactory(value);
}
else if (model.isITextSnapshot(value)) {
factory = createTextBufferFactoryFromSnapshot(value);
}
else {
factory = value;
}
return factory.create(defaultEOL);
}
let MODEL_ID = 0;
const LIMIT_FIND_COUNT = 999;
const LONG_LINE_BOUNDARY = 10000;
const LINE_HEIGHT_CEILING = 300;
class TextModelSnapshot {
constructor(source) {
this._source = source;
this._eos = false;
}
read() {
if (this._eos) {
return null;
}
const result = [];
let resultCnt = 0;
let resultLength = 0;
do {
const tmp = this._source.read();
if (tmp === null) {
// end-of-stream
this._eos = true;
if (resultCnt === 0) {
return null;
}
else {
return result.join('');
}
}
if (tmp.length > 0) {
result[resultCnt++] = tmp;
resultLength += tmp.length;
}
if (resultLength >= 64 * 1024) {
return result.join('');
}
} while (true);
}
}
const invalidFunc = () => { throw new Error(`Invalid change accessor`); };
let TextModel = class TextModel extends Disposable {
static { TextModel_1 = this; }
static { this._MODEL_SYNC_LIMIT = 50 * 1024 * 1024; } // 50 MB, // used in tests
static { this.LARGE_FILE_SIZE_THRESHOLD = 20 * 1024 * 1024; } // 20 MB;
static { this.LARGE_FILE_LINE_COUNT_THRESHOLD = 300 * 1000; } // 300K lines
static { this.LARGE_FILE_HEAP_OPERATION_THRESHOLD = 256 * 1024 * 1024; } // 256M characters, usually ~> 512MB memory usage
static { this.DEFAULT_CREATION_OPTIONS = {
isForSimpleWidget: false,
tabSize: EDITOR_MODEL_DEFAULTS.tabSize,
indentSize: EDITOR_MODEL_DEFAULTS.indentSize,
insertSpaces: EDITOR_MODEL_DEFAULTS.insertSpaces,
detectIndentation: false,
defaultEOL: 1 /* model.DefaultEndOfLine.LF */,
trimAutoWhitespace: EDITOR_MODEL_DEFAULTS.trimAutoWhitespace,
largeFileOptimizations: EDITOR_MODEL_DEFAULTS.largeFileOptimizations,
bracketPairColorizationOptions: EDITOR_MODEL_DEFAULTS.bracketPairColorizationOptions,
}; }
static resolveOptions(textBuffer, options) {
if (options.detectIndentation) {
const guessedIndentation = guessIndentation(textBuffer, options.tabSize, options.insertSpaces);
return new model.TextModelResolvedOptions({
tabSize: guessedIndentation.tabSize,
indentSize: 'tabSize', // TODO@Alex: guess indentSize independent of tabSize
insertSpaces: guessedIndentation.insertSpaces,
trimAutoWhitespace: options.trimAutoWhitespace,
defaultEOL: options.defaultEOL,
bracketPairColorizationOptions: options.bracketPairColorizationOptions,
});
}
return new model.TextModelResolvedOptions(options);
}
get onDidChangeLanguage() { return this._tokenizationTextModelPart.onDidChangeLanguage; }
get onDidChangeLanguageConfiguration() { return this._tokenizationTextModelPart.onDidChangeLanguageConfiguration; }
get onDidChangeTokens() { return this._tokenizationTextModelPart.onDidChangeTokens; }
get onDidChangeOptions() { return this._onDidChangeOptions.event; }
get onDidChangeAttached() { return this._onDidChangeAttached.event; }
get onDidChangeLineHeight() { return this._onDidChangeLineHeight.event; }
get onDidChangeFont() { return this._onDidChangeFont.event; }
onDidChangeContent(listener) {
return this._eventEmitter.slowEvent((e) => listener(e.contentChangedEvent));
}
onDidChangeContentOrInjectedText(listener) {
return combinedDisposable(this._eventEmitter.fastEvent(e => listener(e)), this._onDidChangeInjectedText.event(e => listener(e)));
}
_isDisposing() { return this.__isDisposing; }
get tokenization() { return this._tokenizationTextModelPart; }
get bracketPairs() { return this._bracketPairs; }
get guides() { return this._guidesTextModelPart; }
constructor(source, languageIdOrSelection, creationOptions, associatedResource = null, _undoRedoService, _languageService, _languageConfigurationService, instantiationService) {
super();
this._undoRedoService = _undoRedoService;
this._languageService = _languageService;
this._languageConfigurationService = _languageConfigurationService;
this.instantiationService = instantiationService;
//#region Events
this._onWillDispose = this._register(new Emitter());
this.onWillDispose = this._onWillDispose.event;
this._onDidChangeDecorations = this._register(new DidChangeDecorationsEmitter((affectedInjectedTextLines, affectedLineHeights, affectedFontLines) => this.handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines, affectedLineHeights, affectedFontLines)));
this.onDidChangeDecorations = this._onDidChangeDecorations.event;
this._onDidChangeOptions = this._register(new Emitter());
this._onDidChangeAttached = this._register(new Emitter());
this._onDidChangeInjectedText = this._register(new Emitter());
this._onDidChangeLineHeight = this._register(new Emitter());
this._onDidChangeFont = this._register(new Emitter());
this._eventEmitter = this._register(new DidChangeContentEmitter());
this._languageSelectionListener = this._register(new MutableDisposable());
this._deltaDecorationCallCnt = 0;
this._attachedViews = new AttachedViews();
// Generate a new unique model id
MODEL_ID++;
this.id = '$model' + MODEL_ID;
this.isForSimpleWidget = creationOptions.isForSimpleWidget;
if (typeof associatedResource === 'undefined' || associatedResource === null) {
this._associatedResource = URI.parse('inmemory://model/' + MODEL_ID);
}
else {
this._associatedResource = associatedResource;
}
this._attachedEditorCount = 0;
const { textBuffer, disposable } = createTextBuffer(source, creationOptions.defaultEOL);
this._buffer = textBuffer;
this._bufferDisposable = disposable;
const bufferLineCount = this._buffer.getLineCount();
const bufferTextLength = this._buffer.getValueLengthInRange(new Range(1, 1, bufferLineCount, this._buffer.getLineLength(bufferLineCount) + 1), 0 /* model.EndOfLinePreference.TextDefined */);
// !!! Make a decision in the ctor and permanently respect this decision !!!
// If a model is too large at construction time, it will never get tokenized,
// under no circumstances.
if (creationOptions.largeFileOptimizations) {
this._isTooLargeForTokenization = ((bufferTextLength > TextModel_1.LARGE_FILE_SIZE_THRESHOLD)
|| (bufferLineCount > TextModel_1.LARGE_FILE_LINE_COUNT_THRESHOLD));
this._isTooLargeForHeapOperation = bufferTextLength > TextModel_1.LARGE_FILE_HEAP_OPERATION_THRESHOLD;
}
else {
this._isTooLargeForTokenization = false;
this._isTooLargeForHeapOperation = false;
}
this._options = TextModel_1.resolveOptions(this._buffer, creationOptions);
const languageId = (typeof languageIdOrSelection === 'string' ? languageIdOrSelection : languageIdOrSelection.languageId);
if (typeof languageIdOrSelection !== 'string') {
this._languageSelectionListener.value = languageIdOrSelection.onDidChange(() => this._setLanguage(languageIdOrSelection.languageId));
}
this._bracketPairs = this._register(new BracketPairsTextModelPart(this, this._languageConfigurationService));
this._guidesTextModelPart = this._register(new GuidesTextModelPart(this, this._languageConfigurationService));
this._decorationProvider = this._register(new ColorizedBracketPairsDecorationProvider(this));
this._tokenizationTextModelPart = this.instantiationService.createInstance(TokenizationTextModelPart, this, this._bracketPairs, languageId, this._attachedViews);
this._isTooLargeForSyncing = (bufferTextLength > TextModel_1._MODEL_SYNC_LIMIT);
this._versionId = 1;
this._alternativeVersionId = 1;
this._initialUndoRedoSnapshot = null;
this._isDisposed = false;
this.__isDisposing = false;
this._instanceId = strings.singleLetterHash(MODEL_ID);
this._lastDecorationId = 0;
this._decorations = Object.create(null);
this._decorationsTree = new DecorationsTrees();
this._commandManager = new EditStack(this, this._undoRedoService);
this._isUndoing = false;
this._isRedoing = false;
this._trimAutoWhitespaceLines = null;
this._register(this._decorationProvider.onDidChange(() => {
this._onDidChangeDecorations.beginDeferredEmit();
this._onDidChangeDecorations.fire();
this._onDidChangeDecorations.endDeferredEmit();
}));
this._languageService.requestRichLanguageFeatures(languageId);
this._register(this._languageConfigurationService.onDidChange(e => {
this._bracketPairs.handleLanguageConfigurationServiceChange(e);
this._tokenizationTextModelPart.handleLanguageConfigurationServiceChange(e);
}));
}
dispose() {
this.__isDisposing = true;
this._onWillDispose.fire();
this._tokenizationTextModelPart.dispose();
this._isDisposed = true;
super.dispose();
this._bufferDisposable.dispose();
this.__isDisposing = false;
// Manually release reference to previous text buffer to avoid large leaks
// in case someone leaks a TextModel reference
const emptyDisposedTextBuffer = new PieceTreeTextBuffer([], '', '\n', false, false, true, true);
emptyDisposedTextBuffer.dispose();
this._buffer = emptyDisposedTextBuffer;
this._bufferDisposable = Disposable.None;
}
_assertNotDisposed() {
if (this._isDisposed) {
throw new BugIndicatingError('Model is disposed!');
}
}
getTextBuffer() {
this._assertNotDisposed();
return this._buffer;
}
_emitContentChangedEvent(rawChange, change) {
if (this.__isDisposing) {
// Do not confuse listeners by emitting any event after disposing
return;
}
this._tokenizationTextModelPart.handleDidChangeContent(change);
this._bracketPairs.handleDidChangeContent(change);
this._eventEmitter.fire(new InternalModelContentChangeEvent(rawChange, change));
}
setValue(value, reason = EditSources.setValue()) {
this._assertNotDisposed();
if (value === null || value === undefined) {
throw illegalArgument();
}
const { textBuffer, disposable } = createTextBuffer(value, this._options.defaultEOL);
this._setValueFromTextBuffer(textBuffer, disposable, reason);
}
_createContentChanged2(range, rangeOffset, rangeLength, rangeEndPosition, text, isUndoing, isRedoing, isFlush, isEolChange, reason) {
return {
changes: [{
range: range,
rangeOffset: rangeOffset,
rangeLength: rangeLength,
text: text,
}],
eol: this._buffer.getEOL(),
isEolChange: isEolChange,
versionId: this.getVersionId(),
isUndoing: isUndoing,
isRedoing: isRedoing,
isFlush: isFlush,
detailedReasons: [reason],
detailedReasonsChangeLengths: [1],
};
}
_setValueFromTextBuffer(textBuffer, textBufferDisposable, reason) {
this._assertNotDisposed();
const oldFullModelRange = this.getFullModelRange();
const oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);
const endLineNumber = this.getLineCount();
const endColumn = this.getLineMaxColumn(endLineNumber);
this._buffer = textBuffer;
this._bufferDisposable.dispose();
this._bufferDisposable = textBufferDisposable;
this._increaseVersionId();
// Destroy all my decorations
this._decorations = Object.create(null);
this._decorationsTree = new DecorationsTrees();
// Destroy my edit history and settings
this._commandManager.clear();
this._trimAutoWhitespaceLines = null;
this._emitContentChangedEvent(new ModelRawContentChangedEvent([
new ModelRawFlush()
], this._versionId, false, false), this._createContentChanged2(new Range(1, 1, endLineNumber, endColumn), 0, oldModelValueLength, new Position(endLineNumber, endColumn), this.getValue(), false, false, true, false, reason));
}
setEOL(eol) {
this._assertNotDisposed();
const newEOL = (eol === 1 /* model.EndOfLineSequence.CRLF */ ? '\r\n' : '\n');
if (this._buffer.getEOL() === newEOL) {
// Nothing to do
return;
}
const oldFullModelRange = this.getFullModelRange();
const oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);
const endLineNumber = this.getLineCount();
const endColumn = this.getLineMaxColumn(endLineNumber);
this._onBeforeEOLChange();
this._buffer.setEOL(newEOL);
this._increaseVersionId();
this._onAfterEOLChange();
this._emitContentChangedEvent(new ModelRawContentChangedEvent([
new ModelRawEOLChanged()
], this._versionId, false, false), this._createContentChanged2(new Range(1, 1, endLineNumber, endColumn), 0, oldModelValueLength, new Position(endLineNumber, endColumn), this.getValue(), false, false, false, true, EditSources.eolChange()));
}
_onBeforeEOLChange() {
// Ensure all decorations get their `range` set.
this._decorationsTree.ensureAllNodesHaveRanges(this);
}
_onAfterEOLChange() {
// Transform back `range` to offsets
const versionId = this.getVersionId();
const allDecorations = this._decorationsTree.collectNodesPostOrder();
for (let i = 0, len = allDecorations.length; i < len; i++) {
const node = allDecorations[i];
const range = node.range; // the range is defined due to `_onBeforeEOLChange`
const delta = node.cachedAbsoluteStart - node.start;
const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
node.cachedAbsoluteStart = startOffset;
node.cachedAbsoluteEnd = endOffset;
node.cachedVersionId = versionId;
node.start = startOffset - delta;
node.end = endOffset - delta;
recomputeMaxEnd(node);
}
}
onBeforeAttached() {
this._attachedEditorCount++;
if (this._attachedEditorCount === 1) {
this._tokenizationTextModelPart.handleDidChangeAttached();
this._onDidChangeAttached.fire(undefined);
}
return this._attachedViews.attachView();
}
onBeforeDetached(view) {
this._attachedEditorCount--;
if (this._attachedEditorCount === 0) {
this._tokenizationTextModelPart.handleDidChangeAttached();
this._onDidChangeAttached.fire(undefined);
}
this._attachedViews.detachView(view);
}
isAttachedToEditor() {
return this._attachedEditorCount > 0;
}
getAttachedEditorCount() {
return this._attachedEditorCount;
}
isTooLargeForSyncing() {
return this._isTooLargeForSyncing;
}
isTooLargeForTokenization() {
return this._isTooLargeForTokenization;
}
isTooLargeForHeapOperation() {
return this._isTooLargeForHeapOperation;
}
isDisposed() {
return this._isDisposed;
}
isDominatedByLongLines() {
this._assertNotDisposed();
if (this.isTooLargeForTokenization()) {
// Cannot word wrap huge files anyways, so it doesn't really matter
return false;
}
let smallLineCharCount = 0;
let longLineCharCount = 0;
const lineCount = this._buffer.getLineCount();
for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) {
const lineLength = this._buffer.getLineLength(lineNumber);
if (lineLength >= LONG_LINE_BOUNDARY) {
longLineCharCount += lineLength;
}
else {
smallLineCharCount += lineLength;
}
}
return (longLineCharCount > smallLineCharCount);
}
get uri() {
return this._associatedResource;
}
//#region Options
getOptions() {
this._assertNotDisposed();
return this._options;
}
getFormattingOptions() {
return {
tabSize: this._options.indentSize,
insertSpaces: this._options.insertSpaces
};
}
updateOptions(_newOpts) {
this._assertNotDisposed();
const tabSize = (typeof _newOpts.tabSize !== 'undefined') ? _newOpts.tabSize : this._options.tabSize;
const indentSize = (typeof _newOpts.indentSize !== 'undefined') ? _newOpts.indentSize : this._options.originalIndentSize;
const insertSpaces = (typeof _newOpts.insertSpaces !== 'undefined') ? _newOpts.insertSpaces : this._options.insertSpaces;
const trimAutoWhitespace = (typeof _newOpts.trimAutoWhitespace !== 'undefined') ? _newOpts.trimAutoWhitespace : this._options.trimAutoWhitespace;
const bracketPairColorizationOptions = (typeof _newOpts.bracketColorizationOptions !== 'undefined') ? _newOpts.bracketColorizationOptions : this._options.bracketPairColorizationOptions;
const newOpts = new model.TextModelResolvedOptions({
tabSize: tabSize,
indentSize: indentSize,
insertSpaces: insertSpaces,
defaultEOL: this._options.defaultEOL,
trimAutoWhitespace: trimAutoWhitespace,
bracketPairColorizationOptions,
});
if (this._options.equals(newOpts)) {
return;
}
const e = this._options.createChangeEvent(newOpts);
this._options = newOpts;
this._bracketPairs.handleDidChangeOptions(e);
this._decorationProvider.handleDidChangeOptions(e);
this._onDidChangeOptions.fire(e);
}
detectIndentation(defaultInsertSpaces, defaultTabSize) {
this._assertNotDisposed();
const guessedIndentation = guessIndentation(this._buffer, defaultTabSize, defaultInsertSpaces);
this.updateOptions({
insertSpaces: guessedIndentation.insertSpaces,
tabSize: guessedIndentation.tabSize,
indentSize: guessedIndentation.tabSize, // TODO@Alex: guess indentSize independent of tabSize
});
}
normalizeIndentation(str) {
this._assertNotDisposed();
return normalizeIndentation(str, this._options.indentSize, this._options.insertSpaces);
}
//#endregion
//#region Reading
getVersionId() {
this._assertNotDisposed();
return this._versionId;
}
mightContainRTL() {
return this._buffer.mightContainRTL();
}
mightContainUnusualLineTerminators() {
return this._buffer.mightContainUnusualLineTerminators();
}
removeUnusualLineTerminators(selections = null) {
const matches = this.findMatches(strings.UNUSUAL_LINE_TERMINATORS.source, false, true, false, null, false, 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */);
this._buffer.resetMightContainUnusualLineTerminators();
this.pushEditOperations(selections, matches.map(m => ({ range: m.range, text: null })), () => null);
}
mightContainNonBasicASCII() {
return this._buffer.mightContainNonBasicASCII();
}
getAlternativeVersionId() {
this._assertNotDisposed();
return this._alternativeVersionId;
}
getInitialUndoRedoSnapshot() {
this._assertNotDisposed();
return this._initialUndoRedoSnapshot;
}
getOffsetAt(rawPosition) {
this._assertNotDisposed();
const position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, 0 /* StringOffsetValidationType.Relaxed */);
return this._buffer.getOffsetAt(position.lineNumber, position.column);
}
getPositionAt(rawOffset) {
this._assertNotDisposed();
const offset = (Math.min(this._buffer.getLength(), Math.max(0, rawOffset)));
return this._buffer.getPositionAt(offset);
}
_increaseVersionId() {
this._versionId = this._versionId + 1;
this._alternativeVersionId = this._versionId;
}
_overwriteVersionId(versionId) {
this._versionId = versionId;
}
_overwriteAlternativeVersionId(newAlternativeVersionId) {
this._alternativeVersionId = newAlternativeVersionId;
}
_overwriteInitialUndoRedoSnapshot(newInitialUndoRedoSnapshot) {
this._initialUndoRedoSnapshot = newInitialUndoRedoSnapshot;
}
getValue(eol, preserveBOM = false) {
this._assertNotDisposed();
if (this.isTooLargeForHeapOperation()) {
throw new BugIndicatingError('Operation would exceed heap memory limits');
}
const fullModelRange = this.getFullModelRange();
const fullModelValue = this.getValueInRange(fullModelRange, eol);
if (preserveBOM) {
return this._buffer.getBOM() + fullModelValue;
}
return fullModelValue;
}
createSnapshot(preserveBOM = false) {
return new TextModelSnapshot(this._buffer.createSnapshot(preserveBOM));
}
getValueLength(eol, preserveBOM = false) {
this._assertNotDisposed();
const fullModelRange = this.getFullModelRange();
const fullModelValue = this.getValueLengthInRange(fullModelRange, eol);
if (preserveBOM) {
return this._buffer.getBOM().length + fullModelValue;
}
return fullModelValue;
}
getValueInRange(rawRange, eol = 0 /* model.EndOfLinePreference.TextDefined */) {
this._assertNotDisposed();
return this._buffer.getValueInRange(this.validateRange(rawRange), eol);
}
getValueLengthInRange(rawRange, eol = 0 /* model.EndOfLinePreference.TextDefined */) {
this._assertNotDisposed();
return this._buffer.getValueLengthInRange(this.validateRange(rawRange), eol);
}
getCharacterCountInRange(rawRange, eol = 0 /* model.EndOfLinePreference.TextDefined */) {
this._assertNotDisposed();
return this._buffer.getCharacterCountInRange(this.validateRange(rawRange), eol);
}
getLineCount() {
this._assertNotDisposed();
return this._buffer.getLineCount();
}
getLineContent(lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new BugIndicatingError('Illegal value for lineNumber');
}
return this._buffer.getLineContent(lineNumber);
}
getLineLength(lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new BugIndicatingError('Illegal value for lineNumber');
}
return this._buffer.getLineLength(lineNumber);
}
getLinesContent() {
this._assertNotDisposed();
if (this.isTooLargeForHeapOperation()) {
throw new BugIndicatingError('Operation would exceed heap memory limits');
}
return this._buffer.getLinesContent();
}
getEOL() {
this._assertNotDisposed();
return this._buffer.getEOL();
}
getEndOfLineSequence() {
this._assertNotDisposed();
return (this._buffer.getEOL() === '\n'
? 0 /* model.EndOfLineSequence.LF */
: 1 /* model.EndOfLineSequence.CRLF */);
}
getLineMinColumn(lineNumber) {
this._assertNotDisposed();
return 1;
}
getLineMaxColumn(lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new BugIndicatingError('Illegal value for lineNumber');
}
return this._buffer.getLineLength(lineNumber) + 1;
}
getLineFirstNonWhitespaceColumn(lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new BugIndicatingError('Illegal value for lineNumber');
}
return this._buffer.getLineFirstNonWhitespaceColumn(lineNumber);
}
getLineLastNonWhitespaceColumn(lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new BugIndicatingError('Illegal value for lineNumber');
}
return this._buffer.getLineLastNonWhitespaceColumn(lineNumber);
}
/**
* Validates `range` is within buffer bounds, but allows it to sit in between surrogate pairs, etc.
* Will try to not allocate if possible.
*/
_validateRangeRelaxedNoAllocations(range) {
const linesCount = this._buffer.getLineCount();
const initialStartLineNumber = range.startLineNumber;
const initialStartColumn = range.startColumn;
let startLineNumber = Math.floor((typeof initialStartLineNumber === 'number' && !isNaN(initialStartLineNumber)) ? initialStartLineNumber : 1);
let startColumn = Math.floor((typeof initialStartColumn === 'number' && !isNaN(initialStartColumn)) ? initialStartColumn : 1);
if (startLineNumber < 1) {
startLineNumber = 1;
startColumn = 1;
}
else if (startLineNumber > linesCount) {
startLineNumber = linesCount;
startColumn = this.getLineMaxColumn(startLineNumber);
}
else {
if (startColumn <= 1) {
startColumn = 1;
}
else {
const maxColumn = this.getLineMaxColumn(startLineNumber);
if (startColumn >= maxColumn) {
startColumn = maxColumn;
}
}
}
const initialEndLineNumber = range.endLineNumber;
const initialEndColumn = range.endColumn;
let endLineNumber = Math.floor((typeof initialEndLineNumber === 'number' && !isNaN(initialEndLineNumber)) ? initialEndLineNumber : 1);
let endColumn = Math.floor((typeof initialEndColumn === 'number' && !isNaN(initialEndColumn)) ? initialEndColumn : 1);
if (endLineNumber < 1) {
endLineNumber = 1;
endColumn = 1;
}
else if (endLineNumber > linesCount) {
endLineNumber = linesCount;
endColumn = this.getLineMaxColumn(endLineNumber);
}
else {
if (endColumn <= 1) {
endColumn = 1;
}
else {
const maxColumn = this.getLineMaxColumn(endLineNumber);
if (endColumn >= maxColumn) {
endColumn = maxColumn;
}
}
}
if (initialStartLineNumber === startLineNumber
&& initialStartColumn === startColumn
&& initialEndLineNumber === endLineNumber
&& initialEndColumn === endColumn
&& range instanceof Range
&& !(range instanceof Selection)) {
return range;
}
return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
}
_isValidPosition(lineNumber, column, validationType) {
if (typeof lineNumber !== 'number' || typeof column !== 'number') {
return false;
}
if (isNaN(lineNumber) || isNaN(column)) {
return false;
}
if (lineNumber < 1 || column < 1) {
return false;
}
if ((lineNumber | 0) !== lineNumber || (column | 0) !== column) {
return false;
}
const lineCount = this._buffer.getLineCount();
if (lineNumber > lineCount) {
return false;
}
if (column === 1) {
return true;
}
const maxColumn = this.getLineMaxColumn(lineNumber);
if (column > maxColumn) {
return false;
}
if (validationType === 1 /* StringOffsetValidationType.SurrogatePairs */) {
// !!At this point, column > 1
const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2);
if (strings.isHighSurrogate(charCodeBefore)) {
return false;
}
}
return true;
}
_validatePosition(_lineNumber, _column, validationType) {
const lineNumber = Math.floor((typeof _lineNumber === 'number' && !isNaN(_lineNumber)) ? _lineNumber : 1);
const column = Math.floor((typeof _column === 'number' && !isNaN(_column)) ? _column : 1);
const lineCount = this._buffer.getLineCount();
if (lineNumber < 1) {
return new Position(1, 1);
}
if (lineNumber > lineCount) {
return new Position(lineCount, this.getLineMaxColumn(lineCount));
}
if (column <= 1) {
return new Position(lineNumber, 1);
}
const maxColumn = this.getLineMaxColumn(lineNumber);
if (column >= maxColumn) {
return new Position(lineNumber, maxColumn);
}
if (validationType === 1 /* StringOffsetValidationType.SurrogatePairs */) {
// If the position would end up in the middle of a high-low surrogate pair,
// we move it to before the pair
// !!At this point, column > 1
const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2);
if (strings.isHighSurrogate(charCodeBefore)) {
return new Position(lineNumber, column - 1);
}
}
return new Position(lineNumber, column);
}
validatePosition(position) {
const validationType = 1 /* StringOffsetValidationType.SurrogatePairs */;
this._assertNotDisposed();
// Avoid object allocation and cover most likely case
if (position instanceof Position) {
if (this._isValidPosition(position.lineNumber, position.column, validationType)) {
return position;
}
}
return this._validatePosition(position.lineNumber, position.column, validationType);
}
isValidRange(range) {
return this._isValidRange(range, 1 /* StringOffsetValidationType.SurrogatePairs */);
}
_isValidRange(range, validationType) {
const startLineNumber = range.startLineNumber;
const startColumn = range.startColumn;
const endLineNumber = range.endLineNumber;
const endColumn = range.endColumn;
if (!this._isValidPosition(startLineNumber, startColumn, 0 /* StringOffsetValidationType.Relaxed */)) {
return false;
}
if (!this._isValidPosition(endLineNumber, endColumn, 0 /* StringOffsetValidationType.Relaxed */)) {
return false;
}
if (validationType === 1 /* StringOffsetValidationType.SurrogatePairs */) {
const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0);
const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0);
const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart);
const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd);
if (!startInsideSurrogatePair && !endInsideSurrogatePair) {
return true;
}
return false;
}
return true;
}
validateRange(_range) {
const validationType = 1 /* StringOffsetValidationType.SurrogatePairs */;
this._assertNotDisposed();
// Avoid object allocation and cover most likely case
if ((_range instanceof Range) && !(_range instanceof Selection)) {
if (this._isValidRange(_range, validationType)) {
return _range;
}
}
const start = this._validatePosition(_range.startLineNumber, _range.startColumn, 0 /* StringOffsetValidationType.Relaxed */);
const end = this._validatePosition(_range.endLineNumber, _range.endColumn, 0 /* StringOffsetValidationType.Relaxed */);
const startLineNumber = start.lineNumber;
const startColumn = start.column;
const endLineNumber = end.lineNumber;
const endColumn = end.column;
if (validationType === 1 /* StringOffsetValidationType.SurrogatePairs */) {
const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0);
const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0);
const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart);
const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd);
if (!startInsideSurrogatePair && !endInsideSurrogatePair) {
return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
}
if (startLineNumber === endLineNumber && startColumn === endColumn) {
// do not expand a collapsed range, simply move it to a valid location
return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn - 1);
}
if (startInsideSurrogatePair && endInsideSurrogatePair) {
// expand range at both ends
return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn + 1);
}
if (startInsideSurrogatePair) {
// only expand range at the start
return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn);
}
// only expand range at the end
return new Range(startLineNumber, startColumn, endLineNumber, endColumn + 1);
}
return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
}
modifyPosition(rawPosition, offset) {
this._assertNotDisposed();
const candidate = this.getOffsetAt(rawPosition) + offset;
return this.getPositionAt(Math.min(this._buffer.getLength(), Math.max(0, candidate)));
}
getFullModelRange() {
this._assertNotDisposed();
const lineCount = this.getLineCount();
return new Range(1, 1, lineCount, this.getLineMaxColumn(lineCount));
}
findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount) {
return this._buffer.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
}
findMatches(searchString, rawSearchScope, isRegex, matchCase, wordSeparators, captureMatches, limitResultCount = LIMIT_FIND_COUNT) {
this._assertNotDisposed();
let searchRanges = null;
if (rawSearchScope !== null) {
if (!Array.isArray(rawSearchScope)) {
rawSearchScope = [rawSearchScope];
}
if (rawSearchScope.every((searchScope) => Range.isIRange(searchScope))) {
searchRanges = rawSearchScope.map((searchScope) => this.validateRange(searchScope));
}
}
if (searchRanges === null) {
searchRanges = [this.getFullModelRange()];
}
searchRanges = searchRanges.sort((d1, d2) => d1.startLineNumber - d2.startLineNumber || d1.startColumn - d2.startColumn);
const uniqueSearchRanges = [];
uniqueSearchRanges.push(searchRanges.reduce((prev, curr) => {
if (Range.areIntersecting(prev, curr)) {
return prev.plusRange(curr);
}
uniqueSearchRanges.push(prev);
return curr;
}));
let matchMapper;
if (!isRegex && searchString.indexOf('\n') < 0) {
// not regex, not multi line
const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
const searchData = searchParams.parseSearchRequest();
if (!searchData) {
return [];
}
matchMapper = (searchRange) => this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
}
else {
matchMapper = (searchRange) => TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount);
}
return uniqueSearchRanges.map(matchMapper).reduce((arr, matches) => arr.concat(matches), []);
}
findNextMatch(searchString, rawSearchStart, isRegex, matchCase, wordSeparators, captureMatches) {
this._assertNotDisposed();
const searchStart = this.validatePosition(rawSearchStart);
if (!isRegex && searchString.indexOf('\n') < 0) {
const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
const searchData = searchParams.parseSearchRequest();
if (!searchData) {
return null;
}
const lineCount = this.getLineCount();
let searchRange = new Range(searchStart.lineNumber, searchStart.column, lineCount, this.getLineMaxColumn(lineCount));
let ret = this.findMatchesLineByLine(searchRange, searchData, captureMatches, 1);
TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
if (ret.length > 0) {
return ret[0];
}
searchRange = new Range(1, 1, searchStart.lineNumber, this.getLineMaxColumn(searchStart.lineNumber));
ret = this.findMatchesLineByLine(searchRange, searchData, captureMatches, 1);
if (ret.length > 0) {
return ret[0];
}
return null;
}
return TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
}
findPreviousMatch(searchString, rawSearchStart, isRegex, matchCase, wordSeparators, captureMatches) {
this._assertNotDisposed();
const searchStart = this.validatePosition(rawSearchStart);
return TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
}
//#endregion
//#region Editing
pushStackElement() {
this._commandManager.pushStackElement();
}
popStackElement() {
this._commandManager.popStackElement();
}
pushEOL(eol) {
const currentEOL = (this.getEOL() === '\n' ? 0 /* model.EndOfLineSequence.LF */ : 1 /* model.EndOfLineSequence.CRLF */);
if (currentEOL === eol) {
return;
}
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
if (this._initialUndoRedoSnapshot === null) {
this._initialUndoRedoSnapshot = this._undoRedoService.createSnapshot(this.uri);
}
this._commandManager.pushEOL(eol);
}
finally {
this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit();
}
}
_validateEditOperation(rawOperation) {
if (rawOperation instanceof model.ValidAnnotatedEditOperation) {
return rawOperation;
}
return new model.ValidAnnotatedEditOperation(rawOperation.identifier || null, this.validateRange(rawOperation.range), rawOperation.text, rawOperation.forceMoveMarkers || false, rawOperation.isAutoWhitespaceEdit || false, rawOperation._isTracked || false);
}
_validateEditOperations(rawOperations) {
const result = [];
for (let i = 0, len = rawOperations.length; i < len; i++) {
result[i] = this._validateEditOperation(rawOperations[i]);
}
return result;
}
pushEditOperations(beforeCursorState, editOperations, cursorStateComputer, group, reason) {
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
return this._pushEditOperations(beforeCursorState, this._validateEditOperations(editOperations), cursorStateComputer, group, reason);
}
finally {
this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit();
}
}
_pushEditOperations(beforeCursorState, editOperations, cursorStateComputer, group, reason) {
if (this._options.trimAutoWhitespace && this._trimAutoWhitespaceLines) {
// Go through each saved line number and insert a trim whitespace edit
// if it is safe to do so (no conflicts with other edits).
const incomingEdits = editOperations.map((op) => {
return {
range: this.validateRange(op.range),
text: op.text
};
});
// Sometimes, auto-formatters change ranges automatically which can cause undesired auto whitespace trimming near the cursor
// We'll use the following heuristic: if the edits occur near the cursor, then it's ok to trim auto whitespace
let editsAreNearCursors = true;
if (beforeCursorState) {
for (let i = 0, len = beforeCursorState.length; i < len; i++) {
const sel = beforeCursorState[i];
let foundEditNearSel = false;
for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {
const editRange = incomingEdits[j].range;
const selIsAbove = editRange.startLineNumber > sel.endLineNumber;
const selIsBelow = sel.startLineNumber > editRange.endLineNumber;
if (!selIsAbove && !selIsBelow) {
foundEditNearSel = true;
break;
}
}
if (!foundEditNearSel) {
editsAreNearCursors = false;
break;
}
}
}
if (editsAreNearCursors) {
for (let i = 0, len = this._trimAutoWhitespaceLines.length; i < len; i++) {
const trimLineNumber = this._trimAutoWhitespaceLines[i];
const maxLineColumn = this.getLineMaxColumn(trimLineNumber);
let allowTrimLine = true;
for (let j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {
const editRange = incomingEdits[j].range;
const editText = incomingEdits[j].text;
if (trimLineNumber < editRange.startLineNumber || trimLineNumber > editRange.endLineNumber) {
// `trimLine` is completely outside this edit
continue;
}
// At this point:
// editRange.startLineNumber <= trimLine <= editRange.endLineNumber
if (trimLineNumber === editRange.startLineNumber && editRange.startColumn === maxLineColumn
&& editRange.isEmpty() && editText && editText.length > 0 && editText.charAt(0) === '\n') {
// This edit inserts a new line (and maybe other text) after `trimLine`
continue;
}
if (trimLineNumber === editRange.startLineNumber && editRange.startColumn === 1
&& editRange.isEmpty() && editText && editText.length > 0 && editText.charAt(editText.length - 1) === '\n') {
// This edit inserts a new line (and maybe other text) before `trimLine`
continue;
}
// Looks like we can't trim this line as it would interfere with an incoming edit
allowTrimLine = false;
break;
}
if (allowTrimLine) {
const trimRange = new Range(trimLineNumber, 1, trimLineNumber, maxLineColumn);
editOperations.push(new model.ValidAnnotatedEditOperation(null, trimRange, null, false, false, false));
}
}
}
this._trimAutoWhitespaceLines = null;
}
if (this._initialUndoRedoSnapshot === null) {
this._initialUndoRedoSnapshot = this._undoRedoService.createSnapshot(this.uri);
}
return this._commandManager.pushEditOperation(beforeCursorState, editOperations, cursorStateComputer, group, reason);
}
_applyUndo(changes, eol, resultingAlternativeVersionId, resultingSelection) {
const edits = changes.map((change) => {
const rangeStart = this.getPositionAt(change.newPosition);
const rangeEnd = this.getPositionAt(change.newEnd);
return {
range: new Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column),
text: change.oldText
};
});
this._applyUndoRedoEdits(edits, eol, true, false, resultingAlternativeVersionId, resultingSelection);
}
_applyRedo(changes, eol, resultingAlternativeVersionId, resultingSelection) {
const edits = changes.map((change) => {
const rangeStart = this.getPositionAt(change.oldPosition);
const rangeEnd = this.getPositionAt(change.oldEnd);
return {
range: new Range(rangeStart.lineNumber, rangeStart.column, rangeEnd.lineNumber, rangeEnd.column),
text: change.newText
};
});
this._applyUndoRedoEdits(edits, eol, false, true, resultingAlternativeVersionId, resultingSelection);
}
_applyUndoRedoEdits(edits, eol, isUndoing, isRedoing, resultingAlternativeVersionId, resultingSelection) {
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
this._isUndoing = isUndoing;
this._isRedoing = isRedoing;
this.applyEdits(edits, false);
this.setEOL(eol);
this._overwriteAlternativeVersionId(resultingAlternativeVersionId);
}
finally {
this._isUndoing = false;
this._isRedoing = false;
this._eventEmitter.endDeferredEmit(resultingSelection);
this._onDidChangeDecorations.endDeferredEmit();
}
}
applyEdits(rawOperations, computeUndoEdits, reason) {
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
const operations = this._validateEditOperations(rawOperations);
return this._doApplyEdits(operations, computeUndoEdits ?? false, reason ?? EditSources.applyEdits());
}
finally {
this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit();
}
}
_doApplyEdits(rawOperations, computeUndoEdits, reason) {
const oldLineCount = this._buffer.getLineCount();
const result = this._buffer.applyEdits(rawOperations, this._options.trimAutoWhitespace, computeUndoEdits);
const newLineCount = this._buffer.getLineCount();
const contentChanges = result.changes;
this._trimAutoWhitespaceLines = result.trimAutoWhitespaceLineNumbers;
if (contentChanges.length !== 0) {
// We do a first pass to update decorations
// because we want to read decorations in the second pass
// where we will emit content change events
// and we want to read the final decorations
for (let i = 0, len = contentChanges.length; i < len; i++) {
const change = contentChanges[i];
this._decorationsTree.acceptReplace(change.rangeOffset, change.rangeLength, change.text.length, change.forceMoveMarkers);
}
const rawContentChanges = [];
this._increaseVersionId();
let lineCount = oldLineCount;
for (let i = 0, len = contentChanges.length; i < len; i++) {
const change = contentChanges[i];
const [eolCount] = countEOL(change.text);
this._onDidChangeDecorations.fire();
const startLineNumber = change.range.startLineNumber;
const endLineNumber = change.range.endLineNumber;
const deletingLinesCnt = endLineNumber - startLineNumber;
const insertingLinesCnt = eolCount;
const editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt);
const changeLineCountDelta = (insertingLinesCnt - deletingLinesCnt);
const currentEditStartLineNumber = newLineCount - lineCount - changeLineCountDelta + startLineNumber;
const firstEditLineNumber = currentEditStartLineNumber;
const lastInsertedLineNumber = currentEditStartLineNumber + insertingLinesCnt;
const decorationsWithInjectedTextInEditedRange = this._decorationsTree.getInjectedTextInInterval(this, this.getOffsetAt(new Position(firstEditLineNumber, 1)), this.getOffsetAt(new Position(lastInsertedLineNumber, this.getLineMaxColumn(lastInsertedLineNumber))), 0);
const injectedTextInEditedRange = LineInjectedText.fromDecorations(decorationsWithInjectedTextInEditedRange);
const injectedTextInEditedRangeQueue = new ArrayQueue(injectedTextInEditedRange);
for (let j = editingLinesCnt; j >= 0; j--) {
const editLineNumber = startLineNumber + j;
const currentEditLineNumber = currentEditStartLineNumber + j;
injectedTextInEditedRangeQueue.takeFromEndWhile(r => r.lineNumber > currentEditLineNumber);
const decorationsInCurrentLine = injectedTextInEditedRangeQueue.takeFromEndWhile(r => r.lineNumber === currentEditLineNumber);
rawContentChanges.push(new ModelRawLineChanged(editLineNumber, this.getLineContent(currentEditLineNumber), decorationsInCurrentLine));
}
if (editingLinesCnt < deletingLinesCnt) {
// Must delete some lines
const spliceStartLineNumber = startLineNumber + editingLinesCnt;
rawContentChanges.push(new ModelRawLinesDeleted(spliceStartLineNumber + 1, endLineNumber));
}
if (editingLinesCnt < insertingLinesCnt) {
const injectedTextInEditedRangeQueue = new ArrayQueue(injectedTextInEditedRange);
// Must insert some lines
const spliceLineNumber = startLineNumber + editingLinesCnt;
const cnt = insertingLinesCnt - editingLinesCnt;
const fromLineNumber = newLineCount - lineCount - cnt + spliceLineNumber + 1;
const injectedTexts = [];
const newLines = [];
for (let i = 0; i < cnt; i++) {
const lineNumber = fromLineNumber + i;
newLines[i] = this.getLineContent(lineNumber);
injectedTextInEditedRangeQueue.takeWhile(r => r.lineNumber < lineNumber);
injectedTexts[i] = injectedTextInEditedRangeQueue.takeWhile(r => r.lineNumber === lineNumber);
}
rawContentChanges.push(new ModelRawLinesInserted(spliceLineNumber + 1, startLineNumber + insertingLinesCnt, newLines, injectedTexts));
}
lineCount += changeLineCountDelta;
}
this._emitContentChangedEvent(new ModelRawContentChangedEvent(rawContentChanges, this.getVersionId(), this._isUndoing, this._isRedoing), {
changes: contentChanges,
eol: this._buffer.getEOL(),
isEolChange: false,
versionId: this.getVersionId(),
isUndoing: this._isUndoing,
isRedoing: this._isRedoing,
isFlush: false,
detailedReasons: [reason],
detailedReasonsChangeLengths: [contentChanges.length],
});
}
return (result.reverseEdits === null ? undefined : result.reverseEdits);
}
undo() {
return this._undoRedoService.undo(this.uri);
}
canUndo() {
return this._undoRedoService.canUndo(this.uri);
}
redo() {
return this._undoRedoService.redo(this.uri);
}
canRedo() {
return this._undoRedoService.canRedo(this.uri);
}
//#endregion
//#region Decorations
handleBeforeFireDecorationsChangedEvent(affectedInjectedTextLines, affectedLineHeights, affectedFontLines) {
// This is called before the decoration changed event is fired.
if (affectedInjectedTextLines && affectedInjectedTextLines.size > 0) {
const affectedLines = Array.from(affectedInjectedTextLines);
const lineChangeEvents = affectedLines.map(lineNumber => new ModelRawLineChanged(lineNumber, this.getLineContent(lineNumber), this._getInjectedTextInLine(lineNumber)));
this._onDidChangeInjectedText.fire(new ModelInjectedTextChangedEvent(lineChangeEvents));
}
if (affectedLineHeights && affectedLineHeights.size > 0) {
const affectedLines = Array.from(affectedLineHeights);
const lineHeightChangeEvent = affectedLines.map(specialLineHeightChange => new ModelLineHeightChanged(specialLineHeightChange.ownerId, specialLineHeightChange.decorationId, specialLineHeightChange.lineNumber, specialLineHeightChange.lineHeight));
this._onDidChangeLineHeight.fire(new ModelLineHeightChangedEvent(lineHeightChangeEvent));
}
if (affectedFontLines && affectedFontLines.size > 0) {
const affectedLines = Array.from(affectedFontLines);
const fontChangeEvent = affectedLines.map(fontChange => new ModelFontChanged(fontChange.ownerId, fontChange.lineNumber));
this._onDidChangeFont.fire(new ModelFontChangedEvent(fontChangeEvent));
}
}
changeDecorations(callback, ownerId = 0) {
this._assertNotDisposed();
try {
this._onDidChangeDecorations.beginDeferredEmit();
return this._changeDecorations(ownerId, callback);
}
finally {
this._onDidChangeDecorations.endDeferredEmit();
}
}
_changeDecorations(ownerId, callback) {
const changeAccessor = {
addDecoration: (range, options) => {
return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0];
},
changeDecoration: (id, newRange) => {
this._changeDecorationImpl(ownerId, id, newRange);
},
changeDecorationOptions: (id, options) => {
this._changeDecorationOptionsImpl(ownerId, id, _normalizeOptions(options));
},
removeDecoration: (id) => {
this._deltaDecorationsImpl(ownerId, [id], []);
},
deltaDecorations: (oldDecorations, newDecorations) => {
if (oldDecorations.length === 0 && newDecorations.length === 0) {
// nothing to do
return [];
}
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
}
};
let result = null;
try {
result = callback(changeAccessor);
}
catch (e) {
onUnexpectedError(e);
}
// Invalidate change accessor
changeAccessor.addDecoration = invalidFunc;
changeAccessor.changeDecoration = invalidFunc;
changeAccessor.changeDecorationOptions = invalidFunc;
changeAccessor.removeDecoration = invalidFunc;
changeAccessor.deltaDecorations = invalidFunc;
return result;
}
deltaDecorations(oldDecorations, newDecorations, ownerId = 0) {
this._assertNotDisposed();
if (!oldDecorations) {
oldDecorations = [];
}
if (oldDecorations.length === 0 && newDecorations.length === 0) {
// nothing to do
return [];
}
try {
this._deltaDecorationCallCnt++;
if (this._deltaDecorationCallCnt > 1) {
console.warn(`Invoking deltaDecorations recursively could lead to leaking decorations.`);
onUnexpectedError(new Error(`Invoking deltaDecorations recursively could lead to leaking decorations.`));
}
this._onDidChangeDecorations.beginDeferredEmit();
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
}
finally {
this._onDidChangeDecorations.endDeferredEmit();
this._deltaDecorationCallCnt--;
}
}
_getTrackedRange(id) {
return this.getDecorationRange(id);
}
_setTrackedRange(id, newRange, newStickiness) {
const node = (id ? this._decorations[id] : null);
if (!node) {
if (!newRange) {
// node doesn't exist, the request is to delete => nothing to do
return null;
}
// node doesn't exist, the request is to set => add the tracked range
return this._deltaDecorationsImpl(0, [], [{ range: newRange, options: TRACKED_RANGE_OPTIONS[newStickiness] }], true)[0];
}
if (!newRange) {
// node exists, the request is to delete => delete node
this._decorationsTree.delete(node);
delete this._decorations[node.id];
return null;
}
// node exists, the request is to set => change the tracked range and its options
const range = this._validateRangeRelaxedNoAllocations(newRange);
const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
this._decorationsTree.delete(node);
node.reset(this.getVersionId(), startOffset, endOffset, range);
node.setOptions(TRACKED_RANGE_OPTIONS[newStickiness]);
this._decorationsTree.insert(node);
return node.id;
}
removeAllDecorationsWithOwnerId(ownerId) {
if (this._isDisposed) {
return;
}
const nodes = this._decorationsTree.collectNodesFromOwner(ownerId);
for (let i = 0, len = nodes.length; i < len; i++) {
const node = nodes[i];
this._decorationsTree.delete(node);
delete this._decorations[node.id];
}
}
getDecorationOptions(decorationId) {
const node = this._decorations[decorationId];
if (!node) {
return null;
}
return node.options;
}
getDecorationRange(decorationId) {
const node = this._decorations[decorationId];
if (!node) {
return null;
}
return this._decorationsTree.getNodeRange(this, node);
}
getLineDecorations(lineNumber, ownerId = 0, filterOutValidation = false, filterFontDecorations = false) {
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
return [];
}
return this.getLinesDecorations(lineNumber, lineNumber, ownerId, filterOutValidation, filterFontDecorations);
}
getLinesDecorations(_startLineNumber, _endLineNumber, ownerId = 0, filterOutValidation = false, filterFontDecorations = false, onlyMarginDecorations = false) {
const lineCount = this.getLineCount();
const startLineNumber = Math.min(lineCount, Math.max(1, _startLineNumber));
const endLineNumber = Math.min(lineCount, Math.max(1, _endLineNumber));
const endColumn = this.getLineMaxColumn(endLineNumber);
const range = new Range(startLineNumber, 1, endLineNumber, endColumn);
const decorations = this._getDecorationsInRange(range, ownerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations);
pushMany(decorations, this._decorationProvider.getDecorationsInRange(range, ownerId, filterOutValidation));
return decorations;
}
getDecorationsInRange(range, ownerId = 0, filterOutValidation = false, filterFontDecorations = false, onlyMinimapDecorations = false, onlyMarginDecorations = false) {
const validatedRange = this.validateRange(range);
const decorations = this._getDecorationsInRange(validatedRange, ownerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations);
pushMany(decorations, this._decorationProvider.getDecorationsInRange(validatedRange, ownerId, filterOutValidation, onlyMinimapDecorations));
return decorations;
}
getOverviewRulerDecorations(ownerId = 0, filterOutValidation = false, filterFontDecorations = false) {
return this._decorationsTree.getAll(this, ownerId, filterOutValidation, filterFontDecorations, true, false);
}
getInjectedTextDecorations(ownerId = 0) {
return this._decorationsTree.getAllInjectedText(this, ownerId);
}
getCustomLineHeightsDecorations(ownerId = 0) {
return this._decorationsTree.getAllCustomLineHeights(this, ownerId);
}
_getInjectedTextInLine(lineNumber) {
const startOffset = this._buffer.getOffsetAt(lineNumber, 1);
const endOffset = startOffset + this._buffer.getLineLength(lineNumber);
const result = this._decorationsTree.getInjectedTextInInterval(this, startOffset, endOffset, 0);
return LineInjectedText.fromDecorations(result).filter(t => t.lineNumber === lineNumber);
}
getFontDecorationsInRange(range, ownerId = 0) {
const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
return this._decorationsTree.getFontDecorationsInInterval(this, startOffset, endOffset, ownerId);
}
getAllDecorations(ownerId = 0, filterOutValidation = false, filterFontDecorations = false) {
let result = this._decorationsTree.getAll(this, ownerId, filterOutValidation, filterFontDecorations, false, false);
result = result.concat(this._decorationProvider.getAllDecorations(ownerId, filterOutValidation));
return result;
}
getAllMarginDecorations(ownerId = 0) {
return this._decorationsTree.getAll(this, ownerId, false, false, false, true);
}
_getDecorationsInRange(filterRange, filterOwnerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations) {
const startOffset = this._buffer.getOffsetAt(filterRange.startLineNumber, filterRange.startColumn);
const endOffset = this._buffer.getOffsetAt(filterRange.endLineNumber, filterRange.endColumn);
return this._decorationsTree.getAllInInterval(this, startOffset, endOffset, filterOwnerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations);
}
getRangeAt(start, end) {
return this._buffer.getRangeAt(start, end - start);
}
_changeDecorationImpl(ownerId, decorationId, _range) {
const node = this._decorations[decorationId];
if (!node) {
return;
}
if (node.options.after) {
const oldRange = this.getDecorationRange(decorationId);
this._onDidChangeDecorations.recordLineAffectedByInjectedText(oldRange.endLineNumber);
}
if (node.options.before) {
const oldRange = this.getDecorationRange(decorationId);
this._onDidChangeDecorations.recordLineAffectedByInjectedText(oldRange.startLineNumber);
}
if (node.options.lineHeight !== null) {
const oldRange = this.getDecorationRange(decorationId);
this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, oldRange.startLineNumber, null);
}
if (node.options.affectsFont) {
const oldRange = this.getDecorationRange(decorationId);
this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, oldRange.startLineNumber);
}
const range = this._validateRangeRelaxedNoAllocations(_range);
const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
this._decorationsTree.delete(node);
node.reset(this.getVersionId(), startOffset, endOffset, range);
this._decorationsTree.insert(node);
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
if (node.options.after) {
this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.endLineNumber);
}
if (node.options.before) {
this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.startLineNumber);
}
if (node.options.lineHeight !== null) {
this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, range.startLineNumber, node.options.lineHeight);
}
if (node.options.affectsFont) {
this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, range.startLineNumber);
}
}
_changeDecorationOptionsImpl(ownerId, decorationId, options) {
const node = this._decorations[decorationId];
if (!node) {
return;
}
const nodeWasInOverviewRuler = (node.options.overviewRuler && node.options.overviewRuler.color ? true : false);
const nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false);
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
this._onDidChangeDecorations.checkAffectedAndFire(options);
if (node.options.after || options.after) {
const nodeRange = this._decorationsTree.getNodeRange(this, node);
this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.endLineNumber);
}
if (node.options.before || options.before) {
const nodeRange = this._decorationsTree.getNodeRange(this, node);
this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.startLineNumber);
}
if (node.options.lineHeight !== null || options.lineHeight !== null) {
const nodeRange = this._decorationsTree.getNodeRange(this, node);
this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, nodeRange.startLineNumber, options.lineHeight);
}
if (node.options.affectsFont || options.affectsFont) {
const nodeRange = this._decorationsTree.getNodeRange(this, node);
this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, decorationId, nodeRange.startLineNumber);
}
const movedInOverviewRuler = nodeWasInOverviewRuler !== nodeIsInOverviewRuler;
const changedWhetherInjectedText = isOptionsInjectedText(options) !== isNodeInjectedText(node);
if (movedInOverviewRuler || changedWhetherInjectedText) {
this._decorationsTree.delete(node);
node.setOptions(options);
this._decorationsTree.insert(node);
}
else {
node.setOptions(options);
}
}
_deltaDecorationsImpl(ownerId, oldDecorationsIds, newDecorations, suppressEvents = false) {
const versionId = this.getVersionId();
const oldDecorationsLen = oldDecorationsIds.length;
let oldDecorationIndex = 0;
const newDecorationsLen = newDecorations.length;
let newDecorationIndex = 0;
this._onDidChangeDecorations.beginDeferredEmit();
try {
const result = new Array(newDecorationsLen);
while (oldDecorationIndex < oldDecorationsLen || newDecorationIndex < newDecorationsLen) {
let node = null;
if (oldDecorationIndex < oldDecorationsLen) {
// (1) get ourselves an old node
let decorationId;
do {
decorationId = oldDecorationsIds[oldDecorationIndex++];
node = this._decorations[decorationId];
} while (!node && oldDecorationIndex < oldDecorationsLen);
// (2) remove the node from the tree (if it exists)
if (node) {
if (node.options.after) {
const nodeRange = this._decorationsTree.getNodeRange(this, node);
this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.endLineNumber);
}
if (node.options.before) {
const nodeRange = this._decorationsTree.getNodeRange(this, node);
this._onDidChangeDecorations.recordLineAffectedByInjectedText(nodeRange.startLineNumber);
}
if (node.options.lineHeight !== null) {
const nodeRange = this._decorationsTree.getNodeRange(this, node);
this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, decorationId, nodeRange.startLineNumber, null);
}
if (node.options.affectsFont) {
const nodeRange = this._decorationsTree.getNodeRange(this, node);
this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, decorationId, nodeRange.startLineNumber);
}
this._decorationsTree.delete(node);
if (!suppressEvents) {
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
}
}
}
if (newDecorationIndex < newDecorationsLen) {
// (3) create a new node if necessary
if (!node) {
const internalDecorationId = (++this._lastDecorationId);
const decorationId = `${this._instanceId};${internalDecorationId}`;
node = new IntervalNode(decorationId, 0, 0);
this._decorations[decorationId] = node;
}
// (4) initialize node
const newDecoration = newDecorations[newDecorationIndex];
const range = this._validateRangeRelaxedNoAllocations(newDecoration.range);
const options = _normalizeOptions(newDecoration.options);
const startOffset = this._buffer.getOffsetAt(range.startLineNumber, range.startColumn);
const endOffset = this._buffer.getOffsetAt(range.endLineNumber, range.endColumn);
node.ownerId = ownerId;
node.reset(versionId, startOffset, endOffset, range);
node.setOptions(options);
if (node.options.after) {
this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.endLineNumber);
}
if (node.options.before) {
this._onDidChangeDecorations.recordLineAffectedByInjectedText(range.startLineNumber);
}
if (node.options.lineHeight !== null) {
this._onDidChangeDecorations.recordLineAffectedByLineHeightChange(ownerId, node.id, range.startLineNumber, node.options.lineHeight);
}
if (node.options.affectsFont) {
this._onDidChangeDecorations.recordLineAffectedByFontChange(ownerId, node.id, range.startLineNumber);
}
if (!suppressEvents) {
this._onDidChangeDecorations.checkAffectedAndFire(options);
}
this._decorationsTree.insert(node);
result[newDecorationIndex] = node.id;
newDecorationIndex++;
}
else {
if (node) {
delete this._decorations[node.id];
}
}
}
return result;
}
finally {
this._onDidChangeDecorations.endDeferredEmit();
}
}
//#endregion
//#region Tokenization
// TODO move them to the tokenization part.
getLanguageId() {
return this.tokenization.getLanguageId();
}
setLanguage(languageIdOrSelection, source) {
if (typeof languageIdOrSelection === 'string') {
this._languageSelectionListener.clear();
this._setLanguage(languageIdOrSelection, source);
}
else {
this._languageSelectionListener.value = languageIdOrSelection.onDidChange(() => this._setLanguage(languageIdOrSelection.languageId, source));
this._setLanguage(languageIdOrSelection.languageId, source);
}
}
_setLanguage(languageId, source) {
this.tokenization.setLanguageId(languageId, source);
this._languageService.requestRichLanguageFeatures(languageId);
}
getLanguageIdAtPosition(lineNumber, column) {
return this.tokenization.getLanguageIdAtPosition(lineNumber, column);
}
getWordAtPosition(position) {
return this._tokenizationTextModelPart.getWordAtPosition(position);
}
getWordUntilPosition(position) {
return this._tokenizationTextModelPart.getWordUntilPosition(position);
}
//#endregion
normalizePosition(position, affinity) {
return position;
}
/**
* Gets the column at which indentation stops at a given line.
* @internal
*/
getLineIndentColumn(lineNumber) {
// Columns start with 1.
return indentOfLine(this.getLineContent(lineNumber)) + 1;
}
toString() {
return `TextModel(${this.uri.toString()})`;
}
};
TextModel = TextModel_1 = __decorate([
__param(4, IUndoRedoService),
__param(5, ILanguageService),
__param(6, ILanguageConfigurationService),
__param(7, IInstantiationService)
], TextModel);
export { TextModel };
export function indentOfLine(line) {
let indent = 0;
for (const c of line) {
if (c === ' ' || c === '\t') {
indent++;
}
else {
break;
}
}
return indent;
}
//#region Decorations
function isNodeInOverviewRuler(node) {
return (node.options.overviewRuler && node.options.overviewRuler.color ? true : false);
}
function isOptionsInjectedText(options) {
return !!options.after || !!options.before;
}
function isNodeInjectedText(node) {
return !!node.options.after || !!node.options.before;
}
class DecorationsTrees {
constructor() {
this._decorationsTree0 = new IntervalTree();
this._decorationsTree1 = new IntervalTree();
this._injectedTextDecorationsTree = new IntervalTree();
}
ensureAllNodesHaveRanges(host) {
this.getAll(host, 0, false, false, false, false);
}
_ensureNodesHaveRanges(host, nodes) {
for (const node of nodes) {
if (node.range === null) {
node.range = host.getRangeAt(node.cachedAbsoluteStart, node.cachedAbsoluteEnd);
}
}
return nodes;
}
getAllInInterval(host, start, end, filterOwnerId, filterOutValidation, filterFontDecorations, onlyMarginDecorations) {
const versionId = host.getVersionId();
const result = this._intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, versionId, onlyMarginDecorations);
return this._ensureNodesHaveRanges(host, result);
}
_intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations) {
const r0 = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
const r1 = this._decorationsTree1.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
const r2 = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
return r0.concat(r1).concat(r2);
}
getInjectedTextInInterval(host, start, end, filterOwnerId) {
const versionId = host.getVersionId();
const result = this._injectedTextDecorationsTree.intervalSearch(start, end, filterOwnerId, false, false, versionId, false);
return this._ensureNodesHaveRanges(host, result).filter((i) => i.options.showIfCollapsed || !i.range.isEmpty());
}
getFontDecorationsInInterval(host, start, end, filterOwnerId) {
const versionId = host.getVersionId();
const decorations = this._decorationsTree0.intervalSearch(start, end, filterOwnerId, false, false, versionId, false);
return this._ensureNodesHaveRanges(host, decorations).filter((i) => i.options.affectsFont);
}
getAllInjectedText(host, filterOwnerId) {
const versionId = host.getVersionId();
const result = this._injectedTextDecorationsTree.search(filterOwnerId, false, false, versionId, false);
return this._ensureNodesHaveRanges(host, result).filter((i) => i.options.showIfCollapsed || !i.range.isEmpty());
}
getAllCustomLineHeights(host, filterOwnerId) {
const versionId = host.getVersionId();
const result = this._search(filterOwnerId, false, false, false, versionId, false);
return this._ensureNodesHaveRanges(host, result).filter((i) => typeof i.options.lineHeight === 'number');
}
getAll(host, filterOwnerId, filterOutValidation, filterFontDecorations, overviewRulerOnly, onlyMarginDecorations) {
const versionId = host.getVersionId();
const result = this._search(filterOwnerId, filterOutValidation, filterFontDecorations, overviewRulerOnly, versionId, onlyMarginDecorations);
return this._ensureNodesHaveRanges(host, result);
}
_search(filterOwnerId, filterOutValidation, filterFontDecorations, overviewRulerOnly, cachedVersionId, onlyMarginDecorations) {
if (overviewRulerOnly) {
return this._decorationsTree1.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
}
else {
const r0 = this._decorationsTree0.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
const r1 = this._decorationsTree1.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
const r2 = this._injectedTextDecorationsTree.search(filterOwnerId, filterOutValidation, filterFontDecorations, cachedVersionId, onlyMarginDecorations);
return r0.concat(r1).concat(r2);
}
}
collectNodesFromOwner(ownerId) {
const r0 = this._decorationsTree0.collectNodesFromOwner(ownerId);
const r1 = this._decorationsTree1.collectNodesFromOwner(ownerId);
const r2 = this._injectedTextDecorationsTree.collectNodesFromOwner(ownerId);
return r0.concat(r1).concat(r2);
}
collectNodesPostOrder() {
const r0 = this._decorationsTree0.collectNodesPostOrder();
const r1 = this._decorationsTree1.collectNodesPostOrder();
const r2 = this._injectedTextDecorationsTree.collectNodesPostOrder();
return r0.concat(r1).concat(r2);
}
insert(node) {
if (isNodeInjectedText(node)) {
this._injectedTextDecorationsTree.insert(node);
}
else if (isNodeInOverviewRuler(node)) {
this._decorationsTree1.insert(node);
}
else {
this._decorationsTree0.insert(node);
}
}
delete(node) {
if (isNodeInjectedText(node)) {
this._injectedTextDecorationsTree.delete(node);
}
else if (isNodeInOverviewRuler(node)) {
this._decorationsTree1.delete(node);
}
else {
this._decorationsTree0.delete(node);
}
}
getNodeRange(host, node) {
const versionId = host.getVersionId();
if (node.cachedVersionId !== versionId) {
this._resolveNode(node, versionId);
}
if (node.range === null) {
node.range = host.getRangeAt(node.cachedAbsoluteStart, node.cachedAbsoluteEnd);
}
return node.range;
}
_resolveNode(node, cachedVersionId) {
if (isNodeInjectedText(node)) {
this._injectedTextDecorationsTree.resolveNode(node, cachedVersionId);
}
else if (isNodeInOverviewRuler(node)) {
this._decorationsTree1.resolveNode(node, cachedVersionId);
}
else {
this._decorationsTree0.resolveNode(node, cachedVersionId);
}
}
acceptReplace(offset, length, textLength, forceMoveMarkers) {
this._decorationsTree0.acceptReplace(offset, length, textLength, forceMoveMarkers);
this._decorationsTree1.acceptReplace(offset, length, textLength, forceMoveMarkers);
this._injectedTextDecorationsTree.acceptReplace(offset, length, textLength, forceMoveMarkers);
}
}
function cleanClassName(className) {
return className.replace(/[^a-z0-9\-_]/gi, ' ');
}
class DecorationOptions {
constructor(options) {
this.color = options.color || '';
this.darkColor = options.darkColor || '';
}
}
export class ModelDecorationOverviewRulerOptions extends DecorationOptions {
constructor(options) {
super(options);
this._resolvedColor = null;
this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center);
}
getColor(theme) {
if (!this._resolvedColor) {
if (theme.type !== 'light' && this.darkColor) {
this._resolvedColor = this._resolveColor(this.darkColor, theme);
}
else {
this._resolvedColor = this._resolveColor(this.color, theme);
}
}
return this._resolvedColor;
}
invalidateCachedColor() {
this._resolvedColor = null;
}
_resolveColor(color, theme) {
if (typeof color === 'string') {
return color;
}
const c = color ? theme.getColor(color.id) : null;
if (!c) {
return '';
}
return c.toString();
}
}
export class ModelDecorationGlyphMarginOptions {
constructor(options) {
this.position = options?.position ?? model.GlyphMarginLane.Center;
this.persistLane = options?.persistLane;
}
}
export class ModelDecorationMinimapOptions extends DecorationOptions {
constructor(options) {
super(options);
this.position = options.position;
this.sectionHeaderStyle = options.sectionHeaderStyle ?? null;
this.sectionHeaderText = options.sectionHeaderText ?? null;
}
getColor(theme) {
if (!this._resolvedColor) {
if (theme.type !== 'light' && this.darkColor) {
this._resolvedColor = this._resolveColor(this.darkColor, theme);
}
else {
this._resolvedColor = this._resolveColor(this.color, theme);
}
}
return this._resolvedColor;
}
invalidateCachedColor() {
this._resolvedColor = undefined;
}
_resolveColor(color, theme) {
if (typeof color === 'string') {
return Color.fromHex(color);
}
return theme.getColor(color.id);
}
}
export class ModelDecorationInjectedTextOptions {
static from(options) {
if (options instanceof ModelDecorationInjectedTextOptions) {
return options;
}
return new ModelDecorationInjectedTextOptions(options);
}
constructor(options) {
this.content = options.content || '';
this.tokens = options.tokens ?? null;
this.inlineClassName = options.inlineClassName || null;
this.inlineClassNameAffectsLetterSpacing = options.inlineClassNameAffectsLetterSpacing || false;
this.attachedData = options.attachedData || null;
this.cursorStops = options.cursorStops || null;
}
}
export class ModelDecorationOptions {
static register(options) {
return new ModelDecorationOptions(options);
}
static createDynamic(options) {
return new ModelDecorationOptions(options);
}
constructor(options) {
this.description = options.description;
this.blockClassName = options.blockClassName ? cleanClassName(options.blockClassName) : null;
this.blockDoesNotCollapse = options.blockDoesNotCollapse ?? null;
this.blockIsAfterEnd = options.blockIsAfterEnd ?? null;
this.blockPadding = options.blockPadding ?? null;
this.stickiness = options.stickiness || 0 /* model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges */;
this.zIndex = options.zIndex || 0;
this.className = options.className ? cleanClassName(options.className) : null;
this.shouldFillLineOnLineBreak = options.shouldFillLineOnLineBreak ?? null;
this.hoverMessage = options.hoverMessage || null;
this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || null;
this.lineNumberHoverMessage = options.lineNumberHoverMessage || null;
this.isWholeLine = options.isWholeLine || false;
this.lineHeight = options.lineHeight ? Math.min(options.lineHeight, LINE_HEIGHT_CEILING) : null;
this.fontSize = options.fontSize || null;
this.affectsFont = !!options.fontSize || !!options.fontFamily || !!options.fontWeight || !!options.fontStyle;
this.showIfCollapsed = options.showIfCollapsed || false;
this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false;
this.overviewRuler = options.overviewRuler ? new ModelDecorationOverviewRulerOptions(options.overviewRuler) : null;
this.minimap = options.minimap ? new ModelDecorationMinimapOptions(options.minimap) : null;
this.glyphMargin = options.glyphMarginClassName ? new ModelDecorationGlyphMarginOptions(options.glyphMargin) : null;
this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : null;
this.linesDecorationsClassName = options.linesDecorationsClassName ? cleanClassName(options.linesDecorationsClassName) : null;
this.lineNumberClassName = options.lineNumberClassName ? cleanClassName(options.lineNumberClassName) : null;
this.linesDecorationsTooltip = options.linesDecorationsTooltip ? strings.htmlAttributeEncodeValue(options.linesDecorationsTooltip) : null;
this.firstLineDecorationClassName = options.firstLineDecorationClassName ? cleanClassName(options.firstLineDecorationClassName) : null;
this.marginClassName = options.marginClassName ? cleanClassName(options.marginClassName) : null;
this.inlineClassName = options.inlineClassName ? cleanClassName(options.inlineClassName) : null;
this.inlineClassNameAffectsLetterSpacing = options.inlineClassNameAffectsLetterSpacing || false;
this.beforeContentClassName = options.beforeContentClassName ? cleanClassName(options.beforeContentClassName) : null;
this.afterContentClassName = options.afterContentClassName ? cleanClassName(options.afterContentClassName) : null;
this.after = options.after ? ModelDecorationInjectedTextOptions.from(options.after) : null;
this.before = options.before ? ModelDecorationInjectedTextOptions.from(options.before) : null;
this.hideInCommentTokens = options.hideInCommentTokens ?? false;
this.hideInStringTokens = options.hideInStringTokens ?? false;
this.textDirection = options.textDirection ?? null;
}
}
ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({ description: 'empty' });
/**
* The order carefully matches the values of the enum.
*/
const TRACKED_RANGE_OPTIONS = [
ModelDecorationOptions.register({ description: 'tracked-range-always-grows-when-typing-at-edges', stickiness: 0 /* model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges */ }),
ModelDecorationOptions.register({ description: 'tracked-range-never-grows-when-typing-at-edges', stickiness: 1 /* model.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges */ }),
ModelDecorationOptions.register({ description: 'tracked-range-grows-only-when-typing-before', stickiness: 2 /* model.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore */ }),
ModelDecorationOptions.register({ description: 'tracked-range-grows-only-when-typing-after', stickiness: 3 /* model.TrackedRangeStickiness.GrowsOnlyWhenTypingAfter */ }),
];
function _normalizeOptions(options) {
if (options instanceof ModelDecorationOptions) {
return options;
}
return ModelDecorationOptions.createDynamic(options);
}
class LineHeightChangingDecoration {
static toKey(obj) {
return `${obj.ownerId};${obj.decorationId};${obj.lineNumber}`;
}
constructor(ownerId, decorationId, lineNumber, lineHeight) {
this.ownerId = ownerId;
this.decorationId = decorationId;
this.lineNumber = lineNumber;
this.lineHeight = lineHeight;
}
}
class LineFontChangingDecoration {
static toKey(obj) {
return `${obj.ownerId};${obj.decorationId};${obj.lineNumber}`;
}
constructor(ownerId, decorationId, lineNumber) {
this.ownerId = ownerId;
this.decorationId = decorationId;
this.lineNumber = lineNumber;
}
}
class DidChangeDecorationsEmitter extends Disposable {
constructor(handleBeforeFire) {
super();
this.handleBeforeFire = handleBeforeFire;
this._actual = this._register(new Emitter());
this.event = this._actual.event;
this._affectedInjectedTextLines = null;
this._affectedLineHeights = null;
this._affectedFontLines = null;
this._deferredCnt = 0;
this._shouldFireDeferred = false;
this._affectsMinimap = false;
this._affectsOverviewRuler = false;
this._affectsGlyphMargin = false;
this._affectsLineNumber = false;
}
beginDeferredEmit() {
this._deferredCnt++;
}
endDeferredEmit() {
this._deferredCnt--;
if (this._deferredCnt === 0) {
if (this._shouldFireDeferred) {
this.doFire();
}
this._affectedInjectedTextLines?.clear();
this._affectedInjectedTextLines = null;
this._affectedLineHeights?.clear();
this._affectedLineHeights = null;
this._affectedFontLines?.clear();
this._affectedFontLines = null;
}
}
recordLineAffectedByInjectedText(lineNumber) {
if (!this._affectedInjectedTextLines) {
this._affectedInjectedTextLines = new Set();
}
this._affectedInjectedTextLines.add(lineNumber);
}
recordLineAffectedByLineHeightChange(ownerId, decorationId, lineNumber, lineHeight) {
if (!this._affectedLineHeights) {
this._affectedLineHeights = new SetWithKey([], LineHeightChangingDecoration.toKey);
}
this._affectedLineHeights.add(new LineHeightChangingDecoration(ownerId, decorationId, lineNumber, lineHeight));
}
recordLineAffectedByFontChange(ownerId, decorationId, lineNumber) {
if (!this._affectedFontLines) {
this._affectedFontLines = new SetWithKey([], LineFontChangingDecoration.toKey);
}
this._affectedFontLines.add(new LineFontChangingDecoration(ownerId, decorationId, lineNumber));
}
checkAffectedAndFire(options) {
this._affectsMinimap ||= !!options.minimap?.position;
this._affectsOverviewRuler ||= !!options.overviewRuler?.color;
this._affectsGlyphMargin ||= !!options.glyphMarginClassName;
this._affectsLineNumber ||= !!options.lineNumberClassName;
this.tryFire();
}
fire() {
this._affectsMinimap = true;
this._affectsOverviewRuler = true;
this._affectsGlyphMargin = true;
this.tryFire();
}
tryFire() {
if (this._deferredCnt === 0) {
this.doFire();
}
else {
this._shouldFireDeferred = true;
}
}
doFire() {
this.handleBeforeFire(this._affectedInjectedTextLines, this._affectedLineHeights, this._affectedFontLines);
const event = {
affectsMinimap: this._affectsMinimap,
affectsOverviewRuler: this._affectsOverviewRuler,
affectsGlyphMargin: this._affectsGlyphMargin,
affectsLineNumber: this._affectsLineNumber,
};
this._shouldFireDeferred = false;
this._affectsMinimap = false;
this._affectsOverviewRuler = false;
this._affectsGlyphMargin = false;
this._actual.fire(event);
}
}
//#endregion
class DidChangeContentEmitter extends Disposable {
constructor() {
super();
/**
* Both `fastEvent` and `slowEvent` work the same way and contain the same events, but first we invoke `fastEvent` and then `slowEvent`.
*/
this._fastEmitter = this._register(new Emitter());
this.fastEvent = this._fastEmitter.event;
this._slowEmitter = this._register(new Emitter());
this.slowEvent = this._slowEmitter.event;
this._deferredCnt = 0;
this._deferredEvent = null;
}
beginDeferredEmit() {
this._deferredCnt++;
}
endDeferredEmit(resultingSelection = null) {
this._deferredCnt--;
if (this._deferredCnt === 0) {
if (this._deferredEvent !== null) {
this._deferredEvent.rawContentChangedEvent.resultingSelection = resultingSelection;
const e = this._deferredEvent;
this._deferredEvent = null;
this._fastEmitter.fire(e);
this._slowEmitter.fire(e);
}
}
}
fire(e) {
if (this._deferredCnt > 0) {
if (this._deferredEvent) {
this._deferredEvent = this._deferredEvent.merge(e);
}
else {
this._deferredEvent = e;
}
return;
}
this._fastEmitter.fire(e);
this._slowEmitter.fire(e);
}
}
//# sourceMappingURL=textModel.js.map