model.test.ts 16.3 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';

A
Alex Dima 已提交
7
import * as assert from 'assert';
J
Johannes Rieken 已提交
8 9 10
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
A
Alex Dima 已提交
11
import {
12 13
	ModelRawContentChangedEvent, ModelRawFlush, ModelRawLineChanged,
	ModelRawLinesDeleted, ModelRawLinesInserted
14
} from 'vs/editor/common/model/textModelEvents';
A
Alex Dima 已提交
15
import { TextModel } from 'vs/editor/common/model/textModel';
M
Matt Bierner 已提交
16 17 18 19 20 21
import { LanguageIdentifier, TokenizationRegistry, IState, MetadataConsts } from 'vs/editor/common/modes';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { dispose, Disposable } from 'vs/base/common/lifecycle';
E
Erich Gamma 已提交
22 23 24 25 26 27 28 29 30 31 32

// --------- utils

var LINE1 = 'My First Line';
var LINE2 = '\t\tMy Second Line';
var LINE3 = '    Third Line';
var LINE4 = '';
var LINE5 = '1';

suite('Editor Model - Model', () => {

A
Alex Dima 已提交
33
	var thisModel: TextModel;
E
Erich Gamma 已提交
34 35 36 37 38 39 40 41

	setup(() => {
		var text =
			LINE1 + '\r\n' +
			LINE2 + '\n' +
			LINE3 + '\n' +
			LINE4 + '\r\n' +
			LINE5;
A
Alex Dima 已提交
42
		thisModel = TextModel.createFromString(text);
E
Erich Gamma 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
	});

	teardown(() => {
		thisModel.dispose();
	});

	// --------- insert text

	test('model getValue', () => {
		assert.equal(thisModel.getValue(), 'My First Line\n\t\tMy Second Line\n    Third Line\n\n1');
	});

	test('model insert empty text', () => {
		thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '')]);
		assert.equal(thisModel.getLineCount(), 5);
		assert.equal(thisModel.getLineContent(1), 'My First Line');
	});

	test('model insert text without newline 1', () => {
		thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'foo ')]);
		assert.equal(thisModel.getLineCount(), 5);
		assert.equal(thisModel.getLineContent(1), 'foo My First Line');
	});

	test('model insert text without newline 2', () => {
		thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' foo')]);
		assert.equal(thisModel.getLineCount(), 5);
		assert.equal(thisModel.getLineContent(1), 'My foo First Line');
	});

	test('model insert text with one newline', () => {
		thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' new line\nNo longer')]);
		assert.equal(thisModel.getLineCount(), 6);
		assert.equal(thisModel.getLineContent(1), 'My new line');
		assert.equal(thisModel.getLineContent(2), 'No longer First Line');
	});

	test('model insert text with two newlines', () => {
		thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' new line\nOne more line in the middle\nNo longer')]);
		assert.equal(thisModel.getLineCount(), 7);
		assert.equal(thisModel.getLineContent(1), 'My new line');
		assert.equal(thisModel.getLineContent(2), 'One more line in the middle');
		assert.equal(thisModel.getLineContent(3), 'No longer First Line');
	});

	test('model insert text with many newlines', () => {
		thisModel.applyEdits([EditOperation.insert(new Position(1, 3), '\n\n\n\n')]);
		assert.equal(thisModel.getLineCount(), 9);
		assert.equal(thisModel.getLineContent(1), 'My');
		assert.equal(thisModel.getLineContent(2), '');
		assert.equal(thisModel.getLineContent(3), '');
		assert.equal(thisModel.getLineContent(4), '');
		assert.equal(thisModel.getLineContent(5), ' First Line');
	});


	// --------- insert text eventing

	test('model insert empty text does not trigger eventing', () => {
A
Alex Dima 已提交
102
		thisModel.onDidChangeRawContent((e) => {
E
Erich Gamma 已提交
103 104 105 106 107 108
			assert.ok(false, 'was not expecting event');
		});
		thisModel.applyEdits([EditOperation.insert(new Position(1, 1), '')]);
	});

	test('model insert text without newline eventing', () => {
109 110 111
		let e: ModelRawContentChangedEvent = null;
		thisModel.onDidChangeRawContent((_e) => {
			if (e !== null) {
112
				assert.fail('Unexpected assertion error');
113 114
			}
			e = _e;
E
Erich Gamma 已提交
115 116
		});
		thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'foo ')]);
117 118 119 120 121 122 123 124
		assert.deepEqual(e, new ModelRawContentChangedEvent(
			[
				new ModelRawLineChanged(1, 'foo My First Line')
			],
			2,
			false,
			false
		));
E
Erich Gamma 已提交
125 126 127
	});

	test('model insert text with one newline eventing', () => {
128 129 130
		let e: ModelRawContentChangedEvent = null;
		thisModel.onDidChangeRawContent((_e) => {
			if (e !== null) {
131
				assert.fail('Unexpected assertion error');
E
Erich Gamma 已提交
132
			}
133
			e = _e;
E
Erich Gamma 已提交
134 135
		});
		thisModel.applyEdits([EditOperation.insert(new Position(1, 3), ' new line\nNo longer')]);
136 137 138
		assert.deepEqual(e, new ModelRawContentChangedEvent(
			[
				new ModelRawLineChanged(1, 'My new line'),
A
Alex Dima 已提交
139
				new ModelRawLinesInserted(2, 2, ['No longer First Line']),
140 141 142 143 144
			],
			2,
			false,
			false
		));
E
Erich Gamma 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
	});


	// --------- delete text

	test('model delete empty text', () => {
		thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 1))]);
		assert.equal(thisModel.getLineCount(), 5);
		assert.equal(thisModel.getLineContent(1), 'My First Line');
	});

	test('model delete text from one line', () => {
		thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]);
		assert.equal(thisModel.getLineCount(), 5);
		assert.equal(thisModel.getLineContent(1), 'y First Line');
	});

	test('model delete text from one line 2', () => {
		thisModel.applyEdits([EditOperation.insert(new Position(1, 1), 'a')]);
		assert.equal(thisModel.getLineContent(1), 'aMy First Line');

		thisModel.applyEdits([EditOperation.delete(new Range(1, 2, 1, 4))]);
		assert.equal(thisModel.getLineCount(), 5);
		assert.equal(thisModel.getLineContent(1), 'a First Line');
	});

	test('model delete all text from a line', () => {
		thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 14))]);
		assert.equal(thisModel.getLineCount(), 5);
		assert.equal(thisModel.getLineContent(1), '');
	});

	test('model delete text from two lines', () => {
		thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 2, 6))]);
		assert.equal(thisModel.getLineCount(), 4);
		assert.equal(thisModel.getLineContent(1), 'My Second Line');
	});

	test('model delete text from many lines', () => {
		thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 3, 5))]);
		assert.equal(thisModel.getLineCount(), 3);
		assert.equal(thisModel.getLineContent(1), 'My Third Line');
	});

	test('model delete everything', () => {
		thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 5, 2))]);
		assert.equal(thisModel.getLineCount(), 1);
		assert.equal(thisModel.getLineContent(1), '');
	});

	// --------- delete text eventing

	test('model delete empty text does not trigger eventing', () => {
A
Alex Dima 已提交
198
		thisModel.onDidChangeRawContent((e) => {
E
Erich Gamma 已提交
199 200 201 202 203 204
			assert.ok(false, 'was not expecting event');
		});
		thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 1))]);
	});

	test('model delete text from one line eventing', () => {
205 206 207
		let e: ModelRawContentChangedEvent = null;
		thisModel.onDidChangeRawContent((_e) => {
			if (e !== null) {
208
				assert.fail('Unexpected assertion error');
209 210
			}
			e = _e;
E
Erich Gamma 已提交
211 212
		});
		thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 2))]);
213 214 215 216 217 218 219 220
		assert.deepEqual(e, new ModelRawContentChangedEvent(
			[
				new ModelRawLineChanged(1, 'y First Line'),
			],
			2,
			false,
			false
		));
E
Erich Gamma 已提交
221 222 223
	});

	test('model delete all text from a line eventing', () => {
224 225 226
		let e: ModelRawContentChangedEvent = null;
		thisModel.onDidChangeRawContent((_e) => {
			if (e !== null) {
227
				assert.fail('Unexpected assertion error');
228 229
			}
			e = _e;
E
Erich Gamma 已提交
230 231
		});
		thisModel.applyEdits([EditOperation.delete(new Range(1, 1, 1, 14))]);
232 233 234 235 236 237 238 239
		assert.deepEqual(e, new ModelRawContentChangedEvent(
			[
				new ModelRawLineChanged(1, ''),
			],
			2,
			false,
			false
		));
E
Erich Gamma 已提交
240 241 242
	});

	test('model delete text from two lines eventing', () => {
243 244 245
		let e: ModelRawContentChangedEvent = null;
		thisModel.onDidChangeRawContent((_e) => {
			if (e !== null) {
246
				assert.fail('Unexpected assertion error');
E
Erich Gamma 已提交
247
			}
248
			e = _e;
E
Erich Gamma 已提交
249 250
		});
		thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 2, 6))]);
251 252 253 254 255 256 257 258 259
		assert.deepEqual(e, new ModelRawContentChangedEvent(
			[
				new ModelRawLineChanged(1, 'My Second Line'),
				new ModelRawLinesDeleted(2, 2),
			],
			2,
			false,
			false
		));
E
Erich Gamma 已提交
260 261 262
	});

	test('model delete text from many lines eventing', () => {
263 264 265
		let e: ModelRawContentChangedEvent = null;
		thisModel.onDidChangeRawContent((_e) => {
			if (e !== null) {
266
				assert.fail('Unexpected assertion error');
E
Erich Gamma 已提交
267
			}
268
			e = _e;
E
Erich Gamma 已提交
269 270
		});
		thisModel.applyEdits([EditOperation.delete(new Range(1, 4, 3, 5))]);
271 272 273 274 275 276 277 278 279
		assert.deepEqual(e, new ModelRawContentChangedEvent(
			[
				new ModelRawLineChanged(1, 'My Third Line'),
				new ModelRawLinesDeleted(2, 3),
			],
			2,
			false,
			false
		));
E
Erich Gamma 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
	});

	// --------- getValueInRange

	test('getValueInRange', () => {
		assert.equal(thisModel.getValueInRange(new Range(1, 1, 1, 1)), '');
		assert.equal(thisModel.getValueInRange(new Range(1, 1, 1, 2)), 'M');
		assert.equal(thisModel.getValueInRange(new Range(1, 2, 1, 3)), 'y');
		assert.equal(thisModel.getValueInRange(new Range(1, 1, 1, 14)), 'My First Line');
		assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 1)), 'My First Line\n');
		assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 2)), 'My First Line\n\t');
		assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 3)), 'My First Line\n\t\t');
		assert.equal(thisModel.getValueInRange(new Range(1, 1, 2, 17)), 'My First Line\n\t\tMy Second Line');
		assert.equal(thisModel.getValueInRange(new Range(1, 1, 3, 1)), 'My First Line\n\t\tMy Second Line\n');
		assert.equal(thisModel.getValueInRange(new Range(1, 1, 4, 1)), 'My First Line\n\t\tMy Second Line\n    Third Line\n');
	});

	// --------- getValueLengthInRange

	test('getValueLengthInRange', () => {
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 1, 1)), ''.length);
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 1, 2)), 'M'.length);
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 2, 1, 3)), 'y'.length);
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 1, 14)), 'My First Line'.length);
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 1)), 'My First Line\n'.length);
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 2)), 'My First Line\n\t'.length);
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 3)), 'My First Line\n\t\t'.length);
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 2, 17)), 'My First Line\n\t\tMy Second Line'.length);
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 3, 1)), 'My First Line\n\t\tMy Second Line\n'.length);
		assert.equal(thisModel.getValueLengthInRange(new Range(1, 1, 4, 1)), 'My First Line\n\t\tMy Second Line\n    Third Line\n'.length);
	});

	// --------- setValue
	test('setValue eventing', () => {
314 315 316
		let e: ModelRawContentChangedEvent = null;
		thisModel.onDidChangeRawContent((_e) => {
			if (e !== null) {
317
				assert.fail('Unexpected assertion error');
318 319
			}
			e = _e;
E
Erich Gamma 已提交
320 321
		});
		thisModel.setValue('new value');
322 323 324 325 326 327 328 329
		assert.deepEqual(e, new ModelRawContentChangedEvent(
			[
				new ModelRawFlush()
			],
			2,
			false,
			false
		));
E
Erich Gamma 已提交
330
	});
331 332 333 334 335 336 337 338 339 340

	test('issue #46342: Maintain edit operation order in applyEdits', () => {
		let res = thisModel.applyEdits([
			{ range: new Range(2, 1, 2, 1), text: 'a' },
			{ range: new Range(1, 1, 1, 1), text: 'b' },
		]);

		assert.deepEqual(res[0].range, new Range(2, 1, 2, 2));
		assert.deepEqual(res[1].range, new Range(1, 1, 1, 2));
	});
E
Erich Gamma 已提交
341 342 343 344 345 346
});


// --------- Special Unicode LINE SEPARATOR character
suite('Editor Model - Model Line Separators', () => {

A
Alex Dima 已提交
347
	var thisModel: TextModel;
E
Erich Gamma 已提交
348 349 350 351 352 353 354 355

	setup(() => {
		var text =
			LINE1 + '\u2028' +
			LINE2 + '\n' +
			LINE3 + '\u2028' +
			LINE4 + '\r\n' +
			LINE5;
A
Alex Dima 已提交
356
		thisModel = TextModel.createFromString(text);
E
Erich Gamma 已提交
357 358 359
	});

	teardown(() => {
A
Alex Dima 已提交
360
		thisModel.dispose();
E
Erich Gamma 已提交
361 362 363 364 365 366 367 368 369 370 371
	});

	test('model getValue', () => {
		assert.equal(thisModel.getValue(), 'My First Line\u2028\t\tMy Second Line\n    Third Line\u2028\n1');
	});

	test('model lines', () => {
		assert.equal(thisModel.getLineCount(), 3);
	});

	test('Bug 13333:Model should line break on lonely CR too', () => {
A
Alex Dima 已提交
372
		var model = TextModel.createFromString('Hello\rWorld!\r\nAnother line');
E
Erich Gamma 已提交
373 374 375 376 377 378 379 380 381 382 383
		assert.equal(model.getLineCount(), 3);
		assert.equal(model.getValue(), 'Hello\r\nWorld!\r\nAnother line');
		model.dispose();
	});
});


// --------- Words

suite('Editor Model - Words', () => {

M
Matt Bierner 已提交
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
	const OUTER_LANGUAGE_ID = new LanguageIdentifier('outerMode', 3);
	const INNER_LANGUAGE_ID = new LanguageIdentifier('innerMode', 4);

	class OuterMode extends MockMode {
		constructor() {
			super(OUTER_LANGUAGE_ID);
			this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {}));

			this._register(TokenizationRegistry.register(this.getLanguageIdentifier().language, {
				getInitialState: (): IState => NULL_STATE,
				tokenize: undefined,
				tokenize2: (line: string, state: IState): TokenizationResult2 => {
					const tokensArr: number[] = [];
					let prevLanguageId: LanguageIdentifier = undefined;
					for (let i = 0; i < line.length; i++) {
						const languageId = (line.charAt(i) === 'x' ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID);
						if (prevLanguageId !== languageId) {
							tokensArr.push(i);
							tokensArr.push((languageId.id << MetadataConsts.LANGUAGEID_OFFSET));
						}
						prevLanguageId = languageId;
					}

					const tokens = new Uint32Array(tokensArr.length);
					for (let i = 0; i < tokens.length; i++) {
						tokens[i] = tokensArr[i];
					}
					return new TokenizationResult2(tokens, state);
				}
			}));
		}
	}

	class InnerMode extends MockMode {
		constructor() {
			super(INNER_LANGUAGE_ID);
			this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {}));
		}
	}

	let disposables: Disposable[] = [];
E
Erich Gamma 已提交
425 426

	setup(() => {
M
Matt Bierner 已提交
427
		disposables = [];
E
Erich Gamma 已提交
428 429 430
	});

	teardown(() => {
M
Matt Bierner 已提交
431 432
		dispose(disposables);
		disposables = [];
E
Erich Gamma 已提交
433 434 435
	});

	test('Get word at position', () => {
M
Matt Bierner 已提交
436 437 438 439
		const text = ['This text has some  words. '];
		const thisModel = TextModel.createFromString(text.join('\n'));
		disposables.push(thisModel);

E
Erich Gamma 已提交
440 441 442 443 444 445 446 447 448 449 450 451
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 1)), { word: 'This', startColumn: 1, endColumn: 5 });
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 2)), { word: 'This', startColumn: 1, endColumn: 5 });
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 4)), { word: 'This', startColumn: 1, endColumn: 5 });
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 5)), { word: 'This', startColumn: 1, endColumn: 5 });
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 6)), { word: 'text', startColumn: 6, endColumn: 10 });
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 19)), { word: 'some', startColumn: 15, endColumn: 19 });
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 20)), null);
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 21)), { word: 'words', startColumn: 21, endColumn: 26 });
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 26)), { word: 'words', startColumn: 21, endColumn: 26 });
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 27)), null);
		assert.deepEqual(thisModel.getWordAtPosition(new Position(1, 28)), null);
	});
M
Matt Bierner 已提交
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468

	test('getWordAtPosition at embedded language boundaries', () => {
		const outerMode = new OuterMode();
		const innerMode = new InnerMode();
		disposables.push(outerMode, innerMode);

		const model = TextModel.createFromString('ab<xx>ab<x>', undefined, outerMode.getLanguageIdentifier());
		disposables.push(model);

		assert.deepEqual(model.getWordAtPosition(new Position(1, 1)), { word: 'ab', startColumn: 1, endColumn: 3 });
		assert.deepEqual(model.getWordAtPosition(new Position(1, 2)), { word: 'ab', startColumn: 1, endColumn: 3 });
		assert.deepEqual(model.getWordAtPosition(new Position(1, 3)), { word: 'ab', startColumn: 1, endColumn: 3 });
		assert.deepEqual(model.getWordAtPosition(new Position(1, 4)), { word: 'xx', startColumn: 4, endColumn: 6 });
		assert.deepEqual(model.getWordAtPosition(new Position(1, 5)), { word: 'xx', startColumn: 4, endColumn: 6 });
		assert.deepEqual(model.getWordAtPosition(new Position(1, 6)), { word: 'xx', startColumn: 4, endColumn: 6 });
		assert.deepEqual(model.getWordAtPosition(new Position(1, 7)), { word: 'ab', startColumn: 7, endColumn: 9 });
	});
E
Erich Gamma 已提交
469
});