monaco-editor
Version:
A browser based code editor
1,047 lines • 113 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 __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import { onUnexpectedError } from '../../../base/common/errors.js';
import { Emitter } from '../../../base/common/event.js';
import { Disposable } from '../../../base/common/lifecycle.js';
import { StopWatch } from '../../../base/common/stopwatch.js';
import * as strings from '../../../base/common/strings.js';
import { URI } from '../../../base/common/uri.js';
import { EDITOR_MODEL_DEFAULTS } from '../config/editorOptions.js';
import { Position } from '../core/position.js';
import { Range } from '../core/range.js';
import { Selection } from '../core/selection.js';
import * as model from '../model.js';
import { EditStack } from './editStack.js';
import { guessIndentation } from './indentationGuesser.js';
import { IntervalNode, IntervalTree, getNodeIsInOverviewRuler, recomputeMaxEnd } from './intervalTree.js';
import { PieceTreeTextBufferBuilder } from './pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js';
import { InternalModelContentChangeEvent, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from './textModelEvents.js';
import { SearchParams, TextModelSearch } from './textModelSearch.js';
import { ModelLinesTokens, ModelTokensChangedEventBuilder } from './textModelTokens.js';
import { getWordAtText } from './wordHelper.js';
import { TokenizationRegistry } from '../modes.js';
import { LanguageConfigurationRegistry } from '../modes/languageConfigurationRegistry.js';
import { NULL_LANGUAGE_IDENTIFIER } from '../modes/nullMode.js';
import { ignoreBracketsInToken } from '../modes/supports.js';
import { BracketsUtils } from '../modes/supports/richEditBrackets.js';
var CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048;
function createTextBufferBuilder() {
return new PieceTreeTextBufferBuilder();
}
export function createTextBufferFactory(text) {
var builder = createTextBufferBuilder();
builder.acceptChunk(text);
return builder.finish();
}
export function createTextBuffer(value, defaultEOL) {
var factory = (typeof value === 'string' ? createTextBufferFactory(value) : value);
return factory.create(defaultEOL);
}
var MODEL_ID = 0;
/**
* Produces 'a'-'z', followed by 'A'-'Z'... followed by 'a'-'z', etc.
*/
function singleLetter(result) {
var LETTERS_CNT = (90 /* Z */ - 65 /* A */ + 1);
result = result % (2 * LETTERS_CNT);
if (result < LETTERS_CNT) {
return String.fromCharCode(97 /* a */ + result);
}
return String.fromCharCode(65 /* A */ + result - LETTERS_CNT);
}
var LIMIT_FIND_COUNT = 999;
export var LONG_LINE_BOUNDARY = 10000;
var invalidFunc = function () { throw new Error("Invalid change accessor"); };
var TextModel = /** @class */ (function (_super) {
__extends(TextModel, _super);
//#endregion
function TextModel(source, creationOptions, languageIdentifier, associatedResource) {
if (associatedResource === void 0) { associatedResource = null; }
var _this = _super.call(this) || this;
//#region Events
_this._onWillDispose = _this._register(new Emitter());
_this.onWillDispose = _this._onWillDispose.event;
_this._onDidChangeDecorations = _this._register(new DidChangeDecorationsEmitter());
_this.onDidChangeDecorations = _this._onDidChangeDecorations.event;
_this._onDidChangeLanguage = _this._register(new Emitter());
_this.onDidChangeLanguage = _this._onDidChangeLanguage.event;
_this._onDidChangeLanguageConfiguration = _this._register(new Emitter());
_this.onDidChangeLanguageConfiguration = _this._onDidChangeLanguageConfiguration.event;
_this._onDidChangeTokens = _this._register(new Emitter());
_this.onDidChangeTokens = _this._onDidChangeTokens.event;
_this._onDidChangeOptions = _this._register(new Emitter());
_this.onDidChangeOptions = _this._onDidChangeOptions.event;
_this._eventEmitter = _this._register(new DidChangeContentEmitter());
// 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;
_this._buffer = createTextBuffer(source, creationOptions.defaultEOL);
_this._options = TextModel.resolveOptions(_this._buffer, creationOptions);
var bufferLineCount = _this._buffer.getLineCount();
var bufferTextLength = _this._buffer.getValueLengthInRange(new Range(1, 1, bufferLineCount, _this._buffer.getLineLength(bufferLineCount) + 1), 0 /* 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.LARGE_FILE_SIZE_THRESHOLD)
|| (bufferLineCount > TextModel.LARGE_FILE_LINE_COUNT_THRESHOLD));
}
else {
_this._isTooLargeForTokenization = false;
}
_this._isTooLargeForSyncing = (bufferTextLength > TextModel.MODEL_SYNC_LIMIT);
_this._setVersionId(1);
_this._isDisposed = false;
_this._isDisposing = false;
_this._languageIdentifier = languageIdentifier || NULL_LANGUAGE_IDENTIFIER;
_this._tokenizationListener = TokenizationRegistry.onDidChange(function (e) {
if (e.changedLanguages.indexOf(_this._languageIdentifier.language) === -1) {
return;
}
_this._resetTokenizationState();
_this.emitModelTokensChangedEvent({
ranges: [{
fromLineNumber: 1,
toLineNumber: _this.getLineCount()
}]
});
if (_this._shouldAutoTokenize()) {
_this._warmUpTokens();
}
});
_this._revalidateTokensTimeout = -1;
_this._languageRegistryListener = LanguageConfigurationRegistry.onDidChange(function (e) {
if (e.languageIdentifier.id === _this._languageIdentifier.id) {
_this._onDidChangeLanguageConfiguration.fire({});
}
});
_this._resetTokenizationState();
_this._instanceId = singleLetter(MODEL_ID);
_this._lastDecorationId = 0;
_this._decorations = Object.create(null);
_this._decorationsTree = new DecorationsTrees();
_this._commandManager = new EditStack(_this);
_this._isUndoing = false;
_this._isRedoing = false;
_this._trimAutoWhitespaceLines = null;
return _this;
}
TextModel.createFromString = function (text, options, languageIdentifier, uri) {
if (options === void 0) { options = TextModel.DEFAULT_CREATION_OPTIONS; }
if (languageIdentifier === void 0) { languageIdentifier = null; }
if (uri === void 0) { uri = null; }
return new TextModel(text, options, languageIdentifier, uri);
};
TextModel.resolveOptions = function (textBuffer, options) {
if (options.detectIndentation) {
var guessedIndentation = guessIndentation(textBuffer, options.tabSize, options.insertSpaces);
return new model.TextModelResolvedOptions({
tabSize: guessedIndentation.tabSize,
insertSpaces: guessedIndentation.insertSpaces,
trimAutoWhitespace: options.trimAutoWhitespace,
defaultEOL: options.defaultEOL
});
}
return new model.TextModelResolvedOptions({
tabSize: options.tabSize,
insertSpaces: options.insertSpaces,
trimAutoWhitespace: options.trimAutoWhitespace,
defaultEOL: options.defaultEOL
});
};
TextModel.prototype.onDidChangeRawContentFast = function (listener) {
return this._eventEmitter.fastEvent(function (e) { return listener(e.rawContentChangedEvent); });
};
TextModel.prototype.onDidChangeRawContent = function (listener) {
return this._eventEmitter.slowEvent(function (e) { return listener(e.rawContentChangedEvent); });
};
TextModel.prototype.onDidChangeContent = function (listener) {
return this._eventEmitter.slowEvent(function (e) { return listener(e.contentChangedEvent); });
};
TextModel.prototype.dispose = function () {
this._isDisposing = true;
this._onWillDispose.fire();
this._tokenizationListener.dispose();
this._languageRegistryListener.dispose();
this._clearTimers();
this._isDisposed = true;
// Null out members, such that any use of a disposed model will throw exceptions sooner rather than later
_super.prototype.dispose.call(this);
this._isDisposing = false;
};
TextModel.prototype._assertNotDisposed = function () {
if (this._isDisposed) {
throw new Error('Model is disposed!');
}
};
TextModel.prototype._emitContentChangedEvent = function (rawChange, change) {
if (this._isDisposing) {
// Do not confuse listeners by emitting any event after disposing
return;
}
this._eventEmitter.fire(new InternalModelContentChangeEvent(rawChange, change));
};
TextModel.prototype.setValue = function (value) {
this._assertNotDisposed();
if (value === null) {
// There's nothing to do
return;
}
var textBuffer = createTextBuffer(value, this._options.defaultEOL);
this.setValueFromTextBuffer(textBuffer);
};
TextModel.prototype._createContentChanged2 = function (range, rangeOffset, rangeLength, text, isUndoing, isRedoing, isFlush) {
return {
changes: [{
range: range,
rangeOffset: rangeOffset,
rangeLength: rangeLength,
text: text,
}],
eol: this._buffer.getEOL(),
versionId: this.getVersionId(),
isUndoing: isUndoing,
isRedoing: isRedoing,
isFlush: isFlush
};
};
TextModel.prototype.setValueFromTextBuffer = function (textBuffer) {
this._assertNotDisposed();
if (textBuffer === null) {
// There's nothing to do
return;
}
var oldFullModelRange = this.getFullModelRange();
var oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);
var endLineNumber = this.getLineCount();
var endColumn = this.getLineMaxColumn(endLineNumber);
this._buffer = textBuffer;
this._increaseVersionId();
// Cancel tokenization, clear all tokens and begin tokenizing
this._resetTokenizationState();
// Destroy all my decorations
this._decorations = Object.create(null);
this._decorationsTree = new DecorationsTrees();
// Destroy my edit history and settings
this._commandManager = new EditStack(this);
this._trimAutoWhitespaceLines = null;
this._emitContentChangedEvent(new ModelRawContentChangedEvent([
new ModelRawFlush()
], this._versionId, false, false), this._createContentChanged2(new Range(1, 1, endLineNumber, endColumn), 0, oldModelValueLength, this.getValue(), false, false, true));
};
TextModel.prototype.setEOL = function (eol) {
this._assertNotDisposed();
var newEOL = (eol === 1 /* CRLF */ ? '\r\n' : '\n');
if (this._buffer.getEOL() === newEOL) {
// Nothing to do
return;
}
var oldFullModelRange = this.getFullModelRange();
var oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);
var endLineNumber = this.getLineCount();
var 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, this.getValue(), false, false, false));
};
TextModel.prototype._onBeforeEOLChange = function () {
// Ensure all decorations get their `range` set.
var versionId = this.getVersionId();
var allDecorations = this._decorationsTree.search(0, false, false, versionId);
this._ensureNodesHaveRanges(allDecorations);
};
TextModel.prototype._onAfterEOLChange = function () {
// Transform back `range` to offsets
var versionId = this.getVersionId();
var allDecorations = this._decorationsTree.collectNodesPostOrder();
for (var i = 0, len = allDecorations.length; i < len; i++) {
var node = allDecorations[i];
var delta = node.cachedAbsoluteStart - node.start;
var startOffset = this._buffer.getOffsetAt(node.range.startLineNumber, node.range.startColumn);
var endOffset = this._buffer.getOffsetAt(node.range.endLineNumber, node.range.endColumn);
node.cachedAbsoluteStart = startOffset;
node.cachedAbsoluteEnd = endOffset;
node.cachedVersionId = versionId;
node.start = startOffset - delta;
node.end = endOffset - delta;
recomputeMaxEnd(node);
}
};
TextModel.prototype._resetTokenizationState = function () {
this._clearTimers();
var tokenizationSupport = (this._isTooLargeForTokenization
? null
: TokenizationRegistry.get(this._languageIdentifier.language));
this._tokens = new ModelLinesTokens(this._languageIdentifier, tokenizationSupport);
this._beginBackgroundTokenization();
};
TextModel.prototype._clearTimers = function () {
if (this._revalidateTokensTimeout !== -1) {
clearTimeout(this._revalidateTokensTimeout);
this._revalidateTokensTimeout = -1;
}
};
TextModel.prototype.onBeforeAttached = function () {
this._attachedEditorCount++;
// Warm up tokens for the editor
this._warmUpTokens();
};
TextModel.prototype.onBeforeDetached = function () {
this._attachedEditorCount--;
};
TextModel.prototype._shouldAutoTokenize = function () {
return this.isAttachedToEditor();
};
TextModel.prototype.isAttachedToEditor = function () {
return this._attachedEditorCount > 0;
};
TextModel.prototype.getAttachedEditorCount = function () {
return this._attachedEditorCount;
};
TextModel.prototype.isTooLargeForSyncing = function () {
return this._isTooLargeForSyncing;
};
TextModel.prototype.isTooLargeForTokenization = function () {
return this._isTooLargeForTokenization;
};
TextModel.prototype.isDisposed = function () {
return this._isDisposed;
};
TextModel.prototype.isDominatedByLongLines = function () {
this._assertNotDisposed();
if (this.isTooLargeForTokenization()) {
// Cannot word wrap huge files anyways, so it doesn't really matter
return false;
}
var smallLineCharCount = 0;
var longLineCharCount = 0;
var lineCount = this._buffer.getLineCount();
for (var lineNumber = 1; lineNumber <= lineCount; lineNumber++) {
var lineLength = this._buffer.getLineLength(lineNumber);
if (lineLength >= LONG_LINE_BOUNDARY) {
longLineCharCount += lineLength;
}
else {
smallLineCharCount += lineLength;
}
}
return (longLineCharCount > smallLineCharCount);
};
Object.defineProperty(TextModel.prototype, "uri", {
get: function () {
return this._associatedResource;
},
enumerable: true,
configurable: true
});
//#region Options
TextModel.prototype.getOptions = function () {
this._assertNotDisposed();
return this._options;
};
TextModel.prototype.updateOptions = function (_newOpts) {
this._assertNotDisposed();
var tabSize = (typeof _newOpts.tabSize !== 'undefined') ? _newOpts.tabSize : this._options.tabSize;
var insertSpaces = (typeof _newOpts.insertSpaces !== 'undefined') ? _newOpts.insertSpaces : this._options.insertSpaces;
var trimAutoWhitespace = (typeof _newOpts.trimAutoWhitespace !== 'undefined') ? _newOpts.trimAutoWhitespace : this._options.trimAutoWhitespace;
var newOpts = new model.TextModelResolvedOptions({
tabSize: tabSize,
insertSpaces: insertSpaces,
defaultEOL: this._options.defaultEOL,
trimAutoWhitespace: trimAutoWhitespace
});
if (this._options.equals(newOpts)) {
return;
}
var e = this._options.createChangeEvent(newOpts);
this._options = newOpts;
this._onDidChangeOptions.fire(e);
};
TextModel.prototype.detectIndentation = function (defaultInsertSpaces, defaultTabSize) {
this._assertNotDisposed();
var guessedIndentation = guessIndentation(this._buffer, defaultTabSize, defaultInsertSpaces);
this.updateOptions({
insertSpaces: guessedIndentation.insertSpaces,
tabSize: guessedIndentation.tabSize
});
};
TextModel._normalizeIndentationFromWhitespace = function (str, tabSize, insertSpaces) {
var spacesCnt = 0;
for (var i = 0; i < str.length; i++) {
if (str.charAt(i) === '\t') {
spacesCnt += tabSize;
}
else {
spacesCnt++;
}
}
var result = '';
if (!insertSpaces) {
var tabsCnt = Math.floor(spacesCnt / tabSize);
spacesCnt = spacesCnt % tabSize;
for (var i = 0; i < tabsCnt; i++) {
result += '\t';
}
}
for (var i = 0; i < spacesCnt; i++) {
result += ' ';
}
return result;
};
TextModel.normalizeIndentation = function (str, tabSize, insertSpaces) {
var firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(str);
if (firstNonWhitespaceIndex === -1) {
firstNonWhitespaceIndex = str.length;
}
return TextModel._normalizeIndentationFromWhitespace(str.substring(0, firstNonWhitespaceIndex), tabSize, insertSpaces) + str.substring(firstNonWhitespaceIndex);
};
TextModel.prototype.normalizeIndentation = function (str) {
this._assertNotDisposed();
return TextModel.normalizeIndentation(str, this._options.tabSize, this._options.insertSpaces);
};
TextModel.prototype.getOneIndent = function () {
this._assertNotDisposed();
var tabSize = this._options.tabSize;
var insertSpaces = this._options.insertSpaces;
if (insertSpaces) {
var result = '';
for (var i = 0; i < tabSize; i++) {
result += ' ';
}
return result;
}
else {
return '\t';
}
};
//#endregion
//#region Reading
TextModel.prototype.getVersionId = function () {
this._assertNotDisposed();
return this._versionId;
};
TextModel.prototype.mightContainRTL = function () {
return this._buffer.mightContainRTL();
};
TextModel.prototype.mightContainNonBasicASCII = function () {
return this._buffer.mightContainNonBasicASCII();
};
TextModel.prototype.getAlternativeVersionId = function () {
this._assertNotDisposed();
return this._alternativeVersionId;
};
TextModel.prototype.getOffsetAt = function (rawPosition) {
this._assertNotDisposed();
var position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, false);
return this._buffer.getOffsetAt(position.lineNumber, position.column);
};
TextModel.prototype.getPositionAt = function (rawOffset) {
this._assertNotDisposed();
var offset = (Math.min(this._buffer.getLength(), Math.max(0, rawOffset)));
return this._buffer.getPositionAt(offset);
};
TextModel.prototype._increaseVersionId = function () {
this._setVersionId(this._versionId + 1);
};
TextModel.prototype._setVersionId = function (newVersionId) {
this._versionId = newVersionId;
this._alternativeVersionId = this._versionId;
};
TextModel.prototype._overwriteAlternativeVersionId = function (newAlternativeVersionId) {
this._alternativeVersionId = newAlternativeVersionId;
};
TextModel.prototype.getValue = function (eol, preserveBOM) {
if (preserveBOM === void 0) { preserveBOM = false; }
this._assertNotDisposed();
var fullModelRange = this.getFullModelRange();
var fullModelValue = this.getValueInRange(fullModelRange, eol);
if (preserveBOM) {
return this._buffer.getBOM() + fullModelValue;
}
return fullModelValue;
};
TextModel.prototype.getValueLength = function (eol, preserveBOM) {
if (preserveBOM === void 0) { preserveBOM = false; }
this._assertNotDisposed();
var fullModelRange = this.getFullModelRange();
var fullModelValue = this.getValueLengthInRange(fullModelRange, eol);
if (preserveBOM) {
return this._buffer.getBOM().length + fullModelValue;
}
return fullModelValue;
};
TextModel.prototype.getValueInRange = function (rawRange, eol) {
if (eol === void 0) { eol = 0 /* TextDefined */; }
this._assertNotDisposed();
return this._buffer.getValueInRange(this.validateRange(rawRange), eol);
};
TextModel.prototype.getValueLengthInRange = function (rawRange, eol) {
if (eol === void 0) { eol = 0 /* TextDefined */; }
this._assertNotDisposed();
return this._buffer.getValueLengthInRange(this.validateRange(rawRange), eol);
};
TextModel.prototype.getLineCount = function () {
this._assertNotDisposed();
return this._buffer.getLineCount();
};
TextModel.prototype.getLineContent = function (lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new Error('Illegal value for lineNumber');
}
return this._buffer.getLineContent(lineNumber);
};
TextModel.prototype.getLineLength = function (lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new Error('Illegal value for lineNumber');
}
return this._buffer.getLineLength(lineNumber);
};
TextModel.prototype.getLinesContent = function () {
this._assertNotDisposed();
return this._buffer.getLinesContent();
};
TextModel.prototype.getEOL = function () {
this._assertNotDisposed();
return this._buffer.getEOL();
};
TextModel.prototype.getLineMinColumn = function (lineNumber) {
this._assertNotDisposed();
return 1;
};
TextModel.prototype.getLineMaxColumn = function (lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new Error('Illegal value for lineNumber');
}
return this._buffer.getLineLength(lineNumber) + 1;
};
TextModel.prototype.getLineFirstNonWhitespaceColumn = function (lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new Error('Illegal value for lineNumber');
}
return this._buffer.getLineFirstNonWhitespaceColumn(lineNumber);
};
TextModel.prototype.getLineLastNonWhitespaceColumn = function (lineNumber) {
this._assertNotDisposed();
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new Error('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.
*/
TextModel.prototype._validateRangeRelaxedNoAllocations = function (range) {
var linesCount = this._buffer.getLineCount();
var initialStartLineNumber = range.startLineNumber;
var initialStartColumn = range.startColumn;
var startLineNumber;
var startColumn;
if (initialStartLineNumber < 1) {
startLineNumber = 1;
startColumn = 1;
}
else if (initialStartLineNumber > linesCount) {
startLineNumber = linesCount;
startColumn = this.getLineMaxColumn(startLineNumber);
}
else {
startLineNumber = initialStartLineNumber | 0;
if (initialStartColumn <= 1) {
startColumn = 1;
}
else {
var maxColumn = this.getLineMaxColumn(startLineNumber);
if (initialStartColumn >= maxColumn) {
startColumn = maxColumn;
}
else {
startColumn = initialStartColumn | 0;
}
}
}
var initialEndLineNumber = range.endLineNumber;
var initialEndColumn = range.endColumn;
var endLineNumber;
var endColumn;
if (initialEndLineNumber < 1) {
endLineNumber = 1;
endColumn = 1;
}
else if (initialEndLineNumber > linesCount) {
endLineNumber = linesCount;
endColumn = this.getLineMaxColumn(endLineNumber);
}
else {
endLineNumber = initialEndLineNumber | 0;
if (initialEndColumn <= 1) {
endColumn = 1;
}
else {
var maxColumn = this.getLineMaxColumn(endLineNumber);
if (initialEndColumn >= maxColumn) {
endColumn = maxColumn;
}
else {
endColumn = initialEndColumn | 0;
}
}
}
if (initialStartLineNumber === startLineNumber
&& initialStartColumn === startColumn
&& initialEndLineNumber === endLineNumber
&& initialEndColumn === endColumn
&& range instanceof Range
&& !(range instanceof Selection)) {
return range;
}
return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
};
/**
* @param strict Do NOT allow a position inside a high-low surrogate pair
*/
TextModel.prototype._isValidPosition = function (lineNumber, column, strict) {
if (isNaN(lineNumber)) {
return false;
}
if (lineNumber < 1) {
return false;
}
var lineCount = this._buffer.getLineCount();
if (lineNumber > lineCount) {
return false;
}
if (isNaN(column)) {
return false;
}
if (column < 1) {
return false;
}
var maxColumn = this.getLineMaxColumn(lineNumber);
if (column > maxColumn) {
return false;
}
if (strict) {
if (column > 1) {
var charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2);
if (strings.isHighSurrogate(charCodeBefore)) {
return false;
}
}
}
return true;
};
/**
* @param strict Do NOT allow a position inside a high-low surrogate pair
*/
TextModel.prototype._validatePosition = function (_lineNumber, _column, strict) {
var lineNumber = Math.floor((typeof _lineNumber === 'number' && !isNaN(_lineNumber)) ? _lineNumber : 1);
var column = Math.floor((typeof _column === 'number' && !isNaN(_column)) ? _column : 1);
var 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);
}
var maxColumn = this.getLineMaxColumn(lineNumber);
if (column >= maxColumn) {
return new Position(lineNumber, maxColumn);
}
if (strict) {
// 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
var charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2);
if (strings.isHighSurrogate(charCodeBefore)) {
return new Position(lineNumber, column - 1);
}
}
return new Position(lineNumber, column);
};
TextModel.prototype.validatePosition = function (position) {
this._assertNotDisposed();
// Avoid object allocation and cover most likely case
if (position instanceof Position) {
if (this._isValidPosition(position.lineNumber, position.column, true)) {
return position;
}
}
return this._validatePosition(position.lineNumber, position.column, true);
};
/**
* @param strict Do NOT allow a range to have its boundaries inside a high-low surrogate pair
*/
TextModel.prototype._isValidRange = function (range, strict) {
var startLineNumber = range.startLineNumber;
var startColumn = range.startColumn;
var endLineNumber = range.endLineNumber;
var endColumn = range.endColumn;
if (!this._isValidPosition(startLineNumber, startColumn, false)) {
return false;
}
if (!this._isValidPosition(endLineNumber, endColumn, false)) {
return false;
}
if (strict) {
var charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0);
var charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0);
var startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart);
var endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd);
if (!startInsideSurrogatePair && !endInsideSurrogatePair) {
return true;
}
return false;
}
return true;
};
TextModel.prototype.validateRange = function (_range) {
this._assertNotDisposed();
// Avoid object allocation and cover most likely case
if ((_range instanceof Range) && !(_range instanceof Selection)) {
if (this._isValidRange(_range, true)) {
return _range;
}
}
var start = this._validatePosition(_range.startLineNumber, _range.startColumn, false);
var end = this._validatePosition(_range.endLineNumber, _range.endColumn, false);
var startLineNumber = start.lineNumber;
var startColumn = start.column;
var endLineNumber = end.lineNumber;
var endColumn = end.column;
var charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0);
var charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0);
var startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart);
var 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);
};
TextModel.prototype.modifyPosition = function (rawPosition, offset) {
this._assertNotDisposed();
var candidate = this.getOffsetAt(rawPosition) + offset;
return this.getPositionAt(Math.min(this._buffer.getLength(), Math.max(0, candidate)));
};
TextModel.prototype.getFullModelRange = function () {
this._assertNotDisposed();
var lineCount = this.getLineCount();
return new Range(1, 1, lineCount, this.getLineMaxColumn(lineCount));
};
TextModel.prototype.findMatchesLineByLine = function (searchRange, searchData, captureMatches, limitResultCount) {
return this._buffer.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
};
TextModel.prototype.findMatches = function (searchString, rawSearchScope, isRegex, matchCase, wordSeparators, captureMatches, limitResultCount) {
if (limitResultCount === void 0) { limitResultCount = LIMIT_FIND_COUNT; }
this._assertNotDisposed();
var searchRange;
if (Range.isIRange(rawSearchScope)) {
searchRange = this.validateRange(rawSearchScope);
}
else {
searchRange = this.getFullModelRange();
}
if (!isRegex && searchString.indexOf('\n') < 0) {
// not regex, not multi line
var searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
var searchData = searchParams.parseSearchRequest();
if (!searchData) {
return [];
}
return this.findMatchesLineByLine(searchRange, searchData, captureMatches, limitResultCount);
}
return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount);
};
TextModel.prototype.findNextMatch = function (searchString, rawSearchStart, isRegex, matchCase, wordSeparators, captureMatches) {
this._assertNotDisposed();
var searchStart = this.validatePosition(rawSearchStart);
if (!isRegex && searchString.indexOf('\n') < 0) {
var searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
var searchData = searchParams.parseSearchRequest();
if (!searchData) {
return null;
}
var lineCount = this.getLineCount();
var searchRange = new Range(searchStart.lineNumber, searchStart.column, lineCount, this.getLineMaxColumn(lineCount));
var 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);
};
TextModel.prototype.findPreviousMatch = function (searchString, rawSearchStart, isRegex, matchCase, wordSeparators, captureMatches) {
this._assertNotDisposed();
var searchStart = this.validatePosition(rawSearchStart);
return TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
};
//#endregion
//#region Editing
TextModel.prototype.pushStackElement = function () {
this._commandManager.pushStackElement();
};
TextModel.prototype.pushEOL = function (eol) {
var currentEOL = (this.getEOL() === '\n' ? 0 /* LF */ : 1 /* CRLF */);
if (currentEOL === eol) {
return;
}
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
this._commandManager.pushEOL(eol);
}
finally {
this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit();
}
};
TextModel.prototype.pushEditOperations = function (beforeCursorState, editOperations, cursorStateComputer) {
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
return this._pushEditOperations(beforeCursorState, editOperations, cursorStateComputer);
}
finally {
this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit();
}
};
TextModel.prototype._pushEditOperations = function (beforeCursorState, editOperations, cursorStateComputer) {
var _this = this;
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).
var incomingEdits = editOperations.map(function (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
var editsAreNearCursors = true;
for (var i = 0, len = beforeCursorState.length; i < len; i++) {
var sel = beforeCursorState[i];
var foundEditNearSel = false;
for (var j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {
var editRange = incomingEdits[j].range;
var selIsAbove = editRange.startLineNumber > sel.endLineNumber;
var selIsBelow = sel.startLineNumber > editRange.endLineNumber;
if (!selIsAbove && !selIsBelow) {
foundEditNearSel = true;
break;
}
}
if (!foundEditNearSel) {
editsAreNearCursors = false;
break;
}
}
if (editsAreNearCursors) {
for (var i = 0, len = this._trimAutoWhitespaceLines.length; i < len; i++) {
var trimLineNumber = this._trimAutoWhitespaceLines[i];
var maxLineColumn = this.getLineMaxColumn(trimLineNumber);
var allowTrimLine = true;
for (var j = 0, lenJ = incomingEdits.length; j < lenJ; j++) {
var editRange = incomingEdits[j].range;
var 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) {
editOperations.push({
range: new Range(trimLineNumber, 1, trimLineNumber, maxLineColumn),
text: null
});
}
}
}
this._trimAutoWhitespaceLines = null;
}
return this._commandManager.pushEditOperation(beforeCursorState, editOperations, cursorStateComputer);
};
TextModel.prototype.applyEdits = function (rawOperations) {
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
return this._applyEdits(rawOperations);
}
finally {
this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit();
}
};
TextModel._eolCount = function (text) {
var eolCount = 0;
var firstLineLength = 0;
for (var i = 0, len = text.length; i < len; i++) {
var chr = text.charCodeAt(i);
if (chr === 13 /* CarriageReturn */) {
if (eolCount === 0) {
firstLineLength = i;
}
eolCount++;
if (i + 1 < len && text.charCodeAt(i + 1) === 10 /* LineFeed */) {
// \r\n... case
i++; // skip \n
}
else {
// \r... case
}
}
else if (chr === 10 /* LineFeed */) {
if (eolCount === 0) {
firstLineLength = i;
}
eolCount++;
}
}
if (eolCount === 0) {
firstLineLength = text.length;
}
return [eolCount, firstLineLength];
};
TextModel.prototype._applyEdits = function (rawOperations) {
for (var i = 0, len = rawOperations.length; i < len; i++) {
rawOperations[i].range = this.validateRange(rawOperations[i].range);
}
var oldLineCount = this._buffer.getLineCount();
var result = this._buffer.applyEdits(rawOperations, this._options.trimAutoWhitespace);
var newLineCount = this._buffer.getLineCount();
var contentChanges = result.changes;
this._trimAutoWhitespaceLines = result.trimAutoWhitespaceLineNumbers;
if (contentChanges.length !== 0) {
var rawContentChanges = [];
var lineCount = oldLineCount;
for (var i = 0, len = contentChanges.length; i < len; i++) {
var change = contentChanges[i];
var _a = TextModel._eolCount(change.text), eolCount = _a[0], firstLineLength = _a[1];
try {
this._tokens.applyEdits(change.range, eolCount, firstLineLength);
}
catch (err) {
// emergency recovery => reset tokens
this._tokens = new ModelLinesTokens(this._tokens.languageIdentifier, this._tokens.tokenizationSupport);
}
this._onDidChangeDecorations.fire();
this._decorationsTree.acceptReplace(change.rangeOffset, change.rangeLength, change.text.length, change.forceMoveMarkers);
var startLineNumber = change.range.startLineNumber;
var endLineNumber = change.range.endLineNumber;
var deletingLinesCnt = endLineNumber - startLineNumber;
var insertingLinesCnt = eolCount;
var editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt);
var changeLineCountDelta = (insertingLinesCnt - deletingLinesCnt);
for (var j = editingLinesCnt; j >= 0; j--) {
var editLineNumber = startLineNumber + j;
var currentEditLineNumber = newLineCount - lineCount - changeLineCountDelta + editLineNumber;
rawContentChanges.push(new ModelRawLineChanged(editLineNumber, this.getLineContent(currentEditLineNumber)));
}
if (editingLinesCnt < deletingLinesCnt) {
// Must delete some lines
var spliceStartLineNumber = startLineNumber + editingLinesCnt;
rawContentChanges.push(new ModelRawLinesDeleted(spliceStartLineNumber + 1, endLineNumber));
}
if (editingLinesCnt < insertingLinesCnt) {
// Must insert some lines
var spliceLineNumber = startLineNumber + editingLinesCnt;
var cnt = insertingLinesCnt - editingLinesCnt;
var fromLineNumber = newLineCount - lineCount - cnt + spliceLineNumber + 1;
var newLines = [];
for (var i_1 = 0; i_1 < cnt; i_1++) {
var lineNumber = fromLineNumber + i_1;
newLines[lineNumber - fromLineNumber] = this.getLineContent(lineNumber);
}
rawContentChanges.push(new ModelRawLinesInserted(spliceLineNumber + 1, startLineNumber + insertingLinesCnt, newLines));
}
lineCount += changeLineCountDelta;
}
this._increaseVersionId();
this._emitContentChangedEvent(new ModelRawContentChangedEvent(rawContentChanges, this.getVersionId(), this._isUndoing, this._isRedoing), {
changes: contentChanges,
eol: this._buffer.getEOL(),
versionId: this.getVersionId(),
isUndoing: this._isUndoing,
isRedoing: this._isRedoing,
isFlush: false
});
}
if (this._tokens.hasLinesToTokenize(this._buffer)) {
this._beginBackgroundTokenization();
}
return result.reverseEdits;
};
TextModel.prototype._undo = function () {
this._isUndoing = true;
var r = this._commandManager.undo();
this._