microsoft/vscode-languagedetection

Public

mirrored fromhttps://github.com/microsoft/vscode-languagedetectionAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
d9c01ba7f55ea6994ee3edc9e5d98c4b11bbd24a

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

test/large.ts.txt

6344lines · modecode

1/*---------------------------------------------------------------------------------------------
2 * Copyright (c) Microsoft Corporation. All rights reserved.
3 * Licensed under the MIT License. See License.txt in the project root for license information.
4 *--------------------------------------------------------------------------------------------*/
5
6import * as assert from 'assert';
7import { CoreEditingCommands, CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands';
8import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
9import { EditOperation } from 'vs/editor/common/core/editOperation';
10import { Position } from 'vs/editor/common/core/position';
11import { Range } from 'vs/editor/common/core/range';
12import { Selection } from 'vs/editor/common/core/selection';
13import { TokenizationResult2 } from 'vs/editor/common/core/token';
14import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon';
15import { EndOfLinePreference, EndOfLineSequence, ITextModel } from 'vs/editor/common/model';
16import { TextModel } from 'vs/editor/common/model/textModel';
17import { IState, ITokenizationSupport, LanguageIdentifier, TokenizationRegistry } from 'vs/editor/common/modes';
18import { IndentAction, IndentationRule } from 'vs/editor/common/modes/languageConfiguration';
19import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
20import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
21import { withTestCodeEditor, TestCodeEditorCreationOptions, ITestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
22import { IRelaxedTextModelCreationOptions, createTextModel } from 'vs/editor/test/common/editorTestUtils';
23import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
24import { javascriptOnEnterRules } from 'vs/editor/test/common/modes/supports/javascriptOnEnterRules';
25import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
26import { OutgoingViewModelEventKind } from 'vs/editor/common/viewModel/viewModelEventDispatcher';
27
28// --------- utils
29
30function moveTo(editor: ITestCodeEditor, viewModel: ViewModel, lineNumber: number, column: number, inSelectionMode: boolean = false) {
31 if (inSelectionMode) {
32 CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(viewModel, {
33 position: new Position(lineNumber, column)
34 });
35 } else {
36 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, {
37 position: new Position(lineNumber, column)
38 });
39 }
40}
41
42function moveLeft(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {
43 if (inSelectionMode) {
44 CoreNavigationCommands.CursorLeftSelect.runCoreEditorCommand(viewModel, {});
45 } else {
46 CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {});
47 }
48}
49
50function moveRight(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {
51 if (inSelectionMode) {
52 CoreNavigationCommands.CursorRightSelect.runCoreEditorCommand(viewModel, {});
53 } else {
54 CoreNavigationCommands.CursorRight.runCoreEditorCommand(viewModel, {});
55 }
56}
57
58function moveDown(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {
59 if (inSelectionMode) {
60 CoreNavigationCommands.CursorDownSelect.runCoreEditorCommand(viewModel, {});
61 } else {
62 CoreNavigationCommands.CursorDown.runCoreEditorCommand(viewModel, {});
63 }
64}
65
66function moveUp(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {
67 if (inSelectionMode) {
68 CoreNavigationCommands.CursorUpSelect.runCoreEditorCommand(viewModel, {});
69 } else {
70 CoreNavigationCommands.CursorUp.runCoreEditorCommand(viewModel, {});
71 }
72}
73
74function moveToBeginningOfLine(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {
75 if (inSelectionMode) {
76 CoreNavigationCommands.CursorHomeSelect.runCoreEditorCommand(viewModel, {});
77 } else {
78 CoreNavigationCommands.CursorHome.runCoreEditorCommand(viewModel, {});
79 }
80}
81
82function moveToEndOfLine(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {
83 if (inSelectionMode) {
84 CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(viewModel, {});
85 } else {
86 CoreNavigationCommands.CursorEnd.runCoreEditorCommand(viewModel, {});
87 }
88}
89
90function moveToBeginningOfBuffer(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {
91 if (inSelectionMode) {
92 CoreNavigationCommands.CursorTopSelect.runCoreEditorCommand(viewModel, {});
93 } else {
94 CoreNavigationCommands.CursorTop.runCoreEditorCommand(viewModel, {});
95 }
96}
97
98function moveToEndOfBuffer(editor: ITestCodeEditor, viewModel: ViewModel, inSelectionMode: boolean = false) {
99 if (inSelectionMode) {
100 CoreNavigationCommands.CursorBottomSelect.runCoreEditorCommand(viewModel, {});
101 } else {
102 CoreNavigationCommands.CursorBottom.runCoreEditorCommand(viewModel, {});
103 }
104}
105
106function assertCursor(viewModel: ViewModel, what: Position | Selection | Selection[]): void {
107 let selections: Selection[];
108 if (what instanceof Position) {
109 selections = [new Selection(what.lineNumber, what.column, what.lineNumber, what.column)];
110 } else if (what instanceof Selection) {
111 selections = [what];
112 } else {
113 selections = what;
114 }
115 let actual = viewModel.getSelections().map(s => s.toString());
116 let expected = selections.map(s => s.toString());
117
118 assert.deepStrictEqual(actual, expected);
119}
120
121suite('Editor Controller - Cursor', () => {
122 const LINE1 = ' \tMy First Line\t ';
123 const LINE2 = '\tMy Second Line';
124 const LINE3 = ' Third Line🐶';
125 const LINE4 = '';
126 const LINE5 = '1';
127
128 const TEXT =
129 LINE1 + '\r\n' +
130 LINE2 + '\n' +
131 LINE3 + '\n' +
132 LINE4 + '\r\n' +
133 LINE5;
134
135 // let thisModel: TextModel;
136 // let thisConfiguration: TestConfiguration;
137 // let thisViewModel: ViewModel;
138 // let cursor: Cursor;
139
140 // setup(() => {
141 // let text =
142 // LINE1 + '\r\n' +
143 // LINE2 + '\n' +
144 // LINE3 + '\n' +
145 // LINE4 + '\r\n' +
146 // LINE5;
147
148 // thisModel = createTextModel(text);
149 // thisConfiguration = new TestConfiguration({});
150 // thisViewModel = createViewModel(thisConfiguration, thisModel);
151
152 // cursor = new Cursor(thisConfiguration, thisModel, thisViewModel);
153 // });
154
155 // teardown(() => {
156 // cursor.dispose();
157 // thisViewModel.dispose();
158 // thisModel.dispose();
159 // thisConfiguration.dispose();
160 // });
161
162 function runTest(callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void {
163 withTestCodeEditor(TEXT, {}, (editor, viewModel) => {
164 callback(editor, viewModel);
165 });
166 }
167
168 test('cursor initialized', () => {
169 runTest((editor, viewModel) => {
170 assertCursor(viewModel, new Position(1, 1));
171 });
172 });
173
174 // --------- absolute move
175
176 test('no move', () => {
177 runTest((editor, viewModel) => {
178 moveTo(editor, viewModel, 1, 1);
179 assertCursor(viewModel, new Position(1, 1));
180 });
181 });
182
183 test('move', () => {
184 runTest((editor, viewModel) => {
185 moveTo(editor, viewModel, 1, 2);
186 assertCursor(viewModel, new Position(1, 2));
187 });
188 });
189
190 test('move in selection mode', () => {
191 runTest((editor, viewModel) => {
192 moveTo(editor, viewModel, 1, 2, true);
193 assertCursor(viewModel, new Selection(1, 1, 1, 2));
194 });
195 });
196
197 test('move beyond line end', () => {
198 runTest((editor, viewModel) => {
199 moveTo(editor, viewModel, 1, 25);
200 assertCursor(viewModel, new Position(1, LINE1.length + 1));
201 });
202 });
203
204 test('move empty line', () => {
205 runTest((editor, viewModel) => {
206 moveTo(editor, viewModel, 4, 20);
207 assertCursor(viewModel, new Position(4, 1));
208 });
209 });
210
211 test('move one char line', () => {
212 runTest((editor, viewModel) => {
213 moveTo(editor, viewModel, 5, 20);
214 assertCursor(viewModel, new Position(5, 2));
215 });
216 });
217
218 test('selection down', () => {
219 runTest((editor, viewModel) => {
220 moveTo(editor, viewModel, 2, 1, true);
221 assertCursor(viewModel, new Selection(1, 1, 2, 1));
222 });
223 });
224
225 test('move and then select', () => {
226 runTest((editor, viewModel) => {
227 moveTo(editor, viewModel, 2, 3);
228 assertCursor(viewModel, new Position(2, 3));
229
230 moveTo(editor, viewModel, 2, 15, true);
231 assertCursor(viewModel, new Selection(2, 3, 2, 15));
232
233 moveTo(editor, viewModel, 1, 2, true);
234 assertCursor(viewModel, new Selection(2, 3, 1, 2));
235 });
236 });
237
238 // --------- move left
239
240 test('move left on top left position', () => {
241 runTest((editor, viewModel) => {
242 moveLeft(editor, viewModel);
243 assertCursor(viewModel, new Position(1, 1));
244 });
245 });
246
247 test('move left', () => {
248 runTest((editor, viewModel) => {
249 moveTo(editor, viewModel, 1, 3);
250 assertCursor(viewModel, new Position(1, 3));
251 moveLeft(editor, viewModel);
252 assertCursor(viewModel, new Position(1, 2));
253 });
254 });
255
256 test('move left with surrogate pair', () => {
257 runTest((editor, viewModel) => {
258 moveTo(editor, viewModel, 3, 17);
259 assertCursor(viewModel, new Position(3, 17));
260 moveLeft(editor, viewModel);
261 assertCursor(viewModel, new Position(3, 15));
262 });
263 });
264
265 test('move left goes to previous row', () => {
266 runTest((editor, viewModel) => {
267 moveTo(editor, viewModel, 2, 1);
268 assertCursor(viewModel, new Position(2, 1));
269 moveLeft(editor, viewModel);
270 assertCursor(viewModel, new Position(1, 21));
271 });
272 });
273
274 test('move left selection', () => {
275 runTest((editor, viewModel) => {
276 moveTo(editor, viewModel, 2, 1);
277 assertCursor(viewModel, new Position(2, 1));
278 moveLeft(editor, viewModel, true);
279 assertCursor(viewModel, new Selection(2, 1, 1, 21));
280 });
281 });
282
283 // --------- move right
284
285 test('move right on bottom right position', () => {
286 runTest((editor, viewModel) => {
287 moveTo(editor, viewModel, 5, 2);
288 assertCursor(viewModel, new Position(5, 2));
289 moveRight(editor, viewModel);
290 assertCursor(viewModel, new Position(5, 2));
291 });
292 });
293
294 test('move right', () => {
295 runTest((editor, viewModel) => {
296 moveTo(editor, viewModel, 1, 3);
297 assertCursor(viewModel, new Position(1, 3));
298 moveRight(editor, viewModel);
299 assertCursor(viewModel, new Position(1, 4));
300 });
301 });
302
303 test('move right with surrogate pair', () => {
304 runTest((editor, viewModel) => {
305 moveTo(editor, viewModel, 3, 15);
306 assertCursor(viewModel, new Position(3, 15));
307 moveRight(editor, viewModel);
308 assertCursor(viewModel, new Position(3, 17));
309 });
310 });
311
312 test('move right goes to next row', () => {
313 runTest((editor, viewModel) => {
314 moveTo(editor, viewModel, 1, 21);
315 assertCursor(viewModel, new Position(1, 21));
316 moveRight(editor, viewModel);
317 assertCursor(viewModel, new Position(2, 1));
318 });
319 });
320
321 test('move right selection', () => {
322 runTest((editor, viewModel) => {
323 moveTo(editor, viewModel, 1, 21);
324 assertCursor(viewModel, new Position(1, 21));
325 moveRight(editor, viewModel, true);
326 assertCursor(viewModel, new Selection(1, 21, 2, 1));
327 });
328 });
329
330 // --------- move down
331
332 test('move down', () => {
333 runTest((editor, viewModel) => {
334 moveDown(editor, viewModel);
335 assertCursor(viewModel, new Position(2, 1));
336 moveDown(editor, viewModel);
337 assertCursor(viewModel, new Position(3, 1));
338 moveDown(editor, viewModel);
339 assertCursor(viewModel, new Position(4, 1));
340 moveDown(editor, viewModel);
341 assertCursor(viewModel, new Position(5, 1));
342 moveDown(editor, viewModel);
343 assertCursor(viewModel, new Position(5, 2));
344 });
345 });
346
347 test('move down with selection', () => {
348 runTest((editor, viewModel) => {
349 moveDown(editor, viewModel, true);
350 assertCursor(viewModel, new Selection(1, 1, 2, 1));
351 moveDown(editor, viewModel, true);
352 assertCursor(viewModel, new Selection(1, 1, 3, 1));
353 moveDown(editor, viewModel, true);
354 assertCursor(viewModel, new Selection(1, 1, 4, 1));
355 moveDown(editor, viewModel, true);
356 assertCursor(viewModel, new Selection(1, 1, 5, 1));
357 moveDown(editor, viewModel, true);
358 assertCursor(viewModel, new Selection(1, 1, 5, 2));
359 });
360 });
361
362 test('move down with tabs', () => {
363 runTest((editor, viewModel) => {
364 moveTo(editor, viewModel, 1, 5);
365 assertCursor(viewModel, new Position(1, 5));
366 moveDown(editor, viewModel);
367 assertCursor(viewModel, new Position(2, 2));
368 moveDown(editor, viewModel);
369 assertCursor(viewModel, new Position(3, 5));
370 moveDown(editor, viewModel);
371 assertCursor(viewModel, new Position(4, 1));
372 moveDown(editor, viewModel);
373 assertCursor(viewModel, new Position(5, 2));
374 });
375 });
376
377 // --------- move up
378
379 test('move up', () => {
380 runTest((editor, viewModel) => {
381 moveTo(editor, viewModel, 3, 5);
382 assertCursor(viewModel, new Position(3, 5));
383
384 moveUp(editor, viewModel);
385 assertCursor(viewModel, new Position(2, 2));
386
387 moveUp(editor, viewModel);
388 assertCursor(viewModel, new Position(1, 5));
389 });
390 });
391
392 test('move up with selection', () => {
393 runTest((editor, viewModel) => {
394 moveTo(editor, viewModel, 3, 5);
395 assertCursor(viewModel, new Position(3, 5));
396
397 moveUp(editor, viewModel, true);
398 assertCursor(viewModel, new Selection(3, 5, 2, 2));
399
400 moveUp(editor, viewModel, true);
401 assertCursor(viewModel, new Selection(3, 5, 1, 5));
402 });
403 });
404
405 test('move up and down with tabs', () => {
406 runTest((editor, viewModel) => {
407 moveTo(editor, viewModel, 1, 5);
408 assertCursor(viewModel, new Position(1, 5));
409 moveDown(editor, viewModel);
410 moveDown(editor, viewModel);
411 moveDown(editor, viewModel);
412 moveDown(editor, viewModel);
413 assertCursor(viewModel, new Position(5, 2));
414 moveUp(editor, viewModel);
415 assertCursor(viewModel, new Position(4, 1));
416 moveUp(editor, viewModel);
417 assertCursor(viewModel, new Position(3, 5));
418 moveUp(editor, viewModel);
419 assertCursor(viewModel, new Position(2, 2));
420 moveUp(editor, viewModel);
421 assertCursor(viewModel, new Position(1, 5));
422 });
423 });
424
425 test('move up and down with end of lines starting from a long one', () => {
426 runTest((editor, viewModel) => {
427 moveToEndOfLine(editor, viewModel);
428 assertCursor(viewModel, new Position(1, LINE1.length + 1));
429 moveToEndOfLine(editor, viewModel);
430 assertCursor(viewModel, new Position(1, LINE1.length + 1));
431 moveDown(editor, viewModel);
432 assertCursor(viewModel, new Position(2, LINE2.length + 1));
433 moveDown(editor, viewModel);
434 assertCursor(viewModel, new Position(3, LINE3.length + 1));
435 moveDown(editor, viewModel);
436 assertCursor(viewModel, new Position(4, LINE4.length + 1));
437 moveDown(editor, viewModel);
438 assertCursor(viewModel, new Position(5, LINE5.length + 1));
439 moveUp(editor, viewModel);
440 moveUp(editor, viewModel);
441 moveUp(editor, viewModel);
442 moveUp(editor, viewModel);
443 assertCursor(viewModel, new Position(1, LINE1.length + 1));
444 });
445 });
446
447 test('issue #44465: cursor position not correct when move', () => {
448 runTest((editor, viewModel) => {
449 viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
450 // going once up on the first line remembers the offset visual columns
451 moveUp(editor, viewModel);
452 assertCursor(viewModel, new Position(1, 1));
453 moveDown(editor, viewModel);
454 assertCursor(viewModel, new Position(2, 2));
455 moveUp(editor, viewModel);
456 assertCursor(viewModel, new Position(1, 5));
457
458 // going twice up on the first line discards the offset visual columns
459 moveUp(editor, viewModel);
460 assertCursor(viewModel, new Position(1, 1));
461 moveUp(editor, viewModel);
462 assertCursor(viewModel, new Position(1, 1));
463 moveDown(editor, viewModel);
464 assertCursor(viewModel, new Position(2, 1));
465 });
466 });
467
468 // --------- move to beginning of line
469
470 test('move to beginning of line', () => {
471 runTest((editor, viewModel) => {
472 moveToBeginningOfLine(editor, viewModel);
473 assertCursor(viewModel, new Position(1, 6));
474 moveToBeginningOfLine(editor, viewModel);
475 assertCursor(viewModel, new Position(1, 1));
476 });
477 });
478
479 test('move to beginning of line from within line', () => {
480 runTest((editor, viewModel) => {
481 moveTo(editor, viewModel, 1, 8);
482 moveToBeginningOfLine(editor, viewModel);
483 assertCursor(viewModel, new Position(1, 6));
484 moveToBeginningOfLine(editor, viewModel);
485 assertCursor(viewModel, new Position(1, 1));
486 });
487 });
488
489 test('move to beginning of line from whitespace at beginning of line', () => {
490 runTest((editor, viewModel) => {
491 moveTo(editor, viewModel, 1, 2);
492 moveToBeginningOfLine(editor, viewModel);
493 assertCursor(viewModel, new Position(1, 6));
494 moveToBeginningOfLine(editor, viewModel);
495 assertCursor(viewModel, new Position(1, 1));
496 });
497 });
498
499 test('move to beginning of line from within line selection', () => {
500 runTest((editor, viewModel) => {
501 moveTo(editor, viewModel, 1, 8);
502 moveToBeginningOfLine(editor, viewModel, true);
503 assertCursor(viewModel, new Selection(1, 8, 1, 6));
504 moveToBeginningOfLine(editor, viewModel, true);
505 assertCursor(viewModel, new Selection(1, 8, 1, 1));
506 });
507 });
508
509 test('move to beginning of line with selection multiline forward', () => {
510 runTest((editor, viewModel) => {
511 moveTo(editor, viewModel, 1, 8);
512 moveTo(editor, viewModel, 3, 9, true);
513 moveToBeginningOfLine(editor, viewModel, false);
514 assertCursor(viewModel, new Selection(3, 5, 3, 5));
515 });
516 });
517
518 test('move to beginning of line with selection multiline backward', () => {
519 runTest((editor, viewModel) => {
520 moveTo(editor, viewModel, 3, 9);
521 moveTo(editor, viewModel, 1, 8, true);
522 moveToBeginningOfLine(editor, viewModel, false);
523 assertCursor(viewModel, new Selection(1, 6, 1, 6));
524 });
525 });
526
527 test('move to beginning of line with selection single line forward', () => {
528 runTest((editor, viewModel) => {
529 moveTo(editor, viewModel, 3, 2);
530 moveTo(editor, viewModel, 3, 9, true);
531 moveToBeginningOfLine(editor, viewModel, false);
532 assertCursor(viewModel, new Selection(3, 5, 3, 5));
533 });
534 });
535
536 test('move to beginning of line with selection single line backward', () => {
537 runTest((editor, viewModel) => {
538 moveTo(editor, viewModel, 3, 9);
539 moveTo(editor, viewModel, 3, 2, true);
540 moveToBeginningOfLine(editor, viewModel, false);
541 assertCursor(viewModel, new Selection(3, 5, 3, 5));
542 });
543 });
544
545 test('issue #15401: "End" key is behaving weird when text is selected part 1', () => {
546 runTest((editor, viewModel) => {
547 moveTo(editor, viewModel, 1, 8);
548 moveTo(editor, viewModel, 3, 9, true);
549 moveToBeginningOfLine(editor, viewModel, false);
550 assertCursor(viewModel, new Selection(3, 5, 3, 5));
551 });
552 });
553
554 test('issue #17011: Shift+home/end now go to the end of the selection start\'s line, not the selection\'s end', () => {
555 runTest((editor, viewModel) => {
556 moveTo(editor, viewModel, 1, 8);
557 moveTo(editor, viewModel, 3, 9, true);
558 moveToBeginningOfLine(editor, viewModel, true);
559 assertCursor(viewModel, new Selection(1, 8, 3, 5));
560 });
561 });
562
563 // --------- move to end of line
564
565 test('move to end of line', () => {
566 runTest((editor, viewModel) => {
567 moveToEndOfLine(editor, viewModel);
568 assertCursor(viewModel, new Position(1, LINE1.length + 1));
569 moveToEndOfLine(editor, viewModel);
570 assertCursor(viewModel, new Position(1, LINE1.length + 1));
571 });
572 });
573
574 test('move to end of line from within line', () => {
575 runTest((editor, viewModel) => {
576 moveTo(editor, viewModel, 1, 6);
577 moveToEndOfLine(editor, viewModel);
578 assertCursor(viewModel, new Position(1, LINE1.length + 1));
579 moveToEndOfLine(editor, viewModel);
580 assertCursor(viewModel, new Position(1, LINE1.length + 1));
581 });
582 });
583
584 test('move to end of line from whitespace at end of line', () => {
585 runTest((editor, viewModel) => {
586 moveTo(editor, viewModel, 1, 20);
587 moveToEndOfLine(editor, viewModel);
588 assertCursor(viewModel, new Position(1, LINE1.length + 1));
589 moveToEndOfLine(editor, viewModel);
590 assertCursor(viewModel, new Position(1, LINE1.length + 1));
591 });
592 });
593
594 test('move to end of line from within line selection', () => {
595 runTest((editor, viewModel) => {
596 moveTo(editor, viewModel, 1, 6);
597 moveToEndOfLine(editor, viewModel, true);
598 assertCursor(viewModel, new Selection(1, 6, 1, LINE1.length + 1));
599 moveToEndOfLine(editor, viewModel, true);
600 assertCursor(viewModel, new Selection(1, 6, 1, LINE1.length + 1));
601 });
602 });
603
604 test('move to end of line with selection multiline forward', () => {
605 runTest((editor, viewModel) => {
606 moveTo(editor, viewModel, 1, 1);
607 moveTo(editor, viewModel, 3, 9, true);
608 moveToEndOfLine(editor, viewModel, false);
609 assertCursor(viewModel, new Selection(3, 17, 3, 17));
610 });
611 });
612
613 test('move to end of line with selection multiline backward', () => {
614 runTest((editor, viewModel) => {
615 moveTo(editor, viewModel, 3, 9);
616 moveTo(editor, viewModel, 1, 1, true);
617 moveToEndOfLine(editor, viewModel, false);
618 assertCursor(viewModel, new Selection(1, 21, 1, 21));
619 });
620 });
621
622 test('move to end of line with selection single line forward', () => {
623 runTest((editor, viewModel) => {
624 moveTo(editor, viewModel, 3, 1);
625 moveTo(editor, viewModel, 3, 9, true);
626 moveToEndOfLine(editor, viewModel, false);
627 assertCursor(viewModel, new Selection(3, 17, 3, 17));
628 });
629 });
630
631 test('move to end of line with selection single line backward', () => {
632 runTest((editor, viewModel) => {
633 moveTo(editor, viewModel, 3, 9);
634 moveTo(editor, viewModel, 3, 1, true);
635 moveToEndOfLine(editor, viewModel, false);
636 assertCursor(viewModel, new Selection(3, 17, 3, 17));
637 });
638 });
639
640 test('issue #15401: "End" key is behaving weird when text is selected part 2', () => {
641 runTest((editor, viewModel) => {
642 moveTo(editor, viewModel, 1, 1);
643 moveTo(editor, viewModel, 3, 9, true);
644 moveToEndOfLine(editor, viewModel, false);
645 assertCursor(viewModel, new Selection(3, 17, 3, 17));
646 });
647 });
648
649 // --------- move to beginning of buffer
650
651 test('move to beginning of buffer', () => {
652 runTest((editor, viewModel) => {
653 moveToBeginningOfBuffer(editor, viewModel);
654 assertCursor(viewModel, new Position(1, 1));
655 });
656 });
657
658 test('move to beginning of buffer from within first line', () => {
659 runTest((editor, viewModel) => {
660 moveTo(editor, viewModel, 1, 3);
661 moveToBeginningOfBuffer(editor, viewModel);
662 assertCursor(viewModel, new Position(1, 1));
663 });
664 });
665
666 test('move to beginning of buffer from within another line', () => {
667 runTest((editor, viewModel) => {
668 moveTo(editor, viewModel, 3, 3);
669 moveToBeginningOfBuffer(editor, viewModel);
670 assertCursor(viewModel, new Position(1, 1));
671 });
672 });
673
674 test('move to beginning of buffer from within first line selection', () => {
675 runTest((editor, viewModel) => {
676 moveTo(editor, viewModel, 1, 3);
677 moveToBeginningOfBuffer(editor, viewModel, true);
678 assertCursor(viewModel, new Selection(1, 3, 1, 1));
679 });
680 });
681
682 test('move to beginning of buffer from within another line selection', () => {
683 runTest((editor, viewModel) => {
684 moveTo(editor, viewModel, 3, 3);
685 moveToBeginningOfBuffer(editor, viewModel, true);
686 assertCursor(viewModel, new Selection(3, 3, 1, 1));
687 });
688 });
689
690 // --------- move to end of buffer
691
692 test('move to end of buffer', () => {
693 runTest((editor, viewModel) => {
694 moveToEndOfBuffer(editor, viewModel);
695 assertCursor(viewModel, new Position(5, LINE5.length + 1));
696 });
697 });
698
699 test('move to end of buffer from within last line', () => {
700 runTest((editor, viewModel) => {
701 moveTo(editor, viewModel, 5, 1);
702 moveToEndOfBuffer(editor, viewModel);
703 assertCursor(viewModel, new Position(5, LINE5.length + 1));
704 });
705 });
706
707 test('move to end of buffer from within another line', () => {
708 runTest((editor, viewModel) => {
709 moveTo(editor, viewModel, 3, 3);
710 moveToEndOfBuffer(editor, viewModel);
711 assertCursor(viewModel, new Position(5, LINE5.length + 1));
712 });
713 });
714
715 test('move to end of buffer from within last line selection', () => {
716 runTest((editor, viewModel) => {
717 moveTo(editor, viewModel, 5, 1);
718 moveToEndOfBuffer(editor, viewModel, true);
719 assertCursor(viewModel, new Selection(5, 1, 5, LINE5.length + 1));
720 });
721 });
722
723 test('move to end of buffer from within another line selection', () => {
724 runTest((editor, viewModel) => {
725 moveTo(editor, viewModel, 3, 3);
726 moveToEndOfBuffer(editor, viewModel, true);
727 assertCursor(viewModel, new Selection(3, 3, 5, LINE5.length + 1));
728 });
729 });
730
731 // --------- misc
732
733 test('select all', () => {
734 runTest((editor, viewModel) => {
735 CoreNavigationCommands.SelectAll.runCoreEditorCommand(viewModel, {});
736 assertCursor(viewModel, new Selection(1, 1, 5, LINE5.length + 1));
737 });
738 });
739
740 test('expandLineSelection', () => {
741 runTest((editor, viewModel) => {
742 // 0 1 2
743 // 01234 56789012345678 0
744 // let LINE1 = ' \tMy First Line\t ';
745 moveTo(editor, viewModel, 1, 1);
746 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
747 assertCursor(viewModel, new Selection(1, 1, 2, 1));
748
749 moveTo(editor, viewModel, 1, 2);
750 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
751 assertCursor(viewModel, new Selection(1, 1, 2, 1));
752
753 moveTo(editor, viewModel, 1, 5);
754 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
755 assertCursor(viewModel, new Selection(1, 1, 2, 1));
756
757 moveTo(editor, viewModel, 1, 19);
758 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
759 assertCursor(viewModel, new Selection(1, 1, 2, 1));
760
761 moveTo(editor, viewModel, 1, 20);
762 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
763 assertCursor(viewModel, new Selection(1, 1, 2, 1));
764
765 moveTo(editor, viewModel, 1, 21);
766 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
767 assertCursor(viewModel, new Selection(1, 1, 2, 1));
768 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
769 assertCursor(viewModel, new Selection(1, 1, 3, 1));
770 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
771 assertCursor(viewModel, new Selection(1, 1, 4, 1));
772 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
773 assertCursor(viewModel, new Selection(1, 1, 5, 1));
774 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
775 assertCursor(viewModel, new Selection(1, 1, 5, LINE5.length + 1));
776 CoreNavigationCommands.ExpandLineSelection.runCoreEditorCommand(viewModel, {});
777 assertCursor(viewModel, new Selection(1, 1, 5, LINE5.length + 1));
778 });
779 });
780
781 // --------- eventing
782
783 test('no move doesn\'t trigger event', () => {
784 runTest((editor, viewModel) => {
785 viewModel.onEvent((e) => {
786 assert.ok(false, 'was not expecting event');
787 });
788 moveTo(editor, viewModel, 1, 1);
789 });
790 });
791
792 test('move eventing', () => {
793 runTest((editor, viewModel) => {
794 let events = 0;
795 viewModel.onEvent((e) => {
796 if (e.kind === OutgoingViewModelEventKind.CursorStateChanged) {
797 events++;
798 assert.deepStrictEqual(e.selections, [new Selection(1, 2, 1, 2)]);
799 }
800 });
801 moveTo(editor, viewModel, 1, 2);
802 assert.strictEqual(events, 1, 'receives 1 event');
803 });
804 });
805
806 test('move in selection mode eventing', () => {
807 runTest((editor, viewModel) => {
808 let events = 0;
809 viewModel.onEvent((e) => {
810 if (e.kind === OutgoingViewModelEventKind.CursorStateChanged) {
811 events++;
812 assert.deepStrictEqual(e.selections, [new Selection(1, 1, 1, 2)]);
813 }
814 });
815 moveTo(editor, viewModel, 1, 2, true);
816 assert.strictEqual(events, 1, 'receives 1 event');
817 });
818 });
819
820 // --------- state save & restore
821
822 test('saveState & restoreState', () => {
823 runTest((editor, viewModel) => {
824 moveTo(editor, viewModel, 2, 1, true);
825 assertCursor(viewModel, new Selection(1, 1, 2, 1));
826
827 let savedState = JSON.stringify(viewModel.saveCursorState());
828
829 moveTo(editor, viewModel, 1, 1, false);
830 assertCursor(viewModel, new Position(1, 1));
831
832 viewModel.restoreCursorState(JSON.parse(savedState));
833 assertCursor(viewModel, new Selection(1, 1, 2, 1));
834 });
835 });
836
837 // --------- updating cursor
838
839 test('Independent model edit 1', () => {
840 runTest((editor, viewModel) => {
841 moveTo(editor, viewModel, 2, 16, true);
842
843 editor.getModel().applyEdits([EditOperation.delete(new Range(2, 1, 2, 2))]);
844 assertCursor(viewModel, new Selection(1, 1, 2, 15));
845 });
846 });
847
848 test('column select 1', () => {
849 withTestCodeEditor([
850 '\tprivate compute(a:number): boolean {',
851 '\t\tif (a + 3 === 0 || a + 5 === 0) {',
852 '\t\t\treturn false;',
853 '\t\t}',
854 '\t}'
855 ], {}, (editor, viewModel) => {
856
857 moveTo(editor, viewModel, 1, 7, false);
858 assertCursor(viewModel, new Position(1, 7));
859
860 CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, {
861 position: new Position(4, 4),
862 viewPosition: new Position(4, 4),
863 mouseColumn: 15,
864 doColumnSelect: true
865 });
866
867 let expectedSelections = [
868 new Selection(1, 7, 1, 12),
869 new Selection(2, 4, 2, 9),
870 new Selection(3, 3, 3, 6),
871 new Selection(4, 4, 4, 4),
872 ];
873
874 assertCursor(viewModel, expectedSelections);
875
876 });
877 });
878
879 test('grapheme breaking', () => {
880 withTestCodeEditor([
881 'abcabc',
882 'ãããããã',
883 '辻󠄀辻󠄀辻󠄀',
884 'பு',
885 ], {}, (editor, viewModel) => {
886
887 viewModel.setSelections('test', [new Selection(2, 1, 2, 1)]);
888 moveRight(editor, viewModel);
889 assertCursor(viewModel, new Position(2, 3));
890 moveLeft(editor, viewModel);
891 assertCursor(viewModel, new Position(2, 1));
892
893 viewModel.setSelections('test', [new Selection(3, 1, 3, 1)]);
894 moveRight(editor, viewModel);
895 assertCursor(viewModel, new Position(3, 4));
896 moveLeft(editor, viewModel);
897 assertCursor(viewModel, new Position(3, 1));
898
899 viewModel.setSelections('test', [new Selection(4, 1, 4, 1)]);
900 moveRight(editor, viewModel);
901 assertCursor(viewModel, new Position(4, 3));
902 moveLeft(editor, viewModel);
903 assertCursor(viewModel, new Position(4, 1));
904
905 viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
906 moveDown(editor, viewModel);
907 assertCursor(viewModel, new Position(2, 5));
908 moveDown(editor, viewModel);
909 assertCursor(viewModel, new Position(3, 4));
910 moveUp(editor, viewModel);
911 assertCursor(viewModel, new Position(2, 5));
912 moveUp(editor, viewModel);
913 assertCursor(viewModel, new Position(1, 3));
914
915 });
916 });
917
918 test('issue #4905 - column select is biased to the right', () => {
919 withTestCodeEditor([
920 'var gulp = require("gulp");',
921 'var path = require("path");',
922 'var rimraf = require("rimraf");',
923 'var isarray = require("isarray");',
924 'var merge = require("merge-stream");',
925 'var concat = require("gulp-concat");',
926 'var newer = require("gulp-newer");',
927 ].join('\n'), {}, (editor, viewModel) => {
928 moveTo(editor, viewModel, 1, 4, false);
929 assertCursor(viewModel, new Position(1, 4));
930
931 CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, {
932 position: new Position(4, 1),
933 viewPosition: new Position(4, 1),
934 mouseColumn: 1,
935 doColumnSelect: true
936 });
937
938 assertCursor(viewModel, [
939 new Selection(1, 4, 1, 1),
940 new Selection(2, 4, 2, 1),
941 new Selection(3, 4, 3, 1),
942 new Selection(4, 4, 4, 1),
943 ]);
944 });
945 });
946
947 test('issue #20087: column select with mouse', () => {
948 withTestCodeEditor([
949 '<property id="SomeThing" key="SomeKey" value="000"/>',
950 '<property id="SomeThing" key="SomeKey" value="000"/>',
951 '<property id="SomeThing" Key="SomeKey" value="000"/>',
952 '<property id="SomeThing" key="SomeKey" value="000"/>',
953 '<property id="SomeThing" key="SoMEKEy" value="000"/>',
954 '<property id="SomeThing" key="SomeKey" value="000"/>',
955 '<property id="SomeThing" key="SomeKey" value="000"/>',
956 '<property id="SomeThing" key="SomeKey" valuE="000"/>',
957 '<property id="SomeThing" key="SomeKey" value="000"/>',
958 '<property id="SomeThing" key="SomeKey" value="00X"/>',
959 ].join('\n'), {}, (editor, viewModel) => {
960
961 moveTo(editor, viewModel, 10, 10, false);
962 assertCursor(viewModel, new Position(10, 10));
963
964 CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, {
965 position: new Position(1, 1),
966 viewPosition: new Position(1, 1),
967 mouseColumn: 1,
968 doColumnSelect: true
969 });
970 assertCursor(viewModel, [
971 new Selection(10, 10, 10, 1),
972 new Selection(9, 10, 9, 1),
973 new Selection(8, 10, 8, 1),
974 new Selection(7, 10, 7, 1),
975 new Selection(6, 10, 6, 1),
976 new Selection(5, 10, 5, 1),
977 new Selection(4, 10, 4, 1),
978 new Selection(3, 10, 3, 1),
979 new Selection(2, 10, 2, 1),
980 new Selection(1, 10, 1, 1),
981 ]);
982
983 CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(viewModel, {
984 position: new Position(1, 1),
985 viewPosition: new Position(1, 1),
986 mouseColumn: 1,
987 doColumnSelect: true
988 });
989 assertCursor(viewModel, [
990 new Selection(10, 10, 10, 1),
991 new Selection(9, 10, 9, 1),
992 new Selection(8, 10, 8, 1),
993 new Selection(7, 10, 7, 1),
994 new Selection(6, 10, 6, 1),
995 new Selection(5, 10, 5, 1),
996 new Selection(4, 10, 4, 1),
997 new Selection(3, 10, 3, 1),
998 new Selection(2, 10, 2, 1),
999 new Selection(1, 10, 1, 1),
1000 ]);
1001
1002 });
1003 });
1004
1005 test('issue #20087: column select with keyboard', () => {
1006 withTestCodeEditor([
1007 '<property id="SomeThing" key="SomeKey" value="000"/>',
1008 '<property id="SomeThing" key="SomeKey" value="000"/>',
1009 '<property id="SomeThing" Key="SomeKey" value="000"/>',
1010 '<property id="SomeThing" key="SomeKey" value="000"/>',
1011 '<property id="SomeThing" key="SoMEKEy" value="000"/>',
1012 '<property id="SomeThing" key="SomeKey" value="000"/>',
1013 '<property id="SomeThing" key="SomeKey" value="000"/>',
1014 '<property id="SomeThing" key="SomeKey" valuE="000"/>',
1015 '<property id="SomeThing" key="SomeKey" value="000"/>',
1016 '<property id="SomeThing" key="SomeKey" value="00X"/>',
1017 ].join('\n'), {}, (editor, viewModel) => {
1018
1019 moveTo(editor, viewModel, 10, 10, false);
1020 assertCursor(viewModel, new Position(10, 10));
1021
1022 CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {});
1023 assertCursor(viewModel, [
1024 new Selection(10, 10, 10, 9)
1025 ]);
1026
1027 CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {});
1028 assertCursor(viewModel, [
1029 new Selection(10, 10, 10, 8)
1030 ]);
1031
1032 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1033 assertCursor(viewModel, [
1034 new Selection(10, 10, 10, 9)
1035 ]);
1036
1037 CoreNavigationCommands.CursorColumnSelectUp.runCoreEditorCommand(viewModel, {});
1038 assertCursor(viewModel, [
1039 new Selection(10, 10, 10, 9),
1040 new Selection(9, 10, 9, 9),
1041 ]);
1042
1043 CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});
1044 assertCursor(viewModel, [
1045 new Selection(10, 10, 10, 9)
1046 ]);
1047 });
1048 });
1049
1050 test('issue #118062: Column selection cannot select first position of a line', () => {
1051 withTestCodeEditor([
1052 'hello world',
1053 ].join('\n'), {}, (editor, viewModel) => {
1054
1055 moveTo(editor, viewModel, 1, 2, false);
1056 assertCursor(viewModel, new Position(1, 2));
1057
1058 CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {});
1059 assertCursor(viewModel, [
1060 new Selection(1, 2, 1, 1)
1061 ]);
1062 });
1063 });
1064
1065 test('column select with keyboard', () => {
1066 withTestCodeEditor([
1067 'var gulp = require("gulp");',
1068 'var path = require("path");',
1069 'var rimraf = require("rimraf");',
1070 'var isarray = require("isarray");',
1071 'var merge = require("merge-stream");',
1072 'var concat = require("gulp-concat");',
1073 'var newer = require("gulp-newer");',
1074 ].join('\n'), {}, (editor, viewModel) => {
1075
1076 moveTo(editor, viewModel, 1, 4, false);
1077 assertCursor(viewModel, new Position(1, 4));
1078
1079 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1080 assertCursor(viewModel, [
1081 new Selection(1, 4, 1, 5)
1082 ]);
1083
1084 CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});
1085 assertCursor(viewModel, [
1086 new Selection(1, 4, 1, 5),
1087 new Selection(2, 4, 2, 5)
1088 ]);
1089
1090 CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});
1091 assertCursor(viewModel, [
1092 new Selection(1, 4, 1, 5),
1093 new Selection(2, 4, 2, 5),
1094 new Selection(3, 4, 3, 5),
1095 ]);
1096
1097 CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});
1098 CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});
1099 CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});
1100 CoreNavigationCommands.CursorColumnSelectDown.runCoreEditorCommand(viewModel, {});
1101 assertCursor(viewModel, [
1102 new Selection(1, 4, 1, 5),
1103 new Selection(2, 4, 2, 5),
1104 new Selection(3, 4, 3, 5),
1105 new Selection(4, 4, 4, 5),
1106 new Selection(5, 4, 5, 5),
1107 new Selection(6, 4, 6, 5),
1108 new Selection(7, 4, 7, 5),
1109 ]);
1110
1111 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1112 assertCursor(viewModel, [
1113 new Selection(1, 4, 1, 6),
1114 new Selection(2, 4, 2, 6),
1115 new Selection(3, 4, 3, 6),
1116 new Selection(4, 4, 4, 6),
1117 new Selection(5, 4, 5, 6),
1118 new Selection(6, 4, 6, 6),
1119 new Selection(7, 4, 7, 6),
1120 ]);
1121
1122 // 10 times
1123 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1124 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1125 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1126 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1127 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1128 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1129 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1130 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1131 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1132 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1133 assertCursor(viewModel, [
1134 new Selection(1, 4, 1, 16),
1135 new Selection(2, 4, 2, 16),
1136 new Selection(3, 4, 3, 16),
1137 new Selection(4, 4, 4, 16),
1138 new Selection(5, 4, 5, 16),
1139 new Selection(6, 4, 6, 16),
1140 new Selection(7, 4, 7, 16),
1141 ]);
1142
1143 // 10 times
1144 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1145 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1146 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1147 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1148 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1149 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1150 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1151 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1152 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1153 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1154 assertCursor(viewModel, [
1155 new Selection(1, 4, 1, 26),
1156 new Selection(2, 4, 2, 26),
1157 new Selection(3, 4, 3, 26),
1158 new Selection(4, 4, 4, 26),
1159 new Selection(5, 4, 5, 26),
1160 new Selection(6, 4, 6, 26),
1161 new Selection(7, 4, 7, 26),
1162 ]);
1163
1164 // 2 times => reaching the ending of lines 1 and 2
1165 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1166 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1167 assertCursor(viewModel, [
1168 new Selection(1, 4, 1, 28),
1169 new Selection(2, 4, 2, 28),
1170 new Selection(3, 4, 3, 28),
1171 new Selection(4, 4, 4, 28),
1172 new Selection(5, 4, 5, 28),
1173 new Selection(6, 4, 6, 28),
1174 new Selection(7, 4, 7, 28),
1175 ]);
1176
1177 // 4 times => reaching the ending of line 3
1178 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1179 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1180 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1181 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1182 assertCursor(viewModel, [
1183 new Selection(1, 4, 1, 28),
1184 new Selection(2, 4, 2, 28),
1185 new Selection(3, 4, 3, 32),
1186 new Selection(4, 4, 4, 32),
1187 new Selection(5, 4, 5, 32),
1188 new Selection(6, 4, 6, 32),
1189 new Selection(7, 4, 7, 32),
1190 ]);
1191
1192 // 2 times => reaching the ending of line 4
1193 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1194 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1195 assertCursor(viewModel, [
1196 new Selection(1, 4, 1, 28),
1197 new Selection(2, 4, 2, 28),
1198 new Selection(3, 4, 3, 32),
1199 new Selection(4, 4, 4, 34),
1200 new Selection(5, 4, 5, 34),
1201 new Selection(6, 4, 6, 34),
1202 new Selection(7, 4, 7, 34),
1203 ]);
1204
1205 // 1 time => reaching the ending of line 7
1206 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1207 assertCursor(viewModel, [
1208 new Selection(1, 4, 1, 28),
1209 new Selection(2, 4, 2, 28),
1210 new Selection(3, 4, 3, 32),
1211 new Selection(4, 4, 4, 34),
1212 new Selection(5, 4, 5, 35),
1213 new Selection(6, 4, 6, 35),
1214 new Selection(7, 4, 7, 35),
1215 ]);
1216
1217 // 3 times => reaching the ending of lines 5 & 6
1218 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1219 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1220 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1221 assertCursor(viewModel, [
1222 new Selection(1, 4, 1, 28),
1223 new Selection(2, 4, 2, 28),
1224 new Selection(3, 4, 3, 32),
1225 new Selection(4, 4, 4, 34),
1226 new Selection(5, 4, 5, 37),
1227 new Selection(6, 4, 6, 37),
1228 new Selection(7, 4, 7, 35),
1229 ]);
1230
1231 // cannot go anywhere anymore
1232 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1233 assertCursor(viewModel, [
1234 new Selection(1, 4, 1, 28),
1235 new Selection(2, 4, 2, 28),
1236 new Selection(3, 4, 3, 32),
1237 new Selection(4, 4, 4, 34),
1238 new Selection(5, 4, 5, 37),
1239 new Selection(6, 4, 6, 37),
1240 new Selection(7, 4, 7, 35),
1241 ]);
1242
1243 // cannot go anywhere anymore even if we insist
1244 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1245 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1246 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1247 CoreNavigationCommands.CursorColumnSelectRight.runCoreEditorCommand(viewModel, {});
1248 assertCursor(viewModel, [
1249 new Selection(1, 4, 1, 28),
1250 new Selection(2, 4, 2, 28),
1251 new Selection(3, 4, 3, 32),
1252 new Selection(4, 4, 4, 34),
1253 new Selection(5, 4, 5, 37),
1254 new Selection(6, 4, 6, 37),
1255 new Selection(7, 4, 7, 35),
1256 ]);
1257
1258 // can easily go back
1259 CoreNavigationCommands.CursorColumnSelectLeft.runCoreEditorCommand(viewModel, {});
1260 assertCursor(viewModel, [
1261 new Selection(1, 4, 1, 28),
1262 new Selection(2, 4, 2, 28),
1263 new Selection(3, 4, 3, 32),
1264 new Selection(4, 4, 4, 34),
1265 new Selection(5, 4, 5, 36),
1266 new Selection(6, 4, 6, 36),
1267 new Selection(7, 4, 7, 35),
1268 ]);
1269 });
1270 });
1271});
1272
1273class SurroundingMode extends MockMode {
1274
1275 private static readonly _id = new LanguageIdentifier('surroundingMode', 3);
1276
1277 constructor() {
1278 super(SurroundingMode._id);
1279 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
1280 autoClosingPairs: [{ open: '(', close: ')' }]
1281 }));
1282 }
1283}
1284
1285class OnEnterMode extends MockMode {
1286 private static readonly _id = new LanguageIdentifier('onEnterMode', 3);
1287
1288 constructor(indentAction: IndentAction) {
1289 super(OnEnterMode._id);
1290 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
1291 onEnterRules: [{
1292 beforeText: /.*/,
1293 action: {
1294 indentAction: indentAction
1295 }
1296 }]
1297 }));
1298 }
1299}
1300
1301class IndentRulesMode extends MockMode {
1302 private static readonly _id = new LanguageIdentifier('indentRulesMode', 4);
1303 constructor(indentationRules: IndentationRule) {
1304 super(IndentRulesMode._id);
1305 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
1306 indentationRules: indentationRules
1307 }));
1308 }
1309}
1310
1311suite('Editor Controller - Regression tests', () => {
1312
1313 test('issue microsoft/monaco-editor#443: Indentation of a single row deletes selected text in some cases', () => {
1314 let model = createTextModel(
1315 [
1316 'Hello world!',
1317 'another line'
1318 ].join('\n'),
1319 {
1320 insertSpaces: false
1321 },
1322 );
1323
1324 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1325 viewModel.setSelections('test', [new Selection(1, 1, 1, 13)]);
1326
1327 // Check that indenting maintains the selection start at column 1
1328 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
1329 assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 1, 1, 14));
1330 });
1331
1332 model.dispose();
1333 });
1334
1335 test('Bug 9121: Auto indent + undo + redo is funky', () => {
1336 let model = createTextModel(
1337 [
1338 ''
1339 ].join('\n'),
1340 {
1341 insertSpaces: false,
1342 trimAutoWhitespace: false
1343 },
1344 );
1345
1346 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1347 viewModel.type('\n', 'keyboard');
1348 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n', 'assert1');
1349
1350 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
1351 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2');
1352
1353 viewModel.type('\n', 'keyboard');
1354 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\n\t', 'assert3');
1355
1356 viewModel.type('x');
1357 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert4');
1358
1359 CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {});
1360 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert5');
1361
1362 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
1363 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert6');
1364
1365 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
1366 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tx', 'assert7');
1367
1368 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
1369 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert8');
1370
1371 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
1372 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert9');
1373
1374 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1375 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert10');
1376
1377 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1378 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert11');
1379
1380 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1381 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\n\tx', 'assert12');
1382
1383 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
1384 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t\nx', 'assert13');
1385
1386 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
1387 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert14');
1388
1389 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
1390 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert15');
1391 });
1392
1393 model.dispose();
1394 });
1395
1396 test('issue #23539: Setting model EOL isn\'t undoable', () => {
1397 withTestCodeEditor([
1398 'Hello',
1399 'world'
1400 ], {}, (editor, viewModel) => {
1401 const model = editor.getModel()!;
1402
1403 assertCursor(viewModel, new Position(1, 1));
1404 model.setEOL(EndOfLineSequence.LF);
1405 assert.strictEqual(model.getValue(), 'Hello\nworld');
1406
1407 model.pushEOL(EndOfLineSequence.CRLF);
1408 assert.strictEqual(model.getValue(), 'Hello\r\nworld');
1409
1410 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1411 assert.strictEqual(model.getValue(), 'Hello\nworld');
1412 });
1413 });
1414
1415 test('issue #47733: Undo mangles unicode characters', () => {
1416 const languageId = new LanguageIdentifier('myMode', 3);
1417 class MyMode extends MockMode {
1418 constructor() {
1419 super(languageId);
1420 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
1421 surroundingPairs: [{ open: '%', close: '%' }]
1422 }));
1423 }
1424 }
1425
1426 const mode = new MyMode();
1427 const model = createTextModel('\'👁\'', undefined, languageId);
1428
1429 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1430 editor.setSelection(new Selection(1, 1, 1, 2));
1431
1432 viewModel.type('%', 'keyboard');
1433 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '%\'%👁\'', 'assert1');
1434
1435 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1436 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\'👁\'', 'assert2');
1437 });
1438
1439 model.dispose();
1440 mode.dispose();
1441 });
1442
1443 test('issue #46208: Allow empty selections in the undo/redo stack', () => {
1444 let model = createTextModel('');
1445
1446 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1447 viewModel.type('Hello', 'keyboard');
1448 viewModel.type(' ', 'keyboard');
1449 viewModel.type('world', 'keyboard');
1450 viewModel.type(' ', 'keyboard');
1451 assert.strictEqual(model.getLineContent(1), 'Hello world ');
1452 assertCursor(viewModel, new Position(1, 13));
1453
1454 moveLeft(editor, viewModel);
1455 moveRight(editor, viewModel);
1456
1457 model.pushEditOperations([], [EditOperation.replaceMove(new Range(1, 12, 1, 13), '')], () => []);
1458 assert.strictEqual(model.getLineContent(1), 'Hello world');
1459 assertCursor(viewModel, new Position(1, 12));
1460
1461 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1462 assert.strictEqual(model.getLineContent(1), 'Hello world ');
1463 assertCursor(viewModel, new Selection(1, 12, 1, 13));
1464
1465 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1466 assert.strictEqual(model.getLineContent(1), 'Hello world');
1467 assertCursor(viewModel, new Position(1, 12));
1468
1469 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1470 assert.strictEqual(model.getLineContent(1), 'Hello');
1471 assertCursor(viewModel, new Position(1, 6));
1472
1473 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1474 assert.strictEqual(model.getLineContent(1), '');
1475 assertCursor(viewModel, new Position(1, 1));
1476
1477 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
1478 assert.strictEqual(model.getLineContent(1), 'Hello');
1479 assertCursor(viewModel, new Position(1, 6));
1480
1481 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
1482 assert.strictEqual(model.getLineContent(1), 'Hello world');
1483 assertCursor(viewModel, new Position(1, 12));
1484
1485 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
1486 assert.strictEqual(model.getLineContent(1), 'Hello world ');
1487 assertCursor(viewModel, new Position(1, 13));
1488
1489 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
1490 assert.strictEqual(model.getLineContent(1), 'Hello world');
1491 assertCursor(viewModel, new Position(1, 12));
1492
1493 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
1494 assert.strictEqual(model.getLineContent(1), 'Hello world');
1495 assertCursor(viewModel, new Position(1, 12));
1496 });
1497
1498 model.dispose();
1499 });
1500
1501 test('bug #16815:Shift+Tab doesn\'t go back to tabstop', () => {
1502 let mode = new OnEnterMode(IndentAction.IndentOutdent);
1503 let model = createTextModel(
1504 [
1505 ' function baz() {'
1506 ].join('\n'),
1507 undefined,
1508 mode.getLanguageIdentifier()
1509 );
1510
1511 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1512 moveTo(editor, viewModel, 1, 6, false);
1513 assertCursor(viewModel, new Selection(1, 6, 1, 6));
1514
1515 CoreEditingCommands.Outdent.runEditorCommand(null, editor, null);
1516 assert.strictEqual(model.getLineContent(1), ' function baz() {');
1517 assertCursor(viewModel, new Selection(1, 5, 1, 5));
1518 });
1519
1520 model.dispose();
1521 mode.dispose();
1522 });
1523
1524 test('Bug #18293:[regression][editor] Can\'t outdent whitespace line', () => {
1525 let model = createTextModel(
1526 [
1527 ' '
1528 ].join('\n')
1529 );
1530
1531 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1532 moveTo(editor, viewModel, 1, 7, false);
1533 assertCursor(viewModel, new Selection(1, 7, 1, 7));
1534
1535 CoreEditingCommands.Outdent.runEditorCommand(null, editor, null);
1536 assert.strictEqual(model.getLineContent(1), ' ');
1537 assertCursor(viewModel, new Selection(1, 5, 1, 5));
1538 });
1539
1540 model.dispose();
1541 });
1542
1543 test('issue #95591: Unindenting moves cursor to beginning of line', () => {
1544 let model = createTextModel(
1545 [
1546 ' '
1547 ].join('\n')
1548 );
1549
1550 withTestCodeEditor(null, {
1551 model: model,
1552 useTabStops: false
1553 }, (editor, viewModel) => {
1554 moveTo(editor, viewModel, 1, 9, false);
1555 assertCursor(viewModel, new Selection(1, 9, 1, 9));
1556
1557 CoreEditingCommands.Outdent.runEditorCommand(null, editor, null);
1558 assert.strictEqual(model.getLineContent(1), ' ');
1559 assertCursor(viewModel, new Selection(1, 5, 1, 5));
1560 });
1561
1562 model.dispose();
1563 });
1564
1565 test('Bug #16657: [editor] Tab on empty line of zero indentation moves cursor to position (1,1)', () => {
1566 let model = createTextModel(
1567 [
1568 'function baz() {',
1569 '\tfunction hello() { // something here',
1570 '\t',
1571 '',
1572 '\t}',
1573 '}',
1574 ''
1575 ].join('\n'),
1576 {
1577 insertSpaces: false,
1578 },
1579 );
1580
1581 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1582 moveTo(editor, viewModel, 7, 1, false);
1583 assertCursor(viewModel, new Selection(7, 1, 7, 1));
1584
1585 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
1586 assert.strictEqual(model.getLineContent(7), '\t');
1587 assertCursor(viewModel, new Selection(7, 2, 7, 2));
1588 });
1589
1590 model.dispose();
1591 });
1592
1593 test('bug #16740: [editor] Cut line doesn\'t quite cut the last line', () => {
1594
1595 // Part 1 => there is text on the last line
1596 withTestCodeEditor([
1597 'asdasd',
1598 'qwerty'
1599 ], {}, (editor, viewModel) => {
1600 const model = editor.getModel()!;
1601
1602 moveTo(editor, viewModel, 2, 1, false);
1603 assertCursor(viewModel, new Selection(2, 1, 2, 1));
1604
1605 viewModel.cut('keyboard');
1606 assert.strictEqual(model.getLineCount(), 1);
1607 assert.strictEqual(model.getLineContent(1), 'asdasd');
1608
1609 });
1610
1611 // Part 2 => there is no text on the last line
1612 withTestCodeEditor([
1613 'asdasd',
1614 ''
1615 ], {}, (editor, viewModel) => {
1616 const model = editor.getModel()!;
1617
1618 moveTo(editor, viewModel, 2, 1, false);
1619 assertCursor(viewModel, new Selection(2, 1, 2, 1));
1620
1621 viewModel.cut('keyboard');
1622 assert.strictEqual(model.getLineCount(), 1);
1623 assert.strictEqual(model.getLineContent(1), 'asdasd');
1624
1625 viewModel.cut('keyboard');
1626 assert.strictEqual(model.getLineCount(), 1);
1627 assert.strictEqual(model.getLineContent(1), '');
1628 });
1629 });
1630
1631 test('Bug #11476: Double bracket surrounding + undo is broken', () => {
1632 let mode = new SurroundingMode();
1633 usingCursor({
1634 text: [
1635 'hello'
1636 ],
1637 languageIdentifier: mode.getLanguageIdentifier()
1638 }, (editor, model, viewModel) => {
1639 moveTo(editor, viewModel, 1, 3, false);
1640 moveTo(editor, viewModel, 1, 5, true);
1641 assertCursor(viewModel, new Selection(1, 3, 1, 5));
1642
1643 viewModel.type('(', 'keyboard');
1644 assertCursor(viewModel, new Selection(1, 4, 1, 6));
1645
1646 viewModel.type('(', 'keyboard');
1647 assertCursor(viewModel, new Selection(1, 5, 1, 7));
1648 });
1649 mode.dispose();
1650 });
1651
1652 test('issue #1140: Backspace stops prematurely', () => {
1653 let mode = new SurroundingMode();
1654 let model = createTextModel(
1655 [
1656 'function baz() {',
1657 ' return 1;',
1658 '};'
1659 ].join('\n')
1660 );
1661
1662 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1663 moveTo(editor, viewModel, 3, 2, false);
1664 moveTo(editor, viewModel, 1, 14, true);
1665 assertCursor(viewModel, new Selection(3, 2, 1, 14));
1666
1667 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
1668 assertCursor(viewModel, new Selection(1, 14, 1, 14));
1669 assert.strictEqual(model.getLineCount(), 1);
1670 assert.strictEqual(model.getLineContent(1), 'function baz(;');
1671 });
1672
1673 model.dispose();
1674 mode.dispose();
1675 });
1676
1677 test('issue #10212: Pasting entire line does not replace selection', () => {
1678 usingCursor({
1679 text: [
1680 'line1',
1681 'line2'
1682 ],
1683 }, (editor, model, viewModel) => {
1684 moveTo(editor, viewModel, 2, 1, false);
1685 moveTo(editor, viewModel, 2, 6, true);
1686
1687 viewModel.paste('line1\n', true);
1688
1689 assert.strictEqual(model.getLineContent(1), 'line1');
1690 assert.strictEqual(model.getLineContent(2), 'line1');
1691 assert.strictEqual(model.getLineContent(3), '');
1692 });
1693 });
1694
1695 test('issue #74722: Pasting whole line does not replace selection', () => {
1696 usingCursor({
1697 text: [
1698 'line1',
1699 'line sel 2',
1700 'line3'
1701 ],
1702 }, (editor, model, viewModel) => {
1703 viewModel.setSelections('test', [new Selection(2, 6, 2, 9)]);
1704
1705 viewModel.paste('line1\n', true);
1706
1707 assert.strictEqual(model.getLineContent(1), 'line1');
1708 assert.strictEqual(model.getLineContent(2), 'line line1');
1709 assert.strictEqual(model.getLineContent(3), ' 2');
1710 assert.strictEqual(model.getLineContent(4), 'line3');
1711 });
1712 });
1713
1714 test('issue #4996: Multiple cursor paste pastes contents of all cursors', () => {
1715 usingCursor({
1716 text: [
1717 'line1',
1718 'line2',
1719 'line3'
1720 ],
1721 }, (editor, model, viewModel) => {
1722 viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]);
1723
1724 viewModel.paste(
1725 'a\nb\nc\nd',
1726 false,
1727 [
1728 'a\nb',
1729 'c\nd'
1730 ]
1731 );
1732
1733 assert.strictEqual(model.getValue(), [
1734 'a',
1735 'bline1',
1736 'c',
1737 'dline2',
1738 'line3'
1739 ].join('\n'));
1740 });
1741 });
1742
1743 test('issue #16155: Paste into multiple cursors has edge case when number of lines equals number of cursors - 1', () => {
1744 usingCursor({
1745 text: [
1746 'test',
1747 'test',
1748 'test',
1749 'test'
1750 ],
1751 }, (editor, model, viewModel) => {
1752 viewModel.setSelections('test', [
1753 new Selection(1, 1, 1, 5),
1754 new Selection(2, 1, 2, 5),
1755 new Selection(3, 1, 3, 5),
1756 new Selection(4, 1, 4, 5),
1757 ]);
1758
1759 viewModel.paste(
1760 'aaa\nbbb\nccc\n',
1761 false,
1762 null
1763 );
1764
1765 assert.strictEqual(model.getValue(), [
1766 'aaa',
1767 'bbb',
1768 'ccc',
1769 '',
1770 'aaa',
1771 'bbb',
1772 'ccc',
1773 '',
1774 'aaa',
1775 'bbb',
1776 'ccc',
1777 '',
1778 'aaa',
1779 'bbb',
1780 'ccc',
1781 '',
1782 ].join('\n'));
1783 });
1784 });
1785
1786 test('issue #43722: Multiline paste doesn\'t work anymore', () => {
1787 usingCursor({
1788 text: [
1789 'test',
1790 'test',
1791 'test',
1792 'test'
1793 ],
1794 }, (editor, model, viewModel) => {
1795 viewModel.setSelections('test', [
1796 new Selection(1, 1, 1, 5),
1797 new Selection(2, 1, 2, 5),
1798 new Selection(3, 1, 3, 5),
1799 new Selection(4, 1, 4, 5),
1800 ]);
1801
1802 viewModel.paste(
1803 'aaa\r\nbbb\r\nccc\r\nddd\r\n',
1804 false,
1805 null
1806 );
1807
1808 assert.strictEqual(model.getValue(), [
1809 'aaa',
1810 'bbb',
1811 'ccc',
1812 'ddd',
1813 ].join('\n'));
1814 });
1815 });
1816
1817 test('issue #46440: (1) Pasting a multi-line selection pastes entire selection into every insertion point', () => {
1818 usingCursor({
1819 text: [
1820 'line1',
1821 'line2',
1822 'line3'
1823 ],
1824 }, (editor, model, viewModel) => {
1825 viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), new Selection(3, 1, 3, 1)]);
1826
1827 viewModel.paste(
1828 'a\nb\nc',
1829 false,
1830 null
1831 );
1832
1833 assert.strictEqual(model.getValue(), [
1834 'aline1',
1835 'bline2',
1836 'cline3'
1837 ].join('\n'));
1838 });
1839 });
1840
1841 test('issue #46440: (2) Pasting a multi-line selection pastes entire selection into every insertion point', () => {
1842 usingCursor({
1843 text: [
1844 'line1',
1845 'line2',
1846 'line3'
1847 ],
1848 }, (editor, model, viewModel) => {
1849 viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1), new Selection(3, 1, 3, 1)]);
1850
1851 viewModel.paste(
1852 'a\nb\nc\n',
1853 false,
1854 null
1855 );
1856
1857 assert.strictEqual(model.getValue(), [
1858 'aline1',
1859 'bline2',
1860 'cline3'
1861 ].join('\n'));
1862 });
1863 });
1864
1865 test('issue #3071: Investigate why undo stack gets corrupted', () => {
1866 let model = createTextModel(
1867 [
1868 'some lines',
1869 'and more lines',
1870 'just some text',
1871 ].join('\n')
1872 );
1873
1874 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1875 moveTo(editor, viewModel, 1, 1, false);
1876 moveTo(editor, viewModel, 3, 4, true);
1877
1878 let isFirst = true;
1879 model.onDidChangeContent(() => {
1880 if (isFirst) {
1881 isFirst = false;
1882 viewModel.type('\t', 'keyboard');
1883 }
1884 });
1885
1886 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
1887 assert.strictEqual(model.getValue(), [
1888 '\t just some text'
1889 ].join('\n'), '001');
1890
1891 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1892 assert.strictEqual(model.getValue(), [
1893 ' some lines',
1894 ' and more lines',
1895 ' just some text',
1896 ].join('\n'), '002');
1897
1898 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1899 assert.strictEqual(model.getValue(), [
1900 'some lines',
1901 'and more lines',
1902 'just some text',
1903 ].join('\n'), '003');
1904
1905 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
1906 assert.strictEqual(model.getValue(), [
1907 'some lines',
1908 'and more lines',
1909 'just some text',
1910 ].join('\n'), '004');
1911 });
1912
1913 model.dispose();
1914 });
1915
1916 test('issue #12950: Cannot Double Click To Insert Emoji Using OSX Emoji Panel', () => {
1917 usingCursor({
1918 text: [
1919 'some lines',
1920 'and more lines',
1921 'just some text',
1922 ],
1923 languageIdentifier: null
1924 }, (editor, model, viewModel) => {
1925 moveTo(editor, viewModel, 3, 1, false);
1926
1927 viewModel.type('😍', 'keyboard');
1928
1929 assert.strictEqual(model.getValue(), [
1930 'some lines',
1931 'and more lines',
1932 '😍just some text',
1933 ].join('\n'));
1934 });
1935 });
1936
1937 test('issue #3463: pressing tab adds spaces, but not as many as for a tab', () => {
1938 let model = createTextModel(
1939 [
1940 'function a() {',
1941 '\tvar a = {',
1942 '\t\tx: 3',
1943 '\t};',
1944 '}',
1945 ].join('\n')
1946 );
1947
1948 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1949 moveTo(editor, viewModel, 3, 2, false);
1950 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
1951 assert.strictEqual(model.getLineContent(3), '\t \tx: 3');
1952 });
1953
1954 model.dispose();
1955 });
1956
1957 test('issue #4312: trying to type a tab character over a sequence of spaces results in unexpected behaviour', () => {
1958 let model = createTextModel(
1959 [
1960 'var foo = 123; // this is a comment',
1961 'var bar = 4; // another comment'
1962 ].join('\n'),
1963 {
1964 insertSpaces: false,
1965 }
1966 );
1967
1968 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
1969 moveTo(editor, viewModel, 1, 15, false);
1970 moveTo(editor, viewModel, 1, 22, true);
1971 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
1972 assert.strictEqual(model.getLineContent(1), 'var foo = 123;\t// this is a comment');
1973 });
1974
1975 model.dispose();
1976 });
1977
1978 test('issue #832: word right', () => {
1979
1980 usingCursor({
1981 text: [
1982 ' /* Just some more text a+= 3 +5-3 + 7 */ '
1983 ],
1984 }, (editor, model, viewModel) => {
1985 moveTo(editor, viewModel, 1, 1, false);
1986
1987 function assertWordRight(col: number, expectedCol: number) {
1988 let args = {
1989 position: {
1990 lineNumber: 1,
1991 column: col
1992 }
1993 };
1994 if (col === 1) {
1995 CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, args);
1996 } else {
1997 CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, args);
1998 }
1999
2000 assert.strictEqual(viewModel.getSelection().startColumn, 1, 'TEST FOR ' + col);
2001 assert.strictEqual(viewModel.getSelection().endColumn, expectedCol, 'TEST FOR ' + col);
2002 }
2003
2004 assertWordRight(1, ' '.length + 1);
2005 assertWordRight(2, ' '.length + 1);
2006 assertWordRight(3, ' '.length + 1);
2007 assertWordRight(4, ' '.length + 1);
2008 assertWordRight(5, ' /'.length + 1);
2009 assertWordRight(6, ' /*'.length + 1);
2010 assertWordRight(7, ' /* '.length + 1);
2011 assertWordRight(8, ' /* Just'.length + 1);
2012 assertWordRight(9, ' /* Just'.length + 1);
2013 assertWordRight(10, ' /* Just'.length + 1);
2014 assertWordRight(11, ' /* Just'.length + 1);
2015 assertWordRight(12, ' /* Just '.length + 1);
2016 assertWordRight(13, ' /* Just some'.length + 1);
2017 assertWordRight(14, ' /* Just some'.length + 1);
2018 assertWordRight(15, ' /* Just some'.length + 1);
2019 assertWordRight(16, ' /* Just some'.length + 1);
2020 assertWordRight(17, ' /* Just some '.length + 1);
2021 assertWordRight(18, ' /* Just some '.length + 1);
2022 assertWordRight(19, ' /* Just some '.length + 1);
2023 assertWordRight(20, ' /* Just some more'.length + 1);
2024 assertWordRight(21, ' /* Just some more'.length + 1);
2025 assertWordRight(22, ' /* Just some more'.length + 1);
2026 assertWordRight(23, ' /* Just some more'.length + 1);
2027 assertWordRight(24, ' /* Just some more '.length + 1);
2028 assertWordRight(25, ' /* Just some more '.length + 1);
2029 assertWordRight(26, ' /* Just some more '.length + 1);
2030 assertWordRight(27, ' /* Just some more text'.length + 1);
2031 assertWordRight(28, ' /* Just some more text'.length + 1);
2032 assertWordRight(29, ' /* Just some more text'.length + 1);
2033 assertWordRight(30, ' /* Just some more text'.length + 1);
2034 assertWordRight(31, ' /* Just some more text '.length + 1);
2035 assertWordRight(32, ' /* Just some more text a'.length + 1);
2036 assertWordRight(33, ' /* Just some more text a+'.length + 1);
2037 assertWordRight(34, ' /* Just some more text a+='.length + 1);
2038 assertWordRight(35, ' /* Just some more text a+= '.length + 1);
2039 assertWordRight(36, ' /* Just some more text a+= 3'.length + 1);
2040 assertWordRight(37, ' /* Just some more text a+= 3 '.length + 1);
2041 assertWordRight(38, ' /* Just some more text a+= 3 +'.length + 1);
2042 assertWordRight(39, ' /* Just some more text a+= 3 +5'.length + 1);
2043 assertWordRight(40, ' /* Just some more text a+= 3 +5-'.length + 1);
2044 assertWordRight(41, ' /* Just some more text a+= 3 +5-3'.length + 1);
2045 assertWordRight(42, ' /* Just some more text a+= 3 +5-3 '.length + 1);
2046 assertWordRight(43, ' /* Just some more text a+= 3 +5-3 +'.length + 1);
2047 assertWordRight(44, ' /* Just some more text a+= 3 +5-3 + '.length + 1);
2048 assertWordRight(45, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1);
2049 assertWordRight(46, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1);
2050 assertWordRight(47, ' /* Just some more text a+= 3 +5-3 + 7 *'.length + 1);
2051 assertWordRight(48, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1);
2052 assertWordRight(49, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1);
2053 assertWordRight(50, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1);
2054 });
2055 });
2056
2057 test('issue #33788: Wrong cursor position when double click to select a word', () => {
2058 let model = createTextModel(
2059 [
2060 'Just some text'
2061 ].join('\n')
2062 );
2063
2064 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
2065 CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 8) });
2066 assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10));
2067
2068 CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, { position: new Position(1, 8) });
2069 assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10));
2070 });
2071
2072 model.dispose();
2073 });
2074
2075 test('issue #12887: Double-click highlighting separating white space', () => {
2076 let model = createTextModel(
2077 [
2078 'abc def'
2079 ].join('\n')
2080 );
2081
2082 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
2083 CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 5) });
2084 assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 5, 1, 8));
2085 });
2086
2087 model.dispose();
2088 });
2089
2090 test('issue #9675: Undo/Redo adds a stop in between CHN Characters', () => {
2091 withTestCodeEditor([], {}, (editor, viewModel) => {
2092 const model = editor.getModel()!;
2093 assertCursor(viewModel, new Position(1, 1));
2094
2095 // Typing sennsei in Japanese - Hiragana
2096 viewModel.type('s', 'keyboard');
2097 viewModel.compositionType('せ', 1, 0, 0);
2098 viewModel.compositionType('せn', 1, 0, 0);
2099 viewModel.compositionType('せん', 2, 0, 0);
2100 viewModel.compositionType('せんs', 2, 0, 0);
2101 viewModel.compositionType('せんせ', 3, 0, 0);
2102 viewModel.compositionType('せんせ', 3, 0, 0);
2103 viewModel.compositionType('せんせい', 3, 0, 0);
2104 viewModel.compositionType('せんせい', 4, 0, 0);
2105 viewModel.compositionType('せんせい', 4, 0, 0);
2106 viewModel.compositionType('せんせい', 4, 0, 0);
2107
2108 assert.strictEqual(model.getLineContent(1), 'せんせい');
2109 assertCursor(viewModel, new Position(1, 5));
2110
2111 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2112 assert.strictEqual(model.getLineContent(1), '');
2113 assertCursor(viewModel, new Position(1, 1));
2114 });
2115 });
2116
2117 test('issue #23913: Greater than 1000+ multi cursor typing replacement text appears inverted, lines begin to drop off selection', function () {
2118 this.timeout(10000);
2119 const LINE_CNT = 2000;
2120
2121 let text: string[] = [];
2122 for (let i = 0; i < LINE_CNT; i++) {
2123 text[i] = 'asd';
2124 }
2125 usingCursor({
2126 text: text
2127 }, (editor, model, viewModel) => {
2128
2129 let selections: Selection[] = [];
2130 for (let i = 0; i < LINE_CNT; i++) {
2131 selections[i] = new Selection(i + 1, 1, i + 1, 1);
2132 }
2133 viewModel.setSelections('test', selections);
2134
2135 viewModel.type('n', 'keyboard');
2136 viewModel.type('n', 'keyboard');
2137
2138 for (let i = 0; i < LINE_CNT; i++) {
2139 assert.strictEqual(model.getLineContent(i + 1), 'nnasd', 'line #' + (i + 1));
2140 }
2141
2142 assert.strictEqual(viewModel.getSelections().length, LINE_CNT);
2143 assert.strictEqual(viewModel.getSelections()[LINE_CNT - 1].startLineNumber, LINE_CNT);
2144 });
2145 });
2146
2147 test('issue #23983: Calling model.setEOL does not reset cursor position', () => {
2148 usingCursor({
2149 text: [
2150 'first line',
2151 'second line'
2152 ]
2153 }, (editor, model, viewModel) => {
2154 model.setEOL(EndOfLineSequence.CRLF);
2155
2156 viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]);
2157 model.setEOL(EndOfLineSequence.LF);
2158
2159 assertCursor(viewModel, new Selection(2, 2, 2, 2));
2160 });
2161 });
2162
2163 test('issue #23983: Calling model.setValue() resets cursor position', () => {
2164 usingCursor({
2165 text: [
2166 'first line',
2167 'second line'
2168 ]
2169 }, (editor, model, viewModel) => {
2170 model.setEOL(EndOfLineSequence.CRLF);
2171
2172 viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]);
2173 model.setValue([
2174 'different first line',
2175 'different second line',
2176 'new third line'
2177 ].join('\n'));
2178
2179 assertCursor(viewModel, new Selection(1, 1, 1, 1));
2180 });
2181 });
2182
2183 test('issue #36740: wordwrap creates an extra step / character at the wrapping point', () => {
2184 // a single model line => 4 view lines
2185 withTestCodeEditor([
2186 [
2187 'Lorem ipsum ',
2188 'dolor sit amet ',
2189 'consectetur ',
2190 'adipiscing elit',
2191 ].join('')
2192 ], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => {
2193 viewModel.setSelections('test', [new Selection(1, 7, 1, 7)]);
2194
2195 moveRight(editor, viewModel);
2196 assertCursor(viewModel, new Selection(1, 8, 1, 8));
2197
2198 moveRight(editor, viewModel);
2199 assertCursor(viewModel, new Selection(1, 9, 1, 9));
2200
2201 moveRight(editor, viewModel);
2202 assertCursor(viewModel, new Selection(1, 10, 1, 10));
2203
2204 moveRight(editor, viewModel);
2205 assertCursor(viewModel, new Selection(1, 11, 1, 11));
2206
2207 moveRight(editor, viewModel);
2208 assertCursor(viewModel, new Selection(1, 12, 1, 12));
2209
2210 moveRight(editor, viewModel);
2211 assertCursor(viewModel, new Selection(1, 13, 1, 13));
2212
2213 // moving to view line 2
2214 moveRight(editor, viewModel);
2215 assertCursor(viewModel, new Selection(1, 14, 1, 14));
2216
2217 moveLeft(editor, viewModel);
2218 assertCursor(viewModel, new Selection(1, 13, 1, 13));
2219
2220 // moving back to view line 1
2221 moveLeft(editor, viewModel);
2222 assertCursor(viewModel, new Selection(1, 12, 1, 12));
2223 });
2224 });
2225
2226 test('issue #110376: multiple selections with wordwrap behave differently', () => {
2227 // a single model line => 4 view lines
2228 withTestCodeEditor([
2229 [
2230 'just a sentence. just a ',
2231 'sentence. just a sentence.',
2232 ].join('')
2233 ], { wordWrap: 'wordWrapColumn', wordWrapColumn: 25 }, (editor, viewModel) => {
2234 viewModel.setSelections('test', [
2235 new Selection(1, 1, 1, 16),
2236 new Selection(1, 18, 1, 33),
2237 new Selection(1, 35, 1, 50),
2238 ]);
2239
2240 moveLeft(editor, viewModel);
2241 assertCursor(viewModel, [
2242 new Selection(1, 1, 1, 1),
2243 new Selection(1, 18, 1, 18),
2244 new Selection(1, 35, 1, 35),
2245 ]);
2246
2247 viewModel.setSelections('test', [
2248 new Selection(1, 1, 1, 16),
2249 new Selection(1, 18, 1, 33),
2250 new Selection(1, 35, 1, 50),
2251 ]);
2252
2253 moveRight(editor, viewModel);
2254 assertCursor(viewModel, [
2255 new Selection(1, 16, 1, 16),
2256 new Selection(1, 33, 1, 33),
2257 new Selection(1, 50, 1, 50),
2258 ]);
2259 });
2260 });
2261
2262 test('issue #98320: Multi-Cursor, Wrap lines and cursorSelectRight ==> cursors out of sync', () => {
2263 // a single model line => 4 view lines
2264 withTestCodeEditor([
2265 [
2266 'lorem_ipsum-1993x11x13',
2267 'dolor_sit_amet-1998x04x27',
2268 'consectetur-2007x10x08',
2269 'adipiscing-2012x07x27',
2270 'elit-2015x02x27',
2271 ].join('\n')
2272 ], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => {
2273 viewModel.setSelections('test', [
2274 new Selection(1, 13, 1, 13),
2275 new Selection(2, 16, 2, 16),
2276 new Selection(3, 13, 3, 13),
2277 new Selection(4, 12, 4, 12),
2278 new Selection(5, 6, 5, 6),
2279 ]);
2280 assertCursor(viewModel, [
2281 new Selection(1, 13, 1, 13),
2282 new Selection(2, 16, 2, 16),
2283 new Selection(3, 13, 3, 13),
2284 new Selection(4, 12, 4, 12),
2285 new Selection(5, 6, 5, 6),
2286 ]);
2287
2288 moveRight(editor, viewModel, true);
2289 assertCursor(viewModel, [
2290 new Selection(1, 13, 1, 14),
2291 new Selection(2, 16, 2, 17),
2292 new Selection(3, 13, 3, 14),
2293 new Selection(4, 12, 4, 13),
2294 new Selection(5, 6, 5, 7),
2295 ]);
2296
2297 moveRight(editor, viewModel, true);
2298 assertCursor(viewModel, [
2299 new Selection(1, 13, 1, 15),
2300 new Selection(2, 16, 2, 18),
2301 new Selection(3, 13, 3, 15),
2302 new Selection(4, 12, 4, 14),
2303 new Selection(5, 6, 5, 8),
2304 ]);
2305
2306 moveRight(editor, viewModel, true);
2307 assertCursor(viewModel, [
2308 new Selection(1, 13, 1, 16),
2309 new Selection(2, 16, 2, 19),
2310 new Selection(3, 13, 3, 16),
2311 new Selection(4, 12, 4, 15),
2312 new Selection(5, 6, 5, 9),
2313 ]);
2314
2315 moveRight(editor, viewModel, true);
2316 assertCursor(viewModel, [
2317 new Selection(1, 13, 1, 17),
2318 new Selection(2, 16, 2, 20),
2319 new Selection(3, 13, 3, 17),
2320 new Selection(4, 12, 4, 16),
2321 new Selection(5, 6, 5, 10),
2322 ]);
2323 });
2324 });
2325
2326 test('issue #41573 - delete across multiple lines does not shrink the selection when word wraps', () => {
2327 withTestCodeEditor([
2328 'Authorization: \'Bearer pHKRfCTFSnGxs6akKlb9ddIXcca0sIUSZJutPHYqz7vEeHdMTMh0SGN0IGU3a0n59DXjTLRsj5EJ2u33qLNIFi9fk5XF8pK39PndLYUZhPt4QvHGLScgSkK0L4gwzkzMloTQPpKhqiikiIOvyNNSpd2o8j29NnOmdTUOKi9DVt74PD2ohKxyOrWZ6oZprTkb3eKajcpnS0LABKfaw2rmv4\','
2329 ].join('\n'), { wordWrap: 'wordWrapColumn', wordWrapColumn: 100 }, (editor, viewModel) => {
2330 moveTo(editor, viewModel, 1, 43, false);
2331 moveTo(editor, viewModel, 1, 147, true);
2332 assertCursor(viewModel, new Selection(1, 43, 1, 147));
2333
2334 editor.getModel().applyEdits([{
2335 range: new Range(1, 1, 1, 43),
2336 text: ''
2337 }]);
2338
2339 assertCursor(viewModel, new Selection(1, 1, 1, 105));
2340 });
2341 });
2342
2343 test('issue #22717: Moving text cursor cause an incorrect position in Chinese', () => {
2344 // a single model line => 4 view lines
2345 withTestCodeEditor([
2346 [
2347 '一二三四五六七八九十',
2348 '12345678901234567890',
2349 ].join('\n')
2350 ], {}, (editor, viewModel) => {
2351 viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
2352
2353 moveDown(editor, viewModel);
2354 assertCursor(viewModel, new Selection(2, 9, 2, 9));
2355
2356 moveRight(editor, viewModel);
2357 assertCursor(viewModel, new Selection(2, 10, 2, 10));
2358
2359 moveRight(editor, viewModel);
2360 assertCursor(viewModel, new Selection(2, 11, 2, 11));
2361
2362 moveUp(editor, viewModel);
2363 assertCursor(viewModel, new Selection(1, 6, 1, 6));
2364 });
2365 });
2366
2367 test('issue #112301: new stickyTabStops feature interferes with word wrap', () => {
2368 withTestCodeEditor([
2369 [
2370 'function hello() {',
2371 ' console.log(`this is a long console message`)',
2372 '}',
2373 ].join('\n')
2374 ], { wordWrap: 'wordWrapColumn', wordWrapColumn: 32, stickyTabStops: true }, (editor, viewModel) => {
2375 viewModel.setSelections('test', [
2376 new Selection(2, 31, 2, 31)
2377 ]);
2378 moveRight(editor, viewModel, false);
2379 assertCursor(viewModel, new Position(2, 32));
2380
2381 moveRight(editor, viewModel, false);
2382 assertCursor(viewModel, new Position(2, 33));
2383
2384 moveRight(editor, viewModel, false);
2385 assertCursor(viewModel, new Position(2, 34));
2386
2387 moveLeft(editor, viewModel, false);
2388 assertCursor(viewModel, new Position(2, 33));
2389
2390 moveLeft(editor, viewModel, false);
2391 assertCursor(viewModel, new Position(2, 32));
2392
2393 moveLeft(editor, viewModel, false);
2394 assertCursor(viewModel, new Position(2, 31));
2395 });
2396 });
2397
2398 test('issue #44805: Should not be able to undo in readonly editor', () => {
2399 let model = createTextModel(
2400 [
2401 ''
2402 ].join('\n')
2403 );
2404
2405 withTestCodeEditor(null, { readOnly: true, model: model }, (editor, viewModel) => {
2406 model.pushEditOperations([new Selection(1, 1, 1, 1)], [{
2407 range: new Range(1, 1, 1, 1),
2408 text: 'Hello world!'
2409 }], () => [new Selection(1, 1, 1, 1)]);
2410 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'Hello world!');
2411
2412 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2413 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'Hello world!');
2414 });
2415
2416 model.dispose();
2417 });
2418
2419 test('issue #46314: ViewModel is out of sync with Model!', () => {
2420
2421 const tokenizationSupport: ITokenizationSupport = {
2422 getInitialState: () => NULL_STATE,
2423 tokenize: undefined!,
2424 tokenize2: (line: string, hasEOL: boolean, state: IState): TokenizationResult2 => {
2425 return new TokenizationResult2(new Uint32Array(0), state);
2426 }
2427 };
2428
2429 const LANGUAGE_ID = 'modelModeTest1';
2430 const languageRegistration = TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
2431 let model = createTextModel('Just text', undefined, new LanguageIdentifier(LANGUAGE_ID, 0));
2432
2433 withTestCodeEditor(null, { model: model }, (editor1, cursor1) => {
2434 withTestCodeEditor(null, { model: model }, (editor2, cursor2) => {
2435
2436 editor1.onDidChangeCursorPosition(() => {
2437 model.tokenizeIfCheap(1);
2438 });
2439
2440 model.applyEdits([{ range: new Range(1, 1, 1, 1), text: '-' }]);
2441 });
2442 });
2443
2444 languageRegistration.dispose();
2445 model.dispose();
2446 });
2447
2448 test('issue #37967: problem replacing consecutive characters', () => {
2449 let model = createTextModel(
2450 [
2451 'const a = "foo";',
2452 'const b = ""'
2453 ].join('\n')
2454 );
2455
2456 withTestCodeEditor(null, { multiCursorMergeOverlapping: false, model: model }, (editor, viewModel) => {
2457 editor.setSelections([
2458 new Selection(1, 12, 1, 12),
2459 new Selection(1, 16, 1, 16),
2460 new Selection(2, 12, 2, 12),
2461 new Selection(2, 13, 2, 13),
2462 ]);
2463
2464 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2465
2466 assertCursor(viewModel, [
2467 new Selection(1, 11, 1, 11),
2468 new Selection(1, 14, 1, 14),
2469 new Selection(2, 11, 2, 11),
2470 new Selection(2, 11, 2, 11),
2471 ]);
2472
2473 viewModel.type('\'', 'keyboard');
2474
2475 assert.strictEqual(model.getLineContent(1), 'const a = \'foo\';');
2476 assert.strictEqual(model.getLineContent(2), 'const b = \'\'');
2477 });
2478
2479 model.dispose();
2480 });
2481
2482 test('issue #15761: Cursor doesn\'t move in a redo operation', () => {
2483 let model = createTextModel(
2484 [
2485 'hello'
2486 ].join('\n')
2487 );
2488
2489 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
2490 editor.setSelections([
2491 new Selection(1, 4, 1, 4)
2492 ]);
2493
2494 editor.executeEdits('test', [{
2495 range: new Range(1, 1, 1, 1),
2496 text: '*',
2497 forceMoveMarkers: true
2498 }]);
2499 assertCursor(viewModel, [
2500 new Selection(1, 5, 1, 5),
2501 ]);
2502
2503 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2504 assertCursor(viewModel, [
2505 new Selection(1, 4, 1, 4),
2506 ]);
2507
2508 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
2509 assertCursor(viewModel, [
2510 new Selection(1, 5, 1, 5),
2511 ]);
2512 });
2513
2514 model.dispose();
2515 });
2516
2517 test('issue #42783: API Calls with Undo Leave Cursor in Wrong Position', () => {
2518 let model = createTextModel(
2519 [
2520 'ab'
2521 ].join('\n')
2522 );
2523
2524 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
2525 editor.setSelections([
2526 new Selection(1, 1, 1, 1)
2527 ]);
2528
2529 editor.executeEdits('test', [{
2530 range: new Range(1, 1, 1, 3),
2531 text: ''
2532 }]);
2533 assertCursor(viewModel, [
2534 new Selection(1, 1, 1, 1),
2535 ]);
2536
2537 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2538 assertCursor(viewModel, [
2539 new Selection(1, 1, 1, 1),
2540 ]);
2541
2542 editor.executeEdits('test', [{
2543 range: new Range(1, 1, 1, 2),
2544 text: ''
2545 }]);
2546 assertCursor(viewModel, [
2547 new Selection(1, 1, 1, 1),
2548 ]);
2549 });
2550
2551 model.dispose();
2552 });
2553
2554 test('issue #85712: Paste line moves cursor to start of current line rather than start of next line', () => {
2555 let model = createTextModel(
2556 [
2557 'abc123',
2558 ''
2559 ].join('\n')
2560 );
2561
2562 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
2563 editor.setSelections([
2564 new Selection(2, 1, 2, 1)
2565 ]);
2566 viewModel.paste('something\n', true);
2567 assert.strictEqual(model.getValue(), [
2568 'abc123',
2569 'something',
2570 ''
2571 ].join('\n'));
2572 assertCursor(viewModel, new Position(3, 1));
2573 });
2574
2575 model.dispose();
2576 });
2577
2578 test('issue #84897: Left delete behavior in some languages is changed', () => {
2579 let model = createTextModel(
2580 [
2581 'สวัสดี'
2582 ].join('\n')
2583 );
2584
2585 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
2586 editor.setSelections([
2587 new Selection(1, 7, 1, 7)
2588 ]);
2589
2590 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2591 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัสด');
2592
2593 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2594 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัส');
2595
2596 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2597 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวั');
2598
2599 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2600 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สว');
2601
2602 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2603 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ส');
2604
2605 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2606 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');
2607 });
2608
2609 model.dispose();
2610 });
2611
2612 test('issue #122914: Left delete behavior in some languages is changed (useTabStops: false)', () => {
2613 let model = createTextModel(
2614 [
2615 'สวัสดี'
2616 ].join('\n')
2617 );
2618
2619 withTestCodeEditor(null, { model: model, useTabStops: false }, (editor, viewModel) => {
2620 editor.setSelections([
2621 new Selection(1, 7, 1, 7)
2622 ]);
2623
2624 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2625 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัสด');
2626
2627 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2628 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัส');
2629
2630 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2631 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวั');
2632
2633 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2634 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สว');
2635
2636 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2637 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ส');
2638
2639 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2640 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');
2641 });
2642
2643 model.dispose();
2644 });
2645
2646 test('issue #99629: Emoji modifiers in text treated separately when using backspace', () => {
2647 const model = createTextModel(
2648 [
2649 '👶🏾'
2650 ].join('\n')
2651 );
2652
2653 withTestCodeEditor(null, { model: model, useTabStops: false }, (editor, viewModel) => {
2654 const len = model.getValueLength();
2655 editor.setSelections([
2656 new Selection(1, 1 + len, 1, 1 + len)
2657 ]);
2658
2659 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2660 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');
2661 });
2662
2663 model.dispose();
2664 });
2665
2666 test('issue #99629: Emoji modifiers in text treated separately when using backspace (ZWJ sequence)', () => {
2667 let model = createTextModel(
2668 [
2669 '👨‍👩🏽‍👧‍👦'
2670 ].join('\n')
2671 );
2672
2673 withTestCodeEditor(null, { model: model, useTabStops: false }, (editor, viewModel) => {
2674 const len = model.getValueLength();
2675 editor.setSelections([
2676 new Selection(1, 1 + len, 1, 1 + len)
2677 ]);
2678
2679 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2680 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨‍👩🏽‍👧');
2681
2682 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2683 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨‍👩🏽');
2684
2685 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2686 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨');
2687
2688 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
2689 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');
2690 });
2691
2692 model.dispose();
2693 });
2694
2695 test('issue #105730: move left behaves differently for multiple cursors', () => {
2696 const model = createTextModel('asdfghjkl, asdfghjkl, asdfghjkl, ');
2697
2698 withTestCodeEditor(
2699 null,
2700 {
2701 model: model,
2702 wordWrap: 'wordWrapColumn',
2703 wordWrapColumn: 24
2704 },
2705 (editor, viewModel) => {
2706 viewModel.setSelections('test', [
2707 new Selection(1, 10, 1, 12),
2708 new Selection(1, 21, 1, 23),
2709 new Selection(1, 32, 1, 34)
2710 ]);
2711 moveLeft(editor, viewModel, false);
2712 assertCursor(viewModel, [
2713 new Selection(1, 10, 1, 10),
2714 new Selection(1, 21, 1, 21),
2715 new Selection(1, 32, 1, 32)
2716 ]);
2717
2718 viewModel.setSelections('test', [
2719 new Selection(1, 10, 1, 12),
2720 new Selection(1, 21, 1, 23),
2721 new Selection(1, 32, 1, 34)
2722 ]);
2723 moveLeft(editor, viewModel, true);
2724 assertCursor(viewModel, [
2725 new Selection(1, 10, 1, 11),
2726 new Selection(1, 21, 1, 22),
2727 new Selection(1, 32, 1, 33)
2728 ]);
2729 });
2730 });
2731
2732 test('issue #105730: move right should always skip wrap point', () => {
2733 const model = createTextModel('asdfghjkl, asdfghjkl, asdfghjkl, \nasdfghjkl,');
2734
2735 withTestCodeEditor(
2736 null,
2737 {
2738 model: model,
2739 wordWrap: 'wordWrapColumn',
2740 wordWrapColumn: 24
2741 },
2742 (editor, viewModel) => {
2743 viewModel.setSelections('test', [
2744 new Selection(1, 22, 1, 22)
2745 ]);
2746 moveRight(editor, viewModel, false);
2747 moveRight(editor, viewModel, false);
2748 assertCursor(viewModel, [
2749 new Selection(1, 24, 1, 24),
2750 ]);
2751
2752 viewModel.setSelections('test', [
2753 new Selection(1, 22, 1, 22)
2754 ]);
2755 moveRight(editor, viewModel, true);
2756 moveRight(editor, viewModel, true);
2757 assertCursor(viewModel, [
2758 new Selection(1, 22, 1, 24),
2759 ]);
2760 }
2761 );
2762 });
2763
2764 test('issue #123178: sticky tab in consecutive wrapped lines', () => {
2765 const model = createTextModel(' aaaa aaaa', { tabSize: 4 });
2766
2767 withTestCodeEditor(
2768 null,
2769 {
2770 model: model,
2771 wordWrap: 'wordWrapColumn',
2772 wordWrapColumn: 8,
2773 stickyTabStops: true,
2774 },
2775 (editor, viewModel) => {
2776 viewModel.setSelections('test', [
2777 new Selection(1, 9, 1, 9)
2778 ]);
2779 moveRight(editor, viewModel, false);
2780 assertCursor(viewModel, [
2781 new Selection(1, 10, 1, 10),
2782 ]);
2783
2784 moveLeft(editor, viewModel, false);
2785 assertCursor(viewModel, [
2786 new Selection(1, 9, 1, 9),
2787 ]);
2788 }
2789 );
2790 });
2791});
2792
2793suite('Editor Controller - Cursor Configuration', () => {
2794
2795 test('Cursor honors insertSpaces configuration on new line', () => {
2796 usingCursor({
2797 text: [
2798 ' \tMy First Line\t ',
2799 '\tMy Second Line',
2800 ' Third Line',
2801 '',
2802 '1'
2803 ]
2804 }, (editor, model, viewModel) => {
2805 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(1, 21), source: 'keyboard' });
2806 viewModel.type('\n', 'keyboard');
2807 assert.strictEqual(model.getLineContent(1), ' \tMy First Line\t ');
2808 assert.strictEqual(model.getLineContent(2), ' ');
2809 });
2810 });
2811
2812 test('Cursor honors insertSpaces configuration on tab', () => {
2813 let model = createTextModel(
2814 [
2815 ' \tMy First Line\t ',
2816 'My Second Line123',
2817 ' Third Line',
2818 '',
2819 '1'
2820 ].join('\n'),
2821 {
2822 tabSize: 13,
2823 indentSize: 13,
2824 }
2825 );
2826
2827 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
2828 // Tab on column 1
2829 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 1) });
2830 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
2831 assert.strictEqual(model.getLineContent(2), ' My Second Line123');
2832 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2833
2834 // Tab on column 2
2835 assert.strictEqual(model.getLineContent(2), 'My Second Line123');
2836 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 2) });
2837 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
2838 assert.strictEqual(model.getLineContent(2), 'M y Second Line123');
2839 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2840
2841 // Tab on column 3
2842 assert.strictEqual(model.getLineContent(2), 'My Second Line123');
2843 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 3) });
2844 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
2845 assert.strictEqual(model.getLineContent(2), 'My Second Line123');
2846 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2847
2848 // Tab on column 4
2849 assert.strictEqual(model.getLineContent(2), 'My Second Line123');
2850 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 4) });
2851 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
2852 assert.strictEqual(model.getLineContent(2), 'My Second Line123');
2853 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2854
2855 // Tab on column 5
2856 assert.strictEqual(model.getLineContent(2), 'My Second Line123');
2857 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) });
2858 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
2859 assert.strictEqual(model.getLineContent(2), 'My S econd Line123');
2860 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2861
2862 // Tab on column 5
2863 assert.strictEqual(model.getLineContent(2), 'My Second Line123');
2864 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) });
2865 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
2866 assert.strictEqual(model.getLineContent(2), 'My S econd Line123');
2867 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2868
2869 // Tab on column 13
2870 assert.strictEqual(model.getLineContent(2), 'My Second Line123');
2871 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 13) });
2872 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
2873 assert.strictEqual(model.getLineContent(2), 'My Second Li ne123');
2874 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
2875
2876 // Tab on column 14
2877 assert.strictEqual(model.getLineContent(2), 'My Second Line123');
2878 CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 14) });
2879 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
2880 assert.strictEqual(model.getLineContent(2), 'My Second Lin e123');
2881 });
2882
2883 model.dispose();
2884 });
2885
2886 test('Enter auto-indents with insertSpaces setting 1', () => {
2887 let mode = new OnEnterMode(IndentAction.Indent);
2888 usingCursor({
2889 text: [
2890 '\thello'
2891 ],
2892 languageIdentifier: mode.getLanguageIdentifier()
2893 }, (editor, model, viewModel) => {
2894 moveTo(editor, viewModel, 1, 7, false);
2895 assertCursor(viewModel, new Selection(1, 7, 1, 7));
2896
2897 viewModel.type('\n', 'keyboard');
2898 assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n ');
2899 });
2900 mode.dispose();
2901 });
2902
2903 test('Enter auto-indents with insertSpaces setting 2', () => {
2904 let mode = new OnEnterMode(IndentAction.None);
2905 usingCursor({
2906 text: [
2907 '\thello'
2908 ],
2909 languageIdentifier: mode.getLanguageIdentifier()
2910 }, (editor, model, viewModel) => {
2911 moveTo(editor, viewModel, 1, 7, false);
2912 assertCursor(viewModel, new Selection(1, 7, 1, 7));
2913
2914 viewModel.type('\n', 'keyboard');
2915 assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n ');
2916 });
2917 mode.dispose();
2918 });
2919
2920 test('Enter auto-indents with insertSpaces setting 3', () => {
2921 let mode = new OnEnterMode(IndentAction.IndentOutdent);
2922 usingCursor({
2923 text: [
2924 '\thell()'
2925 ],
2926 languageIdentifier: mode.getLanguageIdentifier()
2927 }, (editor, model, viewModel) => {
2928 moveTo(editor, viewModel, 1, 7, false);
2929 assertCursor(viewModel, new Selection(1, 7, 1, 7));
2930
2931 viewModel.type('\n', 'keyboard');
2932 assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thell(\r\n \r\n )');
2933 });
2934 mode.dispose();
2935 });
2936
2937 test('removeAutoWhitespace off', () => {
2938 usingCursor({
2939 text: [
2940 ' some line abc '
2941 ],
2942 modelOpts: {
2943 trimAutoWhitespace: false
2944 }
2945 }, (editor, model, viewModel) => {
2946
2947 // Move cursor to the end, verify that we do not trim whitespaces if line has values
2948 moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);
2949 viewModel.type('\n', 'keyboard');
2950 assert.strictEqual(model.getLineContent(1), ' some line abc ');
2951 assert.strictEqual(model.getLineContent(2), ' ');
2952
2953 // Try to enter again, we should trimmed previous line
2954 viewModel.type('\n', 'keyboard');
2955 assert.strictEqual(model.getLineContent(1), ' some line abc ');
2956 assert.strictEqual(model.getLineContent(2), ' ');
2957 assert.strictEqual(model.getLineContent(3), ' ');
2958 });
2959 });
2960
2961 test('removeAutoWhitespace on: removes only whitespace the cursor added 1', () => {
2962 usingCursor({
2963 text: [
2964 ' '
2965 ]
2966 }, (editor, model, viewModel) => {
2967 moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);
2968 viewModel.type('\n', 'keyboard');
2969 assert.strictEqual(model.getLineContent(1), ' ');
2970 assert.strictEqual(model.getLineContent(2), ' ');
2971
2972 viewModel.type('\n', 'keyboard');
2973 assert.strictEqual(model.getLineContent(1), ' ');
2974 assert.strictEqual(model.getLineContent(2), '');
2975 assert.strictEqual(model.getLineContent(3), ' ');
2976 });
2977 });
2978
2979 test('issue #115033: indent and appendText', () => {
2980 const mode = new class extends MockMode {
2981 constructor() {
2982 super(new LanguageIdentifier('onEnterMode', 3));
2983 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
2984 onEnterRules: [{
2985 beforeText: /.*/,
2986 action: {
2987 indentAction: IndentAction.Indent,
2988 appendText: 'x'
2989 }
2990 }]
2991 }));
2992 }
2993 }();
2994 usingCursor({
2995 text: [
2996 'text'
2997 ],
2998 languageIdentifier: mode.getLanguageIdentifier(),
2999 }, (editor, model, viewModel) => {
3000
3001 moveTo(editor, viewModel, 1, 5);
3002 viewModel.type('\n', 'keyboard');
3003 assert.strictEqual(model.getLineContent(1), 'text');
3004 assert.strictEqual(model.getLineContent(2), ' x');
3005 assertCursor(viewModel, new Position(2, 6));
3006 });
3007 mode.dispose();
3008 });
3009
3010 test('issue #6862: Editor removes auto inserted indentation when formatting on type', () => {
3011 let mode = new OnEnterMode(IndentAction.IndentOutdent);
3012 usingCursor({
3013 text: [
3014 'function foo (params: string) {}'
3015 ],
3016 languageIdentifier: mode.getLanguageIdentifier(),
3017 }, (editor, model, viewModel) => {
3018
3019 moveTo(editor, viewModel, 1, 32);
3020 viewModel.type('\n', 'keyboard');
3021 assert.strictEqual(model.getLineContent(1), 'function foo (params: string) {');
3022 assert.strictEqual(model.getLineContent(2), ' ');
3023 assert.strictEqual(model.getLineContent(3), '}');
3024
3025 class TestCommand implements ICommand {
3026
3027 private _selectionId: string | null = null;
3028
3029 public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
3030 builder.addEditOperation(new Range(1, 13, 1, 14), '');
3031 this._selectionId = builder.trackSelection(viewModel.getSelection());
3032 }
3033
3034 public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
3035 return helper.getTrackedSelection(this._selectionId!);
3036 }
3037
3038 }
3039
3040 viewModel.executeCommand(new TestCommand(), 'autoFormat');
3041 assert.strictEqual(model.getLineContent(1), 'function foo(params: string) {');
3042 assert.strictEqual(model.getLineContent(2), ' ');
3043 assert.strictEqual(model.getLineContent(3), '}');
3044 });
3045 mode.dispose();
3046 });
3047
3048 test('removeAutoWhitespace on: removes only whitespace the cursor added 2', () => {
3049 let model = createTextModel(
3050 [
3051 ' if (a) {',
3052 ' ',
3053 '',
3054 '',
3055 ' }'
3056 ].join('\n')
3057 );
3058
3059 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
3060
3061 moveTo(editor, viewModel, 3, 1);
3062 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
3063 assert.strictEqual(model.getLineContent(1), ' if (a) {');
3064 assert.strictEqual(model.getLineContent(2), ' ');
3065 assert.strictEqual(model.getLineContent(3), ' ');
3066 assert.strictEqual(model.getLineContent(4), '');
3067 assert.strictEqual(model.getLineContent(5), ' }');
3068
3069 moveTo(editor, viewModel, 4, 1);
3070 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
3071 assert.strictEqual(model.getLineContent(1), ' if (a) {');
3072 assert.strictEqual(model.getLineContent(2), ' ');
3073 assert.strictEqual(model.getLineContent(3), '');
3074 assert.strictEqual(model.getLineContent(4), ' ');
3075 assert.strictEqual(model.getLineContent(5), ' }');
3076
3077 moveTo(editor, viewModel, 5, model.getLineMaxColumn(5));
3078 viewModel.type('something', 'keyboard');
3079 assert.strictEqual(model.getLineContent(1), ' if (a) {');
3080 assert.strictEqual(model.getLineContent(2), ' ');
3081 assert.strictEqual(model.getLineContent(3), '');
3082 assert.strictEqual(model.getLineContent(4), '');
3083 assert.strictEqual(model.getLineContent(5), ' }something');
3084 });
3085
3086 model.dispose();
3087 });
3088
3089 test('removeAutoWhitespace on: test 1', () => {
3090 let model = createTextModel(
3091 [
3092 ' some line abc '
3093 ].join('\n')
3094 );
3095
3096 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
3097
3098 // Move cursor to the end, verify that we do not trim whitespaces if line has values
3099 moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);
3100 viewModel.type('\n', 'keyboard');
3101 assert.strictEqual(model.getLineContent(1), ' some line abc ');
3102 assert.strictEqual(model.getLineContent(2), ' ');
3103
3104 // Try to enter again, we should trimmed previous line
3105 viewModel.type('\n', 'keyboard');
3106 assert.strictEqual(model.getLineContent(1), ' some line abc ');
3107 assert.strictEqual(model.getLineContent(2), '');
3108 assert.strictEqual(model.getLineContent(3), ' ');
3109
3110 // More whitespaces
3111 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
3112 assert.strictEqual(model.getLineContent(1), ' some line abc ');
3113 assert.strictEqual(model.getLineContent(2), '');
3114 assert.strictEqual(model.getLineContent(3), ' ');
3115
3116 // Enter and verify that trimmed again
3117 viewModel.type('\n', 'keyboard');
3118 assert.strictEqual(model.getLineContent(1), ' some line abc ');
3119 assert.strictEqual(model.getLineContent(2), '');
3120 assert.strictEqual(model.getLineContent(3), '');
3121 assert.strictEqual(model.getLineContent(4), ' ');
3122
3123 // Trimmed if we will keep only text
3124 moveTo(editor, viewModel, 1, 5);
3125 viewModel.type('\n', 'keyboard');
3126 assert.strictEqual(model.getLineContent(1), ' ');
3127 assert.strictEqual(model.getLineContent(2), ' some line abc ');
3128 assert.strictEqual(model.getLineContent(3), '');
3129 assert.strictEqual(model.getLineContent(4), '');
3130 assert.strictEqual(model.getLineContent(5), '');
3131
3132 // Trimmed if we will keep only text by selection
3133 moveTo(editor, viewModel, 2, 5);
3134 moveTo(editor, viewModel, 3, 1, true);
3135 viewModel.type('\n', 'keyboard');
3136 assert.strictEqual(model.getLineContent(1), ' ');
3137 assert.strictEqual(model.getLineContent(2), ' ');
3138 assert.strictEqual(model.getLineContent(3), ' ');
3139 assert.strictEqual(model.getLineContent(4), '');
3140 assert.strictEqual(model.getLineContent(5), '');
3141 });
3142
3143 model.dispose();
3144 });
3145
3146 test('issue #15118: remove auto whitespace when pasting entire line', () => {
3147 let model = createTextModel(
3148 [
3149 ' function f() {',
3150 ' // I\'m gonna copy this line',
3151 ' return 3;',
3152 ' }',
3153 ].join('\n')
3154 );
3155
3156 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
3157
3158 moveTo(editor, viewModel, 3, model.getLineMaxColumn(3));
3159 viewModel.type('\n', 'keyboard');
3160
3161 assert.strictEqual(model.getValue(), [
3162 ' function f() {',
3163 ' // I\'m gonna copy this line',
3164 ' return 3;',
3165 ' ',
3166 ' }',
3167 ].join('\n'));
3168 assertCursor(viewModel, new Position(4, model.getLineMaxColumn(4)));
3169
3170 viewModel.paste(' // I\'m gonna copy this line\n', true);
3171 assert.strictEqual(model.getValue(), [
3172 ' function f() {',
3173 ' // I\'m gonna copy this line',
3174 ' return 3;',
3175 ' // I\'m gonna copy this line',
3176 '',
3177 ' }',
3178 ].join('\n'));
3179 assertCursor(viewModel, new Position(5, 1));
3180 });
3181
3182 model.dispose();
3183 });
3184
3185 test('issue #40695: maintain cursor position when copying lines using ctrl+c, ctrl+v', () => {
3186 let model = createTextModel(
3187 [
3188 ' function f() {',
3189 ' // I\'m gonna copy this line',
3190 ' // Another line',
3191 ' return 3;',
3192 ' }',
3193 ].join('\n')
3194 );
3195
3196 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
3197
3198 editor.setSelections([new Selection(4, 10, 4, 10)]);
3199 viewModel.paste(' // I\'m gonna copy this line\n', true);
3200
3201 assert.strictEqual(model.getValue(), [
3202 ' function f() {',
3203 ' // I\'m gonna copy this line',
3204 ' // Another line',
3205 ' // I\'m gonna copy this line',
3206 ' return 3;',
3207 ' }',
3208 ].join('\n'));
3209 assertCursor(viewModel, new Position(5, 10));
3210 });
3211
3212 model.dispose();
3213 });
3214
3215 test('UseTabStops is off', () => {
3216 let model = createTextModel(
3217 [
3218 ' x',
3219 ' a ',
3220 ' '
3221 ].join('\n')
3222 );
3223
3224 withTestCodeEditor(null, { model: model, useTabStops: false }, (editor, viewModel) => {
3225 // DeleteLeft removes just one whitespace
3226 moveTo(editor, viewModel, 2, 9);
3227 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3228 assert.strictEqual(model.getLineContent(2), ' a ');
3229 });
3230
3231 model.dispose();
3232 });
3233
3234 test('Backspace removes whitespaces with tab size', () => {
3235 let model = createTextModel(
3236 [
3237 ' \t \t x',
3238 ' a ',
3239 ' '
3240 ].join('\n')
3241 );
3242
3243 withTestCodeEditor(null, { model: model, useTabStops: true }, (editor, viewModel) => {
3244 // DeleteLeft does not remove tab size, because some text exists before
3245 moveTo(editor, viewModel, 2, model.getLineContent(2).length + 1);
3246 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3247 assert.strictEqual(model.getLineContent(2), ' a ');
3248
3249 // DeleteLeft removes tab size = 4
3250 moveTo(editor, viewModel, 2, 9);
3251 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3252 assert.strictEqual(model.getLineContent(2), ' a ');
3253
3254 // DeleteLeft removes tab size = 4
3255 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3256 assert.strictEqual(model.getLineContent(2), 'a ');
3257
3258 // Undo DeleteLeft - get us back to original indentation
3259 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
3260 assert.strictEqual(model.getLineContent(2), ' a ');
3261
3262 // Nothing is broken when cursor is in (1,1)
3263 moveTo(editor, viewModel, 1, 1);
3264 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3265 assert.strictEqual(model.getLineContent(1), ' \t \t x');
3266
3267 // DeleteLeft stops at tab stops even in mixed whitespace case
3268 moveTo(editor, viewModel, 1, 10);
3269 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3270 assert.strictEqual(model.getLineContent(1), ' \t \t x');
3271
3272 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3273 assert.strictEqual(model.getLineContent(1), ' \t \tx');
3274
3275 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3276 assert.strictEqual(model.getLineContent(1), ' \tx');
3277
3278 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3279 assert.strictEqual(model.getLineContent(1), 'x');
3280
3281 // DeleteLeft on last line
3282 moveTo(editor, viewModel, 3, model.getLineContent(3).length + 1);
3283 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3284 assert.strictEqual(model.getLineContent(3), '');
3285
3286 // DeleteLeft with removing new line symbol
3287 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3288 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x\n a ');
3289
3290 // In case of selection DeleteLeft only deletes selected text
3291 moveTo(editor, viewModel, 2, 3);
3292 moveTo(editor, viewModel, 2, 4, true);
3293 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3294 assert.strictEqual(model.getLineContent(2), ' a ');
3295 });
3296
3297 model.dispose();
3298 });
3299
3300 test('PR #5423: Auto indent + undo + redo is funky', () => {
3301 let model = createTextModel(
3302 [
3303 ''
3304 ].join('\n'),
3305 {
3306 insertSpaces: false,
3307 }
3308 );
3309
3310 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
3311 viewModel.type('\n', 'keyboard');
3312 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n', 'assert1');
3313
3314 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
3315 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2');
3316
3317 viewModel.type('y', 'keyboard');
3318 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty', 'assert2');
3319
3320 viewModel.type('\n', 'keyboard');
3321 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\t', 'assert3');
3322
3323 viewModel.type('x');
3324 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert4');
3325
3326 CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {});
3327 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert5');
3328
3329 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3330 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert6');
3331
3332 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3333 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tyx', 'assert7');
3334
3335 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3336 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tx', 'assert8');
3337
3338 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3339 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert9');
3340
3341 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
3342 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert10');
3343
3344 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
3345 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert11');
3346
3347 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
3348 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert12');
3349
3350 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
3351 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert13');
3352
3353 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
3354 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert14');
3355
3356 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
3357 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert15');
3358
3359 CoreEditingCommands.Redo.runEditorCommand(null, editor, null);
3360 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert16');
3361 });
3362
3363 model.dispose();
3364 });
3365
3366 test('issue #90973: Undo brings back model alternative version', () => {
3367 let model = createTextModel(
3368 [
3369 ''
3370 ].join('\n'),
3371 {
3372 insertSpaces: false,
3373 }
3374 );
3375
3376 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
3377 const beforeVersion = model.getVersionId();
3378 const beforeAltVersion = model.getAlternativeVersionId();
3379 viewModel.type('Hello', 'keyboard');
3380 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
3381 const afterVersion = model.getVersionId();
3382 const afterAltVersion = model.getAlternativeVersionId();
3383
3384 assert.notStrictEqual(beforeVersion, afterVersion);
3385 assert.strictEqual(beforeAltVersion, afterAltVersion);
3386 });
3387
3388 model.dispose();
3389 });
3390
3391
3392});
3393
3394suite('Editor Controller - Indentation Rules', () => {
3395 let mode = new IndentRulesMode({
3396 decreaseIndentPattern: /^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$/,
3397 increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$/,
3398 indentNextLinePattern: /^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$)/,
3399 unIndentedLinePattern: /^(?!.*([;{}]|\S:)\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!.*(\{[^}"']*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$))/
3400 });
3401
3402 test('Enter honors increaseIndentPattern', () => {
3403 usingCursor({
3404 text: [
3405 'if (true) {',
3406 '\tif (true) {'
3407 ],
3408 languageIdentifier: mode.getLanguageIdentifier(),
3409 modelOpts: { insertSpaces: false },
3410 editorOpts: { autoIndent: 'full' }
3411 }, (editor, model, viewModel) => {
3412 moveTo(editor, viewModel, 1, 12, false);
3413 assertCursor(viewModel, new Selection(1, 12, 1, 12));
3414
3415 viewModel.type('\n', 'keyboard');
3416 model.forceTokenization(model.getLineCount());
3417 assertCursor(viewModel, new Selection(2, 2, 2, 2));
3418
3419 moveTo(editor, viewModel, 3, 13, false);
3420 assertCursor(viewModel, new Selection(3, 13, 3, 13));
3421
3422 viewModel.type('\n', 'keyboard');
3423 assertCursor(viewModel, new Selection(4, 3, 4, 3));
3424 });
3425 });
3426
3427 test('Type honors decreaseIndentPattern', () => {
3428 usingCursor({
3429 text: [
3430 'if (true) {',
3431 '\t'
3432 ],
3433 languageIdentifier: mode.getLanguageIdentifier(),
3434 editorOpts: { autoIndent: 'full' }
3435 }, (editor, model, viewModel) => {
3436 moveTo(editor, viewModel, 2, 2, false);
3437 assertCursor(viewModel, new Selection(2, 2, 2, 2));
3438
3439 viewModel.type('}', 'keyboard');
3440 assertCursor(viewModel, new Selection(2, 2, 2, 2));
3441 assert.strictEqual(model.getLineContent(2), '}', '001');
3442 });
3443 });
3444
3445 test('Enter honors unIndentedLinePattern', () => {
3446 usingCursor({
3447 text: [
3448 'if (true) {',
3449 '\t\t\treturn true'
3450 ],
3451 languageIdentifier: mode.getLanguageIdentifier(),
3452 modelOpts: { insertSpaces: false },
3453 editorOpts: { autoIndent: 'full' }
3454 }, (editor, model, viewModel) => {
3455 moveTo(editor, viewModel, 2, 15, false);
3456 assertCursor(viewModel, new Selection(2, 15, 2, 15));
3457
3458 viewModel.type('\n', 'keyboard');
3459 assertCursor(viewModel, new Selection(3, 2, 3, 2));
3460 });
3461 });
3462
3463 test('Enter honors indentNextLinePattern', () => {
3464 usingCursor({
3465 text: [
3466 'if (true)',
3467 '\treturn true;',
3468 'if (true)',
3469 '\t\t\t\treturn true'
3470 ],
3471 languageIdentifier: mode.getLanguageIdentifier(),
3472 modelOpts: { insertSpaces: false },
3473 editorOpts: { autoIndent: 'full' }
3474 }, (editor, model, viewModel) => {
3475 moveTo(editor, viewModel, 2, 14, false);
3476 assertCursor(viewModel, new Selection(2, 14, 2, 14));
3477
3478 viewModel.type('\n', 'keyboard');
3479 model.forceTokenization(model.getLineCount());
3480 assertCursor(viewModel, new Selection(3, 1, 3, 1));
3481
3482 moveTo(editor, viewModel, 5, 16, false);
3483 assertCursor(viewModel, new Selection(5, 16, 5, 16));
3484
3485 viewModel.type('\n', 'keyboard');
3486 assertCursor(viewModel, new Selection(6, 2, 6, 2));
3487 });
3488 });
3489
3490 test('Enter honors indentNextLinePattern 2', () => {
3491 let model = createTextModel(
3492 [
3493 'if (true)',
3494 '\tif (true)'
3495 ].join('\n'),
3496 {
3497 insertSpaces: false,
3498 },
3499 mode.getLanguageIdentifier()
3500 );
3501
3502 withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, viewModel) => {
3503 moveTo(editor, viewModel, 2, 11, false);
3504 assertCursor(viewModel, new Selection(2, 11, 2, 11));
3505
3506 viewModel.type('\n', 'keyboard');
3507 model.forceTokenization(model.getLineCount());
3508 assertCursor(viewModel, new Selection(3, 3, 3, 3));
3509
3510 viewModel.type('console.log();', 'keyboard');
3511 viewModel.type('\n', 'keyboard');
3512 assertCursor(viewModel, new Selection(4, 1, 4, 1));
3513 });
3514
3515 model.dispose();
3516 });
3517
3518 test('Enter honors intential indent', () => {
3519 usingCursor({
3520 text: [
3521 'if (true) {',
3522 '\tif (true) {',
3523 'return true;',
3524 '}}'
3525 ],
3526 languageIdentifier: mode.getLanguageIdentifier(),
3527 editorOpts: { autoIndent: 'full' }
3528 }, (editor, model, viewModel) => {
3529 moveTo(editor, viewModel, 3, 13, false);
3530 assertCursor(viewModel, new Selection(3, 13, 3, 13));
3531
3532 viewModel.type('\n', 'keyboard');
3533 assertCursor(viewModel, new Selection(4, 1, 4, 1));
3534 assert.strictEqual(model.getLineContent(3), 'return true;', '001');
3535 });
3536 });
3537
3538 test('Enter supports selection 1', () => {
3539 usingCursor({
3540 text: [
3541 'if (true) {',
3542 '\tif (true) {',
3543 '\t\treturn true;',
3544 '\t}a}'
3545 ],
3546 languageIdentifier: mode.getLanguageIdentifier(),
3547 modelOpts: { insertSpaces: false }
3548 }, (editor, model, viewModel) => {
3549 moveTo(editor, viewModel, 4, 3, false);
3550 moveTo(editor, viewModel, 4, 4, true);
3551 assertCursor(viewModel, new Selection(4, 3, 4, 4));
3552
3553 viewModel.type('\n', 'keyboard');
3554 assertCursor(viewModel, new Selection(5, 1, 5, 1));
3555 assert.strictEqual(model.getLineContent(4), '\t}', '001');
3556 });
3557 });
3558
3559 test('Enter supports selection 2', () => {
3560 usingCursor({
3561 text: [
3562 'if (true) {',
3563 '\tif (true) {'
3564 ],
3565 languageIdentifier: mode.getLanguageIdentifier(),
3566 modelOpts: { insertSpaces: false }
3567 }, (editor, model, viewModel) => {
3568 moveTo(editor, viewModel, 2, 12, false);
3569 moveTo(editor, viewModel, 2, 13, true);
3570 assertCursor(viewModel, new Selection(2, 12, 2, 13));
3571
3572 viewModel.type('\n', 'keyboard');
3573 assertCursor(viewModel, new Selection(3, 3, 3, 3));
3574
3575 viewModel.type('\n', 'keyboard');
3576 assertCursor(viewModel, new Selection(4, 3, 4, 3));
3577 });
3578 });
3579
3580 test('Enter honors tabSize and insertSpaces 1', () => {
3581 usingCursor({
3582 text: [
3583 'if (true) {',
3584 '\tif (true) {'
3585 ],
3586 languageIdentifier: mode.getLanguageIdentifier(),
3587 }, (editor, model, viewModel) => {
3588 moveTo(editor, viewModel, 1, 12, false);
3589 assertCursor(viewModel, new Selection(1, 12, 1, 12));
3590
3591 viewModel.type('\n', 'keyboard');
3592 assertCursor(viewModel, new Selection(2, 5, 2, 5));
3593
3594 model.forceTokenization(model.getLineCount());
3595
3596 moveTo(editor, viewModel, 3, 13, false);
3597 assertCursor(viewModel, new Selection(3, 13, 3, 13));
3598
3599 viewModel.type('\n', 'keyboard');
3600 assertCursor(viewModel, new Selection(4, 9, 4, 9));
3601 });
3602 });
3603
3604 test('Enter honors tabSize and insertSpaces 2', () => {
3605 usingCursor({
3606 text: [
3607 'if (true) {',
3608 ' if (true) {'
3609 ],
3610 languageIdentifier: mode.getLanguageIdentifier(),
3611 }, (editor, model, viewModel) => {
3612 moveTo(editor, viewModel, 1, 12, false);
3613 assertCursor(viewModel, new Selection(1, 12, 1, 12));
3614
3615 viewModel.type('\n', 'keyboard');
3616 model.forceTokenization(model.getLineCount());
3617 assertCursor(viewModel, new Selection(2, 5, 2, 5));
3618
3619 moveTo(editor, viewModel, 3, 16, false);
3620 assertCursor(viewModel, new Selection(3, 16, 3, 16));
3621
3622 viewModel.type('\n', 'keyboard');
3623 assert.strictEqual(model.getLineContent(3), ' if (true) {');
3624 assertCursor(viewModel, new Selection(4, 9, 4, 9));
3625 });
3626 });
3627
3628 test('Enter honors tabSize and insertSpaces 3', () => {
3629 usingCursor({
3630 text: [
3631 'if (true) {',
3632 ' if (true) {'
3633 ],
3634 languageIdentifier: mode.getLanguageIdentifier(),
3635 modelOpts: { insertSpaces: false }
3636 }, (editor, model, viewModel) => {
3637 moveTo(editor, viewModel, 1, 12, false);
3638 assertCursor(viewModel, new Selection(1, 12, 1, 12));
3639
3640 viewModel.type('\n', 'keyboard');
3641 model.forceTokenization(model.getLineCount());
3642 assertCursor(viewModel, new Selection(2, 2, 2, 2));
3643
3644 moveTo(editor, viewModel, 3, 16, false);
3645 assertCursor(viewModel, new Selection(3, 16, 3, 16));
3646
3647 viewModel.type('\n', 'keyboard');
3648 assert.strictEqual(model.getLineContent(3), ' if (true) {');
3649 assertCursor(viewModel, new Selection(4, 3, 4, 3));
3650 });
3651 });
3652
3653 test('Enter supports intentional indentation', () => {
3654 usingCursor({
3655 text: [
3656 '\tif (true) {',
3657 '\t\tswitch(true) {',
3658 '\t\t\tcase true:',
3659 '\t\t\t\tbreak;',
3660 '\t\t}',
3661 '\t}'
3662 ],
3663 languageIdentifier: mode.getLanguageIdentifier(),
3664 modelOpts: { insertSpaces: false },
3665 editorOpts: { autoIndent: 'full' }
3666 }, (editor, model, viewModel) => {
3667 moveTo(editor, viewModel, 5, 4, false);
3668 assertCursor(viewModel, new Selection(5, 4, 5, 4));
3669
3670 viewModel.type('\n', 'keyboard');
3671 assert.strictEqual(model.getLineContent(5), '\t\t}');
3672 assertCursor(viewModel, new Selection(6, 3, 6, 3));
3673 });
3674 });
3675
3676 test('Enter should not adjust cursor position when press enter in the middle of a line 1', () => {
3677 usingCursor({
3678 text: [
3679 'if (true) {',
3680 '\tif (true) {',
3681 '\t\treturn true;',
3682 '\t}a}'
3683 ],
3684 languageIdentifier: mode.getLanguageIdentifier(),
3685 modelOpts: { insertSpaces: false }
3686 }, (editor, model, viewModel) => {
3687 moveTo(editor, viewModel, 3, 9, false);
3688 assertCursor(viewModel, new Selection(3, 9, 3, 9));
3689
3690 viewModel.type('\n', 'keyboard');
3691 assertCursor(viewModel, new Selection(4, 3, 4, 3));
3692 assert.strictEqual(model.getLineContent(4), '\t\t true;', '001');
3693 });
3694 });
3695
3696 test('Enter should not adjust cursor position when press enter in the middle of a line 2', () => {
3697 usingCursor({
3698 text: [
3699 'if (true) {',
3700 '\tif (true) {',
3701 '\t\treturn true;',
3702 '\t}a}'
3703 ],
3704 languageIdentifier: mode.getLanguageIdentifier(),
3705 modelOpts: { insertSpaces: false }
3706 }, (editor, model, viewModel) => {
3707 moveTo(editor, viewModel, 3, 3, false);
3708 assertCursor(viewModel, new Selection(3, 3, 3, 3));
3709
3710 viewModel.type('\n', 'keyboard');
3711 assertCursor(viewModel, new Selection(4, 3, 4, 3));
3712 assert.strictEqual(model.getLineContent(4), '\t\treturn true;', '001');
3713 });
3714 });
3715
3716 test('Enter should not adjust cursor position when press enter in the middle of a line 3', () => {
3717 usingCursor({
3718 text: [
3719 'if (true) {',
3720 ' if (true) {',
3721 ' return true;',
3722 ' }a}'
3723 ],
3724 languageIdentifier: mode.getLanguageIdentifier()
3725 }, (editor, model, viewModel) => {
3726 moveTo(editor, viewModel, 3, 11, false);
3727 assertCursor(viewModel, new Selection(3, 11, 3, 11));
3728
3729 viewModel.type('\n', 'keyboard');
3730 assertCursor(viewModel, new Selection(4, 5, 4, 5));
3731 assert.strictEqual(model.getLineContent(4), ' true;', '001');
3732 });
3733 });
3734
3735 test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 1', () => {
3736 usingCursor({
3737 text: [
3738 'if (true) {',
3739 '\tif (true) {',
3740 '\t\treturn true;',
3741 '\t}a}'
3742 ],
3743 languageIdentifier: mode.getLanguageIdentifier(),
3744 modelOpts: { insertSpaces: false }
3745 }, (editor, model, viewModel) => {
3746 moveTo(editor, viewModel, 3, 2, false);
3747 assertCursor(viewModel, new Selection(3, 2, 3, 2));
3748
3749 viewModel.type('\n', 'keyboard');
3750 assertCursor(viewModel, new Selection(4, 2, 4, 2));
3751 assert.strictEqual(model.getLineContent(4), '\t\treturn true;', '001');
3752
3753 moveTo(editor, viewModel, 4, 1, false);
3754 assertCursor(viewModel, new Selection(4, 1, 4, 1));
3755
3756 viewModel.type('\n', 'keyboard');
3757 assertCursor(viewModel, new Selection(5, 1, 5, 1));
3758 assert.strictEqual(model.getLineContent(5), '\t\treturn true;', '002');
3759 });
3760 });
3761
3762 test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 2', () => {
3763 usingCursor({
3764 text: [
3765 '\tif (true) {',
3766 '\t\tif (true) {',
3767 '\t \treturn true;',
3768 '\t\t}a}'
3769 ],
3770 languageIdentifier: mode.getLanguageIdentifier(),
3771 modelOpts: { insertSpaces: false }
3772 }, (editor, model, viewModel) => {
3773 moveTo(editor, viewModel, 3, 4, false);
3774 assertCursor(viewModel, new Selection(3, 4, 3, 4));
3775
3776 viewModel.type('\n', 'keyboard');
3777 assertCursor(viewModel, new Selection(4, 3, 4, 3));
3778 assert.strictEqual(model.getLineContent(4), '\t\t\treturn true;', '001');
3779
3780 moveTo(editor, viewModel, 4, 1, false);
3781 assertCursor(viewModel, new Selection(4, 1, 4, 1));
3782
3783 viewModel.type('\n', 'keyboard');
3784 assertCursor(viewModel, new Selection(5, 1, 5, 1));
3785 assert.strictEqual(model.getLineContent(5), '\t\t\treturn true;', '002');
3786 });
3787 });
3788
3789 test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 3', () => {
3790 usingCursor({
3791 text: [
3792 'if (true) {',
3793 ' if (true) {',
3794 ' return true;',
3795 '}a}'
3796 ],
3797 languageIdentifier: mode.getLanguageIdentifier()
3798 }, (editor, model, viewModel) => {
3799 moveTo(editor, viewModel, 3, 2, false);
3800 assertCursor(viewModel, new Selection(3, 2, 3, 2));
3801
3802 viewModel.type('\n', 'keyboard');
3803 assertCursor(viewModel, new Selection(4, 2, 4, 2));
3804 assert.strictEqual(model.getLineContent(4), ' return true;', '001');
3805
3806 moveTo(editor, viewModel, 4, 3, false);
3807 viewModel.type('\n', 'keyboard');
3808 assertCursor(viewModel, new Selection(5, 3, 5, 3));
3809 assert.strictEqual(model.getLineContent(5), ' return true;', '002');
3810 });
3811 });
3812
3813 test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 4', () => {
3814 usingCursor({
3815 text: [
3816 'if (true) {',
3817 ' if (true) {',
3818 '\t return true;',
3819 '}a}',
3820 '',
3821 'if (true) {',
3822 ' if (true) {',
3823 '\t return true;',
3824 '}a}'
3825 ],
3826 languageIdentifier: mode.getLanguageIdentifier(),
3827 modelOpts: {
3828 tabSize: 2,
3829 indentSize: 2
3830 }
3831 }, (editor, model, viewModel) => {
3832 moveTo(editor, viewModel, 3, 3, false);
3833 assertCursor(viewModel, new Selection(3, 3, 3, 3));
3834
3835 viewModel.type('\n', 'keyboard');
3836 assertCursor(viewModel, new Selection(4, 4, 4, 4));
3837 assert.strictEqual(model.getLineContent(4), ' return true;', '001');
3838
3839 moveTo(editor, viewModel, 9, 4, false);
3840 viewModel.type('\n', 'keyboard');
3841 assertCursor(viewModel, new Selection(10, 5, 10, 5));
3842 assert.strictEqual(model.getLineContent(10), ' return true;', '001');
3843 });
3844 });
3845
3846 test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 5', () => {
3847 usingCursor({
3848 text: [
3849 'if (true) {',
3850 ' if (true) {',
3851 ' return true;',
3852 ' return true;',
3853 ''
3854 ],
3855 languageIdentifier: mode.getLanguageIdentifier(),
3856 modelOpts: { tabSize: 2 }
3857 }, (editor, model, viewModel) => {
3858 moveTo(editor, viewModel, 3, 5, false);
3859 moveTo(editor, viewModel, 4, 3, true);
3860 assertCursor(viewModel, new Selection(3, 5, 4, 3));
3861
3862 viewModel.type('\n', 'keyboard');
3863 assertCursor(viewModel, new Selection(4, 3, 4, 3));
3864 assert.strictEqual(model.getLineContent(4), ' return true;', '001');
3865 });
3866 });
3867
3868 test('issue microsoft/monaco-editor#108 part 1/2: Auto indentation on Enter with selection is half broken', () => {
3869 usingCursor({
3870 text: [
3871 'function baz() {',
3872 '\tvar x = 1;',
3873 '\t\t\t\t\t\t\treturn x;',
3874 '}'
3875 ],
3876 modelOpts: {
3877 insertSpaces: false,
3878 },
3879 languageIdentifier: mode.getLanguageIdentifier(),
3880 }, (editor, model, viewModel) => {
3881 moveTo(editor, viewModel, 3, 8, false);
3882 moveTo(editor, viewModel, 2, 12, true);
3883 assertCursor(viewModel, new Selection(3, 8, 2, 12));
3884
3885 viewModel.type('\n', 'keyboard');
3886 assert.strictEqual(model.getLineContent(3), '\treturn x;');
3887 assertCursor(viewModel, new Position(3, 2));
3888 });
3889 });
3890
3891 test('issue microsoft/monaco-editor#108 part 2/2: Auto indentation on Enter with selection is half broken', () => {
3892 usingCursor({
3893 text: [
3894 'function baz() {',
3895 '\tvar x = 1;',
3896 '\t\t\t\t\t\t\treturn x;',
3897 '}'
3898 ],
3899 modelOpts: {
3900 insertSpaces: false,
3901 },
3902 languageIdentifier: mode.getLanguageIdentifier(),
3903 }, (editor, model, viewModel) => {
3904 moveTo(editor, viewModel, 2, 12, false);
3905 moveTo(editor, viewModel, 3, 8, true);
3906 assertCursor(viewModel, new Selection(2, 12, 3, 8));
3907
3908 viewModel.type('\n', 'keyboard');
3909 assert.strictEqual(model.getLineContent(3), '\treturn x;');
3910 assertCursor(viewModel, new Position(3, 2));
3911 });
3912 });
3913
3914 test('onEnter works if there are no indentation rules', () => {
3915 usingCursor({
3916 text: [
3917 '<?',
3918 '\tif (true) {',
3919 '\t\techo $hi;',
3920 '\t\techo $bye;',
3921 '\t}',
3922 '?>'
3923 ],
3924 modelOpts: { insertSpaces: false }
3925 }, (editor, model, viewModel) => {
3926 moveTo(editor, viewModel, 5, 3, false);
3927 assertCursor(viewModel, new Selection(5, 3, 5, 3));
3928
3929 viewModel.type('\n', 'keyboard');
3930 assert.strictEqual(model.getLineContent(6), '\t');
3931 assertCursor(viewModel, new Selection(6, 2, 6, 2));
3932 assert.strictEqual(model.getLineContent(5), '\t}');
3933 });
3934 });
3935
3936 test('onEnter works if there are no indentation rules 2', () => {
3937 usingCursor({
3938 text: [
3939 ' if (5)',
3940 ' return 5;',
3941 ' '
3942 ],
3943 modelOpts: { insertSpaces: false }
3944 }, (editor, model, viewModel) => {
3945 moveTo(editor, viewModel, 3, 2, false);
3946 assertCursor(viewModel, new Selection(3, 2, 3, 2));
3947
3948 viewModel.type('\n', 'keyboard');
3949 assertCursor(viewModel, new Selection(4, 2, 4, 2));
3950 assert.strictEqual(model.getLineContent(4), '\t');
3951 });
3952 });
3953
3954 test('bug #16543: Tab should indent to correct indentation spot immediately', () => {
3955 let model = createTextModel(
3956 [
3957 'function baz() {',
3958 '\tfunction hello() { // something here',
3959 '\t',
3960 '',
3961 '\t}',
3962 '}'
3963 ].join('\n'),
3964 {
3965 insertSpaces: false,
3966 },
3967 mode.getLanguageIdentifier()
3968 );
3969
3970 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
3971 moveTo(editor, viewModel, 4, 1, false);
3972 assertCursor(viewModel, new Selection(4, 1, 4, 1));
3973
3974 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
3975 assert.strictEqual(model.getLineContent(4), '\t\t');
3976 });
3977
3978 model.dispose();
3979 });
3980
3981
3982 test('bug #2938 (1): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {
3983 let model = createTextModel(
3984 [
3985 '\tfunction baz() {',
3986 '\t\tfunction hello() { // something here',
3987 '\t\t',
3988 '\t',
3989 '\t\t}',
3990 '\t}'
3991 ].join('\n'),
3992 {
3993 insertSpaces: false,
3994 },
3995 mode.getLanguageIdentifier()
3996 );
3997
3998 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
3999 moveTo(editor, viewModel, 4, 2, false);
4000 assertCursor(viewModel, new Selection(4, 2, 4, 2));
4001
4002 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
4003 assert.strictEqual(model.getLineContent(4), '\t\t\t');
4004 });
4005
4006 model.dispose();
4007 });
4008
4009
4010 test('bug #2938 (2): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {
4011 let model = createTextModel(
4012 [
4013 '\tfunction baz() {',
4014 '\t\tfunction hello() { // something here',
4015 '\t\t',
4016 ' ',
4017 '\t\t}',
4018 '\t}'
4019 ].join('\n'),
4020 {
4021 insertSpaces: false,
4022 },
4023 mode.getLanguageIdentifier()
4024 );
4025
4026 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
4027 moveTo(editor, viewModel, 4, 1, false);
4028 assertCursor(viewModel, new Selection(4, 1, 4, 1));
4029
4030 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
4031 assert.strictEqual(model.getLineContent(4), '\t\t\t');
4032 });
4033
4034 model.dispose();
4035 });
4036
4037 test('bug #2938 (3): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {
4038 let model = createTextModel(
4039 [
4040 '\tfunction baz() {',
4041 '\t\tfunction hello() { // something here',
4042 '\t\t',
4043 '\t\t\t',
4044 '\t\t}',
4045 '\t}'
4046 ].join('\n'),
4047 {
4048 insertSpaces: false,
4049 },
4050 mode.getLanguageIdentifier()
4051 );
4052
4053 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
4054 moveTo(editor, viewModel, 4, 3, false);
4055 assertCursor(viewModel, new Selection(4, 3, 4, 3));
4056
4057 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
4058 assert.strictEqual(model.getLineContent(4), '\t\t\t\t');
4059 });
4060
4061 model.dispose();
4062 });
4063
4064 test('bug #2938 (4): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {
4065 let model = createTextModel(
4066 [
4067 '\tfunction baz() {',
4068 '\t\tfunction hello() { // something here',
4069 '\t\t',
4070 '\t\t\t\t',
4071 '\t\t}',
4072 '\t}'
4073 ].join('\n'),
4074 {
4075 insertSpaces: false,
4076 },
4077 mode.getLanguageIdentifier()
4078 );
4079
4080 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
4081 moveTo(editor, viewModel, 4, 4, false);
4082 assertCursor(viewModel, new Selection(4, 4, 4, 4));
4083
4084 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
4085 assert.strictEqual(model.getLineContent(4), '\t\t\t\t\t');
4086 });
4087
4088 model.dispose();
4089 });
4090
4091 test('bug #31015: When pressing Tab on lines and Enter rules are avail, indent straight to the right spotTab', () => {
4092 let mode = new OnEnterMode(IndentAction.Indent);
4093 let model = createTextModel(
4094 [
4095 ' if (a) {',
4096 ' ',
4097 '',
4098 '',
4099 ' }'
4100 ].join('\n'),
4101 undefined,
4102 mode.getLanguageIdentifier()
4103 );
4104
4105 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
4106
4107 moveTo(editor, viewModel, 3, 1);
4108 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
4109 assert.strictEqual(model.getLineContent(1), ' if (a) {');
4110 assert.strictEqual(model.getLineContent(2), ' ');
4111 assert.strictEqual(model.getLineContent(3), ' ');
4112 assert.strictEqual(model.getLineContent(4), '');
4113 assert.strictEqual(model.getLineContent(5), ' }');
4114 });
4115
4116 model.dispose();
4117 });
4118
4119 test('type honors indentation rules: ruby keywords', () => {
4120 let rubyMode = new IndentRulesMode({
4121 increaseIndentPattern: /^\s*((begin|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\sdo\b))\b[^\{;]*$/,
4122 decreaseIndentPattern: /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif|when)\b)/
4123 });
4124 let model = createTextModel(
4125 [
4126 'class Greeter',
4127 ' def initialize(name)',
4128 ' @name = name',
4129 ' en'
4130 ].join('\n'),
4131 undefined,
4132 rubyMode.getLanguageIdentifier()
4133 );
4134
4135 withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, viewModel) => {
4136 moveTo(editor, viewModel, 4, 7, false);
4137 assertCursor(viewModel, new Selection(4, 7, 4, 7));
4138
4139 viewModel.type('d', 'keyboard');
4140 assert.strictEqual(model.getLineContent(4), ' end');
4141 });
4142
4143 rubyMode.dispose();
4144 model.dispose();
4145 });
4146
4147 test('Auto indent on type: increaseIndentPattern has higher priority than decreaseIndent when inheriting', () => {
4148 usingCursor({
4149 text: [
4150 '\tif (true) {',
4151 '\t\tconsole.log();',
4152 '\t} else if {',
4153 '\t\tconsole.log()',
4154 '\t}'
4155 ],
4156 languageIdentifier: mode.getLanguageIdentifier()
4157 }, (editor, model, viewModel) => {
4158 moveTo(editor, viewModel, 5, 3, false);
4159 assertCursor(viewModel, new Selection(5, 3, 5, 3));
4160
4161 viewModel.type('e', 'keyboard');
4162 assertCursor(viewModel, new Selection(5, 4, 5, 4));
4163 assert.strictEqual(model.getLineContent(5), '\t}e', 'This line should not decrease indent');
4164 });
4165 });
4166
4167 test('type honors users indentation adjustment', () => {
4168 usingCursor({
4169 text: [
4170 '\tif (true ||',
4171 '\t ) {',
4172 '\t}',
4173 'if (true ||',
4174 ') {',
4175 '}'
4176 ],
4177 languageIdentifier: mode.getLanguageIdentifier()
4178 }, (editor, model, viewModel) => {
4179 moveTo(editor, viewModel, 2, 3, false);
4180 assertCursor(viewModel, new Selection(2, 3, 2, 3));
4181
4182 viewModel.type(' ', 'keyboard');
4183 assertCursor(viewModel, new Selection(2, 4, 2, 4));
4184 assert.strictEqual(model.getLineContent(2), '\t ) {', 'This line should not decrease indent');
4185 });
4186 });
4187
4188 test('bug 29972: if a line is line comment, open bracket should not indent next line', () => {
4189 usingCursor({
4190 text: [
4191 'if (true) {',
4192 '\t// {',
4193 '\t\t'
4194 ],
4195 languageIdentifier: mode.getLanguageIdentifier(),
4196 editorOpts: { autoIndent: 'full' }
4197 }, (editor, model, viewModel) => {
4198 moveTo(editor, viewModel, 3, 3, false);
4199 assertCursor(viewModel, new Selection(3, 3, 3, 3));
4200
4201 viewModel.type('}', 'keyboard');
4202 assertCursor(viewModel, new Selection(3, 2, 3, 2));
4203 assert.strictEqual(model.getLineContent(3), '}');
4204 });
4205 });
4206
4207 test('issue #36090: JS: editor.autoIndent seems to be broken', () => {
4208 class JSMode extends MockMode {
4209 private static readonly _id = new LanguageIdentifier('indentRulesMode', 4);
4210 constructor() {
4211 super(JSMode._id);
4212 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
4213 brackets: [
4214 ['{', '}'],
4215 ['[', ']'],
4216 ['(', ')']
4217 ],
4218 indentationRules: {
4219 // ^(.*\*/)?\s*\}.*$
4220 decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]\)].*$/,
4221 // ^.*\{[^}"']*$
4222 increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/
4223 },
4224 onEnterRules: javascriptOnEnterRules
4225 }));
4226 }
4227 }
4228
4229 let mode = new JSMode();
4230 let model = createTextModel(
4231 [
4232 'class ItemCtrl {',
4233 ' getPropertiesByItemId(id) {',
4234 ' return this.fetchItem(id)',
4235 ' .then(item => {',
4236 ' return this.getPropertiesOfItem(item);',
4237 ' });',
4238 ' }',
4239 '}',
4240 ].join('\n'),
4241 undefined,
4242 mode.getLanguageIdentifier()
4243 );
4244
4245 withTestCodeEditor(null, { model: model, autoIndent: 'advanced' }, (editor, viewModel) => {
4246 moveTo(editor, viewModel, 7, 6, false);
4247 assertCursor(viewModel, new Selection(7, 6, 7, 6));
4248
4249 viewModel.type('\n', 'keyboard');
4250 assert.strictEqual(model.getValue(),
4251 [
4252 'class ItemCtrl {',
4253 ' getPropertiesByItemId(id) {',
4254 ' return this.fetchItem(id)',
4255 ' .then(item => {',
4256 ' return this.getPropertiesOfItem(item);',
4257 ' });',
4258 ' }',
4259 ' ',
4260 '}',
4261 ].join('\n')
4262 );
4263 assertCursor(viewModel, new Selection(8, 5, 8, 5));
4264 });
4265
4266 model.dispose();
4267 mode.dispose();
4268 });
4269
4270 test('issue #115304: OnEnter broken for TS', () => {
4271 class JSMode extends MockMode {
4272 private static readonly _id = new LanguageIdentifier('indentRulesMode', 4);
4273 constructor() {
4274 super(JSMode._id);
4275 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
4276 onEnterRules: javascriptOnEnterRules
4277 }));
4278 }
4279 }
4280
4281 const mode = new JSMode();
4282 const model = createTextModel(
4283 [
4284 '/** */',
4285 'function f() {}',
4286 ].join('\n'),
4287 undefined,
4288 mode.getLanguageIdentifier()
4289 );
4290
4291 withTestCodeEditor(null, { model: model, autoIndent: 'advanced' }, (editor, viewModel) => {
4292 moveTo(editor, viewModel, 1, 4, false);
4293 assertCursor(viewModel, new Selection(1, 4, 1, 4));
4294
4295 viewModel.type('\n', 'keyboard');
4296 assert.strictEqual(model.getValue(),
4297 [
4298 '/**',
4299 ' * ',
4300 ' */',
4301 'function f() {}',
4302 ].join('\n')
4303 );
4304 assertCursor(viewModel, new Selection(2, 4, 2, 4));
4305 });
4306
4307 model.dispose();
4308 mode.dispose();
4309 });
4310
4311 test('issue #38261: TAB key results in bizarre indentation in C++ mode ', () => {
4312 class CppMode extends MockMode {
4313 private static readonly _id = new LanguageIdentifier('indentRulesMode', 4);
4314 constructor() {
4315 super(CppMode._id);
4316 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
4317 brackets: [
4318 ['{', '}'],
4319 ['[', ']'],
4320 ['(', ')']
4321 ],
4322 indentationRules: {
4323 increaseIndentPattern: new RegExp('^.*\\{[^}\"\\\']*$|^.*\\([^\\)\"\\\']*$|^\\s*(public|private|protected):\\s*$|^\\s*@(public|private|protected)\\s*$|^\\s*\\{\\}$'),
4324 decreaseIndentPattern: new RegExp('^\\s*(\\s*/[*].*[*]/\\s*)*\\}|^\\s*(\\s*/[*].*[*]/\\s*)*\\)|^\\s*(public|private|protected):\\s*$|^\\s*@(public|private|protected)\\s*$'),
4325 }
4326 }));
4327 }
4328 }
4329
4330 let mode = new CppMode();
4331 let model = createTextModel(
4332 [
4333 'int main() {',
4334 ' return 0;',
4335 '}',
4336 '',
4337 'bool Foo::bar(const string &a,',
4338 ' const string &b) {',
4339 ' foo();',
4340 '',
4341 ')',
4342 ].join('\n'),
4343 {
4344 tabSize: 2,
4345 indentSize: 2
4346 },
4347 mode.getLanguageIdentifier()
4348 );
4349
4350 withTestCodeEditor(null, { model: model, autoIndent: 'advanced' }, (editor, viewModel) => {
4351 moveTo(editor, viewModel, 8, 1, false);
4352 assertCursor(viewModel, new Selection(8, 1, 8, 1));
4353
4354 CoreEditingCommands.Tab.runEditorCommand(null, editor, null);
4355 assert.strictEqual(model.getValue(),
4356 [
4357 'int main() {',
4358 ' return 0;',
4359 '}',
4360 '',
4361 'bool Foo::bar(const string &a,',
4362 ' const string &b) {',
4363 ' foo();',
4364 ' ',
4365 ')',
4366 ].join('\n')
4367 );
4368 assert.deepStrictEqual(viewModel.getSelection(), new Selection(8, 3, 8, 3));
4369 });
4370
4371 model.dispose();
4372 mode.dispose();
4373 });
4374
4375 test('issue #57197: indent rules regex should be stateless', () => {
4376 usingCursor({
4377 text: [
4378 'Project:',
4379 ],
4380 languageIdentifier: (new IndentRulesMode({
4381 decreaseIndentPattern: /^\s*}$/gm,
4382 increaseIndentPattern: /^(?![^\S\n]*(?!--|––|——)(?:[-❍❑■⬜□☐▪▫–—≡→›✘xX✔✓☑+]|\[[ xX+-]?\])\s[^\n]*)[^\S\n]*(.+:)[^\S\n]*(?:(?=@[^\s*~(]+(?::\/\/[^\s*~(:]+)?(?:\([^)]*\))?)|$)/gm,
4383 })).getLanguageIdentifier(),
4384 modelOpts: { insertSpaces: false },
4385 editorOpts: { autoIndent: 'full' }
4386 }, (editor, model, viewModel) => {
4387 moveTo(editor, viewModel, 1, 9, false);
4388 assertCursor(viewModel, new Selection(1, 9, 1, 9));
4389
4390 viewModel.type('\n', 'keyboard');
4391 model.forceTokenization(model.getLineCount());
4392 assertCursor(viewModel, new Selection(2, 2, 2, 2));
4393
4394 moveTo(editor, viewModel, 1, 9, false);
4395 assertCursor(viewModel, new Selection(1, 9, 1, 9));
4396 viewModel.type('\n', 'keyboard');
4397 model.forceTokenization(model.getLineCount());
4398 assertCursor(viewModel, new Selection(2, 2, 2, 2));
4399 });
4400 });
4401
4402 test('', () => {
4403 class JSONMode extends MockMode {
4404 private static readonly _id = new LanguageIdentifier('indentRulesMode', 4);
4405 constructor() {
4406 super(JSONMode._id);
4407 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
4408 brackets: [
4409 ['{', '}'],
4410 ['[', ']'],
4411 ['(', ')']
4412 ],
4413 indentationRules: {
4414 increaseIndentPattern: new RegExp('^.*\\{[^}\"\\\']*$|^.*\\([^\\)\"\\\']*$|^\\s*(public|private|protected):\\s*$|^\\s*@(public|private|protected)\\s*$|^\\s*\\{\\}$'),
4415 decreaseIndentPattern: new RegExp('^\\s*(\\s*/[*].*[*]/\\s*)*\\}|^\\s*(\\s*/[*].*[*]/\\s*)*\\)|^\\s*(public|private|protected):\\s*$|^\\s*@(public|private|protected)\\s*$'),
4416 }
4417 }));
4418 }
4419 }
4420
4421 let mode = new JSONMode();
4422 let model = createTextModel(
4423 [
4424 '{',
4425 ' "scripts: {"',
4426 ' "watch": "a {"',
4427 ' "build{": "b"',
4428 ' "tasks": []',
4429 ' "tasks": ["a"]',
4430 ' "}"',
4431 '"}"'
4432 ].join('\n'),
4433 {
4434 tabSize: 2,
4435 indentSize: 2
4436 },
4437 mode.getLanguageIdentifier()
4438 );
4439
4440 withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, viewModel) => {
4441 moveTo(editor, viewModel, 3, 19, false);
4442 assertCursor(viewModel, new Selection(3, 19, 3, 19));
4443
4444 viewModel.type('\n', 'keyboard');
4445 assert.deepStrictEqual(model.getLineContent(4), ' ');
4446
4447 moveTo(editor, viewModel, 5, 18, false);
4448 assertCursor(viewModel, new Selection(5, 18, 5, 18));
4449
4450 viewModel.type('\n', 'keyboard');
4451 assert.deepStrictEqual(model.getLineContent(6), ' ');
4452
4453 moveTo(editor, viewModel, 7, 15, false);
4454 assertCursor(viewModel, new Selection(7, 15, 7, 15));
4455
4456 viewModel.type('\n', 'keyboard');
4457 assert.deepStrictEqual(model.getLineContent(8), ' ');
4458 assert.deepStrictEqual(model.getLineContent(9), ' ]');
4459
4460 moveTo(editor, viewModel, 10, 18, false);
4461 assertCursor(viewModel, new Selection(10, 18, 10, 18));
4462
4463 viewModel.type('\n', 'keyboard');
4464 assert.deepStrictEqual(model.getLineContent(11), ' ]');
4465 });
4466
4467 model.dispose();
4468 mode.dispose();
4469 });
4470
4471 test('issue #111128: Multicursor `Enter` issue with indentation', () => {
4472 const model = createTextModel(' let a, b, c;', { detectIndentation: false, insertSpaces: false, tabSize: 4 }, mode.getLanguageIdentifier());
4473 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
4474 editor.setSelections([
4475 new Selection(1, 11, 1, 11),
4476 new Selection(1, 14, 1, 14),
4477 ]);
4478 viewModel.type('\n', 'keyboard');
4479 assert.strictEqual(model.getValue(), ' let a,\n\t b,\n\t c;');
4480 });
4481 });
4482
4483 test('issue #122714: tabSize=1 prevent typing a string matching decreaseIndentPattern in an empty file', () => {
4484 let latexMode = new IndentRulesMode({
4485 increaseIndentPattern: new RegExp('\\\\begin{(?!document)([^}]*)}(?!.*\\\\end{\\1})'),
4486 decreaseIndentPattern: new RegExp('^\\s*\\\\end{(?!document)')
4487 });
4488 let model = createTextModel(
4489 '\\end',
4490 { tabSize: 1 },
4491 latexMode.getLanguageIdentifier()
4492 );
4493
4494 withTestCodeEditor(null, { model: model, autoIndent: 'full' }, (editor, viewModel) => {
4495 moveTo(editor, viewModel, 1, 5, false);
4496 assertCursor(viewModel, new Selection(1, 5, 1, 5));
4497
4498 viewModel.type('{', 'keyboard');
4499 assert.strictEqual(model.getLineContent(1), '\\end{}');
4500 });
4501
4502 latexMode.dispose();
4503 model.dispose();
4504 });
4505});
4506
4507interface ICursorOpts {
4508 text: string[];
4509 languageIdentifier?: LanguageIdentifier | null;
4510 modelOpts?: IRelaxedTextModelCreationOptions;
4511 editorOpts?: IEditorOptions;
4512}
4513
4514function usingCursor(opts: ICursorOpts, callback: (editor: ITestCodeEditor, model: TextModel, viewModel: ViewModel) => void): void {
4515 const model = createTextModel(opts.text.join('\n'), opts.modelOpts, opts.languageIdentifier);
4516 const editorOptions: TestCodeEditorCreationOptions = opts.editorOpts || {};
4517 editorOptions.model = model;
4518 withTestCodeEditor(null, editorOptions, (editor, viewModel) => {
4519 callback(editor, model, viewModel);
4520 });
4521}
4522
4523class ElectricCharMode extends MockMode {
4524
4525 private static readonly _id = new LanguageIdentifier('electricCharMode', 3);
4526
4527 constructor() {
4528 super(ElectricCharMode._id);
4529 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
4530 __electricCharacterSupport: {
4531 docComment: { open: '/**', close: ' */' }
4532 },
4533 brackets: [
4534 ['{', '}'],
4535 ['[', ']'],
4536 ['(', ')']
4537 ]
4538 }));
4539 }
4540}
4541
4542suite('ElectricCharacter', () => {
4543 test('does nothing if no electric char', () => {
4544 let mode = new ElectricCharMode();
4545 usingCursor({
4546 text: [
4547 ' if (a) {',
4548 ''
4549 ],
4550 languageIdentifier: mode.getLanguageIdentifier()
4551 }, (editor, model, viewModel) => {
4552 moveTo(editor, viewModel, 2, 1);
4553 viewModel.type('*', 'keyboard');
4554 assert.deepStrictEqual(model.getLineContent(2), '*');
4555 });
4556 mode.dispose();
4557 });
4558
4559 test('indents in order to match bracket', () => {
4560 let mode = new ElectricCharMode();
4561 usingCursor({
4562 text: [
4563 ' if (a) {',
4564 ''
4565 ],
4566 languageIdentifier: mode.getLanguageIdentifier()
4567 }, (editor, model, viewModel) => {
4568 moveTo(editor, viewModel, 2, 1);
4569 viewModel.type('}', 'keyboard');
4570 assert.deepStrictEqual(model.getLineContent(2), ' }');
4571 });
4572 mode.dispose();
4573 });
4574
4575 test('unindents in order to match bracket', () => {
4576 let mode = new ElectricCharMode();
4577 usingCursor({
4578 text: [
4579 ' if (a) {',
4580 ' '
4581 ],
4582 languageIdentifier: mode.getLanguageIdentifier()
4583 }, (editor, model, viewModel) => {
4584 moveTo(editor, viewModel, 2, 5);
4585 viewModel.type('}', 'keyboard');
4586 assert.deepStrictEqual(model.getLineContent(2), ' }');
4587 });
4588 mode.dispose();
4589 });
4590
4591 test('matches with correct bracket', () => {
4592 let mode = new ElectricCharMode();
4593 usingCursor({
4594 text: [
4595 ' if (a) {',
4596 ' if (b) {',
4597 ' }',
4598 ' '
4599 ],
4600 languageIdentifier: mode.getLanguageIdentifier()
4601 }, (editor, model, viewModel) => {
4602 moveTo(editor, viewModel, 4, 1);
4603 viewModel.type('}', 'keyboard');
4604 assert.deepStrictEqual(model.getLineContent(4), ' } ');
4605 });
4606 mode.dispose();
4607 });
4608
4609 test('does nothing if bracket does not match', () => {
4610 let mode = new ElectricCharMode();
4611 usingCursor({
4612 text: [
4613 ' if (a) {',
4614 ' if (b) {',
4615 ' }',
4616 ' } '
4617 ],
4618 languageIdentifier: mode.getLanguageIdentifier()
4619 }, (editor, model, viewModel) => {
4620 moveTo(editor, viewModel, 4, 6);
4621 viewModel.type('}', 'keyboard');
4622 assert.deepStrictEqual(model.getLineContent(4), ' } }');
4623 });
4624 mode.dispose();
4625 });
4626
4627 test('matches bracket even in line with content', () => {
4628 let mode = new ElectricCharMode();
4629 usingCursor({
4630 text: [
4631 ' if (a) {',
4632 '// hello'
4633 ],
4634 languageIdentifier: mode.getLanguageIdentifier()
4635 }, (editor, model, viewModel) => {
4636 moveTo(editor, viewModel, 2, 1);
4637 viewModel.type('}', 'keyboard');
4638 assert.deepStrictEqual(model.getLineContent(2), ' }// hello');
4639 });
4640 mode.dispose();
4641 });
4642
4643 test('is no-op if bracket is lined up', () => {
4644 let mode = new ElectricCharMode();
4645 usingCursor({
4646 text: [
4647 ' if (a) {',
4648 ' '
4649 ],
4650 languageIdentifier: mode.getLanguageIdentifier()
4651 }, (editor, model, viewModel) => {
4652 moveTo(editor, viewModel, 2, 3);
4653 viewModel.type('}', 'keyboard');
4654 assert.deepStrictEqual(model.getLineContent(2), ' }');
4655 });
4656 mode.dispose();
4657 });
4658
4659 test('is no-op if there is non-whitespace text before', () => {
4660 let mode = new ElectricCharMode();
4661 usingCursor({
4662 text: [
4663 ' if (a) {',
4664 'a'
4665 ],
4666 languageIdentifier: mode.getLanguageIdentifier()
4667 }, (editor, model, viewModel) => {
4668 moveTo(editor, viewModel, 2, 2);
4669 viewModel.type('}', 'keyboard');
4670 assert.deepStrictEqual(model.getLineContent(2), 'a}');
4671 });
4672 mode.dispose();
4673 });
4674
4675 test('is no-op if pairs are all matched before', () => {
4676 let mode = new ElectricCharMode();
4677 usingCursor({
4678 text: [
4679 'foo(() => {',
4680 ' ( 1 + 2 ) ',
4681 '})'
4682 ],
4683 languageIdentifier: mode.getLanguageIdentifier()
4684 }, (editor, model, viewModel) => {
4685 moveTo(editor, viewModel, 2, 13);
4686 viewModel.type('*', 'keyboard');
4687 assert.deepStrictEqual(model.getLineContent(2), ' ( 1 + 2 ) *');
4688 });
4689 mode.dispose();
4690 });
4691
4692 test('is no-op if matching bracket is on the same line', () => {
4693 let mode = new ElectricCharMode();
4694 usingCursor({
4695 text: [
4696 '(div',
4697 ],
4698 languageIdentifier: mode.getLanguageIdentifier()
4699 }, (editor, model, viewModel) => {
4700 moveTo(editor, viewModel, 1, 5);
4701 let changeText: string | null = null;
4702 model.onDidChangeContent(e => {
4703 changeText = e.changes[0].text;
4704 });
4705 viewModel.type(')', 'keyboard');
4706 assert.deepStrictEqual(model.getLineContent(1), '(div)');
4707 assert.deepStrictEqual(changeText, ')');
4708 });
4709 mode.dispose();
4710 });
4711
4712 test('is no-op if the line has other content', () => {
4713 let mode = new ElectricCharMode();
4714 usingCursor({
4715 text: [
4716 'Math.max(',
4717 '\t2',
4718 '\t3'
4719 ],
4720 languageIdentifier: mode.getLanguageIdentifier()
4721 }, (editor, model, viewModel) => {
4722 moveTo(editor, viewModel, 3, 3);
4723 viewModel.type(')', 'keyboard');
4724 assert.deepStrictEqual(model.getLineContent(3), '\t3)');
4725 });
4726 mode.dispose();
4727 });
4728
4729 test('appends text', () => {
4730 let mode = new ElectricCharMode();
4731 usingCursor({
4732 text: [
4733 ' if (a) {',
4734 '/*'
4735 ],
4736 languageIdentifier: mode.getLanguageIdentifier()
4737 }, (editor, model, viewModel) => {
4738 moveTo(editor, viewModel, 2, 3);
4739 viewModel.type('*', 'keyboard');
4740 assert.deepStrictEqual(model.getLineContent(2), '/** */');
4741 });
4742 mode.dispose();
4743 });
4744
4745 test('appends text 2', () => {
4746 let mode = new ElectricCharMode();
4747 usingCursor({
4748 text: [
4749 ' if (a) {',
4750 ' /*'
4751 ],
4752 languageIdentifier: mode.getLanguageIdentifier()
4753 }, (editor, model, viewModel) => {
4754 moveTo(editor, viewModel, 2, 5);
4755 viewModel.type('*', 'keyboard');
4756 assert.deepStrictEqual(model.getLineContent(2), ' /** */');
4757 });
4758 mode.dispose();
4759 });
4760
4761 test('issue #23711: Replacing selected text with )]} fails to delete old text with backwards-dragged selection', () => {
4762 let mode = new ElectricCharMode();
4763 usingCursor({
4764 text: [
4765 '{',
4766 'word'
4767 ],
4768 languageIdentifier: mode.getLanguageIdentifier()
4769 }, (editor, model, viewModel) => {
4770 moveTo(editor, viewModel, 2, 5);
4771 moveTo(editor, viewModel, 2, 1, true);
4772 viewModel.type('}', 'keyboard');
4773 assert.deepStrictEqual(model.getLineContent(2), '}');
4774 });
4775 mode.dispose();
4776 });
4777});
4778
4779suite('autoClosingPairs', () => {
4780
4781 class AutoClosingMode extends MockMode {
4782
4783 private static readonly _id = new LanguageIdentifier('autoClosingMode', 5);
4784
4785 constructor() {
4786 super(AutoClosingMode._id);
4787 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
4788 autoClosingPairs: [
4789 { open: '{', close: '}' },
4790 { open: '[', close: ']' },
4791 { open: '(', close: ')' },
4792 { open: '\'', close: '\'', notIn: ['string', 'comment'] },
4793 { open: '\"', close: '\"', notIn: ['string'] },
4794 { open: '`', close: '`', notIn: ['string', 'comment'] },
4795 { open: '/**', close: ' */', notIn: ['string'] },
4796 { open: 'begin', close: 'end', notIn: ['string'] }
4797 ],
4798 __electricCharacterSupport: {
4799 docComment: { open: '/**', close: ' */' }
4800 }
4801 }));
4802 }
4803
4804 public setAutocloseEnabledSet(chars: string) {
4805 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
4806 autoCloseBefore: chars,
4807 autoClosingPairs: [
4808 { open: '{', close: '}' },
4809 { open: '[', close: ']' },
4810 { open: '(', close: ')' },
4811 { open: '\'', close: '\'', notIn: ['string', 'comment'] },
4812 { open: '\"', close: '\"', notIn: ['string'] },
4813 { open: '`', close: '`', notIn: ['string', 'comment'] },
4814 { open: '/**', close: ' */', notIn: ['string'] }
4815 ],
4816 }));
4817 }
4818 }
4819
4820 const enum ColumnType {
4821 Normal = 0,
4822 Special1 = 1,
4823 Special2 = 2
4824 }
4825
4826 function extractSpecialColumns(maxColumn: number, annotatedLine: string): ColumnType[] {
4827 let result: ColumnType[] = [];
4828 for (let j = 1; j <= maxColumn; j++) {
4829 result[j] = ColumnType.Normal;
4830 }
4831 let column = 1;
4832 for (let j = 0; j < annotatedLine.length; j++) {
4833 if (annotatedLine.charAt(j) === '|') {
4834 result[column] = ColumnType.Special1;
4835 } else if (annotatedLine.charAt(j) === '!') {
4836 result[column] = ColumnType.Special2;
4837 } else {
4838 column++;
4839 }
4840 }
4841 return result;
4842 }
4843
4844 function assertType(editor: ITestCodeEditor, model: TextModel, viewModel: ViewModel, lineNumber: number, column: number, chr: string, expectedInsert: string, message: string): void {
4845 let lineContent = model.getLineContent(lineNumber);
4846 let expected = lineContent.substr(0, column - 1) + expectedInsert + lineContent.substr(column - 1);
4847 moveTo(editor, viewModel, lineNumber, column);
4848 viewModel.type(chr, 'keyboard');
4849 assert.deepStrictEqual(model.getLineContent(lineNumber), expected, message);
4850 model.undo();
4851 }
4852
4853 test('open parens: default', () => {
4854 let mode = new AutoClosingMode();
4855 usingCursor({
4856 text: [
4857 'var a = [];',
4858 'var b = `asd`;',
4859 'var c = \'asd\';',
4860 'var d = "asd";',
4861 'var e = /*3*/ 3;',
4862 'var f = /** 3 */3;',
4863 'var g = (3+5);',
4864 'var h = { a: \'value\' };',
4865 ],
4866 languageIdentifier: mode.getLanguageIdentifier()
4867 }, (editor, model, viewModel) => {
4868
4869 let autoClosePositions = [
4870 'var| a| |=| [|]|;|',
4871 'var| b| |=| `asd`|;|',
4872 'var| c| |=| \'asd\'|;|',
4873 'var| d| |=| "asd"|;|',
4874 'var| e| |=| /*3*/| 3|;|',
4875 'var| f| |=| /**| 3| */3|;|',
4876 'var| g| |=| (3+5|)|;|',
4877 'var| h| |=| {| a|:| \'value\'| |}|;|',
4878 ];
4879 for (let i = 0, len = autoClosePositions.length; i < len; i++) {
4880 const lineNumber = i + 1;
4881 const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
4882
4883 for (let column = 1; column < autoCloseColumns.length; column++) {
4884 model.forceTokenization(lineNumber);
4885 if (autoCloseColumns[column] === ColumnType.Special1) {
4886 assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
4887 } else {
4888 assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
4889 }
4890 }
4891 }
4892 });
4893 mode.dispose();
4894 });
4895
4896 test('open parens: whitespace', () => {
4897 let mode = new AutoClosingMode();
4898 usingCursor({
4899 text: [
4900 'var a = [];',
4901 'var b = `asd`;',
4902 'var c = \'asd\';',
4903 'var d = "asd";',
4904 'var e = /*3*/ 3;',
4905 'var f = /** 3 */3;',
4906 'var g = (3+5);',
4907 'var h = { a: \'value\' };',
4908 ],
4909 languageIdentifier: mode.getLanguageIdentifier(),
4910 editorOpts: {
4911 autoClosingBrackets: 'beforeWhitespace'
4912 }
4913 }, (editor, model, viewModel) => {
4914
4915 let autoClosePositions = [
4916 'var| a| =| [|];|',
4917 'var| b| =| `asd`;|',
4918 'var| c| =| \'asd\';|',
4919 'var| d| =| "asd";|',
4920 'var| e| =| /*3*/| 3;|',
4921 'var| f| =| /**| 3| */3;|',
4922 'var| g| =| (3+5|);|',
4923 'var| h| =| {| a:| \'value\'| |};|',
4924 ];
4925 for (let i = 0, len = autoClosePositions.length; i < len; i++) {
4926 const lineNumber = i + 1;
4927 const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
4928
4929 for (let column = 1; column < autoCloseColumns.length; column++) {
4930 model.forceTokenization(lineNumber);
4931 if (autoCloseColumns[column] === ColumnType.Special1) {
4932 assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
4933 } else {
4934 assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
4935 }
4936 }
4937 }
4938 });
4939 mode.dispose();
4940 });
4941
4942 test('open parens disabled/enabled open quotes enabled/disabled', () => {
4943 let mode = new AutoClosingMode();
4944 usingCursor({
4945 text: [
4946 'var a = [];',
4947 ],
4948 languageIdentifier: mode.getLanguageIdentifier(),
4949 editorOpts: {
4950 autoClosingBrackets: 'beforeWhitespace',
4951 autoClosingQuotes: 'never'
4952 }
4953 }, (editor, model, viewModel) => {
4954
4955 let autoClosePositions = [
4956 'var| a| =| [|];|',
4957 ];
4958 for (let i = 0, len = autoClosePositions.length; i < len; i++) {
4959 const lineNumber = i + 1;
4960 const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
4961
4962 for (let column = 1; column < autoCloseColumns.length; column++) {
4963 model.forceTokenization(lineNumber);
4964 if (autoCloseColumns[column] === ColumnType.Special1) {
4965 assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
4966 } else {
4967 assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
4968 }
4969 assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);
4970 }
4971 }
4972 });
4973
4974 usingCursor({
4975 text: [
4976 'var b = [];',
4977 ],
4978 languageIdentifier: mode.getLanguageIdentifier(),
4979 editorOpts: {
4980 autoClosingBrackets: 'never',
4981 autoClosingQuotes: 'beforeWhitespace'
4982 }
4983 }, (editor, model, viewModel) => {
4984
4985 let autoClosePositions = [
4986 'var b =| [|];|',
4987 ];
4988 for (let i = 0, len = autoClosePositions.length; i < len; i++) {
4989 const lineNumber = i + 1;
4990 const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
4991
4992 for (let column = 1; column < autoCloseColumns.length; column++) {
4993 model.forceTokenization(lineNumber);
4994 if (autoCloseColumns[column] === ColumnType.Special1) {
4995 assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`);
4996 } else {
4997 assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);
4998 }
4999 assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
5000 }
5001 }
5002 });
5003 mode.dispose();
5004 });
5005
5006 test('configurable open parens', () => {
5007 let mode = new AutoClosingMode();
5008 mode.setAutocloseEnabledSet('abc');
5009 usingCursor({
5010 text: [
5011 'var a = [];',
5012 'var b = `asd`;',
5013 'var c = \'asd\';',
5014 'var d = "asd";',
5015 'var e = /*3*/ 3;',
5016 'var f = /** 3 */3;',
5017 'var g = (3+5);',
5018 'var h = { a: \'value\' };',
5019 ],
5020 languageIdentifier: mode.getLanguageIdentifier(),
5021 editorOpts: {
5022 autoClosingBrackets: 'languageDefined'
5023 }
5024 }, (editor, model, viewModel) => {
5025
5026 let autoClosePositions = [
5027 'v|ar |a = [|];|',
5028 'v|ar |b = `|asd`;|',
5029 'v|ar |c = \'|asd\';|',
5030 'v|ar d = "|asd";|',
5031 'v|ar e = /*3*/ 3;|',
5032 'v|ar f = /** 3| */3;|',
5033 'v|ar g = (3+5|);|',
5034 'v|ar h = { |a: \'v|alue\' |};|',
5035 ];
5036 for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5037 const lineNumber = i + 1;
5038 const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5039
5040 for (let column = 1; column < autoCloseColumns.length; column++) {
5041 model.forceTokenization(lineNumber);
5042 if (autoCloseColumns[column] === ColumnType.Special1) {
5043 assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
5044 } else {
5045 assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
5046 }
5047 }
5048 }
5049 });
5050 mode.dispose();
5051 });
5052
5053 test('auto-pairing can be disabled', () => {
5054 let mode = new AutoClosingMode();
5055 usingCursor({
5056 text: [
5057 'var a = [];',
5058 'var b = `asd`;',
5059 'var c = \'asd\';',
5060 'var d = "asd";',
5061 'var e = /*3*/ 3;',
5062 'var f = /** 3 */3;',
5063 'var g = (3+5);',
5064 'var h = { a: \'value\' };',
5065 ],
5066 languageIdentifier: mode.getLanguageIdentifier(),
5067 editorOpts: {
5068 autoClosingBrackets: 'never',
5069 autoClosingQuotes: 'never'
5070 }
5071 }, (editor, model, viewModel) => {
5072
5073 let autoClosePositions = [
5074 'var a = [];',
5075 'var b = `asd`;',
5076 'var c = \'asd\';',
5077 'var d = "asd";',
5078 'var e = /*3*/ 3;',
5079 'var f = /** 3 */3;',
5080 'var g = (3+5);',
5081 'var h = { a: \'value\' };',
5082 ];
5083 for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5084 const lineNumber = i + 1;
5085 const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5086
5087 for (let column = 1; column < autoCloseColumns.length; column++) {
5088 model.forceTokenization(lineNumber);
5089 if (autoCloseColumns[column] === ColumnType.Special1) {
5090 assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
5091 assertType(editor, model, viewModel, lineNumber, column, '"', '""', `auto closes @ (${lineNumber}, ${column})`);
5092 } else {
5093 assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
5094 assertType(editor, model, viewModel, lineNumber, column, '"', '"', `does not auto close @ (${lineNumber}, ${column})`);
5095 }
5096 }
5097 }
5098 });
5099 mode.dispose();
5100 });
5101
5102 test('auto wrapping is configurable', () => {
5103 let mode = new AutoClosingMode();
5104 usingCursor({
5105 text: [
5106 'var a = asd'
5107 ],
5108 languageIdentifier: mode.getLanguageIdentifier()
5109 }, (editor, model, viewModel) => {
5110
5111 viewModel.setSelections('test', [
5112 new Selection(1, 1, 1, 4),
5113 new Selection(1, 9, 1, 12),
5114 ]);
5115
5116 // type a `
5117 viewModel.type('`', 'keyboard');
5118
5119 assert.strictEqual(model.getValue(), '`var` a = `asd`');
5120
5121 // type a (
5122 viewModel.type('(', 'keyboard');
5123
5124 assert.strictEqual(model.getValue(), '`(var)` a = `(asd)`');
5125 });
5126
5127 usingCursor({
5128 text: [
5129 'var a = asd'
5130 ],
5131 languageIdentifier: mode.getLanguageIdentifier(),
5132 editorOpts: {
5133 autoSurround: 'never'
5134 }
5135 }, (editor, model, viewModel) => {
5136
5137 viewModel.setSelections('test', [
5138 new Selection(1, 1, 1, 4),
5139 ]);
5140
5141 // type a `
5142 viewModel.type('`', 'keyboard');
5143
5144 assert.strictEqual(model.getValue(), '` a = asd');
5145 });
5146
5147 usingCursor({
5148 text: [
5149 'var a = asd'
5150 ],
5151 languageIdentifier: mode.getLanguageIdentifier(),
5152 editorOpts: {
5153 autoSurround: 'quotes'
5154 }
5155 }, (editor, model, viewModel) => {
5156
5157 viewModel.setSelections('test', [
5158 new Selection(1, 1, 1, 4),
5159 ]);
5160
5161 // type a `
5162 viewModel.type('`', 'keyboard');
5163 assert.strictEqual(model.getValue(), '`var` a = asd');
5164
5165 // type a (
5166 viewModel.type('(', 'keyboard');
5167 assert.strictEqual(model.getValue(), '`(` a = asd');
5168 });
5169
5170 usingCursor({
5171 text: [
5172 'var a = asd'
5173 ],
5174 languageIdentifier: mode.getLanguageIdentifier(),
5175 editorOpts: {
5176 autoSurround: 'brackets'
5177 }
5178 }, (editor, model, viewModel) => {
5179
5180 viewModel.setSelections('test', [
5181 new Selection(1, 1, 1, 4),
5182 ]);
5183
5184 // type a (
5185 viewModel.type('(', 'keyboard');
5186 assert.strictEqual(model.getValue(), '(var) a = asd');
5187
5188 // type a `
5189 viewModel.type('`', 'keyboard');
5190 assert.strictEqual(model.getValue(), '(`) a = asd');
5191 });
5192 mode.dispose();
5193 });
5194
5195 test('quote', () => {
5196 let mode = new AutoClosingMode();
5197 usingCursor({
5198 text: [
5199 'var a = [];',
5200 'var b = `asd`;',
5201 'var c = \'asd\';',
5202 'var d = "asd";',
5203 'var e = /*3*/ 3;',
5204 'var f = /** 3 */3;',
5205 'var g = (3+5);',
5206 'var h = { a: \'value\' };',
5207 ],
5208 languageIdentifier: mode.getLanguageIdentifier()
5209 }, (editor, model, viewModel) => {
5210
5211 let autoClosePositions = [
5212 'var a |=| [|]|;|',
5213 'var b |=| `asd`|;|',
5214 'var c |=| \'asd\'|;|',
5215 'var d |=| "asd"|;|',
5216 'var e |=| /*3*/| 3;|',
5217 'var f |=| /**| 3 */3;|',
5218 'var g |=| (3+5)|;|',
5219 'var h |=| {| a:| \'value\'| |}|;|',
5220 ];
5221 for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5222 const lineNumber = i + 1;
5223 const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5224
5225 for (let column = 1; column < autoCloseColumns.length; column++) {
5226 model.forceTokenization(lineNumber);
5227 if (autoCloseColumns[column] === ColumnType.Special1) {
5228 assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`);
5229 } else if (autoCloseColumns[column] === ColumnType.Special2) {
5230 assertType(editor, model, viewModel, lineNumber, column, '\'', '', `over types @ (${lineNumber}, ${column})`);
5231 } else {
5232 assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);
5233 }
5234 }
5235 }
5236 });
5237 mode.dispose();
5238 });
5239
5240 test('multi-character autoclose', () => {
5241 let mode = new AutoClosingMode();
5242 usingCursor({
5243 text: [
5244 '',
5245 ],
5246 languageIdentifier: mode.getLanguageIdentifier()
5247 }, (editor, model, viewModel) => {
5248
5249 model.setValue('begi');
5250 viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
5251 viewModel.type('n', 'keyboard');
5252 assert.strictEqual(model.getLineContent(1), 'beginend');
5253
5254 model.setValue('/*');
5255 viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
5256 viewModel.type('*', 'keyboard');
5257 assert.strictEqual(model.getLineContent(1), '/** */');
5258 });
5259 mode.dispose();
5260 });
5261
5262 test('issue #72177: multi-character autoclose with conflicting patterns', () => {
5263 const languageId = new LanguageIdentifier('autoClosingModeMultiChar', 5);
5264 class AutoClosingModeMultiChar extends MockMode {
5265 constructor() {
5266 super(languageId);
5267 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
5268 autoClosingPairs: [
5269 { open: '(', close: ')' },
5270 { open: '(*', close: '*)' },
5271 { open: '<@', close: '@>' },
5272 { open: '<@@', close: '@@>' },
5273 ],
5274 }));
5275 }
5276 }
5277
5278 const mode = new AutoClosingModeMultiChar();
5279
5280 usingCursor({
5281 text: [
5282 '',
5283 ],
5284 languageIdentifier: mode.getLanguageIdentifier()
5285 }, (editor, model, viewModel) => {
5286 viewModel.type('(', 'keyboard');
5287 assert.strictEqual(model.getLineContent(1), '()');
5288 viewModel.type('*', 'keyboard');
5289 assert.strictEqual(model.getLineContent(1), '(**)', `doesn't add entire close when already closed substring is there`);
5290
5291 model.setValue('(');
5292 viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]);
5293 viewModel.type('*', 'keyboard');
5294 assert.strictEqual(model.getLineContent(1), '(**)', `does add entire close if not already there`);
5295
5296 model.setValue('');
5297 viewModel.type('<@', 'keyboard');
5298 assert.strictEqual(model.getLineContent(1), '<@@>');
5299 viewModel.type('@', 'keyboard');
5300 assert.strictEqual(model.getLineContent(1), '<@@@@>', `autocloses when before multi-character closing brace`);
5301 viewModel.type('(', 'keyboard');
5302 assert.strictEqual(model.getLineContent(1), '<@@()@@>', `autocloses when before multi-character closing brace`);
5303 });
5304 mode.dispose();
5305 });
5306
5307 test('issue #55314: Do not auto-close when ending with open', () => {
5308 const languageId = new LanguageIdentifier('myElectricMode', 5);
5309 class ElectricMode extends MockMode {
5310 constructor() {
5311 super(languageId);
5312 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
5313 autoClosingPairs: [
5314 { open: '{', close: '}' },
5315 { open: '[', close: ']' },
5316 { open: '(', close: ')' },
5317 { open: '\'', close: '\'', notIn: ['string', 'comment'] },
5318 { open: '\"', close: '\"', notIn: ['string'] },
5319 { open: 'B\"', close: '\"', notIn: ['string', 'comment'] },
5320 { open: '`', close: '`', notIn: ['string', 'comment'] },
5321 { open: '/**', close: ' */', notIn: ['string'] }
5322 ],
5323 }));
5324 }
5325 }
5326
5327 const mode = new ElectricMode();
5328
5329 usingCursor({
5330 text: [
5331 'little goat',
5332 'little LAMB',
5333 'little sheep',
5334 'Big LAMB'
5335 ],
5336 languageIdentifier: mode.getLanguageIdentifier()
5337 }, (editor, model, viewModel) => {
5338 model.forceTokenization(model.getLineCount());
5339 assertType(editor, model, viewModel, 1, 4, '"', '"', `does not double quote when ending with open`);
5340 model.forceTokenization(model.getLineCount());
5341 assertType(editor, model, viewModel, 2, 4, '"', '"', `does not double quote when ending with open`);
5342 model.forceTokenization(model.getLineCount());
5343 assertType(editor, model, viewModel, 3, 4, '"', '"', `does not double quote when ending with open`);
5344 model.forceTokenization(model.getLineCount());
5345 assertType(editor, model, viewModel, 4, 2, '"', '"', `does not double quote when ending with open`);
5346 model.forceTokenization(model.getLineCount());
5347 assertType(editor, model, viewModel, 4, 3, '"', '"', `does not double quote when ending with open`);
5348 });
5349 mode.dispose();
5350 });
5351
5352 test('issue #27937: Trying to add an item to the front of a list is cumbersome', () => {
5353 let mode = new AutoClosingMode();
5354 usingCursor({
5355 text: [
5356 'var arr = ["b", "c"];'
5357 ],
5358 languageIdentifier: mode.getLanguageIdentifier()
5359 }, (editor, model, viewModel) => {
5360 assertType(editor, model, viewModel, 1, 12, '"', '"', `does not over type and will not auto close`);
5361 });
5362 mode.dispose();
5363 });
5364
5365 test('issue #25658 - Do not auto-close single/double quotes after word characters', () => {
5366 let mode = new AutoClosingMode();
5367 usingCursor({
5368 text: [
5369 '',
5370 ],
5371 languageIdentifier: mode.getLanguageIdentifier()
5372 }, (editor, model, viewModel) => {
5373
5374 function typeCharacters(viewModel: ViewModel, chars: string): void {
5375 for (let i = 0, len = chars.length; i < len; i++) {
5376 viewModel.type(chars[i], 'keyboard');
5377 }
5378 }
5379
5380 // First gif
5381 model.forceTokenization(model.getLineCount());
5382 typeCharacters(viewModel, 'teste1 = teste\' ok');
5383 assert.strictEqual(model.getLineContent(1), 'teste1 = teste\' ok');
5384
5385 viewModel.setSelections('test', [new Selection(1, 1000, 1, 1000)]);
5386 typeCharacters(viewModel, '\n');
5387 model.forceTokenization(model.getLineCount());
5388 typeCharacters(viewModel, 'teste2 = teste \'ok');
5389 assert.strictEqual(model.getLineContent(2), 'teste2 = teste \'ok\'');
5390
5391 viewModel.setSelections('test', [new Selection(2, 1000, 2, 1000)]);
5392 typeCharacters(viewModel, '\n');
5393 model.forceTokenization(model.getLineCount());
5394 typeCharacters(viewModel, 'teste3 = teste" ok');
5395 assert.strictEqual(model.getLineContent(3), 'teste3 = teste" ok');
5396
5397 viewModel.setSelections('test', [new Selection(3, 1000, 3, 1000)]);
5398 typeCharacters(viewModel, '\n');
5399 model.forceTokenization(model.getLineCount());
5400 typeCharacters(viewModel, 'teste4 = teste "ok');
5401 assert.strictEqual(model.getLineContent(4), 'teste4 = teste "ok"');
5402
5403 // Second gif
5404 viewModel.setSelections('test', [new Selection(4, 1000, 4, 1000)]);
5405 typeCharacters(viewModel, '\n');
5406 model.forceTokenization(model.getLineCount());
5407 typeCharacters(viewModel, 'teste \'');
5408 assert.strictEqual(model.getLineContent(5), 'teste \'\'');
5409
5410 viewModel.setSelections('test', [new Selection(5, 1000, 5, 1000)]);
5411 typeCharacters(viewModel, '\n');
5412 model.forceTokenization(model.getLineCount());
5413 typeCharacters(viewModel, 'teste "');
5414 assert.strictEqual(model.getLineContent(6), 'teste ""');
5415
5416 viewModel.setSelections('test', [new Selection(6, 1000, 6, 1000)]);
5417 typeCharacters(viewModel, '\n');
5418 model.forceTokenization(model.getLineCount());
5419 typeCharacters(viewModel, 'teste\'');
5420 assert.strictEqual(model.getLineContent(7), 'teste\'');
5421
5422 viewModel.setSelections('test', [new Selection(7, 1000, 7, 1000)]);
5423 typeCharacters(viewModel, '\n');
5424 model.forceTokenization(model.getLineCount());
5425 typeCharacters(viewModel, 'teste"');
5426 assert.strictEqual(model.getLineContent(8), 'teste"');
5427 });
5428 mode.dispose();
5429 });
5430
5431 test('issue #37315 - overtypes only those characters that it inserted', () => {
5432 let mode = new AutoClosingMode();
5433 usingCursor({
5434 text: [
5435 '',
5436 'y=();'
5437 ],
5438 languageIdentifier: mode.getLanguageIdentifier()
5439 }, (editor, model, viewModel) => {
5440 assertCursor(viewModel, new Position(1, 1));
5441
5442 viewModel.type('x=(', 'keyboard');
5443 assert.strictEqual(model.getLineContent(1), 'x=()');
5444
5445 viewModel.type('asd', 'keyboard');
5446 assert.strictEqual(model.getLineContent(1), 'x=(asd)');
5447
5448 // overtype!
5449 viewModel.type(')', 'keyboard');
5450 assert.strictEqual(model.getLineContent(1), 'x=(asd)');
5451
5452 // do not overtype!
5453 viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);
5454 viewModel.type(')', 'keyboard');
5455 assert.strictEqual(model.getLineContent(2), 'y=());');
5456
5457 });
5458 mode.dispose();
5459 });
5460
5461 test('issue #37315 - stops overtyping once cursor leaves area', () => {
5462 let mode = new AutoClosingMode();
5463 usingCursor({
5464 text: [
5465 '',
5466 'y=();'
5467 ],
5468 languageIdentifier: mode.getLanguageIdentifier()
5469 }, (editor, model, viewModel) => {
5470 assertCursor(viewModel, new Position(1, 1));
5471
5472 viewModel.type('x=(', 'keyboard');
5473 assert.strictEqual(model.getLineContent(1), 'x=()');
5474
5475 viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
5476 viewModel.type(')', 'keyboard');
5477 assert.strictEqual(model.getLineContent(1), 'x=())');
5478 });
5479 mode.dispose();
5480 });
5481
5482 test('issue #37315 - it overtypes only once', () => {
5483 let mode = new AutoClosingMode();
5484 usingCursor({
5485 text: [
5486 '',
5487 'y=();'
5488 ],
5489 languageIdentifier: mode.getLanguageIdentifier()
5490 }, (editor, model, viewModel) => {
5491 assertCursor(viewModel, new Position(1, 1));
5492
5493 viewModel.type('x=(', 'keyboard');
5494 assert.strictEqual(model.getLineContent(1), 'x=()');
5495
5496 viewModel.type(')', 'keyboard');
5497 assert.strictEqual(model.getLineContent(1), 'x=()');
5498
5499 viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);
5500 viewModel.type(')', 'keyboard');
5501 assert.strictEqual(model.getLineContent(1), 'x=())');
5502 });
5503 mode.dispose();
5504 });
5505
5506 test('issue #37315 - it can remember multiple auto-closed instances', () => {
5507 let mode = new AutoClosingMode();
5508 usingCursor({
5509 text: [
5510 '',
5511 'y=();'
5512 ],
5513 languageIdentifier: mode.getLanguageIdentifier()
5514 }, (editor, model, viewModel) => {
5515 assertCursor(viewModel, new Position(1, 1));
5516
5517 viewModel.type('x=(', 'keyboard');
5518 assert.strictEqual(model.getLineContent(1), 'x=()');
5519
5520 viewModel.type('(', 'keyboard');
5521 assert.strictEqual(model.getLineContent(1), 'x=(())');
5522
5523 viewModel.type(')', 'keyboard');
5524 assert.strictEqual(model.getLineContent(1), 'x=(())');
5525
5526 viewModel.type(')', 'keyboard');
5527 assert.strictEqual(model.getLineContent(1), 'x=(())');
5528 });
5529 mode.dispose();
5530 });
5531
5532 test('issue #118270 - auto closing deletes only those characters that it inserted', () => {
5533 let mode = new AutoClosingMode();
5534 usingCursor({
5535 text: [
5536 '',
5537 'y=();'
5538 ],
5539 languageIdentifier: mode.getLanguageIdentifier()
5540 }, (editor, model, viewModel) => {
5541 assertCursor(viewModel, new Position(1, 1));
5542
5543 viewModel.type('x=(', 'keyboard');
5544 assert.strictEqual(model.getLineContent(1), 'x=()');
5545
5546 viewModel.type('asd', 'keyboard');
5547 assert.strictEqual(model.getLineContent(1), 'x=(asd)');
5548
5549 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
5550 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
5551 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
5552 assert.strictEqual(model.getLineContent(1), 'x=()');
5553
5554 // delete closing char!
5555 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
5556 assert.strictEqual(model.getLineContent(1), 'x=');
5557
5558 // do not delete closing char!
5559 viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);
5560 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
5561 assert.strictEqual(model.getLineContent(2), 'y=);');
5562
5563 });
5564 mode.dispose();
5565 });
5566
5567 test('issue #78527 - does not close quote on odd count', () => {
5568 let mode = new AutoClosingMode();
5569 usingCursor({
5570 text: [
5571 'std::cout << \'"\' << entryMap'
5572 ],
5573 languageIdentifier: mode.getLanguageIdentifier()
5574 }, (editor, model, viewModel) => {
5575 viewModel.setSelections('test', [new Selection(1, 29, 1, 29)]);
5576
5577 viewModel.type('[', 'keyboard');
5578 assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[]');
5579
5580 viewModel.type('"', 'keyboard');
5581 assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[""]');
5582
5583 viewModel.type('a', 'keyboard');
5584 assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');
5585
5586 viewModel.type('"', 'keyboard');
5587 assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');
5588
5589 viewModel.type(']', 'keyboard');
5590 assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');
5591 });
5592 mode.dispose();
5593 });
5594
5595 test('issue #85983 - editor.autoClosingBrackets: beforeWhitespace is incorrect for Python', () => {
5596 const languageId = new LanguageIdentifier('pythonMode', 5);
5597 class PythonMode extends MockMode {
5598 constructor() {
5599 super(languageId);
5600 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
5601 autoClosingPairs: [
5602 { open: '{', close: '}' },
5603 { open: '[', close: ']' },
5604 { open: '(', close: ')' },
5605 { open: '\"', close: '\"', notIn: ['string'] },
5606 { open: 'r\"', close: '\"', notIn: ['string', 'comment'] },
5607 { open: 'R\"', close: '\"', notIn: ['string', 'comment'] },
5608 { open: 'u\"', close: '\"', notIn: ['string', 'comment'] },
5609 { open: 'U\"', close: '\"', notIn: ['string', 'comment'] },
5610 { open: 'f\"', close: '\"', notIn: ['string', 'comment'] },
5611 { open: 'F\"', close: '\"', notIn: ['string', 'comment'] },
5612 { open: 'b\"', close: '\"', notIn: ['string', 'comment'] },
5613 { open: 'B\"', close: '\"', notIn: ['string', 'comment'] },
5614 { open: '\'', close: '\'', notIn: ['string', 'comment'] },
5615 { open: 'r\'', close: '\'', notIn: ['string', 'comment'] },
5616 { open: 'R\'', close: '\'', notIn: ['string', 'comment'] },
5617 { open: 'u\'', close: '\'', notIn: ['string', 'comment'] },
5618 { open: 'U\'', close: '\'', notIn: ['string', 'comment'] },
5619 { open: 'f\'', close: '\'', notIn: ['string', 'comment'] },
5620 { open: 'F\'', close: '\'', notIn: ['string', 'comment'] },
5621 { open: 'b\'', close: '\'', notIn: ['string', 'comment'] },
5622 { open: 'B\'', close: '\'', notIn: ['string', 'comment'] },
5623 { open: '`', close: '`', notIn: ['string'] }
5624 ],
5625 }));
5626 }
5627 }
5628 const mode = new PythonMode();
5629 usingCursor({
5630 text: [
5631 'foo\'hello\''
5632 ],
5633 languageIdentifier: mode.getLanguageIdentifier()
5634 }, (editor, model, viewModel) => {
5635 assertType(editor, model, viewModel, 1, 4, '(', '(', `does not auto close @ (1, 4)`);
5636 });
5637 mode.dispose();
5638 });
5639
5640 test('issue #78975 - Parentheses swallowing does not work when parentheses are inserted by autocomplete', () => {
5641 let mode = new AutoClosingMode();
5642 usingCursor({
5643 text: [
5644 '<div id'
5645 ],
5646 languageIdentifier: mode.getLanguageIdentifier()
5647 }, (editor, model, viewModel) => {
5648 viewModel.setSelections('test', [new Selection(1, 8, 1, 8)]);
5649
5650 viewModel.executeEdits('snippet', [{ range: new Range(1, 6, 1, 8), text: 'id=""' }], () => [new Selection(1, 10, 1, 10)]);
5651 assert.strictEqual(model.getLineContent(1), '<div id=""');
5652
5653 viewModel.type('a', 'keyboard');
5654 assert.strictEqual(model.getLineContent(1), '<div id="a"');
5655
5656 viewModel.type('"', 'keyboard');
5657 assert.strictEqual(model.getLineContent(1), '<div id="a"');
5658 });
5659 mode.dispose();
5660 });
5661
5662 test('issue #78833 - Add config to use old brackets/quotes overtyping', () => {
5663 let mode = new AutoClosingMode();
5664 usingCursor({
5665 text: [
5666 '',
5667 'y=();'
5668 ],
5669 languageIdentifier: mode.getLanguageIdentifier(),
5670 editorOpts: {
5671 autoClosingOvertype: 'always'
5672 }
5673 }, (editor, model, viewModel) => {
5674 assertCursor(viewModel, new Position(1, 1));
5675
5676 viewModel.type('x=(', 'keyboard');
5677 assert.strictEqual(model.getLineContent(1), 'x=()');
5678
5679 viewModel.type(')', 'keyboard');
5680 assert.strictEqual(model.getLineContent(1), 'x=()');
5681
5682 viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);
5683 viewModel.type(')', 'keyboard');
5684 assert.strictEqual(model.getLineContent(1), 'x=()');
5685
5686 viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);
5687 viewModel.type(')', 'keyboard');
5688 assert.strictEqual(model.getLineContent(2), 'y=();');
5689 });
5690 mode.dispose();
5691 });
5692
5693 test('issue #15825: accents on mac US intl keyboard', () => {
5694 let mode = new AutoClosingMode();
5695 usingCursor({
5696 text: [
5697 ],
5698 languageIdentifier: mode.getLanguageIdentifier()
5699 }, (editor, model, viewModel) => {
5700 assertCursor(viewModel, new Position(1, 1));
5701
5702 // Typing ` + e on the mac US intl kb layout
5703 viewModel.startComposition();
5704 viewModel.type('`', 'keyboard');
5705 viewModel.compositionType('è', 1, 0, 0, 'keyboard');
5706 viewModel.endComposition('keyboard');
5707
5708 assert.strictEqual(model.getValue(), 'è');
5709 });
5710 mode.dispose();
5711 });
5712
5713 test('issue #90016: allow accents on mac US intl keyboard to surround selection', () => {
5714 let mode = new AutoClosingMode();
5715 usingCursor({
5716 text: [
5717 'test'
5718 ],
5719 languageIdentifier: mode.getLanguageIdentifier()
5720 }, (editor, model, viewModel) => {
5721 viewModel.setSelections('test', [new Selection(1, 1, 1, 5)]);
5722
5723 // Typing ` + e on the mac US intl kb layout
5724 viewModel.startComposition();
5725 viewModel.type('\'', 'keyboard');
5726 viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5727 viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5728 viewModel.endComposition('keyboard');
5729
5730 assert.strictEqual(model.getValue(), '\'test\'');
5731 });
5732 mode.dispose();
5733 });
5734
5735 test('issue #53357: Over typing ignores characters after backslash', () => {
5736 let mode = new AutoClosingMode();
5737 usingCursor({
5738 text: [
5739 'console.log();'
5740 ],
5741 languageIdentifier: mode.getLanguageIdentifier()
5742 }, (editor, model, viewModel) => {
5743
5744 viewModel.setSelections('test', [new Selection(1, 13, 1, 13)]);
5745
5746 viewModel.type('\'', 'keyboard');
5747 assert.strictEqual(model.getValue(), 'console.log(\'\');');
5748
5749 viewModel.type('it', 'keyboard');
5750 assert.strictEqual(model.getValue(), 'console.log(\'it\');');
5751
5752 viewModel.type('\\', 'keyboard');
5753 assert.strictEqual(model.getValue(), 'console.log(\'it\\\');');
5754
5755 viewModel.type('\'', 'keyboard');
5756 assert.strictEqual(model.getValue(), 'console.log(\'it\\\'\');');
5757 });
5758 mode.dispose();
5759 });
5760
5761 test('issue #84998: Overtyping Brackets doesn\'t work after backslash', () => {
5762 let mode = new AutoClosingMode();
5763 usingCursor({
5764 text: [
5765 ''
5766 ],
5767 languageIdentifier: mode.getLanguageIdentifier()
5768 }, (editor, model, viewModel) => {
5769
5770 viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);
5771
5772 viewModel.type('\\', 'keyboard');
5773 assert.strictEqual(model.getValue(), '\\');
5774
5775 viewModel.type('(', 'keyboard');
5776 assert.strictEqual(model.getValue(), '\\()');
5777
5778 viewModel.type('abc', 'keyboard');
5779 assert.strictEqual(model.getValue(), '\\(abc)');
5780
5781 viewModel.type('\\', 'keyboard');
5782 assert.strictEqual(model.getValue(), '\\(abc\\)');
5783
5784 viewModel.type(')', 'keyboard');
5785 assert.strictEqual(model.getValue(), '\\(abc\\)');
5786 });
5787 mode.dispose();
5788 });
5789
5790 test('issue #2773: Accents (´`¨^, others?) are inserted in the wrong position (Mac)', () => {
5791 let mode = new AutoClosingMode();
5792 usingCursor({
5793 text: [
5794 'hello',
5795 'world'
5796 ],
5797 languageIdentifier: mode.getLanguageIdentifier()
5798 }, (editor, model, viewModel) => {
5799 assertCursor(viewModel, new Position(1, 1));
5800
5801 // Typing ` and pressing shift+down on the mac US intl kb layout
5802 // Here we're just replaying what the cursor gets
5803 viewModel.startComposition();
5804 viewModel.type('`', 'keyboard');
5805 moveDown(editor, viewModel, true);
5806 viewModel.compositionType('`', 1, 0, 0, 'keyboard');
5807 viewModel.compositionType('`', 1, 0, 0, 'keyboard');
5808 viewModel.endComposition('keyboard');
5809
5810 assert.strictEqual(model.getValue(), '`hello\nworld');
5811 assertCursor(viewModel, new Selection(1, 2, 2, 2));
5812 });
5813 mode.dispose();
5814 });
5815
5816 test('issue #26820: auto close quotes when not used as accents', () => {
5817 let mode = new AutoClosingMode();
5818 usingCursor({
5819 text: [
5820 ''
5821 ],
5822 languageIdentifier: mode.getLanguageIdentifier()
5823 }, (editor, model, viewModel) => {
5824 assertCursor(viewModel, new Position(1, 1));
5825
5826 // on the mac US intl kb layout
5827
5828 // Typing ' + space
5829 viewModel.startComposition();
5830 viewModel.type('\'', 'keyboard');
5831 viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5832 viewModel.endComposition('keyboard');
5833 assert.strictEqual(model.getValue(), '\'\'');
5834
5835 // Typing one more ' + space
5836 viewModel.startComposition();
5837 viewModel.type('\'', 'keyboard');
5838 viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5839 viewModel.endComposition('keyboard');
5840 assert.strictEqual(model.getValue(), '\'\'');
5841
5842 // Typing ' as a closing tag
5843 model.setValue('\'abc');
5844 viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
5845 viewModel.startComposition();
5846 viewModel.type('\'', 'keyboard');
5847 viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5848 viewModel.endComposition('keyboard');
5849
5850 assert.strictEqual(model.getValue(), '\'abc\'');
5851
5852 // quotes before the newly added character are all paired.
5853 model.setValue('\'abc\'def ');
5854 viewModel.setSelections('test', [new Selection(1, 10, 1, 10)]);
5855 viewModel.startComposition();
5856 viewModel.type('\'', 'keyboard');
5857 viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5858 viewModel.endComposition('keyboard');
5859
5860 assert.strictEqual(model.getValue(), '\'abc\'def \'\'');
5861
5862 // No auto closing if there is non-whitespace character after the cursor
5863 model.setValue('abc');
5864 viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);
5865 viewModel.startComposition();
5866 viewModel.type('\'', 'keyboard');
5867 viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5868 viewModel.endComposition('keyboard');
5869
5870 // No auto closing if it's after a word.
5871 model.setValue('abc');
5872 viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);
5873 viewModel.startComposition();
5874 viewModel.type('\'', 'keyboard');
5875 viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5876 viewModel.endComposition('keyboard');
5877
5878 assert.strictEqual(model.getValue(), 'abc\'');
5879 });
5880 mode.dispose();
5881 });
5882
5883 test('issue #82701: auto close does not execute when IME is canceled via backspace', () => {
5884 let mode = new AutoClosingMode();
5885 usingCursor({
5886 text: [
5887 '{}'
5888 ],
5889 languageIdentifier: mode.getLanguageIdentifier()
5890 }, (editor, model, viewModel) => {
5891 viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]);
5892
5893 // Typing a + backspace
5894 viewModel.startComposition();
5895 viewModel.type('a', 'keyboard');
5896 viewModel.compositionType('', 1, 0, 0, 'keyboard');
5897 viewModel.endComposition('keyboard');
5898 assert.strictEqual(model.getValue(), '{}');
5899 });
5900 mode.dispose();
5901 });
5902
5903 test('issue #20891: All cursors should do the same thing', () => {
5904 let mode = new AutoClosingMode();
5905 usingCursor({
5906 text: [
5907 'var a = asd'
5908 ],
5909 languageIdentifier: mode.getLanguageIdentifier()
5910 }, (editor, model, viewModel) => {
5911
5912 viewModel.setSelections('test', [
5913 new Selection(1, 9, 1, 9),
5914 new Selection(1, 12, 1, 12),
5915 ]);
5916
5917 // type a `
5918 viewModel.type('`', 'keyboard');
5919
5920 assert.strictEqual(model.getValue(), 'var a = `asd`');
5921 });
5922 mode.dispose();
5923 });
5924
5925 test('issue #41825: Special handling of quotes in surrounding pairs', () => {
5926 const languageId = new LanguageIdentifier('myMode', 3);
5927 class MyMode extends MockMode {
5928 constructor() {
5929 super(languageId);
5930 this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
5931 surroundingPairs: [
5932 { open: '"', close: '"' },
5933 { open: '\'', close: '\'' },
5934 ]
5935 }));
5936 }
5937 }
5938
5939 const mode = new MyMode();
5940 const model = createTextModel('var x = \'hi\';', undefined, languageId);
5941
5942 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
5943 editor.setSelections([
5944 new Selection(1, 9, 1, 10),
5945 new Selection(1, 12, 1, 13)
5946 ]);
5947 viewModel.type('"', 'keyboard');
5948 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'var x = "hi";', 'assert1');
5949
5950 editor.setSelections([
5951 new Selection(1, 9, 1, 10),
5952 new Selection(1, 12, 1, 13)
5953 ]);
5954 viewModel.type('\'', 'keyboard');
5955 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'var x = \'hi\';', 'assert2');
5956 });
5957
5958 model.dispose();
5959 mode.dispose();
5960 });
5961
5962 test('All cursors should do the same thing when deleting left', () => {
5963 let mode = new AutoClosingMode();
5964 let model = createTextModel(
5965 [
5966 'var a = ()'
5967 ].join('\n'),
5968 TextModel.DEFAULT_CREATION_OPTIONS,
5969 mode.getLanguageIdentifier()
5970 );
5971
5972 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
5973 viewModel.setSelections('test', [
5974 new Selection(1, 4, 1, 4),
5975 new Selection(1, 10, 1, 10),
5976 ]);
5977
5978 // delete left
5979 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
5980
5981 assert.strictEqual(model.getValue(), 'va a = )');
5982 });
5983 model.dispose();
5984 mode.dispose();
5985 });
5986
5987 test('issue #7100: Mouse word selection is strange when non-word character is at the end of line', () => {
5988 let model = createTextModel(
5989 [
5990 'before.a',
5991 'before',
5992 'hello:',
5993 'there:',
5994 'this is strange:',
5995 'here',
5996 'it',
5997 'is',
5998 ].join('\n')
5999 );
6000
6001 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6002 CoreNavigationCommands.WordSelect.runEditorCommand(null, editor, {
6003 position: new Position(3, 7)
6004 });
6005 assertCursor(viewModel, new Selection(3, 7, 3, 7));
6006
6007 CoreNavigationCommands.WordSelectDrag.runEditorCommand(null, editor, {
6008 position: new Position(4, 7)
6009 });
6010 assertCursor(viewModel, new Selection(3, 7, 4, 7));
6011 });
6012 });
6013});
6014
6015suite('Undo stops', () => {
6016
6017 test('there is an undo stop between typing and deleting left', () => {
6018 let model = createTextModel(
6019 [
6020 'A line',
6021 'Another line',
6022 ].join('\n')
6023 );
6024
6025 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6026 viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
6027 viewModel.type('first', 'keyboard');
6028 assert.strictEqual(model.getLineContent(1), 'A first line');
6029 assertCursor(viewModel, new Selection(1, 8, 1, 8));
6030
6031 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6032 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6033 assert.strictEqual(model.getLineContent(1), 'A fir line');
6034 assertCursor(viewModel, new Selection(1, 6, 1, 6));
6035
6036 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6037 assert.strictEqual(model.getLineContent(1), 'A first line');
6038 assertCursor(viewModel, new Selection(1, 8, 1, 8));
6039
6040 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6041 assert.strictEqual(model.getLineContent(1), 'A line');
6042 assertCursor(viewModel, new Selection(1, 3, 1, 3));
6043 });
6044 });
6045
6046 test('there is an undo stop between typing and deleting right', () => {
6047 let model = createTextModel(
6048 [
6049 'A line',
6050 'Another line',
6051 ].join('\n')
6052 );
6053
6054 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6055 viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
6056 viewModel.type('first', 'keyboard');
6057 assert.strictEqual(model.getLineContent(1), 'A first line');
6058 assertCursor(viewModel, new Selection(1, 8, 1, 8));
6059
6060 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6061 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6062 assert.strictEqual(model.getLineContent(1), 'A firstine');
6063 assertCursor(viewModel, new Selection(1, 8, 1, 8));
6064
6065 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6066 assert.strictEqual(model.getLineContent(1), 'A first line');
6067 assertCursor(viewModel, new Selection(1, 8, 1, 8));
6068
6069 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6070 assert.strictEqual(model.getLineContent(1), 'A line');
6071 assertCursor(viewModel, new Selection(1, 3, 1, 3));
6072 });
6073 });
6074
6075 test('there is an undo stop between deleting left and typing', () => {
6076 let model = createTextModel(
6077 [
6078 'A line',
6079 'Another line',
6080 ].join('\n')
6081 );
6082
6083 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6084 viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]);
6085 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6086 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6087 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6088 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6089 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6090 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6091 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6092 assert.strictEqual(model.getLineContent(2), ' line');
6093 assertCursor(viewModel, new Selection(2, 1, 2, 1));
6094
6095 viewModel.type('Second', 'keyboard');
6096 assert.strictEqual(model.getLineContent(2), 'Second line');
6097 assertCursor(viewModel, new Selection(2, 7, 2, 7));
6098
6099 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6100 assert.strictEqual(model.getLineContent(2), ' line');
6101 assertCursor(viewModel, new Selection(2, 1, 2, 1));
6102
6103 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6104 assert.strictEqual(model.getLineContent(2), 'Another line');
6105 assertCursor(viewModel, new Selection(2, 8, 2, 8));
6106 });
6107 });
6108
6109 test('there is an undo stop between deleting left and deleting right', () => {
6110 let model = createTextModel(
6111 [
6112 'A line',
6113 'Another line',
6114 ].join('\n')
6115 );
6116
6117 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6118 viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]);
6119 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6120 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6121 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6122 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6123 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6124 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6125 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6126 assert.strictEqual(model.getLineContent(2), ' line');
6127 assertCursor(viewModel, new Selection(2, 1, 2, 1));
6128
6129 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6130 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6131 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6132 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6133 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6134 assert.strictEqual(model.getLineContent(2), '');
6135 assertCursor(viewModel, new Selection(2, 1, 2, 1));
6136
6137 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6138 assert.strictEqual(model.getLineContent(2), ' line');
6139 assertCursor(viewModel, new Selection(2, 1, 2, 1));
6140
6141 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6142 assert.strictEqual(model.getLineContent(2), 'Another line');
6143 assertCursor(viewModel, new Selection(2, 8, 2, 8));
6144 });
6145 });
6146
6147 test('there is an undo stop between deleting right and typing', () => {
6148 let model = createTextModel(
6149 [
6150 'A line',
6151 'Another line',
6152 ].join('\n')
6153 );
6154
6155 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6156 viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]);
6157 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6158 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6159 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6160 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6161 assert.strictEqual(model.getLineContent(2), 'Another ');
6162 assertCursor(viewModel, new Selection(2, 9, 2, 9));
6163
6164 viewModel.type('text', 'keyboard');
6165 assert.strictEqual(model.getLineContent(2), 'Another text');
6166 assertCursor(viewModel, new Selection(2, 13, 2, 13));
6167
6168 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6169 assert.strictEqual(model.getLineContent(2), 'Another ');
6170 assertCursor(viewModel, new Selection(2, 9, 2, 9));
6171
6172 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6173 assert.strictEqual(model.getLineContent(2), 'Another line');
6174 assertCursor(viewModel, new Selection(2, 9, 2, 9));
6175 });
6176 });
6177
6178 test('there is an undo stop between deleting right and deleting left', () => {
6179 let model = createTextModel(
6180 [
6181 'A line',
6182 'Another line',
6183 ].join('\n')
6184 );
6185
6186 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6187 viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]);
6188 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6189 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6190 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6191 CoreEditingCommands.DeleteRight.runEditorCommand(null, editor, null);
6192 assert.strictEqual(model.getLineContent(2), 'Another ');
6193 assertCursor(viewModel, new Selection(2, 9, 2, 9));
6194
6195 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6196 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6197 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6198 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6199 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6200 CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
6201 assert.strictEqual(model.getLineContent(2), 'An');
6202 assertCursor(viewModel, new Selection(2, 3, 2, 3));
6203
6204 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6205 assert.strictEqual(model.getLineContent(2), 'Another ');
6206 assertCursor(viewModel, new Selection(2, 9, 2, 9));
6207
6208 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6209 assert.strictEqual(model.getLineContent(2), 'Another line');
6210 assertCursor(viewModel, new Selection(2, 9, 2, 9));
6211 });
6212 });
6213
6214 test('inserts undo stop when typing space', () => {
6215 let model = createTextModel(
6216 [
6217 'A line',
6218 'Another line',
6219 ].join('\n')
6220 );
6221
6222 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6223 viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
6224 viewModel.type('first and interesting', 'keyboard');
6225 assert.strictEqual(model.getLineContent(1), 'A first and interesting line');
6226 assertCursor(viewModel, new Selection(1, 24, 1, 24));
6227
6228 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6229 assert.strictEqual(model.getLineContent(1), 'A first and line');
6230 assertCursor(viewModel, new Selection(1, 12, 1, 12));
6231
6232 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6233 assert.strictEqual(model.getLineContent(1), 'A first line');
6234 assertCursor(viewModel, new Selection(1, 8, 1, 8));
6235
6236 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6237 assert.strictEqual(model.getLineContent(1), 'A line');
6238 assertCursor(viewModel, new Selection(1, 3, 1, 3));
6239 });
6240 });
6241
6242 test('can undo typing and EOL change in one undo stop', () => {
6243 let model = createTextModel(
6244 [
6245 'A line',
6246 'Another line',
6247 ].join('\n')
6248 );
6249
6250 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6251 viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
6252 viewModel.type('first', 'keyboard');
6253 assert.strictEqual(model.getValue(), 'A first line\nAnother line');
6254 assertCursor(viewModel, new Selection(1, 8, 1, 8));
6255
6256 model.pushEOL(EndOfLineSequence.CRLF);
6257 assert.strictEqual(model.getValue(), 'A first line\r\nAnother line');
6258 assertCursor(viewModel, new Selection(1, 8, 1, 8));
6259
6260 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6261 assert.strictEqual(model.getValue(), 'A line\nAnother line');
6262 assertCursor(viewModel, new Selection(1, 3, 1, 3));
6263 });
6264 });
6265
6266 test('issue #93585: Undo multi cursor edit corrupts document', () => {
6267 let model = createTextModel(
6268 [
6269 'hello world',
6270 'hello world',
6271 ].join('\n')
6272 );
6273
6274 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6275 viewModel.setSelections('test', [
6276 new Selection(2, 7, 2, 12),
6277 new Selection(1, 7, 1, 12),
6278 ]);
6279 viewModel.type('no', 'keyboard');
6280 assert.strictEqual(model.getValue(), 'hello no\nhello no');
6281
6282 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6283 assert.strictEqual(model.getValue(), 'hello world\nhello world');
6284 });
6285 });
6286
6287 test('there is a single undo stop for consecutive whitespaces', () => {
6288 let model = createTextModel(
6289 [
6290 ''
6291 ].join('\n'),
6292 {
6293 insertSpaces: false,
6294 }
6295 );
6296
6297 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6298 viewModel.type('a', 'keyboard');
6299 viewModel.type('b', 'keyboard');
6300 viewModel.type(' ', 'keyboard');
6301 viewModel.type(' ', 'keyboard');
6302 viewModel.type('c', 'keyboard');
6303 viewModel.type('d', 'keyboard');
6304
6305 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab cd', 'assert1');
6306
6307 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6308 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab ', 'assert2');
6309
6310 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6311 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab', 'assert3');
6312
6313 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6314 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '', 'assert4');
6315 });
6316 });
6317
6318 test('there is no undo stop after a single whitespace', () => {
6319 let model = createTextModel(
6320 [
6321 ''
6322 ].join('\n'),
6323 {
6324 insertSpaces: false,
6325 }
6326 );
6327
6328 withTestCodeEditor(null, { model: model }, (editor, viewModel) => {
6329 viewModel.type('a', 'keyboard');
6330 viewModel.type('b', 'keyboard');
6331 viewModel.type(' ', 'keyboard');
6332 viewModel.type('c', 'keyboard');
6333 viewModel.type('d', 'keyboard');
6334
6335 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab cd', 'assert1');
6336
6337 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6338 assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab', 'assert3');
6339
6340 CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
6341 assert.strictEqual(model.getValue(EndOfLinePreference.LF), '', 'assert4');
6342 });
6343 });
6344});
6345