textModel.ts 27.4 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

J
Johannes Rieken 已提交
7
import { OrderGuaranteeEventEmitter } from 'vs/base/common/eventEmitter';
A
Alex Dima 已提交
8
import * as strings from 'vs/base/common/strings';
J
Johannes Rieken 已提交
9 10
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
A
Alex Dima 已提交
11
import * as editorCommon from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
12 13 14 15 16
import { ModelLine } from 'vs/editor/common/model/modelLine';
import { guessIndentation } from 'vs/editor/common/model/indentationGuesser';
import { DEFAULT_INDENTATION, DEFAULT_TRIM_AUTO_WHITESPACE } from 'vs/editor/common/config/defaultConfig';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { IndentRange, computeRanges } from 'vs/editor/common/model/indentRanges';
A
Alex Dima 已提交
17
import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelSearch';
E
Erich Gamma 已提交
18

A
Alex Dima 已提交
19
const LIMIT_FIND_COUNT = 999;
A
Alex Dima 已提交
20
export const LONG_LINE_BOUNDARY = 1000;
E
Erich Gamma 已提交
21

A
Alex Dima 已提交
22
export class TextModel extends OrderGuaranteeEventEmitter implements editorCommon.ITextModel {
23 24
	private static MODEL_SYNC_LIMIT = 5 * 1024 * 1024; // 5 MB
	private static MODEL_TOKENIZATION_LIMIT = 20 * 1024 * 1024; // 20 MB
E
Erich Gamma 已提交
25

26 27 28 29
	public static DEFAULT_CREATION_OPTIONS: editorCommon.ITextModelCreationOptions = {
		tabSize: DEFAULT_INDENTATION.tabSize,
		insertSpaces: DEFAULT_INDENTATION.insertSpaces,
		detectIndentation: false,
A
Alex Dima 已提交
30
		defaultEOL: editorCommon.DefaultEndOfLine.LF,
31
		trimAutoWhitespace: DEFAULT_TRIM_AUTO_WHITESPACE,
32 33
	};

J
Johannes Rieken 已提交
34 35 36 37
	/*protected*/ _lines: ModelLine[];
	protected _EOL: string;
	protected _isDisposed: boolean;
	protected _isDisposing: boolean;
38
	protected _options: editorCommon.TextModelResolvedOptions;
39
	protected _lineStarts: PrefixSumComputer;
A
Alex Dima 已提交
40
	private _indentRanges: IndentRange[];
E
Erich Gamma 已提交
41

J
Johannes Rieken 已提交
42
	private _versionId: number;
E
Erich Gamma 已提交
43 44 45 46
	/**
	 * Unlike, versionId, this can go down (via undo) or go to previous values (via redo)
	 */
	private _alternativeVersionId: number;
J
Johannes Rieken 已提交
47
	private _BOM: string;
A
Alex Dima 已提交
48
	protected _mightContainRTL: boolean;
E
Erich Gamma 已提交
49

50 51 52
	private _shouldSimplifyMode: boolean;
	private _shouldDenyMode: boolean;

J
Johannes Rieken 已提交
53
	constructor(allowedEventTypes: string[], rawText: editorCommon.IRawText) {
A
Alex Dima 已提交
54
		allowedEventTypes.push(editorCommon.EventType.ModelRawContentChanged, editorCommon.EventType.ModelOptionsChanged, editorCommon.EventType.ModelContentChanged2);
E
Erich Gamma 已提交
55 56
		super(allowedEventTypes);

57 58 59
		this._shouldSimplifyMode = (rawText.length > TextModel.MODEL_SYNC_LIMIT);
		this._shouldDenyMode = (rawText.length > TextModel.MODEL_TOKENIZATION_LIMIT);

60
		this._options = new editorCommon.TextModelResolvedOptions(rawText.options);
E
Erich Gamma 已提交
61 62 63 64 65 66
		this._constructLines(rawText);
		this._setVersionId(1);
		this._isDisposed = false;
		this._isDisposing = false;
	}

67 68 69 70 71 72
	protected _assertNotDisposed(): void {
		if (this._isDisposed) {
			throw new Error('Model is disposed!');
		}
	}

73
	public isTooLargeForHavingAMode(): boolean {
74
		this._assertNotDisposed();
75 76 77 78
		return this._shouldDenyMode;
	}

	public isTooLargeForHavingARichMode(): boolean {
79
		this._assertNotDisposed();
80 81 82
		return this._shouldSimplifyMode;
	}

83
	public getOptions(): editorCommon.TextModelResolvedOptions {
84
		this._assertNotDisposed();
85 86 87
		return this._options;
	}

88
	public updateOptions(_newOpts: editorCommon.ITextModelUpdateOptions): void {
89
		this._assertNotDisposed();
90 91 92
		let tabSize = (typeof _newOpts.tabSize !== 'undefined') ? _newOpts.tabSize : this._options.tabSize;
		let insertSpaces = (typeof _newOpts.insertSpaces !== 'undefined') ? _newOpts.insertSpaces : this._options.insertSpaces;
		let trimAutoWhitespace = (typeof _newOpts.trimAutoWhitespace !== 'undefined') ? _newOpts.trimAutoWhitespace : this._options.trimAutoWhitespace;
93

94 95 96 97 98 99
		let newOpts = new editorCommon.TextModelResolvedOptions({
			tabSize: tabSize,
			insertSpaces: insertSpaces,
			defaultEOL: this._options.defaultEOL,
			trimAutoWhitespace: trimAutoWhitespace
		});
100

101 102
		if (this._options.equals(newOpts)) {
			return;
103
		}
104 105 106 107 108 109 110 111

		let e = this._options.createChangeEvent(newOpts);
		this._options = newOpts;

		if (e.tabSize) {
			let newTabSize = this._options.tabSize;
			for (let i = 0, len = this._lines.length; i < len; i++) {
				this._lines[i].updateTabSize(newTabSize);
112 113
			}
		}
114

115
		this.emit(editorCommon.EventType.ModelOptionsChanged, e);
116 117
	}

J
Johannes Rieken 已提交
118
	public detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void {
119
		this._assertNotDisposed();
120 121 122 123 124 125 126 127
		let lines = this._lines.map(line => line.text);
		let guessedIndentation = guessIndentation(lines, defaultTabSize, defaultInsertSpaces);
		this.updateOptions({
			insertSpaces: guessedIndentation.insertSpaces,
			tabSize: guessedIndentation.tabSize
		});
	}

128
	private static _normalizeIndentationFromWhitespace(str: string, tabSize: number, insertSpaces: boolean): string {
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
		let spacesCnt = 0;
		for (let i = 0; i < str.length; i++) {
			if (str.charAt(i) === '\t') {
				spacesCnt += tabSize;
			} else {
				spacesCnt++;
			}
		}

		let result = '';
		if (!insertSpaces) {
			let tabsCnt = Math.floor(spacesCnt / tabSize);
			spacesCnt = spacesCnt % tabSize;
			for (let i = 0; i < tabsCnt; i++) {
				result += '\t';
			}
		}

		for (let i = 0; i < spacesCnt; i++) {
			result += ' ';
		}

		return result;
	}

154
	public static normalizeIndentation(str: string, tabSize: number, insertSpaces: boolean): string {
155 156 157 158
		let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(str);
		if (firstNonWhitespaceIndex === -1) {
			firstNonWhitespaceIndex = str.length;
		}
159 160 161 162 163 164
		return TextModel._normalizeIndentationFromWhitespace(str.substring(0, firstNonWhitespaceIndex), tabSize, insertSpaces) + str.substring(firstNonWhitespaceIndex);
	}

	public normalizeIndentation(str: string): string {
		this._assertNotDisposed();
		return TextModel.normalizeIndentation(str, this._options.tabSize, this._options.insertSpaces);
165 166 167
	}

	public getOneIndent(): string {
168
		this._assertNotDisposed();
169 170 171 172 173 174 175 176 177 178 179 180 181 182
		let tabSize = this._options.tabSize;
		let insertSpaces = this._options.insertSpaces;

		if (insertSpaces) {
			let result = '';
			for (let i = 0; i < tabSize; i++) {
				result += ' ';
			}
			return result;
		} else {
			return '\t';
		}
	}

E
Erich Gamma 已提交
183
	public getVersionId(): number {
184
		this._assertNotDisposed();
E
Erich Gamma 已提交
185 186 187
		return this._versionId;
	}

A
Alex Dima 已提交
188 189 190 191
	public mightContainRTL(): boolean {
		return this._mightContainRTL;
	}

E
Erich Gamma 已提交
192
	public getAlternativeVersionId(): number {
193
		this._assertNotDisposed();
E
Erich Gamma 已提交
194 195 196
		return this._alternativeVersionId;
	}

197 198 199
	private _ensureLineStarts(): void {
		if (!this._lineStarts) {
			const eolLength = this._EOL.length;
200 201 202 203
			const linesLength = this._lines.length;
			const lineStartValues = new Uint32Array(linesLength);
			for (let i = 0; i < linesLength; i++) {
				lineStartValues[i] = this._lines[i].text.length + eolLength;
204 205 206 207 208 209
			}
			this._lineStarts = new PrefixSumComputer(lineStartValues);
		}
	}

	public getOffsetAt(rawPosition: editorCommon.IPosition): number {
210
		this._assertNotDisposed();
211
		let position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, false);
212 213 214 215 216
		this._ensureLineStarts();
		return this._lineStarts.getAccumulatedValue(position.lineNumber - 2) + position.column - 1;
	}

	public getPositionAt(offset: number): Position {
217
		this._assertNotDisposed();
218 219 220 221 222 223 224 225 226 227 228 229
		offset = Math.floor(offset);
		offset = Math.max(0, offset);

		this._ensureLineStarts();
		let out = this._lineStarts.getIndexOf(offset);

		let lineLength = this._lines[out.index].text.length;

		// Ensure we return a valid position
		return new Position(out.index + 1, Math.min(out.remainder + 1, lineLength + 1));
	}

A
Alex Dima 已提交
230
	protected _increaseVersionId(): void {
E
Erich Gamma 已提交
231 232 233
		this._setVersionId(this._versionId + 1);
	}

J
Johannes Rieken 已提交
234
	protected _setVersionId(newVersionId: number): void {
E
Erich Gamma 已提交
235 236 237 238
		this._versionId = newVersionId;
		this._alternativeVersionId = this._versionId;
	}

J
Johannes Rieken 已提交
239
	protected _overwriteAlternativeVersionId(newAlternativeVersionId: number): void {
E
Erich Gamma 已提交
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
		this._alternativeVersionId = newAlternativeVersionId;
	}

	public isDisposed(): boolean {
		return this._isDisposed;
	}

	public dispose(): void {
		this._isDisposed = true;
		// Null out members, such that any use of a disposed model will throw exceptions sooner rather than later
		this._lines = null;
		this._EOL = null;
		this._BOM = null;

		super.dispose();
	}

A
Alex Dima 已提交
257
	protected _createContentChangedFlushEvent(): editorCommon.IModelContentChangedFlushEvent {
E
Erich Gamma 已提交
258
		return {
A
Alex Dima 已提交
259
			changeType: editorCommon.EventType.ModelRawContentChangedFlush,
260 261
			detail: this.toRawText(),
			versionId: this._versionId,
E
Erich Gamma 已提交
262 263 264 265 266 267
			// TODO@Alex -> remove these fields from here
			isUndoing: false,
			isRedoing: false
		};
	}

J
Johannes Rieken 已提交
268 269
	protected _emitContentChanged2(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string, isUndoing: boolean, isRedoing: boolean): void {
		var e: editorCommon.IModelContentChangedEvent2 = {
E
Erich Gamma 已提交
270 271 272
			range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
			rangeLength: rangeLength,
			text: text,
273
			eol: this._EOL,
E
Erich Gamma 已提交
274 275 276 277 278
			versionId: this.getVersionId(),
			isUndoing: isUndoing,
			isRedoing: isRedoing
		};
		if (!this._isDisposing) {
A
Alex Dima 已提交
279
			this.emit(editorCommon.EventType.ModelContentChanged2, e);
E
Erich Gamma 已提交
280 281 282
		}
	}

283
	protected _resetValue(newValue: editorCommon.IRawText): void {
284
		this._constructLines(newValue);
E
Erich Gamma 已提交
285 286 287
		this._increaseVersionId();
	}

A
Alex Dima 已提交
288
	public toRawText(): editorCommon.IRawText {
289
		this._assertNotDisposed();
E
Erich Gamma 已提交
290 291 292 293
		return {
			BOM: this._BOM,
			EOL: this._EOL,
			lines: this.getLinesContent(),
294
			length: this.getValueLength(),
A
Alex Dima 已提交
295
			containsRTL: this._mightContainRTL,
296
			options: this._options
E
Erich Gamma 已提交
297 298 299
		};
	}

300
	public equals(other: editorCommon.IRawText): boolean {
301
		this._assertNotDisposed();
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
		if (this._BOM !== other.BOM) {
			return false;
		}
		if (this._EOL !== other.EOL) {
			return false;
		}
		if (this._lines.length !== other.lines.length) {
			return false;
		}
		for (let i = 0, len = this._lines.length; i < len; i++) {
			if (this._lines[i].text !== other.lines[i]) {
				return false;
			}
		}
		return true;
	}

J
Johannes Rieken 已提交
319
	public setValue(value: string): void {
320
		this._assertNotDisposed();
A
Alex Dima 已提交
321 322 323
		if (value === null) {
			// There's nothing to do
			return;
324
		}
A
Alex Dima 已提交
325 326 327 328 329 330 331 332
		let rawText: editorCommon.IRawText = null;
		rawText = TextModel.toRawText(value, {
			tabSize: this._options.tabSize,
			insertSpaces: this._options.insertSpaces,
			trimAutoWhitespace: this._options.trimAutoWhitespace,
			detectIndentation: false,
			defaultEOL: this._options.defaultEOL
		});
333 334 335
		this.setValueFromRawText(rawText);
	}

J
Johannes Rieken 已提交
336
	public setValueFromRawText(newValue: editorCommon.IRawText): void {
337
		this._assertNotDisposed();
E
Erich Gamma 已提交
338 339 340 341 342 343 344 345
		if (newValue === 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);
346

347 348 349 350
		this._resetValue(newValue);

		this._emitModelContentChangedFlushEvent(this._createContentChangedFlushEvent());

E
Erich Gamma 已提交
351 352 353
		this._emitContentChanged2(1, 1, endLineNumber, endColumn, oldModelValueLength, this.getValue(), false, false);
	}

J
Johannes Rieken 已提交
354
	public getValue(eol?: editorCommon.EndOfLinePreference, preserveBOM: boolean = false): string {
355
		this._assertNotDisposed();
E
Erich Gamma 已提交
356 357 358 359 360 361 362 363 364 365
		var fullModelRange = this.getFullModelRange();
		var fullModelValue = this.getValueInRange(fullModelRange, eol);

		if (preserveBOM) {
			return this._BOM + fullModelValue;
		}

		return fullModelValue;
	}

A
Alex Dima 已提交
366
	public getValueLength(eol?: editorCommon.EndOfLinePreference, preserveBOM: boolean = false): number {
367
		this._assertNotDisposed();
E
Erich Gamma 已提交
368 369 370 371 372 373 374 375 376 377
		var fullModelRange = this.getFullModelRange();
		var fullModelValue = this.getValueLengthInRange(fullModelRange, eol);

		if (preserveBOM) {
			return this._BOM.length + fullModelValue;
		}

		return fullModelValue;
	}

J
Johannes Rieken 已提交
378
	public getEmptiedValueInRange(rawRange: editorCommon.IRange, fillCharacter: string = '', eol: editorCommon.EndOfLinePreference = editorCommon.EndOfLinePreference.TextDefined): string {
379
		this._assertNotDisposed();
E
Erich Gamma 已提交
380 381 382 383 384 385 386 387 388 389 390 391 392
		var range = this.validateRange(rawRange);

		if (range.isEmpty()) {
			return '';
		}

		if (range.startLineNumber === range.endLineNumber) {
			return this._repeatCharacter(fillCharacter, range.endColumn - range.startColumn);
		}

		var lineEnding = this._getEndOfLine(eol),
			startLineIndex = range.startLineNumber - 1,
			endLineIndex = range.endLineNumber - 1,
J
Johannes Rieken 已提交
393
			resultLines: string[] = [];
E
Erich Gamma 已提交
394 395 396 397 398 399 400 401 402 403

		resultLines.push(this._repeatCharacter(fillCharacter, this._lines[startLineIndex].text.length - range.startColumn + 1));
		for (var i = startLineIndex + 1; i < endLineIndex; i++) {
			resultLines.push(this._repeatCharacter(fillCharacter, this._lines[i].text.length));
		}
		resultLines.push(this._repeatCharacter(fillCharacter, range.endColumn - 1));

		return resultLines.join(lineEnding);
	}

J
Johannes Rieken 已提交
404
	private _repeatCharacter(fillCharacter: string, count: number): string {
E
Erich Gamma 已提交
405 406 407 408 409 410 411
		var r = '';
		for (var i = 0; i < count; i++) {
			r += fillCharacter;
		}
		return r;
	}

J
Johannes Rieken 已提交
412
	public getValueInRange(rawRange: editorCommon.IRange, eol: editorCommon.EndOfLinePreference = editorCommon.EndOfLinePreference.TextDefined): string {
413
		this._assertNotDisposed();
E
Erich Gamma 已提交
414 415 416 417 418 419 420 421 422 423 424 425 426
		var range = this.validateRange(rawRange);

		if (range.isEmpty()) {
			return '';
		}

		if (range.startLineNumber === range.endLineNumber) {
			return this._lines[range.startLineNumber - 1].text.substring(range.startColumn - 1, range.endColumn - 1);
		}

		var lineEnding = this._getEndOfLine(eol),
			startLineIndex = range.startLineNumber - 1,
			endLineIndex = range.endLineNumber - 1,
J
Johannes Rieken 已提交
427
			resultLines: string[] = [];
E
Erich Gamma 已提交
428 429 430 431 432 433 434 435 436 437

		resultLines.push(this._lines[startLineIndex].text.substring(range.startColumn - 1));
		for (var i = startLineIndex + 1; i < endLineIndex; i++) {
			resultLines.push(this._lines[i].text);
		}
		resultLines.push(this._lines[endLineIndex].text.substring(0, range.endColumn - 1));

		return resultLines.join(lineEnding);
	}

J
Johannes Rieken 已提交
438
	public getValueLengthInRange(rawRange: editorCommon.IRange, eol: editorCommon.EndOfLinePreference = editorCommon.EndOfLinePreference.TextDefined): number {
439
		this._assertNotDisposed();
E
Erich Gamma 已提交
440 441 442 443 444 445 446 447 448 449
		var range = this.validateRange(rawRange);

		if (range.isEmpty()) {
			return 0;
		}

		if (range.startLineNumber === range.endLineNumber) {
			return (range.endColumn - range.startColumn);
		}

450 451 452
		let startOffset = this.getOffsetAt(new Position(range.startLineNumber, range.startColumn));
		let endOffset = this.getOffsetAt(new Position(range.endLineNumber, range.endColumn));
		return endOffset - startOffset;
E
Erich Gamma 已提交
453 454
	}

A
Alex Dima 已提交
455
	public isDominatedByLongLines(): boolean {
456
		this._assertNotDisposed();
E
Erich Gamma 已提交
457 458 459 460 461 462 463 464 465
		var smallLineCharCount = 0,
			longLineCharCount = 0,
			i: number,
			len: number,
			lines = this._lines,
			lineLength: number;

		for (i = 0, len = this._lines.length; i < len; i++) {
			lineLength = lines[i].text.length;
A
Alex Dima 已提交
466
			if (lineLength >= LONG_LINE_BOUNDARY) {
E
Erich Gamma 已提交
467 468 469 470 471 472 473 474 475 476
				longLineCharCount += lineLength;
			} else {
				smallLineCharCount += lineLength;
			}
		}

		return (longLineCharCount > smallLineCharCount);
	}

	public getLineCount(): number {
477
		this._assertNotDisposed();
E
Erich Gamma 已提交
478 479 480
		return this._lines.length;
	}

J
Johannes Rieken 已提交
481
	public getLineContent(lineNumber: number): string {
482
		this._assertNotDisposed();
E
Erich Gamma 已提交
483 484 485 486 487 488 489
		if (lineNumber < 1 || lineNumber > this.getLineCount()) {
			throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
		}

		return this._lines[lineNumber - 1].text;
	}

J
Johannes Rieken 已提交
490
	public getIndentLevel(lineNumber: number): number {
491
		this._assertNotDisposed();
492 493 494 495 496 497 498
		if (lineNumber < 1 || lineNumber > this.getLineCount()) {
			throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
		}

		return this._lines[lineNumber - 1].getIndentLevel();
	}

A
Alex Dima 已提交
499 500 501 502
	protected _resetIndentRanges(): void {
		this._indentRanges = null;
	}

A
Alex Dima 已提交
503
	private _getIndentRanges(): IndentRange[] {
A
Alex Dima 已提交
504 505 506
		if (!this._indentRanges) {
			this._indentRanges = computeRanges(this);
		}
A
Alex Dima 已提交
507 508 509 510
		return this._indentRanges;
	}

	public getIndentRanges(): IndentRange[] {
511
		this._assertNotDisposed();
A
Alex Dima 已提交
512 513 514 515
		let indentRanges = this._getIndentRanges();
		return IndentRange.deepCloneArr(indentRanges);
	}

J
Johannes Rieken 已提交
516
	private _toValidLineIndentGuide(lineNumber: number, indentGuide: number): number {
517 518 519 520 521 522 523 524
		let lineIndentLevel = this._lines[lineNumber - 1].getIndentLevel();
		if (lineIndentLevel === -1) {
			return indentGuide;
		}
		let maxIndentGuide = Math.ceil(lineIndentLevel / this._options.tabSize);
		return Math.min(maxIndentGuide, indentGuide);
	}

J
Johannes Rieken 已提交
525
	public getLineIndentGuide(lineNumber: number): number {
526
		this._assertNotDisposed();
A
Alex Dima 已提交
527 528 529 530 531 532 533 534 535
		if (lineNumber < 1 || lineNumber > this.getLineCount()) {
			throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
		}

		let indentRanges = this._getIndentRanges();

		for (let i = indentRanges.length - 1; i >= 0; i--) {
			let rng = indentRanges[i];

536
			if (rng.startLineNumber === lineNumber) {
537
				return this._toValidLineIndentGuide(lineNumber, Math.ceil(rng.indent / this._options.tabSize));
538
			}
A
Alex Dima 已提交
539
			if (rng.startLineNumber < lineNumber && lineNumber <= rng.endLineNumber) {
540
				return this._toValidLineIndentGuide(lineNumber, 1 + Math.floor(rng.indent / this._options.tabSize));
A
Alex Dima 已提交
541
			}
542
			if (rng.endLineNumber + 1 === lineNumber) {
543 544 545 546 547 548 549
				let bestIndent = rng.indent;
				while (i > 0) {
					i--;
					rng = indentRanges[i];
					if (rng.endLineNumber + 1 === lineNumber) {
						bestIndent = rng.indent;
					}
550
				}
551
				return this._toValidLineIndentGuide(lineNumber, Math.ceil(bestIndent / this._options.tabSize));
552
			}
A
Alex Dima 已提交
553 554 555
		}

		return 0;
A
Alex Dima 已提交
556 557
	}

E
Erich Gamma 已提交
558
	public getLinesContent(): string[] {
559
		this._assertNotDisposed();
E
Erich Gamma 已提交
560 561 562 563 564 565 566 567
		var r: string[] = [];
		for (var i = 0, len = this._lines.length; i < len; i++) {
			r[i] = this._lines[i].text;
		}
		return r;
	}

	public getEOL(): string {
568
		this._assertNotDisposed();
E
Erich Gamma 已提交
569 570 571
		return this._EOL;
	}

A
Alex Dima 已提交
572
	public setEOL(eol: editorCommon.EndOfLineSequence): void {
573
		this._assertNotDisposed();
A
Alex Dima 已提交
574
		var newEOL = (eol === editorCommon.EndOfLineSequence.CRLF ? '\r\n' : '\n');
E
Erich Gamma 已提交
575 576 577 578 579 580 581 582 583 584 585
		if (this._EOL === newEOL) {
			// Nothing to do
			return;
		}

		var oldFullModelRange = this.getFullModelRange();
		var oldModelValueLength = this.getValueLengthInRange(oldFullModelRange);
		var endLineNumber = this.getLineCount();
		var endColumn = this.getLineMaxColumn(endLineNumber);

		this._EOL = newEOL;
586
		this._lineStarts = null;
E
Erich Gamma 已提交
587 588
		this._increaseVersionId();

589
		this._emitModelContentChangedFlushEvent(this._createContentChangedFlushEvent());
E
Erich Gamma 已提交
590 591 592 593

		this._emitContentChanged2(1, 1, endLineNumber, endColumn, oldModelValueLength, this.getValue(), false, false);
	}

J
Johannes Rieken 已提交
594
	public getLineMinColumn(lineNumber: number): number {
595
		this._assertNotDisposed();
E
Erich Gamma 已提交
596 597 598
		return 1;
	}

J
Johannes Rieken 已提交
599
	public getLineMaxColumn(lineNumber: number): number {
600
		this._assertNotDisposed();
E
Erich Gamma 已提交
601 602 603 604 605 606 607 608
		if (lineNumber < 1 || lineNumber > this.getLineCount()) {
			throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
		}

		return this._lines[lineNumber - 1].text.length + 1;
	}

	public getLineFirstNonWhitespaceColumn(lineNumber: number): number {
609
		this._assertNotDisposed();
E
Erich Gamma 已提交
610 611 612 613
		if (lineNumber < 1 || lineNumber > this.getLineCount()) {
			throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
		}

A
Alex Dima 已提交
614
		var result = strings.firstNonWhitespaceIndex(this._lines[lineNumber - 1].text);
E
Erich Gamma 已提交
615 616 617 618 619 620 621
		if (result === -1) {
			return 0;
		}
		return result + 1;
	}

	public getLineLastNonWhitespaceColumn(lineNumber: number): number {
622
		this._assertNotDisposed();
E
Erich Gamma 已提交
623 624 625 626
		if (lineNumber < 1 || lineNumber > this.getLineCount()) {
			throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
		}

A
Alex Dima 已提交
627
		var result = strings.lastNonWhitespaceIndex(this._lines[lineNumber - 1].text);
E
Erich Gamma 已提交
628 629 630 631 632 633
		if (result === -1) {
			return 0;
		}
		return result + 2;
	}

J
Johannes Rieken 已提交
634
	public validateLineNumber(lineNumber: number): number {
635
		this._assertNotDisposed();
E
Erich Gamma 已提交
636 637 638 639 640 641 642 643 644
		if (lineNumber < 1) {
			lineNumber = 1;
		}
		if (lineNumber > this._lines.length) {
			lineNumber = this._lines.length;
		}
		return lineNumber;
	}

645 646 647
	/**
	 * @param strict Do NOT allow a position inside a high-low surrogate pair
	 */
J
Johannes Rieken 已提交
648
	private _validatePosition(_lineNumber: number, _column: number, strict: boolean): Position {
649 650
		const lineNumber = Math.floor(typeof _lineNumber === 'number' ? _lineNumber : 1);
		const column = Math.floor(typeof _column === 'number' ? _column : 1);
E
Erich Gamma 已提交
651 652

		if (lineNumber < 1) {
653
			return new Position(1, 1);
E
Erich Gamma 已提交
654
		}
655 656 657

		if (lineNumber > this._lines.length) {
			return new Position(this._lines.length, this.getLineMaxColumn(this._lines.length));
E
Erich Gamma 已提交
658
		}
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675

		if (column <= 1) {
			return new Position(lineNumber, 1);
		}

		const 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
			const charCodeBefore = this._lines[lineNumber - 1].text.charCodeAt(column - 2);
			if (strings.isHighSurrogate(charCodeBefore)) {
				return new Position(lineNumber, column - 1);
A
aioute Gao 已提交
676
			}
E
Erich Gamma 已提交
677 678 679 680 681
		}

		return new Position(lineNumber, column);
	}

J
Johannes Rieken 已提交
682
	public validatePosition(position: editorCommon.IPosition): Position {
683
		this._assertNotDisposed();
684 685 686
		return this._validatePosition(position.lineNumber, position.column, true);
	}

J
Johannes Rieken 已提交
687
	public validateRange(_range: editorCommon.IRange): Range {
688
		this._assertNotDisposed();
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
		const start = this._validatePosition(_range.startLineNumber, _range.startColumn, false);
		const end = this._validatePosition(_range.endLineNumber, _range.endColumn, false);

		const startLineNumber = start.lineNumber;
		const startColumn = start.column;
		const endLineNumber = end.lineNumber;
		const endColumn = end.column;

		const startLineText = this._lines[startLineNumber - 1].text;
		const endLineText = this._lines[endLineNumber - 1].text;

		const charCodeBeforeStart = (startColumn > 1 ? startLineText.charCodeAt(startColumn - 2) : 0);
		const charCodeBeforeEnd = (endColumn > 1 && endColumn <= endLineText.length ? endLineText.charCodeAt(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);
E
Erich Gamma 已提交
727 728
	}

J
Johannes Rieken 已提交
729
	public modifyPosition(rawPosition: editorCommon.IPosition, offset: number): Position {
730
		this._assertNotDisposed();
731
		return this.getPositionAt(this.getOffsetAt(rawPosition) + offset);
E
Erich Gamma 已提交
732 733
	}

734
	public getFullModelRange(): Range {
735
		this._assertNotDisposed();
E
Erich Gamma 已提交
736 737 738 739
		var lineCount = this.getLineCount();
		return new Range(1, 1, lineCount, this.getLineMaxColumn(lineCount));
	}

J
Johannes Rieken 已提交
740
	protected _emitModelContentChangedFlushEvent(e: editorCommon.IModelContentChangedFlushEvent): void {
E
Erich Gamma 已提交
741
		if (!this._isDisposing) {
A
Alex Dima 已提交
742
			this.emit(editorCommon.EventType.ModelRawContentChanged, e);
E
Erich Gamma 已提交
743 744 745
		}
	}

J
Johannes Rieken 已提交
746
	public static toRawText(rawText: string, opts: editorCommon.ITextModelCreationOptions): editorCommon.IRawText {
E
Erich Gamma 已提交
747
		// Count the number of lines that end with \r\n
A
Alex Dima 已提交
748 749
		let carriageReturnCnt = 0;
		let lastCarriageReturnIndex = -1;
E
Erich Gamma 已提交
750 751 752 753
		while ((lastCarriageReturnIndex = rawText.indexOf('\r', lastCarriageReturnIndex + 1)) !== -1) {
			carriageReturnCnt++;
		}

A
Alex Dima 已提交
754 755
		const containsRTL = strings.containsRTL(rawText);

A
Alex Dima 已提交
756
		// Split the text into lines
A
Alex Dima 已提交
757
		const lines = rawText.split(/\r\n|\r|\n/);
E
Erich Gamma 已提交
758 759

		// Remove the BOM (if present)
A
Alex Dima 已提交
760
		let BOM = '';
A
Alex Dima 已提交
761 762
		if (strings.startsWithUTF8BOM(lines[0])) {
			BOM = strings.UTF8_BOM_CHARACTER;
E
Erich Gamma 已提交
763 764 765
			lines[0] = lines[0].substr(1);
		}

A
Alex Dima 已提交
766 767
		const lineFeedCnt = lines.length - 1;
		let EOL = '';
E
Erich Gamma 已提交
768 769
		if (lineFeedCnt === 0) {
			// This is an empty file or a file with precisely one line
770
			EOL = (opts.defaultEOL === editorCommon.DefaultEndOfLine.LF ? '\n' : '\r\n');
E
Erich Gamma 已提交
771 772 773 774 775 776 777 778
		} else if (carriageReturnCnt > lineFeedCnt / 2) {
			// More than half of the file contains \r\n ending lines
			EOL = '\r\n';
		} else {
			// At least one line more ends in \n
			EOL = '\n';
		}

779
		let resolvedOpts: editorCommon.TextModelResolvedOptions;
780
		if (opts.detectIndentation) {
781
			let guessedIndentation = guessIndentation(lines, opts.tabSize, opts.insertSpaces);
782
			resolvedOpts = new editorCommon.TextModelResolvedOptions({
783 784
				tabSize: guessedIndentation.tabSize,
				insertSpaces: guessedIndentation.insertSpaces,
785
				trimAutoWhitespace: opts.trimAutoWhitespace,
786
				defaultEOL: opts.defaultEOL
787
			});
788
		} else {
789
			resolvedOpts = new editorCommon.TextModelResolvedOptions({
790 791
				tabSize: opts.tabSize,
				insertSpaces: opts.insertSpaces,
792
				trimAutoWhitespace: opts.trimAutoWhitespace,
793
				defaultEOL: opts.defaultEOL
794
			});
795 796
		}

E
Erich Gamma 已提交
797 798 799 800
		return {
			BOM: BOM,
			EOL: EOL,
			lines: lines,
801
			length: rawText.length,
A
Alex Dima 已提交
802
			containsRTL: containsRTL,
803
			options: resolvedOpts
E
Erich Gamma 已提交
804 805 806
		};
	}

J
Johannes Rieken 已提交
807
	protected _constructLines(rawText: editorCommon.IRawText): void {
808 809 810
		const tabSize = rawText.options.tabSize;
		let rawLines = rawText.lines;
		let modelLines: ModelLine[] = [];
E
Erich Gamma 已提交
811

812 813
		for (let i = 0, len = rawLines.length; i < len; i++) {
			modelLines[i] = new ModelLine(i + 1, rawLines[i], tabSize);
E
Erich Gamma 已提交
814 815
		}
		this._BOM = rawText.BOM;
A
Alex Dima 已提交
816
		this._mightContainRTL = rawText.containsRTL;
E
Erich Gamma 已提交
817 818
		this._EOL = rawText.EOL;
		this._lines = modelLines;
819
		this._lineStarts = null;
A
Alex Dima 已提交
820
		this._resetIndentRanges();
E
Erich Gamma 已提交
821 822
	}

J
Johannes Rieken 已提交
823
	private _getEndOfLine(eol: editorCommon.EndOfLinePreference): string {
E
Erich Gamma 已提交
824
		switch (eol) {
A
Alex Dima 已提交
825
			case editorCommon.EndOfLinePreference.LF:
E
Erich Gamma 已提交
826
				return '\n';
A
Alex Dima 已提交
827
			case editorCommon.EndOfLinePreference.CRLF:
E
Erich Gamma 已提交
828
				return '\r\n';
A
Alex Dima 已提交
829
			case editorCommon.EndOfLinePreference.TextDefined:
E
Erich Gamma 已提交
830 831 832 833 834
				return this.getEOL();
		}
		throw new Error('Unknown EOL preference');
	}

J
Johannes Rieken 已提交
835
	public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wholeWord: boolean, limitResultCount: number = LIMIT_FIND_COUNT): Range[] {
836
		this._assertNotDisposed();
E
Erich Gamma 已提交
837

J
Johannes Rieken 已提交
838
		let searchRange: Range;
E
Erich Gamma 已提交
839
		if (Range.isIRange(rawSearchScope)) {
A
Alex Dima 已提交
840
			searchRange = this.validateRange(rawSearchScope);
E
Erich Gamma 已提交
841 842 843 844
		} else {
			searchRange = this.getFullModelRange();
		}

A
Alex Dima 已提交
845
		return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), searchRange, limitResultCount);
E
Erich Gamma 已提交
846 847
	}

J
Johannes Rieken 已提交
848
	public findNextMatch(searchString: string, rawSearchStart: editorCommon.IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
849
		this._assertNotDisposed();
A
Alex Dima 已提交
850
		return TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), rawSearchStart);
E
Erich Gamma 已提交
851 852
	}

J
Johannes Rieken 已提交
853
	public findPreviousMatch(searchString: string, rawSearchStart: editorCommon.IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
854
		this._assertNotDisposed();
A
Alex Dima 已提交
855
		return TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wholeWord), rawSearchStart);
E
Erich Gamma 已提交
856
	}
857
}
858 859 860

export class RawText {

J
Johannes Rieken 已提交
861
	public static fromString(rawText: string, opts: editorCommon.ITextModelCreationOptions): editorCommon.IRawText {
862 863 864
		return TextModel.toRawText(rawText, opts);
	}

J
Johannes Rieken 已提交
865
	public static fromStringWithModelOptions(rawText: string, model: editorCommon.IModel): editorCommon.IRawText {
866 867 868 869
		let opts = model.getOptions();
		return TextModel.toRawText(rawText, {
			tabSize: opts.tabSize,
			insertSpaces: opts.insertSpaces,
870
			trimAutoWhitespace: opts.trimAutoWhitespace,
871 872 873 874 875
			detectIndentation: false,
			defaultEOL: opts.defaultEOL
		});
	}

A
aioute Gao 已提交
876
}