Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
8
import { CoreNavigationCommands } from '../../../browser/coreCommands.js';
9
import { Position } from '../../../common/core/position.js';
10
import { Range } from '../../../common/core/range.js';
11
import { Selection } from '../../../common/core/selection.js';
12
import { CursorMove } from '../../../common/cursor/cursorMoveCommands.js';
13
import { ViewModel } from '../../../common/viewModel/viewModelImpl.js';
14
import { ITestCodeEditor, withTestCodeEditor } from '../testCodeEditor.js';
15
16
suite('Cursor move command test', () => {
17
18
ensureNoDisposablesAreLeakedInTestSuite();
19
20
const TEXT = [
21
' \tMy First Line\t ',
22
'\tMy Second Line',
23
' Third Line🐶',
24
'',
25
'1'
26
].join('\n');
27
28
function executeTest(callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void {
29
withTestCodeEditor(TEXT, {}, (editor, viewModel) => {
30
callback(editor, viewModel);
31
});
32
}
33
34
test('move left should move to left character', () => {
35
executeTest((editor, viewModel) => {
36
moveTo(viewModel, 1, 8);
37
moveLeft(viewModel);
38
cursorEqual(viewModel, 1, 7);
39
});
40
});
41
42
test('move left should move to left by n characters', () => {
43
executeTest((editor, viewModel) => {
44
moveTo(viewModel, 1, 8);
45
moveLeft(viewModel, 3);
46
cursorEqual(viewModel, 1, 5);
47
});
48
});
49
50
test('move left should move to left by half line', () => {
51
executeTest((editor, viewModel) => {
52
moveTo(viewModel, 1, 8);
53
moveLeft(viewModel, 1, CursorMove.RawUnit.HalfLine);
54
cursorEqual(viewModel, 1, 1);
55
});
56
});
57
58
test('move left moves to previous line', () => {
59
executeTest((editor, viewModel) => {
60
moveTo(viewModel, 2, 3);
61
moveLeft(viewModel, 10);
62
cursorEqual(viewModel, 1, 21);
63
});
64
});
65
66
test('move right should move to right character', () => {
67
executeTest((editor, viewModel) => {
68
moveTo(viewModel, 1, 5);
69
moveRight(viewModel);
70
cursorEqual(viewModel, 1, 6);
71
});
72
});
73
74
test('move right should move to right by n characters', () => {
75
executeTest((editor, viewModel) => {
76
moveTo(viewModel, 1, 2);
77
moveRight(viewModel, 6);
78
cursorEqual(viewModel, 1, 8);
79
});
80
});
81
82
test('move right should move to right by half line', () => {
83
executeTest((editor, viewModel) => {
84
moveTo(viewModel, 1, 4);
85
moveRight(viewModel, 1, CursorMove.RawUnit.HalfLine);
86
cursorEqual(viewModel, 1, 14);
87
});
88
});
89
90
test('move right moves to next line', () => {
91
executeTest((editor, viewModel) => {
92
moveTo(viewModel, 1, 8);
93
moveRight(viewModel, 100);
94
cursorEqual(viewModel, 2, 1);
95
});
96
});
97
98
test('move to first character of line from middle', () => {
99
executeTest((editor, viewModel) => {
100
moveTo(viewModel, 1, 8);
101
moveToLineStart(viewModel);
102
cursorEqual(viewModel, 1, 1);
103
});
104
});
105
106
test('move to first character of line from first non white space character', () => {
107
executeTest((editor, viewModel) => {
108
moveTo(viewModel, 1, 6);
109
moveToLineStart(viewModel);
110
cursorEqual(viewModel, 1, 1);
111
});
112
});
113
114
test('move to first character of line from first character', () => {
115
executeTest((editor, viewModel) => {
116
moveTo(viewModel, 1, 1);
117
moveToLineStart(viewModel);
118
cursorEqual(viewModel, 1, 1);
119
});
120
});
121
122
test('move to first non white space character of line from middle', () => {
123
executeTest((editor, viewModel) => {
124
moveTo(viewModel, 1, 8);
125
moveToLineFirstNonWhitespaceCharacter(viewModel);
126
cursorEqual(viewModel, 1, 6);
127
});
128
});
129
130
test('move to first non white space character of line from first non white space character', () => {
131
executeTest((editor, viewModel) => {
132
moveTo(viewModel, 1, 6);
133
moveToLineFirstNonWhitespaceCharacter(viewModel);
134
cursorEqual(viewModel, 1, 6);
135
});
136
});
137
138
test('move to first non white space character of line from first character', () => {
139
executeTest((editor, viewModel) => {
140
moveTo(viewModel, 1, 1);
141
moveToLineFirstNonWhitespaceCharacter(viewModel);
142
cursorEqual(viewModel, 1, 6);
143
});
144
});
145
146
test('move to end of line from middle', () => {
147
executeTest((editor, viewModel) => {
148
moveTo(viewModel, 1, 8);
149
moveToLineEnd(viewModel);
150
cursorEqual(viewModel, 1, 21);
151
});
152
});
153
154
test('move to end of line from last non white space character', () => {
155
executeTest((editor, viewModel) => {
156
moveTo(viewModel, 1, 19);
157
moveToLineEnd(viewModel);
158
cursorEqual(viewModel, 1, 21);
159
});
160
});
161
162
test('move to end of line from line end', () => {
163
executeTest((editor, viewModel) => {
164
moveTo(viewModel, 1, 21);
165
moveToLineEnd(viewModel);
166
cursorEqual(viewModel, 1, 21);
167
});
168
});
169
170
test('move to last non white space character from middle', () => {
171
executeTest((editor, viewModel) => {
172
moveTo(viewModel, 1, 8);
173
moveToLineLastNonWhitespaceCharacter(viewModel);
174
cursorEqual(viewModel, 1, 19);
175
});
176
});
177
178
test('move to last non white space character from last non white space character', () => {
179
executeTest((editor, viewModel) => {
180
moveTo(viewModel, 1, 19);
181
moveToLineLastNonWhitespaceCharacter(viewModel);
182
cursorEqual(viewModel, 1, 19);
183
});
184
});
185
186
test('move to last non white space character from line end', () => {
187
executeTest((editor, viewModel) => {
188
moveTo(viewModel, 1, 21);
189
moveToLineLastNonWhitespaceCharacter(viewModel);
190
cursorEqual(viewModel, 1, 19);
191
});
192
});
193
194
test('move to center of line not from center', () => {
195
executeTest((editor, viewModel) => {
196
moveTo(viewModel, 1, 8);
197
moveToLineCenter(viewModel);
198
cursorEqual(viewModel, 1, 11);
199
});
200
});
201
202
test('move to center of line from center', () => {
203
executeTest((editor, viewModel) => {
204
moveTo(viewModel, 1, 11);
205
moveToLineCenter(viewModel);
206
cursorEqual(viewModel, 1, 11);
207
});
208
});
209
210
test('move to center of line from start', () => {
211
executeTest((editor, viewModel) => {
212
moveToLineStart(viewModel);
213
moveToLineCenter(viewModel);
214
cursorEqual(viewModel, 1, 11);
215
});
216
});
217
218
test('move to center of line from end', () => {
219
executeTest((editor, viewModel) => {
220
moveToLineEnd(viewModel);
221
moveToLineCenter(viewModel);
222
cursorEqual(viewModel, 1, 11);
223
});
224
});
225
226
test('move up by cursor move command', () => {
227
executeTest((editor, viewModel) => {
228
moveTo(viewModel, 3, 5);
229
cursorEqual(viewModel, 3, 5);
230
231
moveUp(viewModel, 2);
232
cursorEqual(viewModel, 1, 5);
233
234
moveUp(viewModel, 1);
235
cursorEqual(viewModel, 1, 1);
236
});
237
});
238
239
test('move up by model line cursor move command', () => {
240
executeTest((editor, viewModel) => {
241
moveTo(viewModel, 3, 5);
242
cursorEqual(viewModel, 3, 5);
243
244
moveUpByModelLine(viewModel, 2);
245
cursorEqual(viewModel, 1, 5);
246
247
moveUpByModelLine(viewModel, 1);
248
cursorEqual(viewModel, 1, 1);
249
});
250
});
251
252
test('move down by model line cursor move command', () => {
253
executeTest((editor, viewModel) => {
254
moveTo(viewModel, 3, 5);
255
cursorEqual(viewModel, 3, 5);
256
257
moveDownByModelLine(viewModel, 2);
258
cursorEqual(viewModel, 5, 2);
259
260
moveDownByModelLine(viewModel, 1);
261
cursorEqual(viewModel, 5, 2);
262
});
263
});
264
265
test('move up with selection by cursor move command', () => {
266
executeTest((editor, viewModel) => {
267
moveTo(viewModel, 3, 5);
268
cursorEqual(viewModel, 3, 5);
269
270
moveUp(viewModel, 1, true);
271
cursorEqual(viewModel, 2, 2, 3, 5);
272
273
moveUp(viewModel, 1, true);
274
cursorEqual(viewModel, 1, 5, 3, 5);
275
});
276
});
277
278
test('move up and down with tabs by cursor move command', () => {
279
executeTest((editor, viewModel) => {
280
moveTo(viewModel, 1, 5);
281
cursorEqual(viewModel, 1, 5);
282
283
moveDown(viewModel, 4);
284
cursorEqual(viewModel, 5, 2);
285
286
moveUp(viewModel, 1);
287
cursorEqual(viewModel, 4, 1);
288
289
moveUp(viewModel, 1);
290
cursorEqual(viewModel, 3, 5);
291
292
moveUp(viewModel, 1);
293
cursorEqual(viewModel, 2, 2);
294
295
moveUp(viewModel, 1);
296
cursorEqual(viewModel, 1, 5);
297
});
298
});
299
300
test('move up and down with end of lines starting from a long one by cursor move command', () => {
301
executeTest((editor, viewModel) => {
302
moveToEndOfLine(viewModel);
303
cursorEqual(viewModel, 1, 21);
304
305
moveToEndOfLine(viewModel);
306
cursorEqual(viewModel, 1, 21);
307
308
moveDown(viewModel, 2);
309
cursorEqual(viewModel, 3, 17);
310
311
moveDown(viewModel, 1);
312
cursorEqual(viewModel, 4, 1);
313
314
moveDown(viewModel, 1);
315
cursorEqual(viewModel, 5, 2);
316
317
moveUp(viewModel, 4);
318
cursorEqual(viewModel, 1, 21);
319
});
320
});
321
322
test('move to view top line moves to first visible line if it is first line', () => {
323
executeTest((editor, viewModel) => {
324
viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1);
325
326
moveTo(viewModel, 2, 2);
327
moveToTop(viewModel);
328
329
cursorEqual(viewModel, 1, 6);
330
});
331
});
332
333
test('move to view top line moves to top visible line when first line is not visible', () => {
334
executeTest((editor, viewModel) => {
335
viewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 10, 1);
336
337
moveTo(viewModel, 4, 1);
338
moveToTop(viewModel);
339
340
cursorEqual(viewModel, 2, 2);
341
});
342
});
343
344
test('move to view top line moves to nth line from top', () => {
345
executeTest((editor, viewModel) => {
346
viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 10, 1);
347
348
moveTo(viewModel, 4, 1);
349
moveToTop(viewModel, 3);
350
351
cursorEqual(viewModel, 3, 5);
352
});
353
});
354
355
test('move to view top line moves to last line if n is greater than last visible line number', () => {
356
executeTest((editor, viewModel) => {
357
viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 3, 1);
358
359
moveTo(viewModel, 2, 2);
360
moveToTop(viewModel, 4);
361
362
cursorEqual(viewModel, 3, 5);
363
});
364
});
365
366
test('move to view center line moves to the center line', () => {
367
executeTest((editor, viewModel) => {
368
viewModel.getCompletelyVisibleViewRange = () => new Range(3, 1, 3, 1);
369
370
moveTo(viewModel, 2, 2);
371
moveToCenter(viewModel);
372
373
cursorEqual(viewModel, 3, 5);
374
});
375
});
376
377
test('move to view bottom line moves to last visible line if it is last line', () => {
378
executeTest((editor, viewModel) => {
379
viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1);
380
381
moveTo(viewModel, 2, 2);
382
moveToBottom(viewModel);
383
384
cursorEqual(viewModel, 5, 1);
385
});
386
});
387
388
test('move to view bottom line moves to last visible line when last line is not visible', () => {
389
executeTest((editor, viewModel) => {
390
viewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 3, 1);
391
392
moveTo(viewModel, 2, 2);
393
moveToBottom(viewModel);
394
395
cursorEqual(viewModel, 3, 5);
396
});
397
});
398
399
test('move to view bottom line moves to nth line from bottom', () => {
400
executeTest((editor, viewModel) => {
401
viewModel.getCompletelyVisibleViewRange = () => new Range(1, 1, 5, 1);
402
403
moveTo(viewModel, 4, 1);
404
moveToBottom(viewModel, 3);
405
406
cursorEqual(viewModel, 3, 5);
407
});
408
});
409
410
test('move to view bottom line moves to first line if n is lesser than first visible line number', () => {
411
executeTest((editor, viewModel) => {
412
viewModel.getCompletelyVisibleViewRange = () => new Range(2, 1, 5, 1);
413
414
moveTo(viewModel, 4, 1);
415
moveToBottom(viewModel, 5);
416
417
cursorEqual(viewModel, 2, 2);
418
});
419
});
420
});
421
422
suite('Cursor move by blankline test', () => {
423
424
ensureNoDisposablesAreLeakedInTestSuite();
425
426
const TEXT = [
427
' \tMy First Line\t ',
428
'\tMy Second Line',
429
' Third Line🐶',
430
'',
431
'1',
432
'2',
433
'3',
434
'',
435
' ',
436
'a',
437
'b',
438
].join('\n');
439
440
function executeTest(callback: (editor: ITestCodeEditor, viewModel: ViewModel) => void): void {
441
withTestCodeEditor(TEXT, {}, (editor, viewModel) => {
442
callback(editor, viewModel);
443
});
444
}
445
446
test('move down should move to start of next blank line', () => {
447
executeTest((editor, viewModel) => {
448
moveDownByBlankLine(viewModel, false);
449
cursorEqual(viewModel, 4, 1);
450
});
451
});
452
453
test('move up should move to start of previous blank line', () => {
454
executeTest((editor, viewModel) => {
455
moveTo(viewModel, 7, 1);
456
moveUpByBlankLine(viewModel, false);
457
cursorEqual(viewModel, 4, 1);
458
});
459
});
460
461
test('move down should skip over whitespace if already on blank line', () => {
462
executeTest((editor, viewModel) => {
463
moveTo(viewModel, 8, 1);
464
moveDownByBlankLine(viewModel, false);
465
cursorEqual(viewModel, 11, 1);
466
});
467
});
468
469
test('move up should skip over whitespace if already on blank line', () => {
470
executeTest((editor, viewModel) => {
471
moveTo(viewModel, 9, 1);
472
moveUpByBlankLine(viewModel, false);
473
cursorEqual(viewModel, 4, 1);
474
});
475
});
476
477
test('move up should go to first column of first line if not empty', () => {
478
executeTest((editor, viewModel) => {
479
moveTo(viewModel, 2, 1);
480
moveUpByBlankLine(viewModel, false);
481
cursorEqual(viewModel, 1, 1);
482
});
483
});
484
485
test('move down should go to first column of last line if not empty', () => {
486
executeTest((editor, viewModel) => {
487
moveTo(viewModel, 10, 1);
488
moveDownByBlankLine(viewModel, false);
489
cursorEqual(viewModel, 11, 1);
490
});
491
});
492
493
test('select down should select to start of next blank line', () => {
494
executeTest((editor, viewModel) => {
495
moveDownByBlankLine(viewModel, true);
496
selectionEqual(viewModel.getSelection(), 4, 1, 1, 1);
497
});
498
});
499
500
test('select up should select to start of previous blank line', () => {
501
executeTest((editor, viewModel) => {
502
moveTo(viewModel, 7, 1);
503
moveUpByBlankLine(viewModel, true);
504
selectionEqual(viewModel.getSelection(), 4, 1, 7, 1);
505
});
506
});
507
});
508
509
// Move command
510
511
function move(viewModel: ViewModel, args: any) {
512
CoreNavigationCommands.CursorMove.runCoreEditorCommand(viewModel, args);
513
}
514
515
function moveToLineStart(viewModel: ViewModel) {
516
move(viewModel, { to: CursorMove.RawDirection.WrappedLineStart });
517
}
518
519
function moveToLineFirstNonWhitespaceCharacter(viewModel: ViewModel) {
520
move(viewModel, { to: CursorMove.RawDirection.WrappedLineFirstNonWhitespaceCharacter });
521
}
522
523
function moveToLineCenter(viewModel: ViewModel) {
524
move(viewModel, { to: CursorMove.RawDirection.WrappedLineColumnCenter });
525
}
526
527
function moveToLineEnd(viewModel: ViewModel) {
528
move(viewModel, { to: CursorMove.RawDirection.WrappedLineEnd });
529
}
530
531
function moveToLineLastNonWhitespaceCharacter(viewModel: ViewModel) {
532
move(viewModel, { to: CursorMove.RawDirection.WrappedLineLastNonWhitespaceCharacter });
533
}
534
535
function moveLeft(viewModel: ViewModel, value?: number, by?: string, select?: boolean) {
536
move(viewModel, { to: CursorMove.RawDirection.Left, by: by, value: value, select: select });
537
}
538
539
function moveRight(viewModel: ViewModel, value?: number, by?: string, select?: boolean) {
540
move(viewModel, { to: CursorMove.RawDirection.Right, by: by, value: value, select: select });
541
}
542
543
function moveUp(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {
544
move(viewModel, { to: CursorMove.RawDirection.Up, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select });
545
}
546
547
function moveUpByBlankLine(viewModel: ViewModel, select?: boolean) {
548
move(viewModel, { to: CursorMove.RawDirection.PrevBlankLine, by: CursorMove.RawUnit.WrappedLine, select: select });
549
}
550
551
function moveUpByModelLine(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {
552
move(viewModel, { to: CursorMove.RawDirection.Up, value: noOfLines, select: select });
553
}
554
555
function moveDown(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {
556
move(viewModel, { to: CursorMove.RawDirection.Down, by: CursorMove.RawUnit.WrappedLine, value: noOfLines, select: select });
557
}
558
559
function moveDownByBlankLine(viewModel: ViewModel, select?: boolean) {
560
move(viewModel, { to: CursorMove.RawDirection.NextBlankLine, by: CursorMove.RawUnit.WrappedLine, select: select });
561
}
562
563
function moveDownByModelLine(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {
564
move(viewModel, { to: CursorMove.RawDirection.Down, value: noOfLines, select: select });
565
}
566
567
function moveToTop(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {
568
move(viewModel, { to: CursorMove.RawDirection.ViewPortTop, value: noOfLines, select: select });
569
}
570
571
function moveToCenter(viewModel: ViewModel, select?: boolean) {
572
move(viewModel, { to: CursorMove.RawDirection.ViewPortCenter, select: select });
573
}
574
575
function moveToBottom(viewModel: ViewModel, noOfLines: number = 1, select?: boolean) {
576
move(viewModel, { to: CursorMove.RawDirection.ViewPortBottom, value: noOfLines, select: select });
577
}
578
579
function cursorEqual(viewModel: ViewModel, posLineNumber: number, posColumn: number, selLineNumber: number = posLineNumber, selColumn: number = posColumn) {
580
positionEqual(viewModel.getPosition(), posLineNumber, posColumn);
581
selectionEqual(viewModel.getSelection(), posLineNumber, posColumn, selLineNumber, selColumn);
582
}
583
584
function positionEqual(position: Position, lineNumber: number, column: number) {
585
assert.deepStrictEqual(position, new Position(lineNumber, column), 'position equal');
586
}
587
588
function selectionEqual(selection: Selection, posLineNumber: number, posColumn: number, selLineNumber: number, selColumn: number) {
589
assert.deepStrictEqual({
590
selectionStartLineNumber: selection.selectionStartLineNumber,
591
selectionStartColumn: selection.selectionStartColumn,
592
positionLineNumber: selection.positionLineNumber,
593
positionColumn: selection.positionColumn
594
}, {
595
selectionStartLineNumber: selLineNumber,
596
selectionStartColumn: selColumn,
597
positionLineNumber: posLineNumber,
598
positionColumn: posColumn
599
}, 'selection equal');
600
}
601
602
function moveTo(viewModel: ViewModel, lineNumber: number, column: number, inSelectionMode: boolean = false) {
603
if (inSelectionMode) {
604
CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(viewModel, {
605
position: new Position(lineNumber, column)
606
});
607
} else {
608
CoreNavigationCommands.MoveTo.runCoreEditorCommand(viewModel, {
609
position: new Position(lineNumber, column)
610
});
611
}
612
}
613
614
function moveToEndOfLine(viewModel: ViewModel, inSelectionMode: boolean = false) {
615
if (inSelectionMode) {
616
CoreNavigationCommands.CursorEndSelect.runCoreEditorCommand(viewModel, {});
617
} else {
618
CoreNavigationCommands.CursorEnd.runCoreEditorCommand(viewModel, {});
619
}
620
}
621
622