textModel.ts 36.1 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 17
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';
import { CharCode } from 'vs/base/common/charCode';
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
	private _ensureLineStarts(): void {
		if (!this._lineStarts) {
J
Johannes Rieken 已提交
199
			const lineStartValues: number[] = [];
200 201 202 203 204 205 206 207 208
			const eolLength = this._EOL.length;
			for (let i = 0, len = this._lines.length; i < len; i++) {
				lineStartValues.push(this._lines[i].text.length + eolLength);
			}
			this._lineStarts = new PrefixSumComputer(lineStartValues);
		}
	}

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

	public getPositionAt(offset: number): Position {
216
		this._assertNotDisposed();
217 218 219 220 221 222 223 224 225 226 227 228
		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 已提交
229
	protected _increaseVersionId(): void {
E
Erich Gamma 已提交
230 231 232
		this._setVersionId(this._versionId + 1);
	}

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

J
Johannes Rieken 已提交
238
	protected _overwriteAlternativeVersionId(newAlternativeVersionId: number): void {
E
Erich Gamma 已提交
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
		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 已提交
256
	protected _createContentChangedFlushEvent(): editorCommon.IModelContentChangedFlushEvent {
E
Erich Gamma 已提交
257
		return {
A
Alex Dima 已提交
258
			changeType: editorCommon.EventType.ModelRawContentChangedFlush,
259 260
			detail: this.toRawText(),
			versionId: this._versionId,
E
Erich Gamma 已提交
261 262 263 264 265 266
			// TODO@Alex -> remove these fields from here
			isUndoing: false,
			isRedoing: false
		};
	}

J
Johannes Rieken 已提交
267 268
	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 已提交
269 270 271
			range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
			rangeLength: rangeLength,
			text: text,
272
			eol: this._EOL,
E
Erich Gamma 已提交
273 274 275 276 277
			versionId: this.getVersionId(),
			isUndoing: isUndoing,
			isRedoing: isRedoing
		};
		if (!this._isDisposing) {
A
Alex Dima 已提交
278
			this.emit(editorCommon.EventType.ModelContentChanged2, e);
E
Erich Gamma 已提交
279 280 281
		}
	}

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

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

299
	public equals(other: editorCommon.IRawText): boolean {
300
		this._assertNotDisposed();
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
		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 已提交
318
	public setValue(value: string): void {
319
		this._assertNotDisposed();
A
Alex Dima 已提交
320 321 322
		if (value === null) {
			// There's nothing to do
			return;
323
		}
A
Alex Dima 已提交
324 325 326 327 328 329 330 331
		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
		});
332 333 334
		this.setValueFromRawText(rawText);
	}

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

346 347 348 349
		this._resetValue(newValue);

		this._emitModelContentChangedFlushEvent(this._createContentChangedFlushEvent());

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

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

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

		return fullModelValue;
	}

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

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

		return fullModelValue;
	}

J
Johannes Rieken 已提交
377
	public getEmptiedValueInRange(rawRange: editorCommon.IRange, fillCharacter: string = '', eol: editorCommon.EndOfLinePreference = editorCommon.EndOfLinePreference.TextDefined): string {
378
		this._assertNotDisposed();
E
Erich Gamma 已提交
379 380 381 382 383 384 385 386 387 388 389 390 391
		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 已提交
392
			resultLines: string[] = [];
E
Erich Gamma 已提交
393 394 395 396 397 398 399 400 401 402

		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 已提交
403
	private _repeatCharacter(fillCharacter: string, count: number): string {
E
Erich Gamma 已提交
404 405 406 407 408 409 410
		var r = '';
		for (var i = 0; i < count; i++) {
			r += fillCharacter;
		}
		return r;
	}

J
Johannes Rieken 已提交
411
	public getValueInRange(rawRange: editorCommon.IRange, eol: editorCommon.EndOfLinePreference = editorCommon.EndOfLinePreference.TextDefined): string {
412
		this._assertNotDisposed();
E
Erich Gamma 已提交
413 414 415 416 417 418 419 420 421 422 423 424 425
		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 已提交
426
			resultLines: string[] = [];
E
Erich Gamma 已提交
427 428 429 430 431 432 433 434 435 436

		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 已提交
437
	public getValueLengthInRange(rawRange: editorCommon.IRange, eol: editorCommon.EndOfLinePreference = editorCommon.EndOfLinePreference.TextDefined): number {
438
		this._assertNotDisposed();
E
Erich Gamma 已提交
439 440 441 442 443 444 445 446 447 448
		var range = this.validateRange(rawRange);

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

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

449 450 451
		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 已提交
452 453
	}

A
Alex Dima 已提交
454
	public isDominatedByLongLines(): boolean {
455
		this._assertNotDisposed();
E
Erich Gamma 已提交
456 457 458 459 460 461 462 463 464
		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 已提交
465
			if (lineLength >= LONG_LINE_BOUNDARY) {
E
Erich Gamma 已提交
466 467 468 469 470 471 472 473 474 475
				longLineCharCount += lineLength;
			} else {
				smallLineCharCount += lineLength;
			}
		}

		return (longLineCharCount > smallLineCharCount);
	}

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

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

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

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

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

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

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

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

J
Johannes Rieken 已提交
515
	private _toValidLineIndentGuide(lineNumber: number, indentGuide: number): number {
516 517 518 519 520 521 522 523
		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 已提交
524
	public getLineIndentGuide(lineNumber: number): number {
525
		this._assertNotDisposed();
A
Alex Dima 已提交
526 527 528 529 530 531 532 533 534
		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];

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

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

E
Erich Gamma 已提交
557
	public getLinesContent(): string[] {
558
		this._assertNotDisposed();
E
Erich Gamma 已提交
559 560 561 562 563 564 565 566
		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 {
567
		this._assertNotDisposed();
E
Erich Gamma 已提交
568 569 570
		return this._EOL;
	}

A
Alex Dima 已提交
571
	public setEOL(eol: editorCommon.EndOfLineSequence): void {
572
		this._assertNotDisposed();
A
Alex Dima 已提交
573
		var newEOL = (eol === editorCommon.EndOfLineSequence.CRLF ? '\r\n' : '\n');
E
Erich Gamma 已提交
574 575 576 577 578 579 580 581 582 583 584
		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;
585
		this._lineStarts = null;
E
Erich Gamma 已提交
586 587
		this._increaseVersionId();

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

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

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

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

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

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

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

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

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

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

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

		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 已提交
675
			}
E
Erich Gamma 已提交
676 677 678 679 680
		}

		return new Position(lineNumber, column);
	}

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

J
Johannes Rieken 已提交
686
	public validateRange(_range: editorCommon.IRange): Range {
687
		this._assertNotDisposed();
688 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
		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 已提交
726 727
	}

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

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

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

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

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

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

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

A
Alex Dima 已提交
765 766
		const lineFeedCnt = lines.length - 1;
		let EOL = '';
E
Erich Gamma 已提交
767 768
		if (lineFeedCnt === 0) {
			// This is an empty file or a file with precisely one line
769
			EOL = (opts.defaultEOL === editorCommon.DefaultEndOfLine.LF ? '\n' : '\r\n');
E
Erich Gamma 已提交
770 771 772 773 774 775 776 777
		} 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';
		}

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

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

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

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

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

834
	private static _isMultilineRegexSource(searchString: string): boolean {
835 836 837 838 839 840 841
		if (!searchString || searchString.length === 0) {
			return false;
		}

		for (let i = 0, len = searchString.length; i < len; i++) {
			let chCode = searchString.charCodeAt(i);

A
Alex Dima 已提交
842
			if (chCode === CharCode.Backslash) {
843 844 845 846 847 848 849 850 851 852

				// move to next char
				i++;

				if (i >= len) {
					// string ends with a \
					break;
				}

				let nextChCode = searchString.charCodeAt(i);
A
Alex Dima 已提交
853
				if (nextChCode === CharCode.n || nextChCode === CharCode.r) {
854 855 856 857 858 859 860 861
					return true;
				}
			}
		}

		return false;
	}

J
Johannes Rieken 已提交
862
	public static parseSearchRequest(searchString: string, isRegex: boolean, matchCase: boolean, wholeWord: boolean): RegExp {
863 864 865 866 867
		if (searchString === '') {
			return null;
		}

		// Try to create a RegExp out of the params
868 869 870 871 872 873 874 875
		let multiline: boolean;
		if (isRegex) {
			multiline = TextModel._isMultilineRegexSource(searchString);
		} else {
			multiline = (searchString.indexOf('\n') >= 0);
		}

		let regex: RegExp = null;
876
		try {
J
Johannes Rieken 已提交
877
			regex = strings.createRegExp(searchString, isRegex, { matchCase, wholeWord, multiline, global: true });
878 879 880 881
		} catch (err) {
			return null;
		}

E
Erich Gamma 已提交
882
		if (!regex) {
883 884 885
			return null;
		}

S
Sandeep Somavarapu 已提交
886
		return regex;
887 888
	}

J
Johannes Rieken 已提交
889
	public findMatches(searchString: string, rawSearchScope: any, isRegex: boolean, matchCase: boolean, wholeWord: boolean, limitResultCount: number = LIMIT_FIND_COUNT): Range[] {
890
		this._assertNotDisposed();
S
Sandeep Somavarapu 已提交
891 892
		let regex = TextModel.parseSearchRequest(searchString, isRegex, matchCase, wholeWord);
		if (!regex) {
E
Erich Gamma 已提交
893 894 895
			return [];
		}

J
Johannes Rieken 已提交
896
		let searchRange: Range;
E
Erich Gamma 已提交
897
		if (Range.isIRange(rawSearchScope)) {
A
Alex Dima 已提交
898
			searchRange = this.validateRange(rawSearchScope);
E
Erich Gamma 已提交
899 900 901 902
		} else {
			searchRange = this.getFullModelRange();
		}

S
Sandeep Somavarapu 已提交
903 904
		if (regex.multiline) {
			return this._doFindMatchesMultiline(searchRange, regex, limitResultCount);
905
		}
S
Sandeep Somavarapu 已提交
906
		return this._doFindMatchesLineByLine(searchRange, regex, limitResultCount);
907 908
	}

J
Johannes Rieken 已提交
909
	private _doFindMatchesMultiline(searchRange: Range, searchRegex: RegExp, limitResultCount: number): Range[] {
910 911 912 913 914 915 916 917
		let deltaOffset = this.getOffsetAt(searchRange.getStartPosition());
		let text = this.getValueInRange(searchRange);

		let result: Range[] = [];
		let prevStartOffset = 0;
		let prevEndOffset = 0;
		let counter = 0;

J
Johannes Rieken 已提交
918
		let m: RegExpExecArray;
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
		while ((m = searchRegex.exec(text))) {
			let startOffset = deltaOffset + m.index;
			let endOffset = startOffset + m[0].length;

			if (prevStartOffset === startOffset && prevEndOffset === endOffset) {
				// Exit early if the regex matches the same range
				return result;
			}

			let startPosition = this.getPositionAt(startOffset);
			let endPosition = this.getPositionAt(endOffset);

			result[counter++] = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column);
			if (counter >= limitResultCount) {
				return result;
			}

			prevStartOffset = startOffset;
			prevEndOffset = endOffset;
		}

		return result;
	}

J
Johannes Rieken 已提交
943 944
	private _doFindMatchesLineByLine(searchRange: Range, searchRegex: RegExp, limitResultCount: number): Range[] {
		let result: Range[] = [];
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
		let text: string;
		let counter = 0;

		// Early case for a search range that starts & stops on the same line number
		if (searchRange.startLineNumber === searchRange.endLineNumber) {
			text = this._lines[searchRange.startLineNumber - 1].text.substring(searchRange.startColumn - 1, searchRange.endColumn - 1);
			counter = this._findMatchesInLine(searchRegex, text, searchRange.startLineNumber, searchRange.startColumn - 1, counter, result, limitResultCount);
			return result;
		}

		// Collect results from first line
		text = this._lines[searchRange.startLineNumber - 1].text.substring(searchRange.startColumn - 1);
		counter = this._findMatchesInLine(searchRegex, text, searchRange.startLineNumber, searchRange.startColumn - 1, counter, result, limitResultCount);

		// Collect results from middle lines
		for (let lineNumber = searchRange.startLineNumber + 1; lineNumber < searchRange.endLineNumber && counter < limitResultCount; lineNumber++) {
			counter = this._findMatchesInLine(searchRegex, this._lines[lineNumber - 1].text, lineNumber, 0, counter, result, limitResultCount);
		}

		// Collect results from last line
		if (counter < limitResultCount) {
			text = this._lines[searchRange.endLineNumber - 1].text.substring(0, searchRange.endColumn - 1);
			counter = this._findMatchesInLine(searchRegex, text, searchRange.endLineNumber, 0, counter, result, limitResultCount);
		}

		return result;
E
Erich Gamma 已提交
971 972
	}

J
Johannes Rieken 已提交
973
	public findNextMatch(searchString: string, rawSearchStart: editorCommon.IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
974
		this._assertNotDisposed();
S
Sandeep Somavarapu 已提交
975 976
		let regex = TextModel.parseSearchRequest(searchString, isRegex, matchCase, wholeWord);
		if (!regex) {
E
Erich Gamma 已提交
977 978 979
			return null;
		}

980
		let searchStart = this.validatePosition(rawSearchStart);
S
Sandeep Somavarapu 已提交
981 982
		if (regex.multiline) {
			return this._doFindNextMatchMultiline(searchStart, regex);
983
		}
S
Sandeep Somavarapu 已提交
984
		return this._doFindNextMatchLineByLine(searchStart, regex);
985 986 987

	}

S
Sandeep Somavarapu 已提交
988 989 990 991 992
	private _doFindNextMatchMultiline(searchStart: Position, searchRegex: RegExp): Range {
		let searchTextStart: editorCommon.IPosition = { lineNumber: searchStart.lineNumber, column: 1 };
		let deltaOffset = this.getOffsetAt(searchTextStart);
		let text = this.getValueInRange(new Range(searchTextStart.lineNumber, searchTextStart.column, this.getLineCount(), this.getLineMaxColumn(this.getLineCount())));
		searchRegex.lastIndex = searchStart.column - 1;
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
		let m = searchRegex.exec(text);
		if (m) {
			let startOffset = deltaOffset + m.index;
			let endOffset = startOffset + m[0].length;
			let startPosition = this.getPositionAt(startOffset);
			let endPosition = this.getPositionAt(endOffset);
			return new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column);
		}

		if (searchStart.lineNumber !== 1 || searchStart.column !== -1) {
			// Try again from the top
			return this._doFindNextMatchMultiline(new Position(1, 1), searchRegex);
		}

		return null;
	}

J
Johannes Rieken 已提交
1010
	private _doFindNextMatchLineByLine(searchStart: Position, searchRegex: RegExp): Range {
1011 1012 1013 1014
		let lineCount = this.getLineCount();
		let startLineNumber = searchStart.lineNumber;
		let text: string;
		let r: Range;
E
Erich Gamma 已提交
1015 1016

		// Look in first line
S
Sandeep Somavarapu 已提交
1017 1018
		text = this._lines[startLineNumber - 1].text;
		r = this._findFirstMatchInLine(searchRegex, text, startLineNumber, searchStart.column);
E
Erich Gamma 已提交
1019 1020 1021 1022
		if (r) {
			return r;
		}

1023 1024
		for (let i = 1; i <= lineCount; i++) {
			let lineIndex = (startLineNumber + i - 1) % lineCount;
E
Erich Gamma 已提交
1025
			text = this._lines[lineIndex].text;
S
Sandeep Somavarapu 已提交
1026
			r = this._findFirstMatchInLine(searchRegex, text, lineIndex + 1, 1);
E
Erich Gamma 已提交
1027 1028 1029 1030 1031 1032 1033 1034
			if (r) {
				return r;
			}
		}

		return null;
	}

J
Johannes Rieken 已提交
1035
	public findPreviousMatch(searchString: string, rawSearchStart: editorCommon.IPosition, isRegex: boolean, matchCase: boolean, wholeWord: boolean): Range {
1036
		this._assertNotDisposed();
S
Sandeep Somavarapu 已提交
1037 1038
		let regex = TextModel.parseSearchRequest(searchString, isRegex, matchCase, wholeWord);
		if (!regex) {
1039 1040 1041
			return null;
		}

1042
		let searchStart = this.validatePosition(rawSearchStart);
S
Sandeep Somavarapu 已提交
1043 1044
		if (regex.multiline) {
			return this._doFindPreviousMatchMultiline(searchStart, regex);
1045
		}
S
Sandeep Somavarapu 已提交
1046
		return this._doFindPreviousMatchLineByLine(searchStart, regex);
1047 1048
	}

J
Johannes Rieken 已提交
1049
	private _doFindPreviousMatchMultiline(searchStart: Position, searchRegex: RegExp): Range {
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
		let matches = this._doFindMatchesMultiline(new Range(1, 1, searchStart.lineNumber, searchStart.column), searchRegex, 10 * LIMIT_FIND_COUNT);
		if (matches.length > 0) {
			return matches[matches.length - 1];
		}

		if (searchStart.lineNumber !== this.getLineCount() || searchStart.column !== this.getLineMaxColumn(this.getLineCount())) {
			// Try again with all content
			return this._doFindPreviousMatchMultiline(new Position(this.getLineCount(), this.getLineMaxColumn(this.getLineCount())), searchRegex);
		}

		return null;
	}

J
Johannes Rieken 已提交
1063
	private _doFindPreviousMatchLineByLine(searchStart: Position, searchRegex: RegExp): Range {
1064 1065 1066 1067
		let lineCount = this.getLineCount();
		let startLineNumber = searchStart.lineNumber;
		let text: string;
		let r: Range;
1068 1069 1070

		// Look in first line
		text = this._lines[startLineNumber - 1].text.substring(0, searchStart.column - 1);
1071
		r = this._findLastMatchInLine(searchRegex, text, startLineNumber);
1072 1073 1074 1075
		if (r) {
			return r;
		}

1076
		for (var i = 1; i <= lineCount; i++) {
1077 1078
			var lineIndex = (lineCount + startLineNumber - i - 1) % lineCount;
			text = this._lines[lineIndex].text;
1079
			r = this._findLastMatchInLine(searchRegex, text, lineIndex + 1);
1080 1081 1082 1083 1084 1085 1086 1087
			if (r) {
				return r;
			}
		}

		return null;
	}

S
Sandeep Somavarapu 已提交
1088 1089 1090 1091 1092
	private _findFirstMatchInLine(searchRegex: RegExp, text: string, lineNumber: number, fromColumn: number): Range {
		// Set regex to search from column
		searchRegex.lastIndex = fromColumn - 1;
		var m: RegExpExecArray = searchRegex.exec(text);
		return m ? new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length) : null;
E
Erich Gamma 已提交
1093 1094
	}

J
Johannes Rieken 已提交
1095
	private _findLastMatchInLine(searchRegex: RegExp, text: string, lineNumber: number): Range {
1096
		let bestResult: Range = null;
J
Johannes Rieken 已提交
1097
		let m: RegExpExecArray;
1098 1099 1100 1101 1102 1103
		while ((m = searchRegex.exec(text))) {
			let result = new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length);
			if (result.equalsRange(bestResult)) {
				break;
			}
			bestResult = result;
1104 1105 1106 1107
			if (m.index + m[0].length === text.length) {
				// Reached the end of the line
				break;
			}
1108 1109 1110 1111
		}
		return bestResult;
	}

J
Johannes Rieken 已提交
1112 1113
	private _findMatchesInLine(searchRegex: RegExp, text: string, lineNumber: number, deltaOffset: number, counter: number, result: Range[], limitResultCount: number): number {
		var m: RegExpExecArray;
1114 1115
		// Reset regex to search from the beginning
		searchRegex.lastIndex = 0;
E
Erich Gamma 已提交
1116 1117 1118
		do {
			m = searchRegex.exec(text);
			if (m) {
1119 1120
				var range = new Range(lineNumber, m.index + 1 + deltaOffset, lineNumber, m.index + 1 + m[0].length + deltaOffset);
				if (range.equalsRange(result[result.length - 1])) {
1121
					// Exit early if the regex matches the same range
1122 1123 1124
					return counter;
				}
				result.push(range);
E
Erich Gamma 已提交
1125 1126 1127 1128
				counter++;
				if (counter >= limitResultCount) {
					return counter;
				}
1129 1130 1131 1132
				if (m.index + m[0].length === text.length) {
					// Reached the end of the line
					return counter;
				}
E
Erich Gamma 已提交
1133
			}
J
Johannes Rieken 已提交
1134
		} while (m);
E
Erich Gamma 已提交
1135 1136
		return counter;
	}
1137
}
1138 1139 1140

export class RawText {

J
Johannes Rieken 已提交
1141
	public static fromString(rawText: string, opts: editorCommon.ITextModelCreationOptions): editorCommon.IRawText {
1142 1143 1144
		return TextModel.toRawText(rawText, opts);
	}

J
Johannes Rieken 已提交
1145
	public static fromStringWithModelOptions(rawText: string, model: editorCommon.IModel): editorCommon.IRawText {
1146 1147 1148 1149
		let opts = model.getOptions();
		return TextModel.toRawText(rawText, {
			tabSize: opts.tabSize,
			insertSpaces: opts.insertSpaces,
1150
			trimAutoWhitespace: opts.trimAutoWhitespace,
1151 1152 1153 1154 1155
			detectIndentation: false,
			defaultEOL: opts.defaultEOL
		});
	}

A
aioute Gao 已提交
1156
}