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