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
5237 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 #256039: paste from multiple cursors with empty selections and multiCursorPaste full', () => {
2243
usingCursor({
2244
text: [
2245
'line1',
2246
'line2',
2247
'line3'
2248
],
2249
editorOpts: {
2250
multiCursorPaste: 'full'
2251
}
2252
}, (editor, model, viewModel) => {
2253
// 2 cursors on lines 1 and 2
2254
viewModel.setSelections('test', [new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]);
2255
2256
viewModel.paste(
2257
'line1\nline2\n',
2258
true,
2259
['line1\n', 'line2\n']
2260
);
2261
2262
// Each cursor gets its respective line
2263
assert.strictEqual(model.getValue(), [
2264
'line1',
2265
'line1',
2266
'line2',
2267
'line2',
2268
'line3'
2269
].join('\n'));
2270
});
2271
});
2272
2273
test('issue #3071: Investigate why undo stack gets corrupted', () => {
2274
const model = createTextModel(
2275
[
2276
'some lines',
2277
'and more lines',
2278
'just some text',
2279
].join('\n')
2280
);
2281
2282
withTestCodeEditor(model, {}, (editor, viewModel) => {
2283
moveTo(editor, viewModel, 1, 1, false);
2284
moveTo(editor, viewModel, 3, 4, true);
2285
2286
let isFirst = true;
2287
const disposable = model.onDidChangeContent(() => {
2288
if (isFirst) {
2289
isFirst = false;
2290
viewModel.type('\t', 'keyboard');
2291
}
2292
});
2293
2294
editor.runCommand(CoreEditingCommands.Tab, null);
2295
assert.strictEqual(model.getValue(), [
2296
'\t just some text'
2297
].join('\n'), '001');
2298
2299
editor.runCommand(CoreEditingCommands.Undo, null);
2300
assert.strictEqual(model.getValue(), [
2301
' some lines',
2302
' and more lines',
2303
' just some text',
2304
].join('\n'), '002');
2305
2306
editor.runCommand(CoreEditingCommands.Undo, null);
2307
assert.strictEqual(model.getValue(), [
2308
'some lines',
2309
'and more lines',
2310
'just some text',
2311
].join('\n'), '003');
2312
2313
editor.runCommand(CoreEditingCommands.Undo, null);
2314
assert.strictEqual(model.getValue(), [
2315
'some lines',
2316
'and more lines',
2317
'just some text',
2318
].join('\n'), '004');
2319
2320
disposable.dispose();
2321
});
2322
});
2323
2324
test('issue #12950: Cannot Double Click To Insert Emoji Using OSX Emoji Panel', () => {
2325
usingCursor({
2326
text: [
2327
'some lines',
2328
'and more lines',
2329
'just some text',
2330
],
2331
languageId: null
2332
}, (editor, model, viewModel) => {
2333
moveTo(editor, viewModel, 3, 1, false);
2334
2335
viewModel.type('😍', 'keyboard');
2336
2337
assert.strictEqual(model.getValue(), [
2338
'some lines',
2339
'and more lines',
2340
'😍just some text',
2341
].join('\n'));
2342
});
2343
});
2344
2345
test('issue #3463: pressing tab adds spaces, but not as many as for a tab', () => {
2346
const model = createTextModel(
2347
[
2348
'function a() {',
2349
'\tvar a = {',
2350
'\t\tx: 3',
2351
'\t};',
2352
'}',
2353
].join('\n')
2354
);
2355
2356
withTestCodeEditor(model, {}, (editor, viewModel) => {
2357
moveTo(editor, viewModel, 3, 2, false);
2358
editor.runCommand(CoreEditingCommands.Tab, null);
2359
assert.strictEqual(model.getLineContent(3), '\t \tx: 3');
2360
});
2361
});
2362
2363
test('issue #4312: trying to type a tab character over a sequence of spaces results in unexpected behaviour', () => {
2364
const model = createTextModel(
2365
[
2366
'var foo = 123; // this is a comment',
2367
'var bar = 4; // another comment'
2368
].join('\n'),
2369
undefined,
2370
{
2371
insertSpaces: false,
2372
}
2373
);
2374
2375
withTestCodeEditor(model, {}, (editor, viewModel) => {
2376
moveTo(editor, viewModel, 1, 15, false);
2377
moveTo(editor, viewModel, 1, 22, true);
2378
editor.runCommand(CoreEditingCommands.Tab, null);
2379
assert.strictEqual(model.getLineContent(1), 'var foo = 123;\t// this is a comment');
2380
});
2381
});
2382
2383
test('issue #832: word right', () => {
2384
2385
usingCursor({
2386
text: [
2387
' /* Just some more text a+= 3 +5-3 + 7 */ '
2388
],
2389
}, (editor, model, viewModel) => {
2390
moveTo(editor, viewModel, 1, 1, false);
2391
2392
function assertWordRight(col: number, expectedCol: number) {
2393
const args = {
2394
position: {
2395
lineNumber: 1,
2396
column: col
2397
}
2398
};
2399
if (col === 1) {
2400
CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, args);
2401
} else {
2402
CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, args);
2403
}
2404
2405
assert.strictEqual(viewModel.getSelection().startColumn, 1, 'TEST FOR ' + col);
2406
assert.strictEqual(viewModel.getSelection().endColumn, expectedCol, 'TEST FOR ' + col);
2407
}
2408
2409
assertWordRight(1, ' '.length + 1);
2410
assertWordRight(2, ' '.length + 1);
2411
assertWordRight(3, ' '.length + 1);
2412
assertWordRight(4, ' '.length + 1);
2413
assertWordRight(5, ' /'.length + 1);
2414
assertWordRight(6, ' /*'.length + 1);
2415
assertWordRight(7, ' /* '.length + 1);
2416
assertWordRight(8, ' /* Just'.length + 1);
2417
assertWordRight(9, ' /* Just'.length + 1);
2418
assertWordRight(10, ' /* Just'.length + 1);
2419
assertWordRight(11, ' /* Just'.length + 1);
2420
assertWordRight(12, ' /* Just '.length + 1);
2421
assertWordRight(13, ' /* Just some'.length + 1);
2422
assertWordRight(14, ' /* Just some'.length + 1);
2423
assertWordRight(15, ' /* Just some'.length + 1);
2424
assertWordRight(16, ' /* Just some'.length + 1);
2425
assertWordRight(17, ' /* Just some '.length + 1);
2426
assertWordRight(18, ' /* Just some '.length + 1);
2427
assertWordRight(19, ' /* Just some '.length + 1);
2428
assertWordRight(20, ' /* Just some more'.length + 1);
2429
assertWordRight(21, ' /* Just some more'.length + 1);
2430
assertWordRight(22, ' /* Just some more'.length + 1);
2431
assertWordRight(23, ' /* Just some more'.length + 1);
2432
assertWordRight(24, ' /* Just some more '.length + 1);
2433
assertWordRight(25, ' /* Just some more '.length + 1);
2434
assertWordRight(26, ' /* Just some more '.length + 1);
2435
assertWordRight(27, ' /* Just some more text'.length + 1);
2436
assertWordRight(28, ' /* Just some more text'.length + 1);
2437
assertWordRight(29, ' /* Just some more text'.length + 1);
2438
assertWordRight(30, ' /* Just some more text'.length + 1);
2439
assertWordRight(31, ' /* Just some more text '.length + 1);
2440
assertWordRight(32, ' /* Just some more text a'.length + 1);
2441
assertWordRight(33, ' /* Just some more text a+'.length + 1);
2442
assertWordRight(34, ' /* Just some more text a+='.length + 1);
2443
assertWordRight(35, ' /* Just some more text a+= '.length + 1);
2444
assertWordRight(36, ' /* Just some more text a+= 3'.length + 1);
2445
assertWordRight(37, ' /* Just some more text a+= 3 '.length + 1);
2446
assertWordRight(38, ' /* Just some more text a+= 3 +'.length + 1);
2447
assertWordRight(39, ' /* Just some more text a+= 3 +5'.length + 1);
2448
assertWordRight(40, ' /* Just some more text a+= 3 +5-'.length + 1);
2449
assertWordRight(41, ' /* Just some more text a+= 3 +5-3'.length + 1);
2450
assertWordRight(42, ' /* Just some more text a+= 3 +5-3 '.length + 1);
2451
assertWordRight(43, ' /* Just some more text a+= 3 +5-3 +'.length + 1);
2452
assertWordRight(44, ' /* Just some more text a+= 3 +5-3 + '.length + 1);
2453
assertWordRight(45, ' /* Just some more text a+= 3 +5-3 + 7'.length + 1);
2454
assertWordRight(46, ' /* Just some more text a+= 3 +5-3 + 7 '.length + 1);
2455
assertWordRight(47, ' /* Just some more text a+= 3 +5-3 + 7 *'.length + 1);
2456
assertWordRight(48, ' /* Just some more text a+= 3 +5-3 + 7 */'.length + 1);
2457
assertWordRight(49, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1);
2458
assertWordRight(50, ' /* Just some more text a+= 3 +5-3 + 7 */ '.length + 1);
2459
});
2460
});
2461
2462
test('issue #33788: Wrong cursor position when double click to select a word', () => {
2463
const model = createTextModel(
2464
[
2465
'Just some text'
2466
].join('\n')
2467
);
2468
2469
withTestCodeEditor(model, {}, (editor, viewModel) => {
2470
CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 8) });
2471
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10));
2472
2473
CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(viewModel, { position: new Position(1, 8) });
2474
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 6, 1, 10));
2475
});
2476
});
2477
2478
test('issue #12887: Double-click highlighting separating white space', () => {
2479
const model = createTextModel(
2480
[
2481
'abc def'
2482
].join('\n')
2483
);
2484
2485
withTestCodeEditor(model, {}, (editor, viewModel) => {
2486
CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 5) });
2487
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 5, 1, 8));
2488
});
2489
});
2490
2491
test('Double-click on punctuation should select the character, not adjacent space', () => {
2492
const model = createTextModel(
2493
[
2494
'// a b c 1 2 3 ~ ! @ # $ % ^ & * ( ) _ + \\ /'
2495
].join('\n')
2496
);
2497
2498
withTestCodeEditor(model, {}, (editor, viewModel) => {
2499
// Test double-click on '@' at position 20
2500
CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 20) });
2501
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 20, 1, 21), 'Should select @ character');
2502
2503
// Test double-click on '#' at position 22
2504
CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 22) });
2505
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 22, 1, 23), 'Should select # character');
2506
2507
// Test double-click on '!' at position 18
2508
CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 18) });
2509
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 18, 1, 19), 'Should select ! character');
2510
2511
// Test double-click on first '/' in '//' at position 1
2512
CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 1) });
2513
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 1, 1, 3), 'Should select // token');
2514
2515
// Test double-click on second '/' in '//' at position 2
2516
CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 2) });
2517
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 1, 1, 3), 'Should select // token');
2518
2519
// Test double-click on '\' at position 42
2520
CoreNavigationCommands.WordSelect.runCoreEditorCommand(viewModel, { position: new Position(1, 42) });
2521
assert.deepStrictEqual(viewModel.getSelection(), new Selection(1, 42, 1, 43), 'Should select \\ character');
2522
});
2523
});
2524
2525
test('issue #9675: Undo/Redo adds a stop in between CHN Characters', () => {
2526
withTestCodeEditor([], {}, (editor, viewModel) => {
2527
const model = editor.getModel()!;
2528
assertCursor(viewModel, new Position(1, 1));
2529
2530
// Typing sennsei in Japanese - Hiragana
2531
viewModel.type('s', 'keyboard');
2532
viewModel.compositionType('せ', 1, 0, 0);
2533
viewModel.compositionType('せn', 1, 0, 0);
2534
viewModel.compositionType('せん', 2, 0, 0);
2535
viewModel.compositionType('せんs', 2, 0, 0);
2536
viewModel.compositionType('せんせ', 3, 0, 0);
2537
viewModel.compositionType('せんせ', 3, 0, 0);
2538
viewModel.compositionType('せんせい', 3, 0, 0);
2539
viewModel.compositionType('せんせい', 4, 0, 0);
2540
viewModel.compositionType('せんせい', 4, 0, 0);
2541
viewModel.compositionType('せんせい', 4, 0, 0);
2542
2543
assert.strictEqual(model.getLineContent(1), 'せんせい');
2544
assertCursor(viewModel, new Position(1, 5));
2545
2546
editor.runCommand(CoreEditingCommands.Undo, null);
2547
assert.strictEqual(model.getLineContent(1), '');
2548
assertCursor(viewModel, new Position(1, 1));
2549
});
2550
});
2551
2552
test('issue #23983: Calling model.setEOL does not reset cursor position', () => {
2553
usingCursor({
2554
text: [
2555
'first line',
2556
'second line'
2557
]
2558
}, (editor, model, viewModel) => {
2559
model.setEOL(EndOfLineSequence.CRLF);
2560
2561
viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]);
2562
model.setEOL(EndOfLineSequence.LF);
2563
2564
assertCursor(viewModel, new Selection(2, 2, 2, 2));
2565
});
2566
});
2567
2568
test('issue #23983: Calling model.setValue() resets cursor position', () => {
2569
usingCursor({
2570
text: [
2571
'first line',
2572
'second line'
2573
]
2574
}, (editor, model, viewModel) => {
2575
model.setEOL(EndOfLineSequence.CRLF);
2576
2577
viewModel.setSelections('test', [new Selection(2, 2, 2, 2)]);
2578
model.setValue([
2579
'different first line',
2580
'different second line',
2581
'new third line'
2582
].join('\n'));
2583
2584
assertCursor(viewModel, new Selection(1, 1, 1, 1));
2585
});
2586
});
2587
2588
test('issue #36740: wordwrap creates an extra step / character at the wrapping point', () => {
2589
// a single model line => 4 view lines
2590
withTestCodeEditor([
2591
[
2592
'Lorem ipsum ',
2593
'dolor sit amet ',
2594
'consectetur ',
2595
'adipiscing elit',
2596
].join('')
2597
], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => {
2598
viewModel.setSelections('test', [new Selection(1, 7, 1, 7)]);
2599
2600
moveRight(editor, viewModel);
2601
assertCursor(viewModel, new Selection(1, 8, 1, 8));
2602
2603
moveRight(editor, viewModel);
2604
assertCursor(viewModel, new Selection(1, 9, 1, 9));
2605
2606
moveRight(editor, viewModel);
2607
assertCursor(viewModel, new Selection(1, 10, 1, 10));
2608
2609
moveRight(editor, viewModel);
2610
assertCursor(viewModel, new Selection(1, 11, 1, 11));
2611
2612
moveRight(editor, viewModel);
2613
assertCursor(viewModel, new Selection(1, 12, 1, 12));
2614
2615
moveRight(editor, viewModel);
2616
assertCursor(viewModel, new Selection(1, 13, 1, 13));
2617
2618
// moving to view line 2
2619
moveRight(editor, viewModel);
2620
assertCursor(viewModel, new Selection(1, 14, 1, 14));
2621
2622
moveLeft(editor, viewModel);
2623
assertCursor(viewModel, new Selection(1, 13, 1, 13));
2624
2625
// moving back to view line 1
2626
moveLeft(editor, viewModel);
2627
assertCursor(viewModel, new Selection(1, 12, 1, 12));
2628
});
2629
});
2630
2631
test('issue #110376: multiple selections with wordwrap behave differently', () => {
2632
// a single model line => 4 view lines
2633
withTestCodeEditor([
2634
[
2635
'just a sentence. just a ',
2636
'sentence. just a sentence.',
2637
].join('')
2638
], { wordWrap: 'wordWrapColumn', wordWrapColumn: 25 }, (editor, viewModel) => {
2639
viewModel.setSelections('test', [
2640
new Selection(1, 1, 1, 16),
2641
new Selection(1, 18, 1, 33),
2642
new Selection(1, 35, 1, 50),
2643
]);
2644
2645
moveLeft(editor, viewModel);
2646
assertCursor(viewModel, [
2647
new Selection(1, 1, 1, 1),
2648
new Selection(1, 18, 1, 18),
2649
new Selection(1, 35, 1, 35),
2650
]);
2651
2652
viewModel.setSelections('test', [
2653
new Selection(1, 1, 1, 16),
2654
new Selection(1, 18, 1, 33),
2655
new Selection(1, 35, 1, 50),
2656
]);
2657
2658
moveRight(editor, viewModel);
2659
assertCursor(viewModel, [
2660
new Selection(1, 16, 1, 16),
2661
new Selection(1, 33, 1, 33),
2662
new Selection(1, 50, 1, 50),
2663
]);
2664
});
2665
});
2666
2667
test('issue #98320: Multi-Cursor, Wrap lines and cursorSelectRight ==> cursors out of sync', () => {
2668
// a single model line => 4 view lines
2669
withTestCodeEditor([
2670
[
2671
'lorem_ipsum-1993x11x13',
2672
'dolor_sit_amet-1998x04x27',
2673
'consectetur-2007x10x08',
2674
'adipiscing-2012x07x27',
2675
'elit-2015x02x27',
2676
].join('\n')
2677
], { wordWrap: 'wordWrapColumn', wordWrapColumn: 16 }, (editor, viewModel) => {
2678
viewModel.setSelections('test', [
2679
new Selection(1, 13, 1, 13),
2680
new Selection(2, 16, 2, 16),
2681
new Selection(3, 13, 3, 13),
2682
new Selection(4, 12, 4, 12),
2683
new Selection(5, 6, 5, 6),
2684
]);
2685
assertCursor(viewModel, [
2686
new Selection(1, 13, 1, 13),
2687
new Selection(2, 16, 2, 16),
2688
new Selection(3, 13, 3, 13),
2689
new Selection(4, 12, 4, 12),
2690
new Selection(5, 6, 5, 6),
2691
]);
2692
2693
moveRight(editor, viewModel, true);
2694
assertCursor(viewModel, [
2695
new Selection(1, 13, 1, 14),
2696
new Selection(2, 16, 2, 17),
2697
new Selection(3, 13, 3, 14),
2698
new Selection(4, 12, 4, 13),
2699
new Selection(5, 6, 5, 7),
2700
]);
2701
2702
moveRight(editor, viewModel, true);
2703
assertCursor(viewModel, [
2704
new Selection(1, 13, 1, 15),
2705
new Selection(2, 16, 2, 18),
2706
new Selection(3, 13, 3, 15),
2707
new Selection(4, 12, 4, 14),
2708
new Selection(5, 6, 5, 8),
2709
]);
2710
2711
moveRight(editor, viewModel, true);
2712
assertCursor(viewModel, [
2713
new Selection(1, 13, 1, 16),
2714
new Selection(2, 16, 2, 19),
2715
new Selection(3, 13, 3, 16),
2716
new Selection(4, 12, 4, 15),
2717
new Selection(5, 6, 5, 9),
2718
]);
2719
2720
moveRight(editor, viewModel, true);
2721
assertCursor(viewModel, [
2722
new Selection(1, 13, 1, 17),
2723
new Selection(2, 16, 2, 20),
2724
new Selection(3, 13, 3, 17),
2725
new Selection(4, 12, 4, 16),
2726
new Selection(5, 6, 5, 10),
2727
]);
2728
});
2729
});
2730
2731
test('issue #41573 - delete across multiple lines does not shrink the selection when word wraps', () => {
2732
withTestCodeEditor([
2733
'Authorization: \'Bearer pHKRfCTFSnGxs6akKlb9ddIXcca0sIUSZJutPHYqz7vEeHdMTMh0SGN0IGU3a0n59DXjTLRsj5EJ2u33qLNIFi9fk5XF8pK39PndLYUZhPt4QvHGLScgSkK0L4gwzkzMloTQPpKhqiikiIOvyNNSpd2o8j29NnOmdTUOKi9DVt74PD2ohKxyOrWZ6oZprTkb3eKajcpnS0LABKfaw2rmv4\','
2734
].join('\n'), { wordWrap: 'wordWrapColumn', wordWrapColumn: 100 }, (editor, viewModel) => {
2735
moveTo(editor, viewModel, 1, 43, false);
2736
moveTo(editor, viewModel, 1, 147, true);
2737
assertCursor(viewModel, new Selection(1, 43, 1, 147));
2738
2739
editor.getModel().applyEdits([{
2740
range: new Range(1, 1, 1, 43),
2741
text: ''
2742
}]);
2743
2744
assertCursor(viewModel, new Selection(1, 1, 1, 105));
2745
});
2746
});
2747
2748
test('issue #22717: Moving text cursor cause an incorrect position in Chinese', () => {
2749
// a single model line => 4 view lines
2750
withTestCodeEditor([
2751
[
2752
'一二三四五六七八九十',
2753
'12345678901234567890',
2754
].join('\n')
2755
], {}, (editor, viewModel) => {
2756
viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
2757
2758
moveDown(editor, viewModel);
2759
assertCursor(viewModel, new Selection(2, 9, 2, 9));
2760
2761
moveRight(editor, viewModel);
2762
assertCursor(viewModel, new Selection(2, 10, 2, 10));
2763
2764
moveRight(editor, viewModel);
2765
assertCursor(viewModel, new Selection(2, 11, 2, 11));
2766
2767
moveUp(editor, viewModel);
2768
assertCursor(viewModel, new Selection(1, 6, 1, 6));
2769
});
2770
});
2771
2772
test('issue #112301: new stickyTabStops feature interferes with word wrap', () => {
2773
withTestCodeEditor([
2774
[
2775
'function hello() {',
2776
' console.log(`this is a long console message`)',
2777
'}',
2778
].join('\n')
2779
], { wordWrap: 'wordWrapColumn', wordWrapColumn: 32, stickyTabStops: true }, (editor, viewModel) => {
2780
viewModel.setSelections('test', [
2781
new Selection(2, 31, 2, 31)
2782
]);
2783
moveRight(editor, viewModel, false);
2784
assertCursor(viewModel, new Position(2, 32));
2785
2786
moveRight(editor, viewModel, false);
2787
assertCursor(viewModel, new Position(2, 33));
2788
2789
moveRight(editor, viewModel, false);
2790
assertCursor(viewModel, new Position(2, 34));
2791
2792
moveLeft(editor, viewModel, false);
2793
assertCursor(viewModel, new Position(2, 33));
2794
2795
moveLeft(editor, viewModel, false);
2796
assertCursor(viewModel, new Position(2, 32));
2797
2798
moveLeft(editor, viewModel, false);
2799
assertCursor(viewModel, new Position(2, 31));
2800
});
2801
});
2802
2803
test('issue #44805: Should not be able to undo in readonly editor', () => {
2804
const model = createTextModel(
2805
[
2806
''
2807
].join('\n')
2808
);
2809
2810
withTestCodeEditor(model, { readOnly: true }, (editor, viewModel) => {
2811
model.pushEditOperations([new Selection(1, 1, 1, 1)], [{
2812
range: new Range(1, 1, 1, 1),
2813
text: 'Hello world!'
2814
}], () => [new Selection(1, 1, 1, 1)]);
2815
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'Hello world!');
2816
2817
editor.runCommand(CoreEditingCommands.Undo, null);
2818
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'Hello world!');
2819
});
2820
});
2821
2822
test('issue #46314: ViewModel is out of sync with Model!', () => {
2823
2824
const tokenizationSupport: ITokenizationSupport = {
2825
getInitialState: () => NullState,
2826
tokenize: undefined!,
2827
tokenizeEncoded: (line: string, hasEOL: boolean, state: IState): EncodedTokenizationResult => {
2828
return new EncodedTokenizationResult(new Uint32Array(0), [], state);
2829
}
2830
};
2831
2832
const LANGUAGE_ID = 'modelModeTest1';
2833
const languageRegistration = TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
2834
const model = createTextModel('Just text', LANGUAGE_ID);
2835
2836
withTestCodeEditor(model, {}, (editor1, cursor1) => {
2837
withTestCodeEditor(model, {}, (editor2, cursor2) => {
2838
2839
const disposable = editor1.onDidChangeCursorPosition(() => {
2840
model.tokenization.tokenizeIfCheap(1);
2841
});
2842
2843
model.applyEdits([{ range: new Range(1, 1, 1, 1), text: '-' }]);
2844
2845
disposable.dispose();
2846
});
2847
});
2848
2849
languageRegistration.dispose();
2850
model.dispose();
2851
});
2852
2853
test('issue #37967: problem replacing consecutive characters', () => {
2854
const model = createTextModel(
2855
[
2856
'const a = "foo";',
2857
'const b = ""'
2858
].join('\n')
2859
);
2860
2861
withTestCodeEditor(model, { multiCursorMergeOverlapping: false }, (editor, viewModel) => {
2862
editor.setSelections([
2863
new Selection(1, 12, 1, 12),
2864
new Selection(1, 16, 1, 16),
2865
new Selection(2, 12, 2, 12),
2866
new Selection(2, 13, 2, 13),
2867
]);
2868
2869
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
2870
2871
assertCursor(viewModel, [
2872
new Selection(1, 11, 1, 11),
2873
new Selection(1, 14, 1, 14),
2874
new Selection(2, 11, 2, 11),
2875
new Selection(2, 11, 2, 11),
2876
]);
2877
2878
viewModel.type('\'', 'keyboard');
2879
2880
assert.strictEqual(model.getLineContent(1), 'const a = \'foo\';');
2881
assert.strictEqual(model.getLineContent(2), 'const b = \'\'');
2882
});
2883
});
2884
2885
test('issue #15761: Cursor doesn\'t move in a redo operation', () => {
2886
const model = createTextModel(
2887
[
2888
'hello'
2889
].join('\n')
2890
);
2891
2892
withTestCodeEditor(model, {}, (editor, viewModel) => {
2893
editor.setSelections([
2894
new Selection(1, 4, 1, 4)
2895
]);
2896
2897
editor.executeEdits('test', [{
2898
range: new Range(1, 1, 1, 1),
2899
text: '*',
2900
forceMoveMarkers: true
2901
}]);
2902
assertCursor(viewModel, [
2903
new Selection(1, 5, 1, 5),
2904
]);
2905
2906
editor.runCommand(CoreEditingCommands.Undo, null);
2907
assertCursor(viewModel, [
2908
new Selection(1, 4, 1, 4),
2909
]);
2910
2911
editor.runCommand(CoreEditingCommands.Redo, null);
2912
assertCursor(viewModel, [
2913
new Selection(1, 5, 1, 5),
2914
]);
2915
});
2916
});
2917
2918
test('issue #42783: API Calls with Undo Leave Cursor in Wrong Position', () => {
2919
const model = createTextModel(
2920
[
2921
'ab'
2922
].join('\n')
2923
);
2924
2925
withTestCodeEditor(model, {}, (editor, viewModel) => {
2926
editor.setSelections([
2927
new Selection(1, 1, 1, 1)
2928
]);
2929
2930
editor.executeEdits('test', [{
2931
range: new Range(1, 1, 1, 3),
2932
text: ''
2933
}]);
2934
assertCursor(viewModel, [
2935
new Selection(1, 1, 1, 1),
2936
]);
2937
2938
editor.runCommand(CoreEditingCommands.Undo, null);
2939
assertCursor(viewModel, [
2940
new Selection(1, 1, 1, 1),
2941
]);
2942
2943
editor.executeEdits('test', [{
2944
range: new Range(1, 1, 1, 2),
2945
text: ''
2946
}]);
2947
assertCursor(viewModel, [
2948
new Selection(1, 1, 1, 1),
2949
]);
2950
});
2951
});
2952
2953
test('issue #85712: Paste line moves cursor to start of current line rather than start of next line', () => {
2954
const model = createTextModel(
2955
[
2956
'abc123',
2957
''
2958
].join('\n')
2959
);
2960
2961
withTestCodeEditor(model, {}, (editor, viewModel) => {
2962
editor.setSelections([
2963
new Selection(2, 1, 2, 1)
2964
]);
2965
viewModel.paste('something\n', true);
2966
assert.strictEqual(model.getValue(), [
2967
'abc123',
2968
'something',
2969
''
2970
].join('\n'));
2971
assertCursor(viewModel, new Position(3, 1));
2972
});
2973
});
2974
2975
test('issue #84897: Left delete behavior in some languages is changed', () => {
2976
const model = createTextModel(
2977
[
2978
'สวัสดี'
2979
].join('\n')
2980
);
2981
2982
withTestCodeEditor(model, {}, (editor, viewModel) => {
2983
editor.setSelections([
2984
new Selection(1, 7, 1, 7)
2985
]);
2986
2987
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
2988
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัสด');
2989
2990
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
2991
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัส');
2992
2993
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
2994
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวั');
2995
2996
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
2997
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สว');
2998
2999
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3000
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ส');
3001
3002
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3003
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');
3004
});
3005
});
3006
3007
test('issue #122914: Left delete behavior in some languages is changed (useTabStops: false)', () => {
3008
const model = createTextModel(
3009
[
3010
'สวัสดี'
3011
].join('\n')
3012
);
3013
3014
withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {
3015
editor.setSelections([
3016
new Selection(1, 7, 1, 7)
3017
]);
3018
3019
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3020
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัสด');
3021
3022
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3023
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวัส');
3024
3025
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3026
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สวั');
3027
3028
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3029
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'สว');
3030
3031
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3032
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ส');
3033
3034
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3035
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');
3036
});
3037
});
3038
3039
test('issue #99629: Emoji modifiers in text treated separately when using backspace', () => {
3040
const model = createTextModel(
3041
[
3042
'👶🏾'
3043
].join('\n')
3044
);
3045
3046
withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {
3047
const len = model.getValueLength();
3048
editor.setSelections([
3049
new Selection(1, 1 + len, 1, 1 + len)
3050
]);
3051
3052
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3053
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');
3054
});
3055
});
3056
3057
test('issue #99629: Emoji modifiers in text treated separately when using backspace (ZWJ sequence)', () => {
3058
const model = createTextModel(
3059
[
3060
'👨‍👩🏽‍👧‍👦'
3061
].join('\n')
3062
);
3063
3064
withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {
3065
const len = model.getValueLength();
3066
editor.setSelections([
3067
new Selection(1, 1 + len, 1, 1 + len)
3068
]);
3069
3070
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3071
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨‍👩🏽‍👧');
3072
3073
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3074
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨‍👩🏽');
3075
3076
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3077
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '👨');
3078
3079
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3080
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '');
3081
});
3082
});
3083
3084
test('issue #105730: move left behaves differently for multiple cursors', () => {
3085
const model = createTextModel('asdfghjkl, asdfghjkl, asdfghjkl, ');
3086
3087
withTestCodeEditor(
3088
model,
3089
{
3090
wordWrap: 'wordWrapColumn',
3091
wordWrapColumn: 24
3092
},
3093
(editor, viewModel) => {
3094
viewModel.setSelections('test', [
3095
new Selection(1, 10, 1, 12),
3096
new Selection(1, 21, 1, 23),
3097
new Selection(1, 32, 1, 34)
3098
]);
3099
moveLeft(editor, viewModel, false);
3100
assertCursor(viewModel, [
3101
new Selection(1, 10, 1, 10),
3102
new Selection(1, 21, 1, 21),
3103
new Selection(1, 32, 1, 32)
3104
]);
3105
3106
viewModel.setSelections('test', [
3107
new Selection(1, 10, 1, 12),
3108
new Selection(1, 21, 1, 23),
3109
new Selection(1, 32, 1, 34)
3110
]);
3111
moveLeft(editor, viewModel, true);
3112
assertCursor(viewModel, [
3113
new Selection(1, 10, 1, 11),
3114
new Selection(1, 21, 1, 22),
3115
new Selection(1, 32, 1, 33)
3116
]);
3117
});
3118
});
3119
3120
test('issue #105730: move right should always skip wrap point', () => {
3121
const model = createTextModel('asdfghjkl, asdfghjkl, asdfghjkl, \nasdfghjkl,');
3122
3123
withTestCodeEditor(
3124
model,
3125
{
3126
wordWrap: 'wordWrapColumn',
3127
wordWrapColumn: 24
3128
},
3129
(editor, viewModel) => {
3130
viewModel.setSelections('test', [
3131
new Selection(1, 22, 1, 22)
3132
]);
3133
moveRight(editor, viewModel, false);
3134
moveRight(editor, viewModel, false);
3135
assertCursor(viewModel, [
3136
new Selection(1, 24, 1, 24),
3137
]);
3138
3139
viewModel.setSelections('test', [
3140
new Selection(1, 22, 1, 22)
3141
]);
3142
moveRight(editor, viewModel, true);
3143
moveRight(editor, viewModel, true);
3144
assertCursor(viewModel, [
3145
new Selection(1, 22, 1, 24),
3146
]);
3147
}
3148
);
3149
});
3150
3151
test('issue #123178: sticky tab in consecutive wrapped lines', () => {
3152
const model = createTextModel(' aaaa aaaa', undefined, { tabSize: 4 });
3153
3154
withTestCodeEditor(
3155
model,
3156
{
3157
wordWrap: 'wordWrapColumn',
3158
wordWrapColumn: 8,
3159
stickyTabStops: true,
3160
},
3161
(editor, viewModel) => {
3162
viewModel.setSelections('test', [
3163
new Selection(1, 9, 1, 9)
3164
]);
3165
moveRight(editor, viewModel, false);
3166
assertCursor(viewModel, [
3167
new Selection(1, 10, 1, 10),
3168
]);
3169
3170
moveLeft(editor, viewModel, false);
3171
assertCursor(viewModel, [
3172
new Selection(1, 9, 1, 9),
3173
]);
3174
}
3175
);
3176
});
3177
3178
test('Cursor honors insertSpaces configuration on new line', () => {
3179
usingCursor({
3180
text: [
3181
' \tMy First Line\t ',
3182
'\tMy Second Line',
3183
' Third Line',
3184
'',
3185
'1'
3186
]
3187
}, (editor, model, viewModel) => {
3188
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(1, 21), source: 'keyboard' });
3189
viewModel.type('\n', 'keyboard');
3190
assert.strictEqual(model.getLineContent(1), ' \tMy First Line\t ');
3191
assert.strictEqual(model.getLineContent(2), ' ');
3192
});
3193
});
3194
3195
test('Cursor honors insertSpaces configuration on tab', () => {
3196
const model = createTextModel(
3197
[
3198
' \tMy First Line\t ',
3199
'My Second Line123',
3200
' Third Line',
3201
'',
3202
'1'
3203
].join('\n'),
3204
undefined,
3205
{
3206
tabSize: 13,
3207
indentSize: 13,
3208
}
3209
);
3210
3211
withTestCodeEditor(model, {}, (editor, viewModel) => {
3212
// Tab on column 1
3213
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 1) });
3214
editor.runCommand(CoreEditingCommands.Tab, null);
3215
assert.strictEqual(model.getLineContent(2), ' My Second Line123');
3216
editor.runCommand(CoreEditingCommands.Undo, null);
3217
3218
// Tab on column 2
3219
assert.strictEqual(model.getLineContent(2), 'My Second Line123');
3220
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 2) });
3221
editor.runCommand(CoreEditingCommands.Tab, null);
3222
assert.strictEqual(model.getLineContent(2), 'M y Second Line123');
3223
editor.runCommand(CoreEditingCommands.Undo, null);
3224
3225
// Tab on column 3
3226
assert.strictEqual(model.getLineContent(2), 'My Second Line123');
3227
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 3) });
3228
editor.runCommand(CoreEditingCommands.Tab, null);
3229
assert.strictEqual(model.getLineContent(2), 'My Second Line123');
3230
editor.runCommand(CoreEditingCommands.Undo, null);
3231
3232
// Tab on column 4
3233
assert.strictEqual(model.getLineContent(2), 'My Second Line123');
3234
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 4) });
3235
editor.runCommand(CoreEditingCommands.Tab, null);
3236
assert.strictEqual(model.getLineContent(2), 'My Second Line123');
3237
editor.runCommand(CoreEditingCommands.Undo, null);
3238
3239
// Tab on column 5
3240
assert.strictEqual(model.getLineContent(2), 'My Second Line123');
3241
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) });
3242
editor.runCommand(CoreEditingCommands.Tab, null);
3243
assert.strictEqual(model.getLineContent(2), 'My S econd Line123');
3244
editor.runCommand(CoreEditingCommands.Undo, null);
3245
3246
// Tab on column 5
3247
assert.strictEqual(model.getLineContent(2), 'My Second Line123');
3248
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 5) });
3249
editor.runCommand(CoreEditingCommands.Tab, null);
3250
assert.strictEqual(model.getLineContent(2), 'My S econd Line123');
3251
editor.runCommand(CoreEditingCommands.Undo, null);
3252
3253
// Tab on column 13
3254
assert.strictEqual(model.getLineContent(2), 'My Second Line123');
3255
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 13) });
3256
editor.runCommand(CoreEditingCommands.Tab, null);
3257
assert.strictEqual(model.getLineContent(2), 'My Second Li ne123');
3258
editor.runCommand(CoreEditingCommands.Undo, null);
3259
3260
// Tab on column 14
3261
assert.strictEqual(model.getLineContent(2), 'My Second Line123');
3262
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, { position: new Position(2, 14) });
3263
editor.runCommand(CoreEditingCommands.Tab, null);
3264
assert.strictEqual(model.getLineContent(2), 'My Second Lin e123');
3265
});
3266
});
3267
3268
test('Enter auto-indents with insertSpaces setting 1', () => {
3269
const languageId = setupOnEnterLanguage(IndentAction.Indent);
3270
usingCursor({
3271
text: [
3272
'\thello'
3273
],
3274
languageId: languageId
3275
}, (editor, model, viewModel) => {
3276
moveTo(editor, viewModel, 1, 7, false);
3277
assertCursor(viewModel, new Selection(1, 7, 1, 7));
3278
3279
viewModel.type('\n', 'keyboard');
3280
assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n ');
3281
});
3282
});
3283
3284
test('Enter auto-indents with insertSpaces setting 2', () => {
3285
const languageId = setupOnEnterLanguage(IndentAction.None);
3286
usingCursor({
3287
text: [
3288
'\thello'
3289
],
3290
languageId: languageId
3291
}, (editor, model, viewModel) => {
3292
moveTo(editor, viewModel, 1, 7, false);
3293
assertCursor(viewModel, new Selection(1, 7, 1, 7));
3294
3295
viewModel.type('\n', 'keyboard');
3296
assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thello\r\n ');
3297
});
3298
});
3299
3300
test('Enter auto-indents with insertSpaces setting 3', () => {
3301
const languageId = setupOnEnterLanguage(IndentAction.IndentOutdent);
3302
usingCursor({
3303
text: [
3304
'\thell()'
3305
],
3306
languageId: languageId
3307
}, (editor, model, viewModel) => {
3308
moveTo(editor, viewModel, 1, 7, false);
3309
assertCursor(viewModel, new Selection(1, 7, 1, 7));
3310
3311
viewModel.type('\n', 'keyboard');
3312
assert.strictEqual(model.getValue(EndOfLinePreference.CRLF), '\thell(\r\n \r\n )');
3313
});
3314
});
3315
3316
test('issue #148256: Pressing Enter creates line with bad indent with insertSpaces: true', () => {
3317
usingCursor({
3318
text: [
3319
' \t'
3320
],
3321
}, (editor, model, viewModel) => {
3322
moveTo(editor, viewModel, 1, 4, false);
3323
viewModel.type('\n', 'keyboard');
3324
assert.strictEqual(model.getValue(), ' \t\n ');
3325
});
3326
});
3327
3328
test('issue #148256: Pressing Enter creates line with bad indent with insertSpaces: false', () => {
3329
usingCursor({
3330
text: [
3331
' \t'
3332
]
3333
}, (editor, model, viewModel) => {
3334
model.updateOptions({
3335
insertSpaces: false
3336
});
3337
moveTo(editor, viewModel, 1, 4, false);
3338
viewModel.type('\n', 'keyboard');
3339
assert.strictEqual(model.getValue(), ' \t\n\t');
3340
});
3341
});
3342
3343
test('removeAutoWhitespace off', () => {
3344
usingCursor({
3345
text: [
3346
' some line abc '
3347
],
3348
modelOpts: {
3349
trimAutoWhitespace: false
3350
}
3351
}, (editor, model, viewModel) => {
3352
3353
// Move cursor to the end, verify that we do not trim whitespaces if line has values
3354
moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);
3355
viewModel.type('\n', 'keyboard');
3356
assert.strictEqual(model.getLineContent(1), ' some line abc ');
3357
assert.strictEqual(model.getLineContent(2), ' ');
3358
3359
// Try to enter again, we should trimmed previous line
3360
viewModel.type('\n', 'keyboard');
3361
assert.strictEqual(model.getLineContent(1), ' some line abc ');
3362
assert.strictEqual(model.getLineContent(2), ' ');
3363
assert.strictEqual(model.getLineContent(3), ' ');
3364
});
3365
});
3366
3367
test('removeAutoWhitespace on: removes only whitespace the cursor added 1', () => {
3368
usingCursor({
3369
text: [
3370
' '
3371
]
3372
}, (editor, model, viewModel) => {
3373
moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);
3374
viewModel.type('\n', 'keyboard');
3375
assert.strictEqual(model.getLineContent(1), ' ');
3376
assert.strictEqual(model.getLineContent(2), ' ');
3377
3378
viewModel.type('\n', 'keyboard');
3379
assert.strictEqual(model.getLineContent(1), ' ');
3380
assert.strictEqual(model.getLineContent(2), '');
3381
assert.strictEqual(model.getLineContent(3), ' ');
3382
});
3383
});
3384
3385
test('issue #115033: indent and appendText', () => {
3386
const languageId = 'onEnterMode';
3387
3388
disposables.add(languageService.registerLanguage({ id: languageId }));
3389
disposables.add(languageConfigurationService.register(languageId, {
3390
onEnterRules: [{
3391
beforeText: /.*/,
3392
action: {
3393
indentAction: IndentAction.Indent,
3394
appendText: 'x'
3395
}
3396
}]
3397
}));
3398
usingCursor({
3399
text: [
3400
'text'
3401
],
3402
languageId: languageId,
3403
}, (editor, model, viewModel) => {
3404
3405
moveTo(editor, viewModel, 1, 5);
3406
viewModel.type('\n', 'keyboard');
3407
assert.strictEqual(model.getLineContent(1), 'text');
3408
assert.strictEqual(model.getLineContent(2), ' x');
3409
assertCursor(viewModel, new Position(2, 6));
3410
});
3411
});
3412
3413
test('issue #6862: Editor removes auto inserted indentation when formatting on type', () => {
3414
const languageId = setupOnEnterLanguage(IndentAction.IndentOutdent);
3415
usingCursor({
3416
text: [
3417
'function foo (params: string) {}'
3418
],
3419
languageId: languageId,
3420
}, (editor, model, viewModel) => {
3421
3422
moveTo(editor, viewModel, 1, 32);
3423
viewModel.type('\n', 'keyboard');
3424
assert.strictEqual(model.getLineContent(1), 'function foo (params: string) {');
3425
assert.strictEqual(model.getLineContent(2), ' ');
3426
assert.strictEqual(model.getLineContent(3), '}');
3427
3428
class TestCommand implements ICommand {
3429
3430
private _selectionId: string | null = null;
3431
3432
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
3433
builder.addEditOperation(new Range(1, 13, 1, 14), '');
3434
this._selectionId = builder.trackSelection(viewModel.getSelection());
3435
}
3436
3437
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
3438
return helper.getTrackedSelection(this._selectionId!);
3439
}
3440
3441
}
3442
3443
viewModel.executeCommand(new TestCommand(), 'autoFormat');
3444
assert.strictEqual(model.getLineContent(1), 'function foo(params: string) {');
3445
assert.strictEqual(model.getLineContent(2), ' ');
3446
assert.strictEqual(model.getLineContent(3), '}');
3447
});
3448
});
3449
3450
test('removeAutoWhitespace on: removes only whitespace the cursor added 2', () => {
3451
const languageId = 'testLang';
3452
const registration = languageService.registerLanguage({ id: languageId });
3453
const model = createTextModel(
3454
[
3455
' if (a) {',
3456
' ',
3457
'',
3458
'',
3459
' }'
3460
].join('\n'),
3461
languageId
3462
);
3463
3464
withTestCodeEditor(model, {}, (editor, viewModel) => {
3465
3466
moveTo(editor, viewModel, 3, 1);
3467
editor.runCommand(CoreEditingCommands.Tab, null);
3468
assert.strictEqual(model.getLineContent(1), ' if (a) {');
3469
assert.strictEqual(model.getLineContent(2), ' ');
3470
assert.strictEqual(model.getLineContent(3), ' ');
3471
assert.strictEqual(model.getLineContent(4), '');
3472
assert.strictEqual(model.getLineContent(5), ' }');
3473
3474
moveTo(editor, viewModel, 4, 1);
3475
editor.runCommand(CoreEditingCommands.Tab, null);
3476
assert.strictEqual(model.getLineContent(1), ' if (a) {');
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
moveTo(editor, viewModel, 5, model.getLineMaxColumn(5));
3483
viewModel.type('something', 'keyboard');
3484
assert.strictEqual(model.getLineContent(1), ' if (a) {');
3485
assert.strictEqual(model.getLineContent(2), ' ');
3486
assert.strictEqual(model.getLineContent(3), '');
3487
assert.strictEqual(model.getLineContent(4), '');
3488
assert.strictEqual(model.getLineContent(5), ' }something');
3489
});
3490
3491
registration.dispose();
3492
});
3493
3494
test('removeAutoWhitespace on: test 1', () => {
3495
const model = createTextModel(
3496
[
3497
' some line abc '
3498
].join('\n')
3499
);
3500
3501
withTestCodeEditor(model, {}, (editor, viewModel) => {
3502
3503
// Move cursor to the end, verify that we do not trim whitespaces if line has values
3504
moveTo(editor, viewModel, 1, model.getLineContent(1).length + 1);
3505
viewModel.type('\n', 'keyboard');
3506
assert.strictEqual(model.getLineContent(1), ' some line abc ');
3507
assert.strictEqual(model.getLineContent(2), ' ');
3508
3509
// Try to enter again, we should trimmed previous line
3510
viewModel.type('\n', 'keyboard');
3511
assert.strictEqual(model.getLineContent(1), ' some line abc ');
3512
assert.strictEqual(model.getLineContent(2), '');
3513
assert.strictEqual(model.getLineContent(3), ' ');
3514
3515
// More whitespaces
3516
editor.runCommand(CoreEditingCommands.Tab, null);
3517
assert.strictEqual(model.getLineContent(1), ' some line abc ');
3518
assert.strictEqual(model.getLineContent(2), '');
3519
assert.strictEqual(model.getLineContent(3), ' ');
3520
3521
// Enter and verify that trimmed again
3522
viewModel.type('\n', 'keyboard');
3523
assert.strictEqual(model.getLineContent(1), ' some line abc ');
3524
assert.strictEqual(model.getLineContent(2), '');
3525
assert.strictEqual(model.getLineContent(3), '');
3526
assert.strictEqual(model.getLineContent(4), ' ');
3527
3528
// Trimmed if we will keep only text
3529
moveTo(editor, viewModel, 1, 5);
3530
viewModel.type('\n', 'keyboard');
3531
assert.strictEqual(model.getLineContent(1), ' ');
3532
assert.strictEqual(model.getLineContent(2), ' some line abc ');
3533
assert.strictEqual(model.getLineContent(3), '');
3534
assert.strictEqual(model.getLineContent(4), '');
3535
assert.strictEqual(model.getLineContent(5), '');
3536
3537
// Trimmed if we will keep only text by selection
3538
moveTo(editor, viewModel, 2, 5);
3539
moveTo(editor, viewModel, 3, 1, true);
3540
viewModel.type('\n', 'keyboard');
3541
assert.strictEqual(model.getLineContent(1), ' ');
3542
assert.strictEqual(model.getLineContent(2), ' ');
3543
assert.strictEqual(model.getLineContent(3), ' ');
3544
assert.strictEqual(model.getLineContent(4), '');
3545
assert.strictEqual(model.getLineContent(5), '');
3546
});
3547
});
3548
3549
test('issue #15118: remove auto whitespace when pasting entire line', () => {
3550
const model = createTextModel(
3551
[
3552
' function f() {',
3553
' // I\'m gonna copy this line',
3554
' return 3;',
3555
' }',
3556
].join('\n')
3557
);
3558
3559
withTestCodeEditor(model, {}, (editor, viewModel) => {
3560
3561
moveTo(editor, viewModel, 3, model.getLineMaxColumn(3));
3562
viewModel.type('\n', 'keyboard');
3563
3564
assert.strictEqual(model.getValue(), [
3565
' function f() {',
3566
' // I\'m gonna copy this line',
3567
' return 3;',
3568
' ',
3569
' }',
3570
].join('\n'));
3571
assertCursor(viewModel, new Position(4, model.getLineMaxColumn(4)));
3572
3573
viewModel.paste(' // I\'m gonna copy this line\n', true);
3574
assert.strictEqual(model.getValue(), [
3575
' function f() {',
3576
' // I\'m gonna copy this line',
3577
' return 3;',
3578
' // I\'m gonna copy this line',
3579
'',
3580
' }',
3581
].join('\n'));
3582
assertCursor(viewModel, new Position(5, 1));
3583
});
3584
});
3585
3586
test('issue #40695: maintain cursor position when copying lines using ctrl+c, ctrl+v', () => {
3587
const model = createTextModel(
3588
[
3589
' function f() {',
3590
' // I\'m gonna copy this line',
3591
' // Another line',
3592
' return 3;',
3593
' }',
3594
].join('\n')
3595
);
3596
3597
withTestCodeEditor(model, {}, (editor, viewModel) => {
3598
3599
editor.setSelections([new Selection(4, 10, 4, 10)]);
3600
viewModel.paste(' // I\'m gonna copy this line\n', true);
3601
3602
assert.strictEqual(model.getValue(), [
3603
' function f() {',
3604
' // I\'m gonna copy this line',
3605
' // Another line',
3606
' // I\'m gonna copy this line',
3607
' return 3;',
3608
' }',
3609
].join('\n'));
3610
assertCursor(viewModel, new Position(5, 10));
3611
});
3612
});
3613
3614
test('UseTabStops is off', () => {
3615
const model = createTextModel(
3616
[
3617
' x',
3618
' a ',
3619
' '
3620
].join('\n')
3621
);
3622
3623
withTestCodeEditor(model, { useTabStops: false }, (editor, viewModel) => {
3624
// DeleteLeft removes just one whitespace
3625
moveTo(editor, viewModel, 2, 9);
3626
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3627
assert.strictEqual(model.getLineContent(2), ' a ');
3628
});
3629
});
3630
3631
test('Backspace removes whitespaces with tab size', () => {
3632
const model = createTextModel(
3633
[
3634
' \t \t x',
3635
' a ',
3636
' '
3637
].join('\n')
3638
);
3639
3640
withTestCodeEditor(model, { useTabStops: true }, (editor, viewModel) => {
3641
// DeleteLeft does not remove tab size, because some text exists before
3642
moveTo(editor, viewModel, 2, model.getLineContent(2).length + 1);
3643
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3644
assert.strictEqual(model.getLineContent(2), ' a ');
3645
3646
// DeleteLeft removes tab size = 4
3647
moveTo(editor, viewModel, 2, 9);
3648
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3649
assert.strictEqual(model.getLineContent(2), ' a ');
3650
3651
// DeleteLeft removes tab size = 4
3652
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3653
assert.strictEqual(model.getLineContent(2), 'a ');
3654
3655
// Undo DeleteLeft - get us back to original indentation
3656
editor.runCommand(CoreEditingCommands.Undo, null);
3657
assert.strictEqual(model.getLineContent(2), ' a ');
3658
3659
// Nothing is broken when cursor is in (1,1)
3660
moveTo(editor, viewModel, 1, 1);
3661
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3662
assert.strictEqual(model.getLineContent(1), ' \t \t x');
3663
3664
// DeleteLeft stops at tab stops even in mixed whitespace case
3665
moveTo(editor, viewModel, 1, 10);
3666
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3667
assert.strictEqual(model.getLineContent(1), ' \t \t x');
3668
3669
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3670
assert.strictEqual(model.getLineContent(1), ' \t \tx');
3671
3672
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3673
assert.strictEqual(model.getLineContent(1), ' \tx');
3674
3675
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3676
assert.strictEqual(model.getLineContent(1), 'x');
3677
3678
// DeleteLeft on last line
3679
moveTo(editor, viewModel, 3, model.getLineContent(3).length + 1);
3680
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3681
assert.strictEqual(model.getLineContent(3), '');
3682
3683
// DeleteLeft with removing new line symbol
3684
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3685
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x\n a ');
3686
3687
// In case of selection DeleteLeft only deletes selected text
3688
moveTo(editor, viewModel, 2, 3);
3689
moveTo(editor, viewModel, 2, 4, true);
3690
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3691
assert.strictEqual(model.getLineContent(2), ' a ');
3692
});
3693
});
3694
3695
test('PR #5423: Auto indent + undo + redo is funky', () => {
3696
const model = createTextModel(
3697
[
3698
''
3699
].join('\n'),
3700
undefined,
3701
{
3702
insertSpaces: false,
3703
}
3704
);
3705
3706
withTestCodeEditor(model, {}, (editor, viewModel) => {
3707
viewModel.type('\n', 'keyboard');
3708
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n', 'assert1');
3709
3710
editor.runCommand(CoreEditingCommands.Tab, null);
3711
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\t', 'assert2');
3712
3713
viewModel.type('y', 'keyboard');
3714
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty', 'assert2');
3715
3716
viewModel.type('\n', 'keyboard');
3717
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\t', 'assert3');
3718
3719
viewModel.type('x');
3720
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert4');
3721
3722
CoreNavigationCommands.CursorLeft.runCoreEditorCommand(viewModel, {});
3723
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert5');
3724
3725
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3726
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert6');
3727
3728
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3729
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tyx', 'assert7');
3730
3731
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3732
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\tx', 'assert8');
3733
3734
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3735
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert9');
3736
3737
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
3738
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert10');
3739
3740
editor.runCommand(CoreEditingCommands.Undo, null);
3741
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert11');
3742
3743
editor.runCommand(CoreEditingCommands.Undo, null);
3744
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert12');
3745
3746
editor.runCommand(CoreEditingCommands.Undo, null);
3747
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\n\tx', 'assert13');
3748
3749
editor.runCommand(CoreEditingCommands.Redo, null);
3750
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\n\ty\nx', 'assert14');
3751
3752
editor.runCommand(CoreEditingCommands.Redo, null);
3753
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '\nx', 'assert15');
3754
3755
editor.runCommand(CoreEditingCommands.Redo, null);
3756
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'x', 'assert16');
3757
});
3758
});
3759
3760
test('issue #90973: Undo brings back model alternative version', () => {
3761
const model = createTextModel(
3762
[
3763
''
3764
].join('\n'),
3765
undefined,
3766
{
3767
insertSpaces: false,
3768
}
3769
);
3770
3771
withTestCodeEditor(model, {}, (editor, viewModel) => {
3772
const beforeVersion = model.getVersionId();
3773
const beforeAltVersion = model.getAlternativeVersionId();
3774
viewModel.type('Hello', 'keyboard');
3775
editor.runCommand(CoreEditingCommands.Undo, null);
3776
const afterVersion = model.getVersionId();
3777
const afterAltVersion = model.getAlternativeVersionId();
3778
3779
assert.notStrictEqual(beforeVersion, afterVersion);
3780
assert.strictEqual(beforeAltVersion, afterAltVersion);
3781
});
3782
});
3783
3784
test('Enter honors increaseIndentPattern', () => {
3785
usingCursor({
3786
text: [
3787
'if (true) {',
3788
'\tif (true) {'
3789
],
3790
languageId: indentRulesLanguageId,
3791
modelOpts: { insertSpaces: false },
3792
editorOpts: { autoIndent: 'full' }
3793
}, (editor, model, viewModel) => {
3794
moveTo(editor, viewModel, 1, 12, false);
3795
assertCursor(viewModel, new Selection(1, 12, 1, 12));
3796
3797
viewModel.type('\n', 'keyboard');
3798
model.tokenization.forceTokenization(model.getLineCount());
3799
assertCursor(viewModel, new Selection(2, 2, 2, 2));
3800
3801
moveTo(editor, viewModel, 3, 13, false);
3802
assertCursor(viewModel, new Selection(3, 13, 3, 13));
3803
3804
viewModel.type('\n', 'keyboard');
3805
assertCursor(viewModel, new Selection(4, 3, 4, 3));
3806
});
3807
});
3808
3809
test('Type honors decreaseIndentPattern', () => {
3810
usingCursor({
3811
text: [
3812
'if (true) {',
3813
'\t'
3814
],
3815
languageId: indentRulesLanguageId,
3816
editorOpts: { autoIndent: 'full' }
3817
}, (editor, model, viewModel) => {
3818
moveTo(editor, viewModel, 2, 2, false);
3819
assertCursor(viewModel, new Selection(2, 2, 2, 2));
3820
3821
viewModel.type('}', 'keyboard');
3822
assertCursor(viewModel, new Selection(2, 2, 2, 2));
3823
assert.strictEqual(model.getLineContent(2), '}', '001');
3824
});
3825
});
3826
3827
test('Enter honors unIndentedLinePattern', () => {
3828
usingCursor({
3829
text: [
3830
'if (true) {',
3831
'\t\t\treturn true'
3832
],
3833
languageId: indentRulesLanguageId,
3834
modelOpts: { insertSpaces: false },
3835
editorOpts: { autoIndent: 'full' }
3836
}, (editor, model, viewModel) => {
3837
moveTo(editor, viewModel, 2, 15, false);
3838
assertCursor(viewModel, new Selection(2, 15, 2, 15));
3839
3840
viewModel.type('\n', 'keyboard');
3841
assertCursor(viewModel, new Selection(3, 2, 3, 2));
3842
});
3843
});
3844
3845
test('Enter honors indentNextLinePattern', () => {
3846
usingCursor({
3847
text: [
3848
'if (true)',
3849
'\treturn true;',
3850
'if (true)',
3851
'\t\t\t\treturn true'
3852
],
3853
languageId: indentRulesLanguageId,
3854
modelOpts: { insertSpaces: false },
3855
editorOpts: { autoIndent: 'full' }
3856
}, (editor, model, viewModel) => {
3857
moveTo(editor, viewModel, 2, 14, false);
3858
assertCursor(viewModel, new Selection(2, 14, 2, 14));
3859
3860
viewModel.type('\n', 'keyboard');
3861
model.tokenization.forceTokenization(model.getLineCount());
3862
assertCursor(viewModel, new Selection(3, 1, 3, 1));
3863
3864
moveTo(editor, viewModel, 5, 16, false);
3865
assertCursor(viewModel, new Selection(5, 16, 5, 16));
3866
3867
viewModel.type('\n', 'keyboard');
3868
assertCursor(viewModel, new Selection(6, 2, 6, 2));
3869
});
3870
});
3871
3872
test('Enter honors indentNextLinePattern 2', () => {
3873
const model = createTextModel(
3874
[
3875
'if (true)',
3876
'\tif (true)'
3877
].join('\n'),
3878
indentRulesLanguageId,
3879
{
3880
insertSpaces: false,
3881
}
3882
);
3883
3884
withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {
3885
moveTo(editor, viewModel, 2, 11, false);
3886
assertCursor(viewModel, new Selection(2, 11, 2, 11));
3887
3888
viewModel.type('\n', 'keyboard');
3889
model.tokenization.forceTokenization(model.getLineCount());
3890
assertCursor(viewModel, new Selection(3, 3, 3, 3));
3891
3892
viewModel.type('console.log();', 'keyboard');
3893
viewModel.type('\n', 'keyboard');
3894
assertCursor(viewModel, new Selection(4, 1, 4, 1));
3895
});
3896
});
3897
3898
test('Enter honors intential indent', () => {
3899
usingCursor({
3900
text: [
3901
'if (true) {',
3902
'\tif (true) {',
3903
'return true;',
3904
'}}'
3905
],
3906
languageId: indentRulesLanguageId,
3907
editorOpts: { autoIndent: 'full' }
3908
}, (editor, model, viewModel) => {
3909
moveTo(editor, viewModel, 3, 13, false);
3910
assertCursor(viewModel, new Selection(3, 13, 3, 13));
3911
3912
viewModel.type('\n', 'keyboard');
3913
assertCursor(viewModel, new Selection(4, 1, 4, 1));
3914
assert.strictEqual(model.getLineContent(3), 'return true;', '001');
3915
});
3916
});
3917
3918
test('Enter supports selection 1', () => {
3919
usingCursor({
3920
text: [
3921
'if (true) {',
3922
'\tif (true) {',
3923
'\t\treturn true;',
3924
'\t}a}'
3925
],
3926
languageId: indentRulesLanguageId,
3927
modelOpts: { insertSpaces: false }
3928
}, (editor, model, viewModel) => {
3929
moveTo(editor, viewModel, 4, 3, false);
3930
moveTo(editor, viewModel, 4, 4, true);
3931
assertCursor(viewModel, new Selection(4, 3, 4, 4));
3932
3933
viewModel.type('\n', 'keyboard');
3934
assertCursor(viewModel, new Selection(5, 1, 5, 1));
3935
assert.strictEqual(model.getLineContent(4), '\t}', '001');
3936
});
3937
});
3938
3939
test('Enter supports selection 2', () => {
3940
usingCursor({
3941
text: [
3942
'if (true) {',
3943
'\tif (true) {'
3944
],
3945
languageId: indentRulesLanguageId,
3946
modelOpts: { insertSpaces: false }
3947
}, (editor, model, viewModel) => {
3948
moveTo(editor, viewModel, 2, 12, false);
3949
moveTo(editor, viewModel, 2, 13, true);
3950
assertCursor(viewModel, new Selection(2, 12, 2, 13));
3951
3952
viewModel.type('\n', 'keyboard');
3953
assertCursor(viewModel, new Selection(3, 3, 3, 3));
3954
3955
viewModel.type('\n', 'keyboard');
3956
assertCursor(viewModel, new Selection(4, 3, 4, 3));
3957
});
3958
});
3959
3960
test('Enter honors tabSize and insertSpaces 1', () => {
3961
usingCursor({
3962
text: [
3963
'if (true) {',
3964
'\tif (true) {'
3965
],
3966
languageId: indentRulesLanguageId,
3967
}, (editor, model, viewModel) => {
3968
moveTo(editor, viewModel, 1, 12, false);
3969
assertCursor(viewModel, new Selection(1, 12, 1, 12));
3970
3971
viewModel.type('\n', 'keyboard');
3972
assertCursor(viewModel, new Selection(2, 5, 2, 5));
3973
3974
model.tokenization.forceTokenization(model.getLineCount());
3975
3976
moveTo(editor, viewModel, 3, 13, false);
3977
assertCursor(viewModel, new Selection(3, 13, 3, 13));
3978
3979
viewModel.type('\n', 'keyboard');
3980
assertCursor(viewModel, new Selection(4, 9, 4, 9));
3981
});
3982
});
3983
3984
test('Enter honors tabSize and insertSpaces 2', () => {
3985
usingCursor({
3986
text: [
3987
'if (true) {',
3988
' if (true) {'
3989
],
3990
languageId: indentRulesLanguageId,
3991
}, (editor, model, viewModel) => {
3992
moveTo(editor, viewModel, 1, 12, false);
3993
assertCursor(viewModel, new Selection(1, 12, 1, 12));
3994
3995
viewModel.type('\n', 'keyboard');
3996
model.tokenization.forceTokenization(model.getLineCount());
3997
assertCursor(viewModel, new Selection(2, 5, 2, 5));
3998
3999
moveTo(editor, viewModel, 3, 16, false);
4000
assertCursor(viewModel, new Selection(3, 16, 3, 16));
4001
4002
viewModel.type('\n', 'keyboard');
4003
assert.strictEqual(model.getLineContent(3), ' if (true) {');
4004
assertCursor(viewModel, new Selection(4, 9, 4, 9));
4005
});
4006
});
4007
4008
test('Enter honors tabSize and insertSpaces 3', () => {
4009
usingCursor({
4010
text: [
4011
'if (true) {',
4012
' if (true) {'
4013
],
4014
languageId: indentRulesLanguageId,
4015
modelOpts: { insertSpaces: false }
4016
}, (editor, model, viewModel) => {
4017
moveTo(editor, viewModel, 1, 12, false);
4018
assertCursor(viewModel, new Selection(1, 12, 1, 12));
4019
4020
viewModel.type('\n', 'keyboard');
4021
model.tokenization.forceTokenization(model.getLineCount());
4022
assertCursor(viewModel, new Selection(2, 2, 2, 2));
4023
4024
moveTo(editor, viewModel, 3, 16, false);
4025
assertCursor(viewModel, new Selection(3, 16, 3, 16));
4026
4027
viewModel.type('\n', 'keyboard');
4028
assert.strictEqual(model.getLineContent(3), ' if (true) {');
4029
assertCursor(viewModel, new Selection(4, 3, 4, 3));
4030
});
4031
});
4032
4033
test('Enter supports intentional indentation', () => {
4034
usingCursor({
4035
text: [
4036
'\tif (true) {',
4037
'\t\tswitch(true) {',
4038
'\t\t\tcase true:',
4039
'\t\t\t\tbreak;',
4040
'\t\t}',
4041
'\t}'
4042
],
4043
languageId: indentRulesLanguageId,
4044
modelOpts: { insertSpaces: false },
4045
editorOpts: { autoIndent: 'full' }
4046
}, (editor, model, viewModel) => {
4047
moveTo(editor, viewModel, 5, 4, false);
4048
assertCursor(viewModel, new Selection(5, 4, 5, 4));
4049
4050
viewModel.type('\n', 'keyboard');
4051
assert.strictEqual(model.getLineContent(5), '\t\t}');
4052
assertCursor(viewModel, new Selection(6, 3, 6, 3));
4053
});
4054
});
4055
4056
test('Enter should not adjust cursor position when press enter in the middle of a line 1', () => {
4057
usingCursor({
4058
text: [
4059
'if (true) {',
4060
'\tif (true) {',
4061
'\t\treturn true;',
4062
'\t}a}'
4063
],
4064
languageId: indentRulesLanguageId,
4065
modelOpts: { insertSpaces: false }
4066
}, (editor, model, viewModel) => {
4067
moveTo(editor, viewModel, 3, 9, false);
4068
assertCursor(viewModel, new Selection(3, 9, 3, 9));
4069
4070
viewModel.type('\n', 'keyboard');
4071
assertCursor(viewModel, new Selection(4, 3, 4, 3));
4072
assert.strictEqual(model.getLineContent(4), '\t\t true;', '001');
4073
});
4074
});
4075
4076
test('Enter should not adjust cursor position when press enter in the middle of a line 2', () => {
4077
usingCursor({
4078
text: [
4079
'if (true) {',
4080
'\tif (true) {',
4081
'\t\treturn true;',
4082
'\t}a}'
4083
],
4084
languageId: indentRulesLanguageId,
4085
modelOpts: { insertSpaces: false }
4086
}, (editor, model, viewModel) => {
4087
moveTo(editor, viewModel, 3, 3, false);
4088
assertCursor(viewModel, new Selection(3, 3, 3, 3));
4089
4090
viewModel.type('\n', 'keyboard');
4091
assertCursor(viewModel, new Selection(4, 3, 4, 3));
4092
assert.strictEqual(model.getLineContent(4), '\t\treturn true;', '001');
4093
});
4094
});
4095
4096
test('Enter should not adjust cursor position when press enter in the middle of a line 3', () => {
4097
usingCursor({
4098
text: [
4099
'if (true) {',
4100
' if (true) {',
4101
' return true;',
4102
' }a}'
4103
],
4104
languageId: indentRulesLanguageId
4105
}, (editor, model, viewModel) => {
4106
moveTo(editor, viewModel, 3, 11, false);
4107
assertCursor(viewModel, new Selection(3, 11, 3, 11));
4108
4109
viewModel.type('\n', 'keyboard');
4110
assertCursor(viewModel, new Selection(4, 5, 4, 5));
4111
assert.strictEqual(model.getLineContent(4), ' true;', '001');
4112
});
4113
});
4114
4115
test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 1', () => {
4116
usingCursor({
4117
text: [
4118
'if (true) {',
4119
'\tif (true) {',
4120
'\t\treturn true;',
4121
'\t}a}'
4122
],
4123
languageId: indentRulesLanguageId,
4124
modelOpts: { insertSpaces: false }
4125
}, (editor, model, viewModel) => {
4126
moveTo(editor, viewModel, 3, 2, false);
4127
assertCursor(viewModel, new Selection(3, 2, 3, 2));
4128
4129
viewModel.type('\n', 'keyboard');
4130
assertCursor(viewModel, new Selection(4, 2, 4, 2));
4131
assert.strictEqual(model.getLineContent(4), '\t\treturn true;', '001');
4132
4133
moveTo(editor, viewModel, 4, 1, false);
4134
assertCursor(viewModel, new Selection(4, 1, 4, 1));
4135
4136
viewModel.type('\n', 'keyboard');
4137
assertCursor(viewModel, new Selection(5, 1, 5, 1));
4138
assert.strictEqual(model.getLineContent(5), '\t\treturn true;', '002');
4139
});
4140
});
4141
4142
test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 2', () => {
4143
usingCursor({
4144
text: [
4145
'\tif (true) {',
4146
'\t\tif (true) {',
4147
'\t \treturn true;',
4148
'\t\t}a}'
4149
],
4150
languageId: indentRulesLanguageId,
4151
modelOpts: { insertSpaces: false }
4152
}, (editor, model, viewModel) => {
4153
moveTo(editor, viewModel, 3, 4, false);
4154
assertCursor(viewModel, new Selection(3, 4, 3, 4));
4155
4156
viewModel.type('\n', 'keyboard');
4157
assertCursor(viewModel, new Selection(4, 3, 4, 3));
4158
assert.strictEqual(model.getLineContent(4), '\t\t\treturn true;', '001');
4159
4160
moveTo(editor, viewModel, 4, 1, false);
4161
assertCursor(viewModel, new Selection(4, 1, 4, 1));
4162
4163
viewModel.type('\n', 'keyboard');
4164
assertCursor(viewModel, new Selection(5, 1, 5, 1));
4165
assert.strictEqual(model.getLineContent(5), '\t\t\treturn true;', '002');
4166
});
4167
});
4168
4169
test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 3', () => {
4170
usingCursor({
4171
text: [
4172
'if (true) {',
4173
' if (true) {',
4174
' return true;',
4175
'}a}'
4176
],
4177
languageId: indentRulesLanguageId
4178
}, (editor, model, viewModel) => {
4179
moveTo(editor, viewModel, 3, 2, false);
4180
assertCursor(viewModel, new Selection(3, 2, 3, 2));
4181
4182
viewModel.type('\n', 'keyboard');
4183
assertCursor(viewModel, new Selection(4, 2, 4, 2));
4184
assert.strictEqual(model.getLineContent(4), ' return true;', '001');
4185
4186
moveTo(editor, viewModel, 4, 3, false);
4187
viewModel.type('\n', 'keyboard');
4188
assertCursor(viewModel, new Selection(5, 3, 5, 3));
4189
assert.strictEqual(model.getLineContent(5), ' return true;', '002');
4190
});
4191
});
4192
4193
test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 4', () => {
4194
usingCursor({
4195
text: [
4196
'if (true) {',
4197
' if (true) {',
4198
'\t return true;',
4199
'}a}',
4200
'',
4201
'if (true) {',
4202
' if (true) {',
4203
'\t return true;',
4204
'}a}'
4205
],
4206
languageId: indentRulesLanguageId,
4207
modelOpts: {
4208
tabSize: 2,
4209
indentSize: 2
4210
}
4211
}, (editor, model, viewModel) => {
4212
moveTo(editor, viewModel, 3, 3, false);
4213
assertCursor(viewModel, new Selection(3, 3, 3, 3));
4214
4215
viewModel.type('\n', 'keyboard');
4216
assertCursor(viewModel, new Selection(4, 4, 4, 4));
4217
assert.strictEqual(model.getLineContent(4), ' return true;', '001');
4218
4219
moveTo(editor, viewModel, 9, 4, false);
4220
viewModel.type('\n', 'keyboard');
4221
assertCursor(viewModel, new Selection(10, 5, 10, 5));
4222
assert.strictEqual(model.getLineContent(10), ' return true;', '001');
4223
});
4224
});
4225
4226
test('Enter should adjust cursor position when press enter in the middle of leading whitespaces 5', () => {
4227
usingCursor({
4228
text: [
4229
'if (true) {',
4230
' if (true) {',
4231
' return true;',
4232
' return true;',
4233
''
4234
],
4235
languageId: indentRulesLanguageId,
4236
modelOpts: { tabSize: 2 }
4237
}, (editor, model, viewModel) => {
4238
moveTo(editor, viewModel, 3, 5, false);
4239
moveTo(editor, viewModel, 4, 3, true);
4240
assertCursor(viewModel, new Selection(3, 5, 4, 3));
4241
4242
viewModel.type('\n', 'keyboard');
4243
assertCursor(viewModel, new Selection(4, 3, 4, 3));
4244
assert.strictEqual(model.getLineContent(4), ' return true;', '001');
4245
});
4246
});
4247
4248
test('issue microsoft/monaco-editor#108 part 1/2: Auto indentation on Enter with selection is half broken', () => {
4249
usingCursor({
4250
text: [
4251
'function baz() {',
4252
'\tvar x = 1;',
4253
'\t\t\t\t\t\t\treturn x;',
4254
'}'
4255
],
4256
modelOpts: {
4257
insertSpaces: false,
4258
},
4259
languageId: indentRulesLanguageId,
4260
}, (editor, model, viewModel) => {
4261
moveTo(editor, viewModel, 3, 8, false);
4262
moveTo(editor, viewModel, 2, 12, true);
4263
assertCursor(viewModel, new Selection(3, 8, 2, 12));
4264
4265
viewModel.type('\n', 'keyboard');
4266
assert.strictEqual(model.getLineContent(3), '\treturn x;');
4267
assertCursor(viewModel, new Position(3, 2));
4268
});
4269
});
4270
4271
test('issue microsoft/monaco-editor#108 part 2/2: Auto indentation on Enter with selection is half broken', () => {
4272
usingCursor({
4273
text: [
4274
'function baz() {',
4275
'\tvar x = 1;',
4276
'\t\t\t\t\t\t\treturn x;',
4277
'}'
4278
],
4279
modelOpts: {
4280
insertSpaces: false,
4281
},
4282
languageId: indentRulesLanguageId,
4283
}, (editor, model, viewModel) => {
4284
moveTo(editor, viewModel, 2, 12, false);
4285
moveTo(editor, viewModel, 3, 8, true);
4286
assertCursor(viewModel, new Selection(2, 12, 3, 8));
4287
4288
viewModel.type('\n', 'keyboard');
4289
assert.strictEqual(model.getLineContent(3), '\treturn x;');
4290
assertCursor(viewModel, new Position(3, 2));
4291
});
4292
});
4293
4294
test('onEnter works if there are no indentation rules', () => {
4295
usingCursor({
4296
text: [
4297
'<?',
4298
'\tif (true) {',
4299
'\t\techo $hi;',
4300
'\t\techo $bye;',
4301
'\t}',
4302
'?>'
4303
],
4304
modelOpts: { insertSpaces: false }
4305
}, (editor, model, viewModel) => {
4306
moveTo(editor, viewModel, 5, 3, false);
4307
assertCursor(viewModel, new Selection(5, 3, 5, 3));
4308
4309
viewModel.type('\n', 'keyboard');
4310
assert.strictEqual(model.getLineContent(6), '\t');
4311
assertCursor(viewModel, new Selection(6, 2, 6, 2));
4312
assert.strictEqual(model.getLineContent(5), '\t}');
4313
});
4314
});
4315
4316
test('onEnter works if there are no indentation rules 2', () => {
4317
usingCursor({
4318
text: [
4319
' if (5)',
4320
' return 5;',
4321
' '
4322
],
4323
modelOpts: { insertSpaces: false }
4324
}, (editor, model, viewModel) => {
4325
moveTo(editor, viewModel, 3, 2, false);
4326
assertCursor(viewModel, new Selection(3, 2, 3, 2));
4327
4328
viewModel.type('\n', 'keyboard');
4329
assertCursor(viewModel, new Selection(4, 2, 4, 2));
4330
assert.strictEqual(model.getLineContent(4), '\t');
4331
});
4332
});
4333
4334
test('bug #16543: Tab should indent to correct indentation spot immediately', () => {
4335
const model = createTextModel(
4336
[
4337
'function baz() {',
4338
'\tfunction hello() { // something here',
4339
'\t',
4340
'',
4341
'\t}',
4342
'}'
4343
].join('\n'),
4344
indentRulesLanguageId,
4345
{
4346
insertSpaces: false,
4347
}
4348
);
4349
4350
withTestCodeEditor(model, {}, (editor, viewModel) => {
4351
moveTo(editor, viewModel, 4, 1, false);
4352
assertCursor(viewModel, new Selection(4, 1, 4, 1));
4353
4354
editor.runCommand(CoreEditingCommands.Tab, null);
4355
assert.strictEqual(model.getLineContent(4), '\t\t');
4356
});
4357
});
4358
4359
4360
test('bug #2938 (1): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {
4361
const model = createTextModel(
4362
[
4363
'\tfunction baz() {',
4364
'\t\tfunction hello() { // something here',
4365
'\t\t',
4366
'\t',
4367
'\t\t}',
4368
'\t}'
4369
].join('\n'),
4370
indentRulesLanguageId,
4371
{
4372
insertSpaces: false,
4373
}
4374
);
4375
4376
withTestCodeEditor(model, {}, (editor, viewModel) => {
4377
moveTo(editor, viewModel, 4, 2, false);
4378
assertCursor(viewModel, new Selection(4, 2, 4, 2));
4379
4380
editor.runCommand(CoreEditingCommands.Tab, null);
4381
assert.strictEqual(model.getLineContent(4), '\t\t\t');
4382
});
4383
});
4384
4385
4386
test('bug #2938 (2): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {
4387
const model = createTextModel(
4388
[
4389
'\tfunction baz() {',
4390
'\t\tfunction hello() { // something here',
4391
'\t\t',
4392
' ',
4393
'\t\t}',
4394
'\t}'
4395
].join('\n'),
4396
indentRulesLanguageId,
4397
{
4398
insertSpaces: false,
4399
}
4400
);
4401
4402
withTestCodeEditor(model, {}, (editor, viewModel) => {
4403
moveTo(editor, viewModel, 4, 1, false);
4404
assertCursor(viewModel, new Selection(4, 1, 4, 1));
4405
4406
editor.runCommand(CoreEditingCommands.Tab, null);
4407
assert.strictEqual(model.getLineContent(4), '\t\t\t');
4408
});
4409
});
4410
4411
test('bug #2938 (3): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {
4412
const model = createTextModel(
4413
[
4414
'\tfunction baz() {',
4415
'\t\tfunction hello() { // something here',
4416
'\t\t',
4417
'\t\t\t',
4418
'\t\t}',
4419
'\t}'
4420
].join('\n'),
4421
indentRulesLanguageId,
4422
{
4423
insertSpaces: false,
4424
}
4425
);
4426
4427
withTestCodeEditor(model, {}, (editor, viewModel) => {
4428
moveTo(editor, viewModel, 4, 3, false);
4429
assertCursor(viewModel, new Selection(4, 3, 4, 3));
4430
4431
editor.runCommand(CoreEditingCommands.Tab, null);
4432
assert.strictEqual(model.getLineContent(4), '\t\t\t\t');
4433
});
4434
});
4435
4436
test('bug #2938 (4): When pressing Tab on white-space only lines, indent straight to the right spot (similar to empty lines)', () => {
4437
const model = createTextModel(
4438
[
4439
'\tfunction baz() {',
4440
'\t\tfunction hello() { // something here',
4441
'\t\t',
4442
'\t\t\t\t',
4443
'\t\t}',
4444
'\t}'
4445
].join('\n'),
4446
indentRulesLanguageId,
4447
{
4448
insertSpaces: false,
4449
}
4450
);
4451
4452
withTestCodeEditor(model, {}, (editor, viewModel) => {
4453
moveTo(editor, viewModel, 4, 4, false);
4454
assertCursor(viewModel, new Selection(4, 4, 4, 4));
4455
4456
editor.runCommand(CoreEditingCommands.Tab, null);
4457
assert.strictEqual(model.getLineContent(4), '\t\t\t\t\t');
4458
});
4459
});
4460
4461
test('bug #31015: When pressing Tab on lines and Enter rules are avail, indent straight to the right spotTab', () => {
4462
const onEnterLanguageId = setupOnEnterLanguage(IndentAction.Indent);
4463
const model = createTextModel(
4464
[
4465
' if (a) {',
4466
' ',
4467
'',
4468
'',
4469
' }'
4470
].join('\n'),
4471
onEnterLanguageId
4472
);
4473
4474
withTestCodeEditor(model, {}, (editor, viewModel) => {
4475
4476
moveTo(editor, viewModel, 3, 1);
4477
editor.runCommand(CoreEditingCommands.Tab, null);
4478
assert.strictEqual(model.getLineContent(1), ' if (a) {');
4479
assert.strictEqual(model.getLineContent(2), ' ');
4480
assert.strictEqual(model.getLineContent(3), ' ');
4481
assert.strictEqual(model.getLineContent(4), '');
4482
assert.strictEqual(model.getLineContent(5), ' }');
4483
});
4484
});
4485
4486
test('type honors indentation rules: ruby keywords', () => {
4487
const rubyLanguageId = setupIndentRulesLanguage('ruby', {
4488
increaseIndentPattern: /^\s*((begin|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\sdo\b))\b[^\{;]*$/,
4489
decreaseIndentPattern: /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif|when)\b)/
4490
});
4491
const model = createTextModel(
4492
[
4493
'class Greeter',
4494
' def initialize(name)',
4495
' @name = name',
4496
' en'
4497
].join('\n'),
4498
rubyLanguageId
4499
);
4500
4501
withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {
4502
moveTo(editor, viewModel, 4, 7, false);
4503
assertCursor(viewModel, new Selection(4, 7, 4, 7));
4504
4505
viewModel.type('d', 'keyboard');
4506
assert.strictEqual(model.getLineContent(4), ' end');
4507
});
4508
});
4509
4510
test('Auto indent on type: increaseIndentPattern has higher priority than decreaseIndent when inheriting', () => {
4511
usingCursor({
4512
text: [
4513
'\tif (true) {',
4514
'\t\tconsole.log();',
4515
'\t} else if {',
4516
'\t\tconsole.log()',
4517
'\t}'
4518
],
4519
languageId: indentRulesLanguageId
4520
}, (editor, model, viewModel) => {
4521
moveTo(editor, viewModel, 5, 3, false);
4522
assertCursor(viewModel, new Selection(5, 3, 5, 3));
4523
4524
viewModel.type('e', 'keyboard');
4525
assertCursor(viewModel, new Selection(5, 4, 5, 4));
4526
assert.strictEqual(model.getLineContent(5), '\t}e', 'This line should not decrease indent');
4527
});
4528
});
4529
4530
test('type honors users indentation adjustment', () => {
4531
usingCursor({
4532
text: [
4533
'\tif (true ||',
4534
'\t ) {',
4535
'\t}',
4536
'if (true ||',
4537
') {',
4538
'}'
4539
],
4540
languageId: indentRulesLanguageId
4541
}, (editor, model, viewModel) => {
4542
moveTo(editor, viewModel, 2, 3, false);
4543
assertCursor(viewModel, new Selection(2, 3, 2, 3));
4544
4545
viewModel.type(' ', 'keyboard');
4546
assertCursor(viewModel, new Selection(2, 4, 2, 4));
4547
assert.strictEqual(model.getLineContent(2), '\t ) {', 'This line should not decrease indent');
4548
});
4549
});
4550
4551
test('bug 29972: if a line is line comment, open bracket should not indent next line', () => {
4552
usingCursor({
4553
text: [
4554
'if (true) {',
4555
'\t// {',
4556
'\t\t'
4557
],
4558
languageId: indentRulesLanguageId,
4559
editorOpts: { autoIndent: 'full' }
4560
}, (editor, model, viewModel) => {
4561
moveTo(editor, viewModel, 3, 3, false);
4562
assertCursor(viewModel, new Selection(3, 3, 3, 3));
4563
4564
viewModel.type('}', 'keyboard');
4565
assertCursor(viewModel, new Selection(3, 2, 3, 2));
4566
assert.strictEqual(model.getLineContent(3), '}');
4567
});
4568
});
4569
4570
4571
test('issue #38261: TAB key results in bizarre indentation in C++ mode ', () => {
4572
const languageId = 'indentRulesMode';
4573
4574
disposables.add(languageService.registerLanguage({ id: languageId }));
4575
disposables.add(languageConfigurationService.register(languageId, {
4576
brackets: [
4577
['{', '}'],
4578
['[', ']'],
4579
['(', ')']
4580
],
4581
indentationRules: {
4582
increaseIndentPattern: new RegExp('(^.*\\{[^}]*$)'),
4583
decreaseIndentPattern: new RegExp('^\\s*\\}')
4584
}
4585
}));
4586
4587
const model = createTextModel(
4588
[
4589
'int main() {',
4590
' return 0;',
4591
'}',
4592
'',
4593
'bool Foo::bar(const string &a,',
4594
' const string &b) {',
4595
' foo();',
4596
'',
4597
')',
4598
].join('\n'),
4599
languageId,
4600
{
4601
tabSize: 2,
4602
indentSize: 2
4603
}
4604
);
4605
4606
withTestCodeEditor(model, { autoIndent: 'advanced' }, (editor, viewModel) => {
4607
moveTo(editor, viewModel, 8, 1, false);
4608
assertCursor(viewModel, new Selection(8, 1, 8, 1));
4609
4610
editor.runCommand(CoreEditingCommands.Tab, null);
4611
assert.strictEqual(model.getValue(),
4612
[
4613
'int main() {',
4614
' return 0;',
4615
'}',
4616
'',
4617
'bool Foo::bar(const string &a,',
4618
' const string &b) {',
4619
' foo();',
4620
' ',
4621
')',
4622
].join('\n')
4623
);
4624
assert.deepStrictEqual(viewModel.getSelection(), new Selection(8, 3, 8, 3));
4625
});
4626
});
4627
4628
test('issue #57197: indent rules regex should be stateless', () => {
4629
const languageId = setupIndentRulesLanguage('lang', {
4630
decreaseIndentPattern: /^\s*}$/gm,
4631
increaseIndentPattern: /^(?![^\S\n]*(?!--|––|——)(?:[-❍❑■⬜□☐▪▫–—≡→›✘xX✔✓☑+]|\[[ xX+-]?\])\s[^\n]*)[^\S\n]*(.+:)[^\S\n]*(?:(?=@[^\s*~(]+(?::\/\/[^\s*~(:]+)?(?:\([^)]*\))?)|$)/gm,
4632
});
4633
usingCursor({
4634
text: [
4635
'Project:',
4636
],
4637
languageId: languageId,
4638
modelOpts: { insertSpaces: false },
4639
editorOpts: { autoIndent: 'full' }
4640
}, (editor, model, viewModel) => {
4641
moveTo(editor, viewModel, 1, 9, false);
4642
assertCursor(viewModel, new Selection(1, 9, 1, 9));
4643
4644
viewModel.type('\n', 'keyboard');
4645
model.tokenization.forceTokenization(model.getLineCount());
4646
assertCursor(viewModel, new Selection(2, 2, 2, 2));
4647
4648
moveTo(editor, viewModel, 1, 9, false);
4649
assertCursor(viewModel, new Selection(1, 9, 1, 9));
4650
viewModel.type('\n', 'keyboard');
4651
model.tokenization.forceTokenization(model.getLineCount());
4652
assertCursor(viewModel, new Selection(2, 2, 2, 2));
4653
});
4654
});
4655
4656
test('typing in json', () => {
4657
const languageId = 'indentRulesMode';
4658
4659
disposables.add(languageService.registerLanguage({ id: languageId }));
4660
disposables.add(languageConfigurationService.register(languageId, {
4661
brackets: [
4662
['{', '}'],
4663
['[', ']'],
4664
['(', ')']
4665
],
4666
indentationRules: {
4667
increaseIndentPattern: new RegExp('({+(?=([^"]*"[^"]*")*[^"}]*$))|(\\[+(?=([^"]*"[^"]*")*[^"\\]]*$))'),
4668
decreaseIndentPattern: new RegExp('^\\s*[}\\]],?\\s*$')
4669
}
4670
}));
4671
4672
const model = createTextModel(
4673
[
4674
'{',
4675
' "scripts: {"',
4676
' "watch": "a {"',
4677
' "build{": "b"',
4678
' "tasks": []',
4679
' "tasks": ["a"]',
4680
' "}"',
4681
'"}"'
4682
].join('\n'),
4683
languageId,
4684
{
4685
tabSize: 2,
4686
indentSize: 2
4687
}
4688
);
4689
4690
withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {
4691
moveTo(editor, viewModel, 3, 19, false);
4692
assertCursor(viewModel, new Selection(3, 19, 3, 19));
4693
4694
viewModel.type('\n', 'keyboard');
4695
assert.deepStrictEqual(model.getLineContent(4), ' ');
4696
4697
moveTo(editor, viewModel, 5, 18, false);
4698
assertCursor(viewModel, new Selection(5, 18, 5, 18));
4699
4700
viewModel.type('\n', 'keyboard');
4701
assert.deepStrictEqual(model.getLineContent(6), ' ');
4702
4703
moveTo(editor, viewModel, 7, 15, false);
4704
assertCursor(viewModel, new Selection(7, 15, 7, 15));
4705
4706
viewModel.type('\n', 'keyboard');
4707
assert.deepStrictEqual(model.getLineContent(8), ' ');
4708
assert.deepStrictEqual(model.getLineContent(9), ' ]');
4709
4710
moveTo(editor, viewModel, 10, 18, false);
4711
assertCursor(viewModel, new Selection(10, 18, 10, 18));
4712
4713
viewModel.type('\n', 'keyboard');
4714
assert.deepStrictEqual(model.getLineContent(11), ' ]');
4715
});
4716
});
4717
4718
test('issue #111128: Multicursor `Enter` issue with indentation', () => {
4719
const model = createTextModel(' let a, b, c;', indentRulesLanguageId, { detectIndentation: false, insertSpaces: false, tabSize: 4 });
4720
withTestCodeEditor(model, {}, (editor, viewModel) => {
4721
editor.setSelections([
4722
new Selection(1, 11, 1, 11),
4723
new Selection(1, 14, 1, 14),
4724
]);
4725
viewModel.type('\n', 'keyboard');
4726
assert.strictEqual(model.getValue(), ' let a,\n\t b,\n\t c;');
4727
});
4728
});
4729
4730
test('issue #122714: tabSize=1 prevent typing a string matching decreaseIndentPattern in an empty file', () => {
4731
const latextLanguageId = setupIndentRulesLanguage('latex', {
4732
increaseIndentPattern: new RegExp('\\\\begin{(?!document)([^}]*)}(?!.*\\\\end{\\1})'),
4733
decreaseIndentPattern: new RegExp('^\\s*\\\\end{(?!document)')
4734
});
4735
const model = createTextModel(
4736
'\\end',
4737
latextLanguageId,
4738
{ tabSize: 1 }
4739
);
4740
4741
withTestCodeEditor(model, { autoIndent: 'full' }, (editor, viewModel) => {
4742
moveTo(editor, viewModel, 1, 5, false);
4743
assertCursor(viewModel, new Selection(1, 5, 1, 5));
4744
4745
viewModel.type('{', 'keyboard');
4746
assert.strictEqual(model.getLineContent(1), '\\end{}');
4747
});
4748
});
4749
4750
test('ElectricCharacter - does nothing if no electric char', () => {
4751
usingCursor({
4752
text: [
4753
' if (a) {',
4754
''
4755
],
4756
languageId: electricCharLanguageId
4757
}, (editor, model, viewModel) => {
4758
moveTo(editor, viewModel, 2, 1);
4759
viewModel.type('*', 'keyboard');
4760
assert.deepStrictEqual(model.getLineContent(2), '*');
4761
});
4762
});
4763
4764
test('ElectricCharacter - indents in order to match bracket', () => {
4765
usingCursor({
4766
text: [
4767
' if (a) {',
4768
''
4769
],
4770
languageId: electricCharLanguageId
4771
}, (editor, model, viewModel) => {
4772
moveTo(editor, viewModel, 2, 1);
4773
viewModel.type('}', 'keyboard');
4774
assert.deepStrictEqual(model.getLineContent(2), ' }');
4775
});
4776
});
4777
4778
test('ElectricCharacter - unindents in order to match bracket', () => {
4779
usingCursor({
4780
text: [
4781
' if (a) {',
4782
' '
4783
],
4784
languageId: electricCharLanguageId
4785
}, (editor, model, viewModel) => {
4786
moveTo(editor, viewModel, 2, 5);
4787
viewModel.type('}', 'keyboard');
4788
assert.deepStrictEqual(model.getLineContent(2), ' }');
4789
});
4790
});
4791
4792
test('ElectricCharacter - matches with correct bracket', () => {
4793
usingCursor({
4794
text: [
4795
' if (a) {',
4796
' if (b) {',
4797
' }',
4798
' '
4799
],
4800
languageId: electricCharLanguageId
4801
}, (editor, model, viewModel) => {
4802
moveTo(editor, viewModel, 4, 1);
4803
viewModel.type('}', 'keyboard');
4804
assert.deepStrictEqual(model.getLineContent(4), ' } ');
4805
});
4806
});
4807
4808
test('ElectricCharacter - does nothing if bracket does not match', () => {
4809
usingCursor({
4810
text: [
4811
' if (a) {',
4812
' if (b) {',
4813
' }',
4814
' } '
4815
],
4816
languageId: electricCharLanguageId
4817
}, (editor, model, viewModel) => {
4818
moveTo(editor, viewModel, 4, 6);
4819
viewModel.type('}', 'keyboard');
4820
assert.deepStrictEqual(model.getLineContent(4), ' } }');
4821
});
4822
});
4823
4824
test('ElectricCharacter - matches bracket even in line with content', () => {
4825
usingCursor({
4826
text: [
4827
' if (a) {',
4828
'// hello'
4829
],
4830
languageId: electricCharLanguageId
4831
}, (editor, model, viewModel) => {
4832
moveTo(editor, viewModel, 2, 1);
4833
viewModel.type('}', 'keyboard');
4834
assert.deepStrictEqual(model.getLineContent(2), ' }// hello');
4835
});
4836
});
4837
4838
test('ElectricCharacter - is no-op if bracket is lined up', () => {
4839
usingCursor({
4840
text: [
4841
' if (a) {',
4842
' '
4843
],
4844
languageId: electricCharLanguageId
4845
}, (editor, model, viewModel) => {
4846
moveTo(editor, viewModel, 2, 3);
4847
viewModel.type('}', 'keyboard');
4848
assert.deepStrictEqual(model.getLineContent(2), ' }');
4849
});
4850
});
4851
4852
test('ElectricCharacter - is no-op if there is non-whitespace text before', () => {
4853
usingCursor({
4854
text: [
4855
' if (a) {',
4856
'a'
4857
],
4858
languageId: electricCharLanguageId
4859
}, (editor, model, viewModel) => {
4860
moveTo(editor, viewModel, 2, 2);
4861
viewModel.type('}', 'keyboard');
4862
assert.deepStrictEqual(model.getLineContent(2), 'a}');
4863
});
4864
});
4865
4866
test('ElectricCharacter - is no-op if pairs are all matched before', () => {
4867
usingCursor({
4868
text: [
4869
'foo(() => {',
4870
' ( 1 + 2 ) ',
4871
'})'
4872
],
4873
languageId: electricCharLanguageId
4874
}, (editor, model, viewModel) => {
4875
moveTo(editor, viewModel, 2, 13);
4876
viewModel.type('*', 'keyboard');
4877
assert.deepStrictEqual(model.getLineContent(2), ' ( 1 + 2 ) *');
4878
});
4879
});
4880
4881
test('ElectricCharacter - is no-op if matching bracket is on the same line', () => {
4882
usingCursor({
4883
text: [
4884
'(div',
4885
],
4886
languageId: electricCharLanguageId
4887
}, (editor, model, viewModel) => {
4888
moveTo(editor, viewModel, 1, 5);
4889
let changeText: string | null = null;
4890
const disposable = model.onDidChangeContent(e => {
4891
changeText = e.changes[0].text;
4892
});
4893
viewModel.type(')', 'keyboard');
4894
assert.deepStrictEqual(model.getLineContent(1), '(div)');
4895
assert.deepStrictEqual(changeText, ')');
4896
disposable.dispose();
4897
});
4898
});
4899
4900
test('ElectricCharacter - is no-op if the line has other content', () => {
4901
usingCursor({
4902
text: [
4903
'Math.max(',
4904
'\t2',
4905
'\t3'
4906
],
4907
languageId: electricCharLanguageId
4908
}, (editor, model, viewModel) => {
4909
moveTo(editor, viewModel, 3, 3);
4910
viewModel.type(')', 'keyboard');
4911
assert.deepStrictEqual(model.getLineContent(3), '\t3)');
4912
});
4913
});
4914
4915
test('ElectricCharacter - appends text', () => {
4916
usingCursor({
4917
text: [
4918
' if (a) {',
4919
'/*'
4920
],
4921
languageId: electricCharLanguageId
4922
}, (editor, model, viewModel) => {
4923
moveTo(editor, viewModel, 2, 3);
4924
viewModel.type('*', 'keyboard');
4925
assert.deepStrictEqual(model.getLineContent(2), '/** */');
4926
});
4927
});
4928
4929
test('ElectricCharacter - appends text 2', () => {
4930
usingCursor({
4931
text: [
4932
' if (a) {',
4933
' /*'
4934
],
4935
languageId: electricCharLanguageId
4936
}, (editor, model, viewModel) => {
4937
moveTo(editor, viewModel, 2, 5);
4938
viewModel.type('*', 'keyboard');
4939
assert.deepStrictEqual(model.getLineContent(2), ' /** */');
4940
});
4941
});
4942
4943
test('ElectricCharacter - issue #23711: Replacing selected text with )]} fails to delete old text with backwards-dragged selection', () => {
4944
usingCursor({
4945
text: [
4946
'{',
4947
'word'
4948
],
4949
languageId: electricCharLanguageId
4950
}, (editor, model, viewModel) => {
4951
moveTo(editor, viewModel, 2, 5);
4952
moveTo(editor, viewModel, 2, 1, true);
4953
viewModel.type('}', 'keyboard');
4954
assert.deepStrictEqual(model.getLineContent(2), '}');
4955
});
4956
});
4957
4958
test('issue #61070: backtick (`) should auto-close after a word character', () => {
4959
usingCursor({
4960
text: ['const markup = highlight'],
4961
languageId: autoClosingLanguageId
4962
}, (editor, model, viewModel) => {
4963
model.tokenization.forceTokenization(1);
4964
assertType(editor, model, viewModel, 1, 25, '`', '``', `auto closes \` @ (1, 25)`);
4965
});
4966
});
4967
4968
test('issue #132912: quotes should not auto-close if they are closing a string', () => {
4969
setupAutoClosingLanguageTokenization();
4970
const model = createTextModel('const t2 = `something ${t1}', autoClosingLanguageId);
4971
withTestCodeEditor(
4972
model,
4973
{},
4974
(editor, viewModel) => {
4975
const model = viewModel.model;
4976
model.tokenization.forceTokenization(1);
4977
assertType(editor, model, viewModel, 1, 28, '`', '`', `does not auto close \` @ (1, 28)`);
4978
}
4979
);
4980
});
4981
4982
test('autoClosingPairs - open parens: default', () => {
4983
usingCursor({
4984
text: [
4985
'var a = [];',
4986
'var b = `asd`;',
4987
'var c = \'asd\';',
4988
'var d = "asd";',
4989
'var e = /*3*/ 3;',
4990
'var f = /** 3 */3;',
4991
'var g = (3+5);',
4992
'var h = { a: \'value\' };',
4993
],
4994
languageId: autoClosingLanguageId
4995
}, (editor, model, viewModel) => {
4996
4997
const autoClosePositions = [
4998
'var| a| |=| [|]|;|',
4999
'var| b| |=| |`asd|`|;|',
5000
'var| c| |=| |\'asd|\'|;|',
5001
'var| d| |=| |"asd|"|;|',
5002
'var| e| |=| /*3*/| 3|;|',
5003
'var| f| |=| /**| 3| */3|;|',
5004
'var| g| |=| (3+5|)|;|',
5005
'var| h| |=| {| a|:| |\'value|\'| |}|;|',
5006
];
5007
for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5008
const lineNumber = i + 1;
5009
const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5010
5011
for (let column = 1; column < autoCloseColumns.length; column++) {
5012
model.tokenization.forceTokenization(lineNumber);
5013
if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {
5014
assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
5015
} else {
5016
assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
5017
}
5018
}
5019
}
5020
});
5021
});
5022
5023
test('autoClosingPairs - open parens: whitespace', () => {
5024
usingCursor({
5025
text: [
5026
'var a = [];',
5027
'var b = `asd`;',
5028
'var c = \'asd\';',
5029
'var d = "asd";',
5030
'var e = /*3*/ 3;',
5031
'var f = /** 3 */3;',
5032
'var g = (3+5);',
5033
'var h = { a: \'value\' };',
5034
],
5035
languageId: autoClosingLanguageId,
5036
editorOpts: {
5037
autoClosingBrackets: 'beforeWhitespace'
5038
}
5039
}, (editor, model, viewModel) => {
5040
5041
const autoClosePositions = [
5042
'var| a| =| [|];|',
5043
'var| b| =| `asd`;|',
5044
'var| c| =| \'asd\';|',
5045
'var| d| =| "asd";|',
5046
'var| e| =| /*3*/| 3;|',
5047
'var| f| =| /**| 3| */3;|',
5048
'var| g| =| (3+5|);|',
5049
'var| h| =| {| a:| \'value\'| |};|',
5050
];
5051
for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5052
const lineNumber = i + 1;
5053
const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5054
5055
for (let column = 1; column < autoCloseColumns.length; column++) {
5056
model.tokenization.forceTokenization(lineNumber);
5057
if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {
5058
assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
5059
} else {
5060
assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
5061
}
5062
}
5063
}
5064
});
5065
});
5066
5067
test('autoClosingPairs - open parens disabled/enabled open quotes enabled/disabled', () => {
5068
usingCursor({
5069
text: [
5070
'var a = [];',
5071
],
5072
languageId: autoClosingLanguageId,
5073
editorOpts: {
5074
autoClosingBrackets: 'beforeWhitespace',
5075
autoClosingQuotes: 'never'
5076
}
5077
}, (editor, model, viewModel) => {
5078
5079
const autoClosePositions = [
5080
'var| a| =| [|];|',
5081
];
5082
for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5083
const lineNumber = i + 1;
5084
const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5085
5086
for (let column = 1; column < autoCloseColumns.length; column++) {
5087
model.tokenization.forceTokenization(lineNumber);
5088
if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {
5089
assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
5090
} else {
5091
assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
5092
}
5093
assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);
5094
}
5095
}
5096
});
5097
5098
usingCursor({
5099
text: [
5100
'var b = [];',
5101
],
5102
languageId: autoClosingLanguageId,
5103
editorOpts: {
5104
autoClosingBrackets: 'never',
5105
autoClosingQuotes: 'beforeWhitespace'
5106
}
5107
}, (editor, model, viewModel) => {
5108
5109
const autoClosePositions = [
5110
'var b =| [|];|',
5111
];
5112
for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5113
const lineNumber = i + 1;
5114
const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5115
5116
for (let column = 1; column < autoCloseColumns.length; column++) {
5117
model.tokenization.forceTokenization(lineNumber);
5118
if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {
5119
assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`);
5120
} else {
5121
assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);
5122
}
5123
assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
5124
}
5125
}
5126
});
5127
});
5128
5129
test('autoClosingPairs - configurable open parens', () => {
5130
setAutoClosingLanguageEnabledSet('abc');
5131
usingCursor({
5132
text: [
5133
'var a = [];',
5134
'var b = `asd`;',
5135
'var c = \'asd\';',
5136
'var d = "asd";',
5137
'var e = /*3*/ 3;',
5138
'var f = /** 3 */3;',
5139
'var g = (3+5);',
5140
'var h = { a: \'value\' };',
5141
],
5142
languageId: autoClosingLanguageId,
5143
editorOpts: {
5144
autoClosingBrackets: 'languageDefined'
5145
}
5146
}, (editor, model, viewModel) => {
5147
5148
const autoClosePositions = [
5149
'v|ar |a = [|];|',
5150
'v|ar |b = `|asd`;|',
5151
'v|ar |c = \'|asd\';|',
5152
'v|ar d = "|asd";|',
5153
'v|ar e = /*3*/ 3;|',
5154
'v|ar f = /** 3| */3;|',
5155
'v|ar g = (3+5|);|',
5156
'v|ar h = { |a: \'v|alue\' |};|',
5157
];
5158
for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5159
const lineNumber = i + 1;
5160
const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5161
5162
for (let column = 1; column < autoCloseColumns.length; column++) {
5163
model.tokenization.forceTokenization(lineNumber);
5164
if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {
5165
assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
5166
} else {
5167
assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
5168
}
5169
}
5170
}
5171
});
5172
});
5173
5174
test('autoClosingPairs - auto-pairing can be disabled', () => {
5175
usingCursor({
5176
text: [
5177
'var a = [];',
5178
'var b = `asd`;',
5179
'var c = \'asd\';',
5180
'var d = "asd";',
5181
'var e = /*3*/ 3;',
5182
'var f = /** 3 */3;',
5183
'var g = (3+5);',
5184
'var h = { a: \'value\' };',
5185
],
5186
languageId: autoClosingLanguageId,
5187
editorOpts: {
5188
autoClosingBrackets: 'never',
5189
autoClosingQuotes: 'never'
5190
}
5191
}, (editor, model, viewModel) => {
5192
5193
const autoClosePositions = [
5194
'var a = [];',
5195
'var b = `asd`;',
5196
'var c = \'asd\';',
5197
'var d = "asd";',
5198
'var e = /*3*/ 3;',
5199
'var f = /** 3 */3;',
5200
'var g = (3+5);',
5201
'var h = { a: \'value\' };',
5202
];
5203
for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5204
const lineNumber = i + 1;
5205
const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5206
5207
for (let column = 1; column < autoCloseColumns.length; column++) {
5208
model.tokenization.forceTokenization(lineNumber);
5209
if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {
5210
assertType(editor, model, viewModel, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`);
5211
assertType(editor, model, viewModel, lineNumber, column, '"', '""', `auto closes @ (${lineNumber}, ${column})`);
5212
} else {
5213
assertType(editor, model, viewModel, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`);
5214
assertType(editor, model, viewModel, lineNumber, column, '"', '"', `does not auto close @ (${lineNumber}, ${column})`);
5215
}
5216
}
5217
}
5218
});
5219
});
5220
5221
test('autoClosingPairs - auto wrapping is configurable', () => {
5222
usingCursor({
5223
text: [
5224
'var a = asd'
5225
],
5226
languageId: autoClosingLanguageId
5227
}, (editor, model, viewModel) => {
5228
5229
viewModel.setSelections('test', [
5230
new Selection(1, 1, 1, 4),
5231
new Selection(1, 9, 1, 12),
5232
]);
5233
5234
// type a `
5235
viewModel.type('`', 'keyboard');
5236
5237
assert.strictEqual(model.getValue(), '`var` a = `asd`');
5238
5239
// type a (
5240
viewModel.type('(', 'keyboard');
5241
5242
assert.strictEqual(model.getValue(), '`(var)` a = `(asd)`');
5243
});
5244
5245
usingCursor({
5246
text: [
5247
'var a = asd'
5248
],
5249
languageId: autoClosingLanguageId,
5250
editorOpts: {
5251
autoSurround: 'never'
5252
}
5253
}, (editor, model, viewModel) => {
5254
5255
viewModel.setSelections('test', [
5256
new Selection(1, 1, 1, 4),
5257
]);
5258
5259
// type a `
5260
viewModel.type('`', 'keyboard');
5261
5262
assert.strictEqual(model.getValue(), '` a = asd');
5263
});
5264
5265
usingCursor({
5266
text: [
5267
'var a = asd'
5268
],
5269
languageId: autoClosingLanguageId,
5270
editorOpts: {
5271
autoSurround: 'quotes'
5272
}
5273
}, (editor, model, viewModel) => {
5274
5275
viewModel.setSelections('test', [
5276
new Selection(1, 1, 1, 4),
5277
]);
5278
5279
// type a `
5280
viewModel.type('`', 'keyboard');
5281
assert.strictEqual(model.getValue(), '`var` a = asd');
5282
5283
// type a (
5284
viewModel.type('(', 'keyboard');
5285
assert.strictEqual(model.getValue(), '`(` a = asd');
5286
});
5287
5288
usingCursor({
5289
text: [
5290
'var a = asd'
5291
],
5292
languageId: autoClosingLanguageId,
5293
editorOpts: {
5294
autoSurround: 'brackets'
5295
}
5296
}, (editor, model, viewModel) => {
5297
5298
viewModel.setSelections('test', [
5299
new Selection(1, 1, 1, 4),
5300
]);
5301
5302
// type a (
5303
viewModel.type('(', 'keyboard');
5304
assert.strictEqual(model.getValue(), '(var) a = asd');
5305
5306
// type a `
5307
viewModel.type('`', 'keyboard');
5308
assert.strictEqual(model.getValue(), '(`) a = asd');
5309
});
5310
});
5311
5312
test('autoClosingPairs - quote', () => {
5313
usingCursor({
5314
text: [
5315
'var a = [];',
5316
'var b = `asd`;',
5317
'var c = \'asd\';',
5318
'var d = "asd";',
5319
'var e = /*3*/ 3;',
5320
'var f = /** 3 */3;',
5321
'var g = (3+5);',
5322
'var h = { a: \'value\' };',
5323
],
5324
languageId: autoClosingLanguageId
5325
}, (editor, model, viewModel) => {
5326
5327
const autoClosePositions = [
5328
'var a |=| [|]|;|',
5329
'var b |=| `asd`|;|',
5330
'var c |=| \'asd\'|;|',
5331
'var d |=| "asd"|;|',
5332
'var e |=| /*3*/| 3;|',
5333
'var f |=| /**| 3 */3;|',
5334
'var g |=| (3+5)|;|',
5335
'var h |=| {| a:| \'value\'| |}|;|',
5336
];
5337
for (let i = 0, len = autoClosePositions.length; i < len; i++) {
5338
const lineNumber = i + 1;
5339
const autoCloseColumns = extractAutoClosingSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]);
5340
5341
for (let column = 1; column < autoCloseColumns.length; column++) {
5342
model.tokenization.forceTokenization(lineNumber);
5343
if (autoCloseColumns[column] === AutoClosingColumnType.Special1) {
5344
assertType(editor, model, viewModel, lineNumber, column, '\'', '\'\'', `auto closes @ (${lineNumber}, ${column})`);
5345
} else if (autoCloseColumns[column] === AutoClosingColumnType.Special2) {
5346
assertType(editor, model, viewModel, lineNumber, column, '\'', '', `over types @ (${lineNumber}, ${column})`);
5347
} else {
5348
assertType(editor, model, viewModel, lineNumber, column, '\'', '\'', `does not auto close @ (${lineNumber}, ${column})`);
5349
}
5350
}
5351
}
5352
});
5353
});
5354
5355
test('autoClosingPairs - multi-character autoclose', () => {
5356
usingCursor({
5357
text: [
5358
'',
5359
],
5360
languageId: autoClosingLanguageId
5361
}, (editor, model, viewModel) => {
5362
5363
model.setValue('begi');
5364
viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
5365
viewModel.type('n', 'keyboard');
5366
assert.strictEqual(model.getLineContent(1), 'beginend');
5367
5368
model.setValue('/*');
5369
viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
5370
viewModel.type('*', 'keyboard');
5371
assert.strictEqual(model.getLineContent(1), '/** */');
5372
});
5373
});
5374
5375
test('autoClosingPairs - doc comments can be turned off', () => {
5376
usingCursor({
5377
text: [
5378
'',
5379
],
5380
languageId: autoClosingLanguageId,
5381
editorOpts: {
5382
autoClosingComments: 'never'
5383
}
5384
}, (editor, model, viewModel) => {
5385
5386
model.setValue('/*');
5387
viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
5388
viewModel.type('*', 'keyboard');
5389
assert.strictEqual(model.getLineContent(1), '/**');
5390
});
5391
});
5392
5393
test('issue #72177: multi-character autoclose with conflicting patterns', () => {
5394
const languageId = 'autoClosingModeMultiChar';
5395
5396
disposables.add(languageService.registerLanguage({ id: languageId }));
5397
disposables.add(languageConfigurationService.register(languageId, {
5398
autoClosingPairs: [
5399
{ open: '(', close: ')' },
5400
{ open: '(*', close: '*)' },
5401
{ open: '<@', close: '@>' },
5402
{ open: '<@@', close: '@@>' },
5403
],
5404
}));
5405
5406
usingCursor({
5407
text: [
5408
'',
5409
],
5410
languageId: languageId
5411
}, (editor, model, viewModel) => {
5412
viewModel.type('(', 'keyboard');
5413
assert.strictEqual(model.getLineContent(1), '()');
5414
viewModel.type('*', 'keyboard');
5415
assert.strictEqual(model.getLineContent(1), '(**)', `doesn't add entire close when already closed substring is there`);
5416
5417
model.setValue('(');
5418
viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]);
5419
viewModel.type('*', 'keyboard');
5420
assert.strictEqual(model.getLineContent(1), '(**)', `does add entire close if not already there`);
5421
5422
model.setValue('');
5423
viewModel.type('<@', 'keyboard');
5424
assert.strictEqual(model.getLineContent(1), '<@@>');
5425
viewModel.type('@', 'keyboard');
5426
assert.strictEqual(model.getLineContent(1), '<@@@@>', `autocloses when before multi-character closing brace`);
5427
viewModel.type('(', 'keyboard');
5428
assert.strictEqual(model.getLineContent(1), '<@@()@@>', `autocloses when before multi-character closing brace`);
5429
});
5430
});
5431
5432
test('issue #55314: Do not auto-close when ending with open', () => {
5433
const languageId = 'myElectricMode';
5434
5435
disposables.add(languageService.registerLanguage({ id: languageId }));
5436
disposables.add(languageConfigurationService.register(languageId, {
5437
autoClosingPairs: [
5438
{ open: '{', close: '}' },
5439
{ open: '[', close: ']' },
5440
{ open: '(', close: ')' },
5441
{ open: '\'', close: '\'', notIn: ['string', 'comment'] },
5442
{ open: '\"', close: '\"', notIn: ['string'] },
5443
{ open: 'B\"', close: '\"', notIn: ['string', 'comment'] },
5444
{ open: '`', close: '`', notIn: ['string', 'comment'] },
5445
{ open: '/**', close: ' */', notIn: ['string'] }
5446
],
5447
}));
5448
5449
usingCursor({
5450
text: [
5451
'little goat',
5452
'little LAMB',
5453
'little sheep',
5454
'Big LAMB'
5455
],
5456
languageId: languageId
5457
}, (editor, model, viewModel) => {
5458
model.tokenization.forceTokenization(model.getLineCount());
5459
assertType(editor, model, viewModel, 1, 4, '"', '"', `does not double quote when ending with open`);
5460
model.tokenization.forceTokenization(model.getLineCount());
5461
assertType(editor, model, viewModel, 2, 4, '"', '"', `does not double quote when ending with open`);
5462
model.tokenization.forceTokenization(model.getLineCount());
5463
assertType(editor, model, viewModel, 3, 4, '"', '"', `does not double quote when ending with open`);
5464
model.tokenization.forceTokenization(model.getLineCount());
5465
assertType(editor, model, viewModel, 4, 2, '"', '"', `does not double quote when ending with open`);
5466
model.tokenization.forceTokenization(model.getLineCount());
5467
assertType(editor, model, viewModel, 4, 3, '"', '"', `does not double quote when ending with open`);
5468
});
5469
});
5470
5471
test('issue #27937: Trying to add an item to the front of a list is cumbersome', () => {
5472
usingCursor({
5473
text: [
5474
'var arr = ["b", "c"];'
5475
],
5476
languageId: autoClosingLanguageId
5477
}, (editor, model, viewModel) => {
5478
assertType(editor, model, viewModel, 1, 12, '"', '"', `does not over type and will not auto close`);
5479
});
5480
});
5481
5482
test('issue #25658 - Do not auto-close single/double quotes after word characters', () => {
5483
usingCursor({
5484
text: [
5485
'',
5486
],
5487
languageId: autoClosingLanguageId
5488
}, (editor, model, viewModel) => {
5489
5490
function typeCharacters(viewModel: ViewModel, chars: string): void {
5491
for (let i = 0, len = chars.length; i < len; i++) {
5492
viewModel.type(chars[i], 'keyboard');
5493
}
5494
}
5495
5496
// First gif
5497
model.tokenization.forceTokenization(model.getLineCount());
5498
typeCharacters(viewModel, 'teste1 = teste\' ok');
5499
assert.strictEqual(model.getLineContent(1), 'teste1 = teste\' ok');
5500
5501
viewModel.setSelections('test', [new Selection(1, 1000, 1, 1000)]);
5502
typeCharacters(viewModel, '\n');
5503
model.tokenization.forceTokenization(model.getLineCount());
5504
typeCharacters(viewModel, 'teste2 = teste \'ok');
5505
assert.strictEqual(model.getLineContent(2), 'teste2 = teste \'ok\'');
5506
5507
viewModel.setSelections('test', [new Selection(2, 1000, 2, 1000)]);
5508
typeCharacters(viewModel, '\n');
5509
model.tokenization.forceTokenization(model.getLineCount());
5510
typeCharacters(viewModel, 'teste3 = teste" ok');
5511
assert.strictEqual(model.getLineContent(3), 'teste3 = teste" ok');
5512
5513
viewModel.setSelections('test', [new Selection(3, 1000, 3, 1000)]);
5514
typeCharacters(viewModel, '\n');
5515
model.tokenization.forceTokenization(model.getLineCount());
5516
typeCharacters(viewModel, 'teste4 = teste "ok');
5517
assert.strictEqual(model.getLineContent(4), 'teste4 = teste "ok"');
5518
5519
// Second gif
5520
viewModel.setSelections('test', [new Selection(4, 1000, 4, 1000)]);
5521
typeCharacters(viewModel, '\n');
5522
model.tokenization.forceTokenization(model.getLineCount());
5523
typeCharacters(viewModel, 'teste \'');
5524
assert.strictEqual(model.getLineContent(5), 'teste \'\'');
5525
5526
viewModel.setSelections('test', [new Selection(5, 1000, 5, 1000)]);
5527
typeCharacters(viewModel, '\n');
5528
model.tokenization.forceTokenization(model.getLineCount());
5529
typeCharacters(viewModel, 'teste "');
5530
assert.strictEqual(model.getLineContent(6), 'teste ""');
5531
5532
viewModel.setSelections('test', [new Selection(6, 1000, 6, 1000)]);
5533
typeCharacters(viewModel, '\n');
5534
model.tokenization.forceTokenization(model.getLineCount());
5535
typeCharacters(viewModel, 'teste\'');
5536
assert.strictEqual(model.getLineContent(7), 'teste\'');
5537
5538
viewModel.setSelections('test', [new Selection(7, 1000, 7, 1000)]);
5539
typeCharacters(viewModel, '\n');
5540
model.tokenization.forceTokenization(model.getLineCount());
5541
typeCharacters(viewModel, 'teste"');
5542
assert.strictEqual(model.getLineContent(8), 'teste"');
5543
});
5544
});
5545
5546
test('issue #37315 - overtypes only those characters that it inserted', () => {
5547
usingCursor({
5548
text: [
5549
'',
5550
'y=();'
5551
],
5552
languageId: autoClosingLanguageId
5553
}, (editor, model, viewModel) => {
5554
assertCursor(viewModel, new Position(1, 1));
5555
5556
viewModel.type('x=(', 'keyboard');
5557
assert.strictEqual(model.getLineContent(1), 'x=()');
5558
5559
viewModel.type('asd', 'keyboard');
5560
assert.strictEqual(model.getLineContent(1), 'x=(asd)');
5561
5562
// overtype!
5563
viewModel.type(')', 'keyboard');
5564
assert.strictEqual(model.getLineContent(1), 'x=(asd)');
5565
5566
// do not overtype!
5567
viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);
5568
viewModel.type(')', 'keyboard');
5569
assert.strictEqual(model.getLineContent(2), 'y=());');
5570
5571
});
5572
});
5573
5574
test('issue #37315 - stops overtyping once cursor leaves area', () => {
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.setSelections('test', [new Selection(1, 5, 1, 5)]);
5588
viewModel.type(')', 'keyboard');
5589
assert.strictEqual(model.getLineContent(1), 'x=())');
5590
});
5591
});
5592
5593
test('issue #37315 - it overtypes only once', () => {
5594
usingCursor({
5595
text: [
5596
'',
5597
'y=();'
5598
],
5599
languageId: autoClosingLanguageId
5600
}, (editor, model, viewModel) => {
5601
assertCursor(viewModel, new Position(1, 1));
5602
5603
viewModel.type('x=(', 'keyboard');
5604
assert.strictEqual(model.getLineContent(1), 'x=()');
5605
5606
viewModel.type(')', 'keyboard');
5607
assert.strictEqual(model.getLineContent(1), 'x=()');
5608
5609
viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);
5610
viewModel.type(')', 'keyboard');
5611
assert.strictEqual(model.getLineContent(1), 'x=())');
5612
});
5613
});
5614
5615
test('issue #37315 - it can remember multiple auto-closed instances', () => {
5616
usingCursor({
5617
text: [
5618
'',
5619
'y=();'
5620
],
5621
languageId: autoClosingLanguageId
5622
}, (editor, model, viewModel) => {
5623
assertCursor(viewModel, new Position(1, 1));
5624
5625
viewModel.type('x=(', 'keyboard');
5626
assert.strictEqual(model.getLineContent(1), 'x=()');
5627
5628
viewModel.type('(', 'keyboard');
5629
assert.strictEqual(model.getLineContent(1), 'x=(())');
5630
5631
viewModel.type(')', 'keyboard');
5632
assert.strictEqual(model.getLineContent(1), 'x=(())');
5633
5634
viewModel.type(')', 'keyboard');
5635
assert.strictEqual(model.getLineContent(1), 'x=(())');
5636
});
5637
});
5638
5639
test('issue #118270 - auto closing deletes only those characters that it inserted', () => {
5640
usingCursor({
5641
text: [
5642
'',
5643
'y=();'
5644
],
5645
languageId: autoClosingLanguageId
5646
}, (editor, model, viewModel) => {
5647
assertCursor(viewModel, new Position(1, 1));
5648
5649
viewModel.type('x=(', 'keyboard');
5650
assert.strictEqual(model.getLineContent(1), 'x=()');
5651
5652
viewModel.type('asd', 'keyboard');
5653
assert.strictEqual(model.getLineContent(1), 'x=(asd)');
5654
5655
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
5656
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
5657
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
5658
assert.strictEqual(model.getLineContent(1), 'x=()');
5659
5660
// delete closing char!
5661
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
5662
assert.strictEqual(model.getLineContent(1), 'x=');
5663
5664
// do not delete closing char!
5665
viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);
5666
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
5667
assert.strictEqual(model.getLineContent(2), 'y=);');
5668
5669
});
5670
});
5671
5672
test('issue #78527 - does not close quote on odd count', () => {
5673
usingCursor({
5674
text: [
5675
'std::cout << \'"\' << entryMap'
5676
],
5677
languageId: autoClosingLanguageId
5678
}, (editor, model, viewModel) => {
5679
viewModel.setSelections('test', [new Selection(1, 29, 1, 29)]);
5680
5681
viewModel.type('[', 'keyboard');
5682
assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[]');
5683
5684
viewModel.type('"', 'keyboard');
5685
assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap[""]');
5686
5687
viewModel.type('a', 'keyboard');
5688
assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');
5689
5690
viewModel.type('"', 'keyboard');
5691
assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');
5692
5693
viewModel.type(']', 'keyboard');
5694
assert.strictEqual(model.getLineContent(1), 'std::cout << \'"\' << entryMap["a"]');
5695
});
5696
});
5697
5698
test('issue #85983 - editor.autoClosingBrackets: beforeWhitespace is incorrect for Python', () => {
5699
const languageId = 'pythonMode';
5700
5701
disposables.add(languageService.registerLanguage({ id: languageId }));
5702
disposables.add(languageConfigurationService.register(languageId, {
5703
autoClosingPairs: [
5704
{ open: '{', close: '}' },
5705
{ open: '[', close: ']' },
5706
{ open: '(', close: ')' },
5707
{ open: '\"', close: '\"', notIn: ['string'] },
5708
{ open: 'r\"', close: '\"', notIn: ['string', 'comment'] },
5709
{ open: 'R\"', close: '\"', notIn: ['string', 'comment'] },
5710
{ open: 'u\"', close: '\"', notIn: ['string', 'comment'] },
5711
{ open: 'U\"', close: '\"', notIn: ['string', 'comment'] },
5712
{ open: 'f\"', close: '\"', notIn: ['string', 'comment'] },
5713
{ open: 'F\"', close: '\"', notIn: ['string', 'comment'] },
5714
{ open: 'b\"', close: '\"', notIn: ['string', 'comment'] },
5715
{ open: 'B\"', close: '\"', notIn: ['string', 'comment'] },
5716
{ open: '\'', close: '\'', notIn: ['string', 'comment'] },
5717
{ open: 'r\'', close: '\'', notIn: ['string', 'comment'] },
5718
{ open: 'R\'', close: '\'', notIn: ['string', 'comment'] },
5719
{ open: 'u\'', close: '\'', notIn: ['string', 'comment'] },
5720
{ open: 'U\'', close: '\'', notIn: ['string', 'comment'] },
5721
{ open: 'f\'', close: '\'', notIn: ['string', 'comment'] },
5722
{ open: 'F\'', close: '\'', notIn: ['string', 'comment'] },
5723
{ open: 'b\'', close: '\'', notIn: ['string', 'comment'] },
5724
{ open: 'B\'', close: '\'', notIn: ['string', 'comment'] },
5725
{ open: '`', close: '`', notIn: ['string'] }
5726
],
5727
}));
5728
5729
usingCursor({
5730
text: [
5731
'foo\'hello\''
5732
],
5733
editorOpts: {
5734
autoClosingBrackets: 'beforeWhitespace'
5735
},
5736
languageId: languageId
5737
}, (editor, model, viewModel) => {
5738
assertType(editor, model, viewModel, 1, 4, '(', '(', `does not auto close @ (1, 4)`);
5739
});
5740
});
5741
5742
test('issue #78975 - Parentheses swallowing does not work when parentheses are inserted by autocomplete', () => {
5743
usingCursor({
5744
text: [
5745
'<div id'
5746
],
5747
languageId: autoClosingLanguageId
5748
}, (editor, model, viewModel) => {
5749
viewModel.setSelections('test', [new Selection(1, 8, 1, 8)]);
5750
5751
viewModel.executeEdits('snippet', [{ range: new Range(1, 6, 1, 8), text: 'id=""' }], () => [new Selection(1, 10, 1, 10)], EditSources.unknown({}));
5752
assert.strictEqual(model.getLineContent(1), '<div id=""');
5753
5754
viewModel.type('a', 'keyboard');
5755
assert.strictEqual(model.getLineContent(1), '<div id="a"');
5756
5757
viewModel.type('"', 'keyboard');
5758
assert.strictEqual(model.getLineContent(1), '<div id="a"');
5759
});
5760
});
5761
5762
test('issue #78833 - Add config to use old brackets/quotes overtyping', () => {
5763
usingCursor({
5764
text: [
5765
'',
5766
'y=();'
5767
],
5768
languageId: autoClosingLanguageId,
5769
editorOpts: {
5770
autoClosingOvertype: 'always'
5771
}
5772
}, (editor, model, viewModel) => {
5773
assertCursor(viewModel, new Position(1, 1));
5774
5775
viewModel.type('x=(', 'keyboard');
5776
assert.strictEqual(model.getLineContent(1), 'x=()');
5777
5778
viewModel.type(')', 'keyboard');
5779
assert.strictEqual(model.getLineContent(1), 'x=()');
5780
5781
viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);
5782
viewModel.type(')', 'keyboard');
5783
assert.strictEqual(model.getLineContent(1), 'x=()');
5784
5785
viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]);
5786
viewModel.type(')', 'keyboard');
5787
assert.strictEqual(model.getLineContent(2), 'y=();');
5788
});
5789
});
5790
5791
test('issue #15825: accents on mac US intl keyboard', () => {
5792
usingCursor({
5793
text: [
5794
],
5795
languageId: autoClosingLanguageId
5796
}, (editor, model, viewModel) => {
5797
assertCursor(viewModel, new Position(1, 1));
5798
5799
// Typing ` + e on the mac US intl kb layout
5800
viewModel.startComposition();
5801
viewModel.type('`', 'keyboard');
5802
viewModel.compositionType('è', 1, 0, 0, 'keyboard');
5803
viewModel.endComposition('keyboard');
5804
5805
assert.strictEqual(model.getValue(), 'è');
5806
});
5807
});
5808
5809
test('issue #90016: allow accents on mac US intl keyboard to surround selection', () => {
5810
usingCursor({
5811
text: [
5812
'test'
5813
],
5814
languageId: autoClosingLanguageId
5815
}, (editor, model, viewModel) => {
5816
viewModel.setSelections('test', [new Selection(1, 1, 1, 5)]);
5817
5818
// Typing ` + e on the mac US intl kb layout
5819
viewModel.startComposition();
5820
viewModel.type('\'', 'keyboard');
5821
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5822
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5823
viewModel.endComposition('keyboard');
5824
5825
assert.strictEqual(model.getValue(), '\'test\'');
5826
});
5827
});
5828
5829
test('issue #53357: Over typing ignores characters after backslash', () => {
5830
usingCursor({
5831
text: [
5832
'console.log();'
5833
],
5834
languageId: autoClosingLanguageId
5835
}, (editor, model, viewModel) => {
5836
5837
viewModel.setSelections('test', [new Selection(1, 13, 1, 13)]);
5838
5839
viewModel.type('\'', 'keyboard');
5840
assert.strictEqual(model.getValue(), 'console.log(\'\');');
5841
5842
viewModel.type('it', 'keyboard');
5843
assert.strictEqual(model.getValue(), 'console.log(\'it\');');
5844
5845
viewModel.type('\\', 'keyboard');
5846
assert.strictEqual(model.getValue(), 'console.log(\'it\\\');');
5847
5848
viewModel.type('\'', 'keyboard');
5849
assert.strictEqual(model.getValue(), 'console.log(\'it\\\'\');');
5850
});
5851
});
5852
5853
test('issue #84998: Overtyping Brackets doesn\'t work after backslash', () => {
5854
usingCursor({
5855
text: [
5856
''
5857
],
5858
languageId: autoClosingLanguageId
5859
}, (editor, model, viewModel) => {
5860
5861
viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);
5862
5863
viewModel.type('\\', 'keyboard');
5864
assert.strictEqual(model.getValue(), '\\');
5865
5866
viewModel.type('(', 'keyboard');
5867
assert.strictEqual(model.getValue(), '\\()');
5868
5869
viewModel.type('abc', 'keyboard');
5870
assert.strictEqual(model.getValue(), '\\(abc)');
5871
5872
viewModel.type('\\', 'keyboard');
5873
assert.strictEqual(model.getValue(), '\\(abc\\)');
5874
5875
viewModel.type(')', 'keyboard');
5876
assert.strictEqual(model.getValue(), '\\(abc\\)');
5877
});
5878
});
5879
5880
test('issue #2773: Accents (´`¨^, others?) are inserted in the wrong position (Mac)', () => {
5881
usingCursor({
5882
text: [
5883
'hello',
5884
'world'
5885
],
5886
languageId: autoClosingLanguageId
5887
}, (editor, model, viewModel) => {
5888
assertCursor(viewModel, new Position(1, 1));
5889
5890
// Typing ` and pressing shift+down on the mac US intl kb layout
5891
// Here we're just replaying what the cursor gets
5892
viewModel.startComposition();
5893
viewModel.type('`', 'keyboard');
5894
moveDown(editor, viewModel, true);
5895
viewModel.compositionType('`', 1, 0, 0, 'keyboard');
5896
viewModel.compositionType('`', 1, 0, 0, 'keyboard');
5897
viewModel.endComposition('keyboard');
5898
5899
assert.strictEqual(model.getValue(), '`hello\nworld');
5900
assertCursor(viewModel, new Selection(1, 2, 2, 2));
5901
});
5902
});
5903
5904
test('issue #26820: auto close quotes when not used as accents', () => {
5905
usingCursor({
5906
text: [
5907
''
5908
],
5909
languageId: autoClosingLanguageId
5910
}, (editor, model, viewModel) => {
5911
assertCursor(viewModel, new Position(1, 1));
5912
5913
// on the mac US intl kb layout
5914
5915
// Typing ' + space
5916
viewModel.startComposition();
5917
viewModel.type('\'', 'keyboard');
5918
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5919
viewModel.endComposition('keyboard');
5920
assert.strictEqual(model.getValue(), '\'\'');
5921
5922
// Typing one more ' + space
5923
viewModel.startComposition();
5924
viewModel.type('\'', 'keyboard');
5925
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5926
viewModel.endComposition('keyboard');
5927
assert.strictEqual(model.getValue(), '\'\'');
5928
5929
// Typing ' as a closing tag
5930
model.setValue('\'abc');
5931
viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
5932
viewModel.startComposition();
5933
viewModel.type('\'', 'keyboard');
5934
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5935
viewModel.endComposition('keyboard');
5936
5937
assert.strictEqual(model.getValue(), '\'abc\'');
5938
5939
// quotes before the newly added character are all paired.
5940
model.setValue('\'abc\'def ');
5941
viewModel.setSelections('test', [new Selection(1, 10, 1, 10)]);
5942
viewModel.startComposition();
5943
viewModel.type('\'', 'keyboard');
5944
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5945
viewModel.endComposition('keyboard');
5946
5947
assert.strictEqual(model.getValue(), '\'abc\'def \'\'');
5948
5949
// No auto closing if there is non-whitespace character after the cursor
5950
model.setValue('abc');
5951
viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);
5952
viewModel.startComposition();
5953
viewModel.type('\'', 'keyboard');
5954
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5955
viewModel.endComposition('keyboard');
5956
5957
// No auto closing if it's after a word.
5958
model.setValue('abc');
5959
viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);
5960
viewModel.startComposition();
5961
viewModel.type('\'', 'keyboard');
5962
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
5963
viewModel.endComposition('keyboard');
5964
5965
assert.strictEqual(model.getValue(), 'abc\'');
5966
});
5967
});
5968
5969
test('issue #144690: Quotes do not overtype when using US Intl PC keyboard layout', () => {
5970
usingCursor({
5971
text: [
5972
''
5973
],
5974
languageId: autoClosingLanguageId
5975
}, (editor, model, viewModel) => {
5976
assertCursor(viewModel, new Position(1, 1));
5977
5978
// Pressing ' + ' + ;
5979
5980
viewModel.startComposition();
5981
viewModel.type(`'`, 'keyboard');
5982
viewModel.compositionType(`'`, 1, 0, 0, 'keyboard');
5983
viewModel.compositionType(`'`, 1, 0, 0, 'keyboard');
5984
viewModel.endComposition('keyboard');
5985
viewModel.startComposition();
5986
viewModel.type(`'`, 'keyboard');
5987
viewModel.compositionType(`';`, 1, 0, 0, 'keyboard');
5988
viewModel.compositionType(`';`, 2, 0, 0, 'keyboard');
5989
viewModel.endComposition('keyboard');
5990
5991
assert.strictEqual(model.getValue(), `'';`);
5992
});
5993
});
5994
5995
test('issue #144693: Typing a quote using US Intl PC keyboard layout always surrounds words', () => {
5996
usingCursor({
5997
text: [
5998
'const hello = 3;'
5999
],
6000
languageId: autoClosingLanguageId
6001
}, (editor, model, viewModel) => {
6002
viewModel.setSelections('test', [new Selection(1, 7, 1, 12)]);
6003
6004
// Pressing ' + e
6005
6006
viewModel.startComposition();
6007
viewModel.type(`'`, 'keyboard');
6008
viewModel.compositionType(`é`, 1, 0, 0, 'keyboard');
6009
viewModel.compositionType(`é`, 1, 0, 0, 'keyboard');
6010
viewModel.endComposition('keyboard');
6011
6012
assert.strictEqual(model.getValue(), `const é = 3;`);
6013
});
6014
});
6015
6016
test('issue #82701: auto close does not execute when IME is canceled via backspace', () => {
6017
usingCursor({
6018
text: [
6019
'{}'
6020
],
6021
languageId: autoClosingLanguageId
6022
}, (editor, model, viewModel) => {
6023
viewModel.setSelections('test', [new Selection(1, 2, 1, 2)]);
6024
6025
// Typing a + backspace
6026
viewModel.startComposition();
6027
viewModel.type('a', 'keyboard');
6028
viewModel.compositionType('', 1, 0, 0, 'keyboard');
6029
viewModel.endComposition('keyboard');
6030
assert.strictEqual(model.getValue(), '{}');
6031
});
6032
});
6033
6034
test('issue #20891: All cursors should do the same thing', () => {
6035
usingCursor({
6036
text: [
6037
'var a = asd'
6038
],
6039
languageId: autoClosingLanguageId
6040
}, (editor, model, viewModel) => {
6041
6042
viewModel.setSelections('test', [
6043
new Selection(1, 9, 1, 9),
6044
new Selection(1, 12, 1, 12),
6045
]);
6046
6047
// type a `
6048
viewModel.type('`', 'keyboard');
6049
6050
assert.strictEqual(model.getValue(), 'var a = `asd`');
6051
});
6052
});
6053
6054
test('issue #41825: Special handling of quotes in surrounding pairs', () => {
6055
const languageId = 'myMode';
6056
6057
disposables.add(languageService.registerLanguage({ id: languageId }));
6058
disposables.add(languageConfigurationService.register(languageId, {
6059
surroundingPairs: [
6060
{ open: '"', close: '"' },
6061
{ open: '\'', close: '\'' },
6062
]
6063
}));
6064
6065
const model = createTextModel('var x = \'hi\';', languageId);
6066
6067
withTestCodeEditor(model, {}, (editor, viewModel) => {
6068
editor.setSelections([
6069
new Selection(1, 9, 1, 10),
6070
new Selection(1, 12, 1, 13)
6071
]);
6072
viewModel.type('"', 'keyboard');
6073
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'var x = "hi";', 'assert1');
6074
6075
editor.setSelections([
6076
new Selection(1, 9, 1, 10),
6077
new Selection(1, 12, 1, 13)
6078
]);
6079
viewModel.type('\'', 'keyboard');
6080
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'var x = \'hi\';', 'assert2');
6081
});
6082
});
6083
6084
test('All cursors should do the same thing when deleting left', () => {
6085
const model = createTextModel(
6086
[
6087
'var a = ()'
6088
].join('\n'),
6089
autoClosingLanguageId
6090
);
6091
6092
withTestCodeEditor(model, {}, (editor, viewModel) => {
6093
viewModel.setSelections('test', [
6094
new Selection(1, 4, 1, 4),
6095
new Selection(1, 10, 1, 10),
6096
]);
6097
6098
// delete left
6099
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6100
6101
assert.strictEqual(model.getValue(), 'va a = )');
6102
});
6103
});
6104
6105
test('issue #7100: Mouse word selection is strange when non-word character is at the end of line', () => {
6106
const model = createTextModel(
6107
[
6108
'before.a',
6109
'before',
6110
'hello:',
6111
'there:',
6112
'this is strange:',
6113
'here',
6114
'it',
6115
'is',
6116
].join('\n')
6117
);
6118
6119
withTestCodeEditor(model, {}, (editor, viewModel) => {
6120
editor.runCommand(CoreNavigationCommands.WordSelect, {
6121
position: new Position(3, 7)
6122
});
6123
assertCursor(viewModel, new Selection(3, 7, 3, 7));
6124
6125
editor.runCommand(CoreNavigationCommands.WordSelectDrag, {
6126
position: new Position(4, 7)
6127
});
6128
assertCursor(viewModel, new Selection(3, 7, 4, 7));
6129
});
6130
});
6131
6132
test('issue #112039: shift-continuing a double/triple-click and drag selection does not remember its starting mode', () => {
6133
const model = createTextModel(
6134
[
6135
'just some text',
6136
'and another line',
6137
'and another one',
6138
].join('\n')
6139
);
6140
6141
withTestCodeEditor(model, {}, (editor, viewModel) => {
6142
editor.runCommand(CoreNavigationCommands.WordSelect, {
6143
position: new Position(2, 6)
6144
});
6145
editor.runCommand(CoreNavigationCommands.MoveToSelect, {
6146
position: new Position(1, 8),
6147
});
6148
assertCursor(viewModel, new Selection(2, 12, 1, 6));
6149
});
6150
});
6151
6152
test('issue #158236: Shift click selection does not work on line number indicator', () => {
6153
const model = createTextModel(
6154
[
6155
'just some text',
6156
'and another line',
6157
'and another one',
6158
].join('\n')
6159
);
6160
6161
withTestCodeEditor(model, {}, (editor, viewModel) => {
6162
editor.runCommand(CoreNavigationCommands.MoveTo, {
6163
position: new Position(3, 5)
6164
});
6165
editor.runCommand(CoreNavigationCommands.LineSelectDrag, {
6166
position: new Position(2, 1)
6167
});
6168
assertCursor(viewModel, new Selection(3, 5, 2, 1));
6169
});
6170
});
6171
6172
test('issue #111513: Text gets automatically selected when typing at the same location in another editor', () => {
6173
const model = createTextModel(
6174
[
6175
'just',
6176
'',
6177
'some text',
6178
].join('\n')
6179
);
6180
6181
withTestCodeEditor(model, {}, (editor1, viewModel1) => {
6182
editor1.setSelections([
6183
new Selection(2, 1, 2, 1)
6184
]);
6185
withTestCodeEditor(model, {}, (editor2, viewModel2) => {
6186
editor2.setSelections([
6187
new Selection(2, 1, 2, 1)
6188
]);
6189
viewModel2.type('e', 'keyboard');
6190
assertCursor(viewModel2, new Position(2, 2));
6191
assertCursor(viewModel1, new Position(2, 2));
6192
});
6193
});
6194
});
6195
});
6196
6197
suite('Undo stops', () => {
6198
6199
ensureNoDisposablesAreLeakedInTestSuite();
6200
6201
test('there is an undo stop between typing and deleting left', () => {
6202
const model = createTextModel(
6203
[
6204
'A line',
6205
'Another line',
6206
].join('\n')
6207
);
6208
6209
withTestCodeEditor(model, {}, (editor, viewModel) => {
6210
viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
6211
viewModel.type('first', 'keyboard');
6212
assert.strictEqual(model.getLineContent(1), 'A first line');
6213
assertCursor(viewModel, new Selection(1, 8, 1, 8));
6214
6215
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6216
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6217
assert.strictEqual(model.getLineContent(1), 'A fir line');
6218
assertCursor(viewModel, new Selection(1, 6, 1, 6));
6219
6220
editor.runCommand(CoreEditingCommands.Undo, null);
6221
assert.strictEqual(model.getLineContent(1), 'A first line');
6222
assertCursor(viewModel, new Selection(1, 8, 1, 8));
6223
6224
editor.runCommand(CoreEditingCommands.Undo, null);
6225
assert.strictEqual(model.getLineContent(1), 'A line');
6226
assertCursor(viewModel, new Selection(1, 3, 1, 3));
6227
});
6228
6229
model.dispose();
6230
});
6231
6232
test('there is an undo stop between typing and deleting right', () => {
6233
const model = createTextModel(
6234
[
6235
'A line',
6236
'Another line',
6237
].join('\n')
6238
);
6239
6240
withTestCodeEditor(model, {}, (editor, viewModel) => {
6241
viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
6242
viewModel.type('first', 'keyboard');
6243
assert.strictEqual(model.getLineContent(1), 'A first line');
6244
assertCursor(viewModel, new Selection(1, 8, 1, 8));
6245
6246
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6247
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6248
assert.strictEqual(model.getLineContent(1), 'A firstine');
6249
assertCursor(viewModel, new Selection(1, 8, 1, 8));
6250
6251
editor.runCommand(CoreEditingCommands.Undo, null);
6252
assert.strictEqual(model.getLineContent(1), 'A first line');
6253
assertCursor(viewModel, new Selection(1, 8, 1, 8));
6254
6255
editor.runCommand(CoreEditingCommands.Undo, null);
6256
assert.strictEqual(model.getLineContent(1), 'A line');
6257
assertCursor(viewModel, new Selection(1, 3, 1, 3));
6258
});
6259
6260
model.dispose();
6261
});
6262
6263
test('there is an undo stop between deleting left and typing', () => {
6264
const model = createTextModel(
6265
[
6266
'A line',
6267
'Another line',
6268
].join('\n')
6269
);
6270
6271
withTestCodeEditor(model, {}, (editor, viewModel) => {
6272
viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]);
6273
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6274
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6275
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6276
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6277
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6278
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6279
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6280
assert.strictEqual(model.getLineContent(2), ' line');
6281
assertCursor(viewModel, new Selection(2, 1, 2, 1));
6282
6283
viewModel.type('Second', 'keyboard');
6284
assert.strictEqual(model.getLineContent(2), 'Second line');
6285
assertCursor(viewModel, new Selection(2, 7, 2, 7));
6286
6287
editor.runCommand(CoreEditingCommands.Undo, null);
6288
assert.strictEqual(model.getLineContent(2), ' line');
6289
assertCursor(viewModel, new Selection(2, 1, 2, 1));
6290
6291
editor.runCommand(CoreEditingCommands.Undo, null);
6292
assert.strictEqual(model.getLineContent(2), 'Another line');
6293
assertCursor(viewModel, new Selection(2, 8, 2, 8));
6294
});
6295
6296
model.dispose();
6297
});
6298
6299
test('there is an undo stop between deleting left and deleting right', () => {
6300
const model = createTextModel(
6301
[
6302
'A line',
6303
'Another line',
6304
].join('\n')
6305
);
6306
6307
withTestCodeEditor(model, {}, (editor, viewModel) => {
6308
viewModel.setSelections('test', [new Selection(2, 8, 2, 8)]);
6309
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6310
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6311
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6312
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6313
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6314
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6315
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6316
assert.strictEqual(model.getLineContent(2), ' line');
6317
assertCursor(viewModel, new Selection(2, 1, 2, 1));
6318
6319
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6320
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6321
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6322
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6323
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6324
assert.strictEqual(model.getLineContent(2), '');
6325
assertCursor(viewModel, new Selection(2, 1, 2, 1));
6326
6327
editor.runCommand(CoreEditingCommands.Undo, null);
6328
assert.strictEqual(model.getLineContent(2), ' line');
6329
assertCursor(viewModel, new Selection(2, 1, 2, 1));
6330
6331
editor.runCommand(CoreEditingCommands.Undo, null);
6332
assert.strictEqual(model.getLineContent(2), 'Another line');
6333
assertCursor(viewModel, new Selection(2, 8, 2, 8));
6334
});
6335
6336
model.dispose();
6337
});
6338
6339
test('there is an undo stop between deleting right and typing', () => {
6340
const model = createTextModel(
6341
[
6342
'A line',
6343
'Another line',
6344
].join('\n')
6345
);
6346
6347
withTestCodeEditor(model, {}, (editor, viewModel) => {
6348
viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]);
6349
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6350
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6351
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6352
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6353
assert.strictEqual(model.getLineContent(2), 'Another ');
6354
assertCursor(viewModel, new Selection(2, 9, 2, 9));
6355
6356
viewModel.type('text', 'keyboard');
6357
assert.strictEqual(model.getLineContent(2), 'Another text');
6358
assertCursor(viewModel, new Selection(2, 13, 2, 13));
6359
6360
editor.runCommand(CoreEditingCommands.Undo, null);
6361
assert.strictEqual(model.getLineContent(2), 'Another ');
6362
assertCursor(viewModel, new Selection(2, 9, 2, 9));
6363
6364
editor.runCommand(CoreEditingCommands.Undo, null);
6365
assert.strictEqual(model.getLineContent(2), 'Another line');
6366
assertCursor(viewModel, new Selection(2, 9, 2, 9));
6367
});
6368
6369
model.dispose();
6370
});
6371
6372
test('there is an undo stop between deleting right and deleting left', () => {
6373
const model = createTextModel(
6374
[
6375
'A line',
6376
'Another line',
6377
].join('\n')
6378
);
6379
6380
withTestCodeEditor(model, {}, (editor, viewModel) => {
6381
viewModel.setSelections('test', [new Selection(2, 9, 2, 9)]);
6382
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6383
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6384
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6385
editor.runCommand(CoreEditingCommands.DeleteRight, null);
6386
assert.strictEqual(model.getLineContent(2), 'Another ');
6387
assertCursor(viewModel, new Selection(2, 9, 2, 9));
6388
6389
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6390
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6391
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6392
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6393
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6394
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
6395
assert.strictEqual(model.getLineContent(2), 'An');
6396
assertCursor(viewModel, new Selection(2, 3, 2, 3));
6397
6398
editor.runCommand(CoreEditingCommands.Undo, null);
6399
assert.strictEqual(model.getLineContent(2), 'Another ');
6400
assertCursor(viewModel, new Selection(2, 9, 2, 9));
6401
6402
editor.runCommand(CoreEditingCommands.Undo, null);
6403
assert.strictEqual(model.getLineContent(2), 'Another line');
6404
assertCursor(viewModel, new Selection(2, 9, 2, 9));
6405
});
6406
6407
model.dispose();
6408
});
6409
6410
test('inserts undo stop when typing space', () => {
6411
const model = createTextModel(
6412
[
6413
'A line',
6414
'Another line',
6415
].join('\n')
6416
);
6417
6418
withTestCodeEditor(model, {}, (editor, viewModel) => {
6419
viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
6420
viewModel.type('first and interesting', 'keyboard');
6421
assert.strictEqual(model.getLineContent(1), 'A first and interesting line');
6422
assertCursor(viewModel, new Selection(1, 24, 1, 24));
6423
6424
editor.runCommand(CoreEditingCommands.Undo, null);
6425
assert.strictEqual(model.getLineContent(1), 'A first and line');
6426
assertCursor(viewModel, new Selection(1, 12, 1, 12));
6427
6428
editor.runCommand(CoreEditingCommands.Undo, null);
6429
assert.strictEqual(model.getLineContent(1), 'A first line');
6430
assertCursor(viewModel, new Selection(1, 8, 1, 8));
6431
6432
editor.runCommand(CoreEditingCommands.Undo, null);
6433
assert.strictEqual(model.getLineContent(1), 'A line');
6434
assertCursor(viewModel, new Selection(1, 3, 1, 3));
6435
});
6436
6437
model.dispose();
6438
});
6439
6440
test('can undo typing and EOL change in one undo stop', () => {
6441
const model = createTextModel(
6442
[
6443
'A line',
6444
'Another line',
6445
].join('\n')
6446
);
6447
6448
withTestCodeEditor(model, {}, (editor, viewModel) => {
6449
viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
6450
viewModel.type('first', 'keyboard');
6451
assert.strictEqual(model.getValue(), 'A first line\nAnother line');
6452
assertCursor(viewModel, new Selection(1, 8, 1, 8));
6453
6454
model.pushEOL(EndOfLineSequence.CRLF);
6455
assert.strictEqual(model.getValue(), 'A first line\r\nAnother line');
6456
assertCursor(viewModel, new Selection(1, 8, 1, 8));
6457
6458
editor.runCommand(CoreEditingCommands.Undo, null);
6459
assert.strictEqual(model.getValue(), 'A line\nAnother line');
6460
assertCursor(viewModel, new Selection(1, 3, 1, 3));
6461
});
6462
6463
model.dispose();
6464
});
6465
6466
test('issue #93585: Undo multi cursor edit corrupts document', () => {
6467
const model = createTextModel(
6468
[
6469
'hello world',
6470
'hello world',
6471
].join('\n')
6472
);
6473
6474
withTestCodeEditor(model, {}, (editor, viewModel) => {
6475
viewModel.setSelections('test', [
6476
new Selection(2, 7, 2, 12),
6477
new Selection(1, 7, 1, 12),
6478
]);
6479
viewModel.type('no', 'keyboard');
6480
assert.strictEqual(model.getValue(), 'hello no\nhello no');
6481
6482
editor.runCommand(CoreEditingCommands.Undo, null);
6483
assert.strictEqual(model.getValue(), 'hello world\nhello world');
6484
});
6485
6486
model.dispose();
6487
});
6488
6489
test('there is a single undo stop for consecutive whitespaces', () => {
6490
const model = createTextModel(
6491
[
6492
''
6493
].join('\n'),
6494
undefined,
6495
{
6496
insertSpaces: false,
6497
}
6498
);
6499
6500
withTestCodeEditor(model, {}, (editor, viewModel) => {
6501
viewModel.type('a', 'keyboard');
6502
viewModel.type('b', 'keyboard');
6503
viewModel.type(' ', 'keyboard');
6504
viewModel.type(' ', 'keyboard');
6505
viewModel.type('c', 'keyboard');
6506
viewModel.type('d', 'keyboard');
6507
6508
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab cd', 'assert1');
6509
6510
editor.runCommand(CoreEditingCommands.Undo, null);
6511
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab ', 'assert2');
6512
6513
editor.runCommand(CoreEditingCommands.Undo, null);
6514
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab', 'assert3');
6515
6516
editor.runCommand(CoreEditingCommands.Undo, null);
6517
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '', 'assert4');
6518
});
6519
6520
model.dispose();
6521
});
6522
6523
test('there is no undo stop after a single whitespace', () => {
6524
const model = createTextModel(
6525
[
6526
''
6527
].join('\n'),
6528
undefined,
6529
{
6530
insertSpaces: false,
6531
}
6532
);
6533
6534
withTestCodeEditor(model, {}, (editor, viewModel) => {
6535
viewModel.type('a', 'keyboard');
6536
viewModel.type('b', 'keyboard');
6537
viewModel.type(' ', 'keyboard');
6538
viewModel.type('c', 'keyboard');
6539
viewModel.type('d', 'keyboard');
6540
6541
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab cd', 'assert1');
6542
6543
editor.runCommand(CoreEditingCommands.Undo, null);
6544
assert.strictEqual(model.getValue(EndOfLinePreference.LF), 'ab', 'assert3');
6545
6546
editor.runCommand(CoreEditingCommands.Undo, null);
6547
assert.strictEqual(model.getValue(EndOfLinePreference.LF), '', 'assert4');
6548
});
6549
6550
model.dispose();
6551
});
6552
});
6553
6554
suite('Overtype Mode', () => {
6555
6556
setup(() => {
6557
InputMode.setInputMode('overtype');
6558
});
6559
6560
teardown(() => {
6561
InputMode.setInputMode('insert');
6562
});
6563
6564
ensureNoDisposablesAreLeakedInTestSuite();
6565
6566
test('simple type', () => {
6567
const model = createTextModel(
6568
[
6569
'123456789',
6570
'123456789',
6571
].join('\n'),
6572
undefined,
6573
{
6574
insertSpaces: false,
6575
}
6576
);
6577
6578
withTestCodeEditor(model, {}, (editor, viewModel) => {
6579
viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]);
6580
viewModel.type('a', 'keyboard');
6581
assert.strictEqual(model.getValue(EndOfLinePreference.LF), [
6582
'12a456789',
6583
'123456789',
6584
].join('\n'), 'assert1');
6585
6586
viewModel.setSelections('test', [new Selection(1, 9, 1, 9)]);
6587
viewModel.type('bbb', 'keyboard');
6588
assert.strictEqual(model.getValue(EndOfLinePreference.LF), [
6589
'12a45678bbb',
6590
'123456789',
6591
].join('\n'), 'assert2');
6592
});
6593
6594
model.dispose();
6595
});
6596
6597
test('multi-line selection type', () => {
6598
const model = createTextModel(
6599
[
6600
'123456789',
6601
'123456789',
6602
].join('\n'),
6603
undefined,
6604
{
6605
insertSpaces: false,
6606
}
6607
);
6608
6609
withTestCodeEditor(model, {}, (editor, viewModel) => {
6610
viewModel.setSelections('test', [new Selection(1, 5, 2, 3)]);
6611
viewModel.type('cc', 'keyboard');
6612
assert.strictEqual(model.getValue(EndOfLinePreference.LF), [
6613
'1234cc456789',
6614
].join('\n'), 'assert1');
6615
});
6616
6617
model.dispose();
6618
});
6619
6620
test('simple paste', () => {
6621
const model = createTextModel(
6622
[
6623
'123456789',
6624
'123456789',
6625
].join('\n'),
6626
undefined,
6627
{
6628
insertSpaces: false,
6629
}
6630
);
6631
6632
withTestCodeEditor(model, {}, (editor, viewModel) => {
6633
viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
6634
viewModel.paste('cc', false);
6635
assert.strictEqual(model.getValue(EndOfLinePreference.LF), [
6636
'1234cc789',
6637
'123456789',
6638
].join('\n'), 'assert1');
6639
6640
viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
6641
viewModel.paste('dddddddd', false);
6642
assert.strictEqual(model.getValue(EndOfLinePreference.LF), [
6643
'1234dddddddd',
6644
'123456789',
6645
].join('\n'), 'assert2');
6646
});
6647
6648
model.dispose();
6649
});
6650
6651
test('multi-line selection paste', () => {
6652
const model = createTextModel(
6653
[
6654
'123456789',
6655
'123456789',
6656
].join('\n'),
6657
undefined,
6658
{
6659
insertSpaces: false,
6660
}
6661
);
6662
6663
withTestCodeEditor(model, {}, (editor, viewModel) => {
6664
viewModel.setSelections('test', [new Selection(1, 5, 2, 3)]);
6665
viewModel.paste('cc', false);
6666
assert.strictEqual(model.getValue(EndOfLinePreference.LF), [
6667
'1234cc456789',
6668
].join('\n'), 'assert1');
6669
});
6670
6671
model.dispose();
6672
});
6673
6674
test('paste multi-line text', () => {
6675
const model = createTextModel(
6676
[
6677
'123456789',
6678
'123456789',
6679
].join('\n'),
6680
undefined,
6681
{
6682
insertSpaces: false,
6683
}
6684
);
6685
6686
withTestCodeEditor(model, {}, (editor, viewModel) => {
6687
viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
6688
viewModel.paste([
6689
'aaaaaaa',
6690
'bbbbbbb'
6691
].join('\n'), false);
6692
assert.strictEqual(model.getValue(EndOfLinePreference.LF), [
6693
'1234aaaaaaa',
6694
'bbbbbbb',
6695
'123456789',
6696
].join('\n'), 'assert1');
6697
});
6698
6699
model.dispose();
6700
});
6701
6702
test('composition type', () => {
6703
const model = createTextModel(
6704
[
6705
'123456789',
6706
'123456789',
6707
].join('\n'),
6708
undefined,
6709
{
6710
insertSpaces: false,
6711
}
6712
);
6713
6714
withTestCodeEditor(model, {}, (editor, viewModel) => {
6715
viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
6716
viewModel.startComposition();
6717
viewModel.compositionType('セ', 0, 0, 0, 'keyboard');
6718
assert.strictEqual(model.getValue(EndOfLinePreference.LF), [
6719
'1234セ56789',
6720
'123456789',
6721
].join('\n'), 'assert1');
6722
6723
viewModel.endComposition('keyboard');
6724
assert.strictEqual(model.getValue(EndOfLinePreference.LF), [
6725
'1234セ6789',
6726
'123456789',
6727
].join('\n'), 'assert1');
6728
});
6729
6730
model.dispose();
6731
});
6732
});
6733
6734