Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/cursor/cursorMoveCommands.ts
5243 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 * as types from '../../../base/common/types.js';
7
import { CursorState, ICursorSimpleModel, PartialCursorState, SelectionStartKind, SingleCursorState } from '../cursorCommon.js';
8
import { MoveOperations } from './cursorMoveOperations.js';
9
import { WordOperations } from './cursorWordOperations.js';
10
import { IPosition, Position } from '../core/position.js';
11
import { Range } from '../core/range.js';
12
import { ICommandMetadata } from '../../../platform/commands/common/commands.js';
13
import { IViewModel } from '../viewModel.js';
14
import { TextDirection } from '../model.js';
15
16
export class CursorMoveCommands {
17
18
public static addCursorDown(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
19
const result: PartialCursorState[] = [];
20
let resultLen = 0;
21
for (let i = 0, len = cursors.length; i < len; i++) {
22
const cursor = cursors[i];
23
result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);
24
if (useLogicalLine) {
25
result[resultLen++] = CursorState.fromModelState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel.model, cursor.modelState));
26
} else {
27
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(viewModel.cursorConfig, viewModel, cursor.viewState));
28
}
29
}
30
return result;
31
}
32
33
public static addCursorUp(viewModel: IViewModel, cursors: CursorState[], useLogicalLine: boolean): PartialCursorState[] {
34
const result: PartialCursorState[] = [];
35
let resultLen = 0;
36
for (let i = 0, len = cursors.length; i < len; i++) {
37
const cursor = cursors[i];
38
result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);
39
if (useLogicalLine) {
40
result[resultLen++] = CursorState.fromModelState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel.model, cursor.modelState));
41
} else {
42
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(viewModel.cursorConfig, viewModel, cursor.viewState));
43
}
44
}
45
return result;
46
}
47
48
public static moveToBeginningOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
49
const result: PartialCursorState[] = [];
50
for (let i = 0, len = cursors.length; i < len; i++) {
51
const cursor = cursors[i];
52
result[i] = this._moveToLineStart(viewModel, cursor, inSelectionMode);
53
}
54
55
return result;
56
}
57
58
private static _moveToLineStart(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
59
const currentViewStateColumn = cursor.viewState.position.column;
60
const currentModelStateColumn = cursor.modelState.position.column;
61
const isFirstLineOfWrappedLine = currentViewStateColumn === currentModelStateColumn;
62
63
const currentViewStatelineNumber = cursor.viewState.position.lineNumber;
64
const firstNonBlankColumn = viewModel.getLineFirstNonWhitespaceColumn(currentViewStatelineNumber);
65
const isBeginningOfViewLine = currentViewStateColumn === firstNonBlankColumn;
66
67
if (!isFirstLineOfWrappedLine && !isBeginningOfViewLine) {
68
return this._moveToLineStartByView(viewModel, cursor, inSelectionMode);
69
} else {
70
return this._moveToLineStartByModel(viewModel, cursor, inSelectionMode);
71
}
72
}
73
74
private static _moveToLineStartByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
75
return CursorState.fromViewState(
76
MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)
77
);
78
}
79
80
private static _moveToLineStartByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean): PartialCursorState {
81
return CursorState.fromModelState(
82
MoveOperations.moveToBeginningOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)
83
);
84
}
85
86
public static moveToEndOfLine(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, sticky: boolean): PartialCursorState[] {
87
const result: PartialCursorState[] = [];
88
for (let i = 0, len = cursors.length; i < len; i++) {
89
const cursor = cursors[i];
90
result[i] = this._moveToLineEnd(viewModel, cursor, inSelectionMode, sticky);
91
}
92
93
return result;
94
}
95
96
private static _moveToLineEnd(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, sticky: boolean): PartialCursorState {
97
const viewStatePosition = cursor.viewState.position;
98
const viewModelMaxColumn = viewModel.getLineMaxColumn(viewStatePosition.lineNumber);
99
const isEndOfViewLine = viewStatePosition.column === viewModelMaxColumn;
100
101
const modelStatePosition = cursor.modelState.position;
102
const modelMaxColumn = viewModel.model.getLineMaxColumn(modelStatePosition.lineNumber);
103
const isEndLineOfWrappedLine = viewModelMaxColumn - viewStatePosition.column === modelMaxColumn - modelStatePosition.column;
104
105
if (isEndOfViewLine || isEndLineOfWrappedLine) {
106
return this._moveToLineEndByModel(viewModel, cursor, inSelectionMode, sticky);
107
} else {
108
return this._moveToLineEndByView(viewModel, cursor, inSelectionMode, sticky);
109
}
110
}
111
112
private static _moveToLineEndByView(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, sticky: boolean): PartialCursorState {
113
return CursorState.fromViewState(
114
MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, sticky)
115
);
116
}
117
118
private static _moveToLineEndByModel(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, sticky: boolean): PartialCursorState {
119
return CursorState.fromModelState(
120
MoveOperations.moveToEndOfLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, sticky)
121
);
122
}
123
124
public static expandLineSelection(viewModel: IViewModel, cursors: CursorState[]): PartialCursorState[] {
125
const result: PartialCursorState[] = [];
126
for (let i = 0, len = cursors.length; i < len; i++) {
127
const cursor = cursors[i];
128
129
const startLineNumber = cursor.modelState.selection.startLineNumber;
130
const lineCount = viewModel.model.getLineCount();
131
132
let endLineNumber = cursor.modelState.selection.endLineNumber;
133
let endColumn: number;
134
if (endLineNumber === lineCount) {
135
endColumn = viewModel.model.getLineMaxColumn(lineCount);
136
} else {
137
endLineNumber++;
138
endColumn = 1;
139
}
140
141
result[i] = CursorState.fromModelState(new SingleCursorState(
142
new Range(startLineNumber, 1, startLineNumber, 1), SelectionStartKind.Simple, 0,
143
new Position(endLineNumber, endColumn), 0
144
));
145
}
146
return result;
147
}
148
149
public static moveToBeginningOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
150
const result: PartialCursorState[] = [];
151
for (let i = 0, len = cursors.length; i < len; i++) {
152
const cursor = cursors[i];
153
result[i] = CursorState.fromModelState(MoveOperations.moveToBeginningOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode));
154
}
155
return result;
156
}
157
158
public static moveToEndOfBuffer(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
159
const result: PartialCursorState[] = [];
160
for (let i = 0, len = cursors.length; i < len; i++) {
161
const cursor = cursors[i];
162
result[i] = CursorState.fromModelState(MoveOperations.moveToEndOfBuffer(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode));
163
}
164
return result;
165
}
166
167
public static selectAll(viewModel: IViewModel, cursor: CursorState): PartialCursorState {
168
const lineCount = viewModel.model.getLineCount();
169
const maxColumn = viewModel.model.getLineMaxColumn(lineCount);
170
171
return CursorState.fromModelState(new SingleCursorState(
172
new Range(1, 1, 1, 1), SelectionStartKind.Simple, 0,
173
new Position(lineCount, maxColumn), 0
174
));
175
}
176
177
public static line(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition | undefined): PartialCursorState {
178
const position = viewModel.model.validatePosition(_position);
179
const viewPosition = (
180
_viewPosition
181
? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
182
: viewModel.coordinatesConverter.convertModelPositionToViewPosition(position)
183
);
184
185
if (!inSelectionMode) {
186
// Entering line selection for the first time
187
const lineCount = viewModel.model.getLineCount();
188
189
let selectToLineNumber = position.lineNumber + 1;
190
let selectToColumn = 1;
191
if (selectToLineNumber > lineCount) {
192
selectToLineNumber = lineCount;
193
selectToColumn = viewModel.model.getLineMaxColumn(selectToLineNumber);
194
}
195
196
return CursorState.fromModelState(new SingleCursorState(
197
new Range(position.lineNumber, 1, selectToLineNumber, selectToColumn), SelectionStartKind.Line, 0,
198
new Position(selectToLineNumber, selectToColumn), 0
199
));
200
}
201
202
// Continuing line selection
203
const enteringLineNumber = cursor.modelState.selectionStart.getStartPosition().lineNumber;
204
205
if (position.lineNumber < enteringLineNumber) {
206
207
return CursorState.fromViewState(cursor.viewState.move(
208
true, viewPosition.lineNumber, 1, 0
209
));
210
211
} else if (position.lineNumber > enteringLineNumber) {
212
213
const lineCount = viewModel.getLineCount();
214
215
let selectToViewLineNumber = viewPosition.lineNumber + 1;
216
let selectToViewColumn = 1;
217
if (selectToViewLineNumber > lineCount) {
218
selectToViewLineNumber = lineCount;
219
selectToViewColumn = viewModel.getLineMaxColumn(selectToViewLineNumber);
220
}
221
222
return CursorState.fromViewState(cursor.viewState.move(
223
true, selectToViewLineNumber, selectToViewColumn, 0
224
));
225
226
} else {
227
228
const endPositionOfSelectionStart = cursor.modelState.selectionStart.getEndPosition();
229
return CursorState.fromModelState(cursor.modelState.move(
230
true, endPositionOfSelectionStart.lineNumber, endPositionOfSelectionStart.column, 0
231
));
232
233
}
234
}
235
236
public static word(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition): PartialCursorState {
237
const position = viewModel.model.validatePosition(_position);
238
return CursorState.fromModelState(WordOperations.word(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, position));
239
}
240
241
public static cancelSelection(viewModel: IViewModel, cursor: CursorState): PartialCursorState {
242
if (!cursor.modelState.hasSelection()) {
243
return new CursorState(cursor.modelState, cursor.viewState);
244
}
245
246
const lineNumber = cursor.viewState.position.lineNumber;
247
const column = cursor.viewState.position.column;
248
249
return CursorState.fromViewState(new SingleCursorState(
250
new Range(lineNumber, column, lineNumber, column), SelectionStartKind.Simple, 0,
251
new Position(lineNumber, column), 0
252
));
253
}
254
255
public static moveTo(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, _position: IPosition, _viewPosition: IPosition | undefined): PartialCursorState {
256
if (inSelectionMode) {
257
if (cursor.modelState.selectionStartKind === SelectionStartKind.Word) {
258
return this.word(viewModel, cursor, inSelectionMode, _position);
259
}
260
if (cursor.modelState.selectionStartKind === SelectionStartKind.Line) {
261
return this.line(viewModel, cursor, inSelectionMode, _position, _viewPosition);
262
}
263
}
264
const position = viewModel.model.validatePosition(_position);
265
const viewPosition = (
266
_viewPosition
267
? viewModel.coordinatesConverter.validateViewPosition(new Position(_viewPosition.lineNumber, _viewPosition.column), position)
268
: viewModel.coordinatesConverter.convertModelPositionToViewPosition(position)
269
);
270
return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, viewPosition.lineNumber, viewPosition.column, 0));
271
}
272
273
public static simpleMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.SimpleMoveDirection, inSelectionMode: boolean, value: number, unit: CursorMove.Unit): PartialCursorState[] | null {
274
switch (direction) {
275
case CursorMove.Direction.Left: {
276
if (unit === CursorMove.Unit.HalfLine) {
277
// Move left by half the current line length
278
return this._moveHalfLineLeft(viewModel, cursors, inSelectionMode);
279
} else {
280
// Move left by `moveParams.value` columns
281
return this._moveLeft(viewModel, cursors, inSelectionMode, value);
282
}
283
}
284
case CursorMove.Direction.Right: {
285
if (unit === CursorMove.Unit.HalfLine) {
286
// Move right by half the current line length
287
return this._moveHalfLineRight(viewModel, cursors, inSelectionMode);
288
} else {
289
// Move right by `moveParams.value` columns
290
return this._moveRight(viewModel, cursors, inSelectionMode, value);
291
}
292
}
293
case CursorMove.Direction.Up: {
294
if (unit === CursorMove.Unit.WrappedLine) {
295
// Move up by view lines
296
return this._moveUpByViewLines(viewModel, cursors, inSelectionMode, value);
297
} else {
298
// Move up by model lines
299
return this._moveUpByModelLines(viewModel, cursors, inSelectionMode, value);
300
}
301
}
302
case CursorMove.Direction.Down: {
303
if (unit === CursorMove.Unit.WrappedLine) {
304
// Move down by view lines
305
return this._moveDownByViewLines(viewModel, cursors, inSelectionMode, value);
306
} else {
307
// Move down by model lines
308
return this._moveDownByModelLines(viewModel, cursors, inSelectionMode, value);
309
}
310
}
311
case CursorMove.Direction.PrevBlankLine: {
312
if (unit === CursorMove.Unit.WrappedLine) {
313
return cursors.map(cursor => CursorState.fromViewState(MoveOperations.moveToPrevBlankLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)));
314
} else {
315
return cursors.map(cursor => CursorState.fromModelState(MoveOperations.moveToPrevBlankLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)));
316
}
317
}
318
case CursorMove.Direction.NextBlankLine: {
319
if (unit === CursorMove.Unit.WrappedLine) {
320
return cursors.map(cursor => CursorState.fromViewState(MoveOperations.moveToNextBlankLine(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode)));
321
} else {
322
return cursors.map(cursor => CursorState.fromModelState(MoveOperations.moveToNextBlankLine(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode)));
323
}
324
}
325
case CursorMove.Direction.WrappedLineStart: {
326
// Move to the beginning of the current view line
327
return this._moveToViewMinColumn(viewModel, cursors, inSelectionMode);
328
}
329
case CursorMove.Direction.WrappedLineFirstNonWhitespaceCharacter: {
330
// Move to the first non-whitespace column of the current view line
331
return this._moveToViewFirstNonWhitespaceColumn(viewModel, cursors, inSelectionMode);
332
}
333
case CursorMove.Direction.WrappedLineColumnCenter: {
334
// Move to the "center" of the current view line
335
return this._moveToViewCenterColumn(viewModel, cursors, inSelectionMode);
336
}
337
case CursorMove.Direction.WrappedLineEnd: {
338
// Move to the end of the current view line
339
return this._moveToViewMaxColumn(viewModel, cursors, inSelectionMode);
340
}
341
case CursorMove.Direction.WrappedLineLastNonWhitespaceCharacter: {
342
// Move to the last non-whitespace column of the current view line
343
return this._moveToViewLastNonWhitespaceColumn(viewModel, cursors, inSelectionMode);
344
}
345
default:
346
return null;
347
}
348
349
}
350
351
public static viewportMove(viewModel: IViewModel, cursors: CursorState[], direction: CursorMove.ViewportDirection, inSelectionMode: boolean, value: number): PartialCursorState[] | null {
352
const visibleViewRange = viewModel.getCompletelyVisibleViewRange();
353
const visibleModelRange = viewModel.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
354
switch (direction) {
355
case CursorMove.Direction.ViewPortTop: {
356
// Move to the nth line start in the viewport (from the top)
357
const modelLineNumber = this._firstLineNumberInRange(viewModel.model, visibleModelRange, value);
358
const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
359
return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
360
}
361
case CursorMove.Direction.ViewPortBottom: {
362
// Move to the nth line start in the viewport (from the bottom)
363
const modelLineNumber = this._lastLineNumberInRange(viewModel.model, visibleModelRange, value);
364
const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
365
return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
366
}
367
case CursorMove.Direction.ViewPortCenter: {
368
// Move to the line start in the viewport center
369
const modelLineNumber = Math.round((visibleModelRange.startLineNumber + visibleModelRange.endLineNumber) / 2);
370
const modelColumn = viewModel.model.getLineFirstNonWhitespaceColumn(modelLineNumber);
371
return [this._moveToModelPosition(viewModel, cursors[0], inSelectionMode, modelLineNumber, modelColumn)];
372
}
373
case CursorMove.Direction.ViewPortIfOutside: {
374
// Move to a position inside the viewport
375
const result: PartialCursorState[] = [];
376
for (let i = 0, len = cursors.length; i < len; i++) {
377
const cursor = cursors[i];
378
result[i] = this.findPositionInViewportIfOutside(viewModel, cursor, visibleViewRange, inSelectionMode);
379
}
380
return result;
381
}
382
default:
383
return null;
384
}
385
}
386
387
public static findPositionInViewportIfOutside(viewModel: IViewModel, cursor: CursorState, visibleViewRange: Range, inSelectionMode: boolean): PartialCursorState {
388
const viewLineNumber = cursor.viewState.position.lineNumber;
389
390
if (visibleViewRange.startLineNumber <= viewLineNumber && viewLineNumber <= visibleViewRange.endLineNumber - 1) {
391
// Nothing to do, cursor is in viewport
392
return new CursorState(cursor.modelState, cursor.viewState);
393
394
} else {
395
let newViewLineNumber: number;
396
if (viewLineNumber > visibleViewRange.endLineNumber - 1) {
397
newViewLineNumber = visibleViewRange.endLineNumber - 1;
398
} else if (viewLineNumber < visibleViewRange.startLineNumber) {
399
newViewLineNumber = visibleViewRange.startLineNumber;
400
} else {
401
newViewLineNumber = viewLineNumber;
402
}
403
const position = MoveOperations.vertical(viewModel.cursorConfig, viewModel, viewLineNumber, cursor.viewState.position.column, cursor.viewState.leftoverVisibleColumns, newViewLineNumber, false);
404
return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, position.lineNumber, position.column, position.leftoverVisibleColumns));
405
}
406
}
407
408
/**
409
* Find the nth line start included in the range (from the start).
410
*/
411
private static _firstLineNumberInRange(model: ICursorSimpleModel, range: Range, count: number): number {
412
let startLineNumber = range.startLineNumber;
413
if (range.startColumn !== model.getLineMinColumn(startLineNumber)) {
414
// Move on to the second line if the first line start is not included in the range
415
startLineNumber++;
416
}
417
418
return Math.min(range.endLineNumber, startLineNumber + count - 1);
419
}
420
421
/**
422
* Find the nth line start included in the range (from the end).
423
*/
424
private static _lastLineNumberInRange(model: ICursorSimpleModel, range: Range, count: number): number {
425
let startLineNumber = range.startLineNumber;
426
if (range.startColumn !== model.getLineMinColumn(startLineNumber)) {
427
// Move on to the second line if the first line start is not included in the range
428
startLineNumber++;
429
}
430
431
return Math.max(startLineNumber, range.endLineNumber - count + 1);
432
}
433
434
private static _moveLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
435
return cursors.map(cursor => {
436
const direction = viewModel.getTextDirection(cursor.viewState.position.lineNumber);
437
const isRtl = direction === TextDirection.RTL;
438
439
return CursorState.fromViewState(
440
isRtl
441
? MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)
442
: MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)
443
);
444
});
445
}
446
447
private static _moveHalfLineLeft(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
448
const result: PartialCursorState[] = [];
449
for (let i = 0, len = cursors.length; i < len; i++) {
450
const cursor = cursors[i];
451
const viewLineNumber = cursor.viewState.position.lineNumber;
452
const halfLine = Math.round(viewModel.getLineLength(viewLineNumber) / 2);
453
result[i] = CursorState.fromViewState(MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine));
454
}
455
return result;
456
}
457
458
private static _moveRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, noOfColumns: number): PartialCursorState[] {
459
return cursors.map(cursor => {
460
const direction = viewModel.getTextDirection(cursor.viewState.position.lineNumber);
461
const isRtl = direction === TextDirection.RTL;
462
463
return CursorState.fromViewState(
464
isRtl
465
? MoveOperations.moveLeft(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)
466
: MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, noOfColumns)
467
);
468
});
469
}
470
471
private static _moveHalfLineRight(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
472
const result: PartialCursorState[] = [];
473
for (let i = 0, len = cursors.length; i < len; i++) {
474
const cursor = cursors[i];
475
const viewLineNumber = cursor.viewState.position.lineNumber;
476
const halfLine = Math.round(viewModel.getLineLength(viewLineNumber) / 2);
477
result[i] = CursorState.fromViewState(MoveOperations.moveRight(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, halfLine));
478
}
479
return result;
480
}
481
482
private static _moveDownByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
483
const result: PartialCursorState[] = [];
484
for (let i = 0, len = cursors.length; i < len; i++) {
485
const cursor = cursors[i];
486
result[i] = CursorState.fromViewState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount));
487
}
488
return result;
489
}
490
491
private static _moveDownByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
492
const result: PartialCursorState[] = [];
493
for (let i = 0, len = cursors.length; i < len; i++) {
494
const cursor = cursors[i];
495
result[i] = CursorState.fromModelState(MoveOperations.moveDown(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount));
496
}
497
return result;
498
}
499
500
private static _moveUpByViewLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
501
const result: PartialCursorState[] = [];
502
for (let i = 0, len = cursors.length; i < len; i++) {
503
const cursor = cursors[i];
504
result[i] = CursorState.fromViewState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel, cursor.viewState, inSelectionMode, linesCount));
505
}
506
return result;
507
}
508
509
private static _moveUpByModelLines(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean, linesCount: number): PartialCursorState[] {
510
const result: PartialCursorState[] = [];
511
for (let i = 0, len = cursors.length; i < len; i++) {
512
const cursor = cursors[i];
513
result[i] = CursorState.fromModelState(MoveOperations.moveUp(viewModel.cursorConfig, viewModel.model, cursor.modelState, inSelectionMode, linesCount));
514
}
515
return result;
516
}
517
518
private static _moveToViewPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toViewLineNumber: number, toViewColumn: number): PartialCursorState {
519
return CursorState.fromViewState(cursor.viewState.move(inSelectionMode, toViewLineNumber, toViewColumn, 0));
520
}
521
522
private static _moveToModelPosition(viewModel: IViewModel, cursor: CursorState, inSelectionMode: boolean, toModelLineNumber: number, toModelColumn: number): PartialCursorState {
523
return CursorState.fromModelState(cursor.modelState.move(inSelectionMode, toModelLineNumber, toModelColumn, 0));
524
}
525
526
private static _moveToViewMinColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
527
const result: PartialCursorState[] = [];
528
for (let i = 0, len = cursors.length; i < len; i++) {
529
const cursor = cursors[i];
530
const viewLineNumber = cursor.viewState.position.lineNumber;
531
const viewColumn = viewModel.getLineMinColumn(viewLineNumber);
532
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
533
}
534
return result;
535
}
536
537
private static _moveToViewFirstNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
538
const result: PartialCursorState[] = [];
539
for (let i = 0, len = cursors.length; i < len; i++) {
540
const cursor = cursors[i];
541
const viewLineNumber = cursor.viewState.position.lineNumber;
542
const viewColumn = viewModel.getLineFirstNonWhitespaceColumn(viewLineNumber);
543
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
544
}
545
return result;
546
}
547
548
private static _moveToViewCenterColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
549
const result: PartialCursorState[] = [];
550
for (let i = 0, len = cursors.length; i < len; i++) {
551
const cursor = cursors[i];
552
const viewLineNumber = cursor.viewState.position.lineNumber;
553
const viewColumn = Math.round((viewModel.getLineMaxColumn(viewLineNumber) + viewModel.getLineMinColumn(viewLineNumber)) / 2);
554
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
555
}
556
return result;
557
}
558
559
private static _moveToViewMaxColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
560
const result: PartialCursorState[] = [];
561
for (let i = 0, len = cursors.length; i < len; i++) {
562
const cursor = cursors[i];
563
const viewLineNumber = cursor.viewState.position.lineNumber;
564
const viewColumn = viewModel.getLineMaxColumn(viewLineNumber);
565
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
566
}
567
return result;
568
}
569
570
private static _moveToViewLastNonWhitespaceColumn(viewModel: IViewModel, cursors: CursorState[], inSelectionMode: boolean): PartialCursorState[] {
571
const result: PartialCursorState[] = [];
572
for (let i = 0, len = cursors.length; i < len; i++) {
573
const cursor = cursors[i];
574
const viewLineNumber = cursor.viewState.position.lineNumber;
575
const viewColumn = viewModel.getLineLastNonWhitespaceColumn(viewLineNumber);
576
result[i] = this._moveToViewPosition(viewModel, cursor, inSelectionMode, viewLineNumber, viewColumn);
577
}
578
return result;
579
}
580
}
581
582
export namespace CursorMove {
583
584
const isCursorMoveArgs = function (arg: unknown): boolean {
585
if (!types.isObject(arg)) {
586
return false;
587
}
588
589
const cursorMoveArg: RawArguments = arg as RawArguments;
590
591
if (!types.isString(cursorMoveArg.to)) {
592
return false;
593
}
594
595
if (!types.isUndefined(cursorMoveArg.select) && !types.isBoolean(cursorMoveArg.select)) {
596
return false;
597
}
598
599
if (!types.isUndefined(cursorMoveArg.by) && !types.isString(cursorMoveArg.by)) {
600
return false;
601
}
602
603
if (!types.isUndefined(cursorMoveArg.value) && !types.isNumber(cursorMoveArg.value)) {
604
return false;
605
}
606
607
if (!types.isUndefined(cursorMoveArg.noHistory) && !types.isBoolean(cursorMoveArg.noHistory)) {
608
return false;
609
}
610
611
return true;
612
};
613
614
export const metadata: ICommandMetadata = {
615
description: 'Move cursor to a logical position in the view',
616
args: [
617
{
618
name: 'Cursor move argument object',
619
description: `Property-value pairs that can be passed through this argument:
620
* 'to': A mandatory logical position value providing where to move the cursor.
621
\`\`\`
622
'left', 'right', 'up', 'down', 'prevBlankLine', 'nextBlankLine',
623
'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter'
624
'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter'
625
'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside'
626
\`\`\`
627
* 'by': Unit to move. Default is computed based on 'to' value.
628
\`\`\`
629
'line', 'wrappedLine', 'character', 'halfLine'
630
\`\`\`
631
* 'value': Number of units to move. Default is '1'.
632
* 'select': If 'true' makes the selection. Default is 'false'.
633
* 'noHistory': If 'true' does not add the movement to navigation history. Default is 'false'.
634
`,
635
constraint: isCursorMoveArgs,
636
schema: {
637
'type': 'object',
638
'required': ['to'],
639
'properties': {
640
'to': {
641
'type': 'string',
642
'enum': ['left', 'right', 'up', 'down', 'prevBlankLine', 'nextBlankLine', 'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter', 'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter', 'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside']
643
},
644
'by': {
645
'type': 'string',
646
'enum': ['line', 'wrappedLine', 'character', 'halfLine']
647
},
648
'value': {
649
'type': 'number',
650
'default': 1
651
},
652
'select': {
653
'type': 'boolean',
654
'default': false
655
},
656
'noHistory': {
657
'type': 'boolean',
658
'default': false
659
}
660
}
661
}
662
}
663
]
664
};
665
666
/**
667
* Positions in the view for cursor move command.
668
*/
669
export const RawDirection = {
670
Left: 'left',
671
Right: 'right',
672
Up: 'up',
673
Down: 'down',
674
675
PrevBlankLine: 'prevBlankLine',
676
NextBlankLine: 'nextBlankLine',
677
678
WrappedLineStart: 'wrappedLineStart',
679
WrappedLineFirstNonWhitespaceCharacter: 'wrappedLineFirstNonWhitespaceCharacter',
680
WrappedLineColumnCenter: 'wrappedLineColumnCenter',
681
WrappedLineEnd: 'wrappedLineEnd',
682
WrappedLineLastNonWhitespaceCharacter: 'wrappedLineLastNonWhitespaceCharacter',
683
684
ViewPortTop: 'viewPortTop',
685
ViewPortCenter: 'viewPortCenter',
686
ViewPortBottom: 'viewPortBottom',
687
688
ViewPortIfOutside: 'viewPortIfOutside'
689
};
690
691
/**
692
* Units for Cursor move 'by' argument
693
*/
694
export const RawUnit = {
695
Line: 'line',
696
WrappedLine: 'wrappedLine',
697
Character: 'character',
698
HalfLine: 'halfLine'
699
};
700
701
/**
702
* Arguments for Cursor move command
703
*/
704
export interface RawArguments {
705
to: string;
706
select?: boolean;
707
by?: string;
708
value?: number;
709
noHistory?: boolean;
710
}
711
712
export function parse(args: Partial<RawArguments>): ParsedArguments | null {
713
if (!args.to) {
714
// illegal arguments
715
return null;
716
}
717
718
let direction: Direction;
719
switch (args.to) {
720
case RawDirection.Left:
721
direction = Direction.Left;
722
break;
723
case RawDirection.Right:
724
direction = Direction.Right;
725
break;
726
case RawDirection.Up:
727
direction = Direction.Up;
728
break;
729
case RawDirection.Down:
730
direction = Direction.Down;
731
break;
732
case RawDirection.PrevBlankLine:
733
direction = Direction.PrevBlankLine;
734
break;
735
case RawDirection.NextBlankLine:
736
direction = Direction.NextBlankLine;
737
break;
738
case RawDirection.WrappedLineStart:
739
direction = Direction.WrappedLineStart;
740
break;
741
case RawDirection.WrappedLineFirstNonWhitespaceCharacter:
742
direction = Direction.WrappedLineFirstNonWhitespaceCharacter;
743
break;
744
case RawDirection.WrappedLineColumnCenter:
745
direction = Direction.WrappedLineColumnCenter;
746
break;
747
case RawDirection.WrappedLineEnd:
748
direction = Direction.WrappedLineEnd;
749
break;
750
case RawDirection.WrappedLineLastNonWhitespaceCharacter:
751
direction = Direction.WrappedLineLastNonWhitespaceCharacter;
752
break;
753
case RawDirection.ViewPortTop:
754
direction = Direction.ViewPortTop;
755
break;
756
case RawDirection.ViewPortBottom:
757
direction = Direction.ViewPortBottom;
758
break;
759
case RawDirection.ViewPortCenter:
760
direction = Direction.ViewPortCenter;
761
break;
762
case RawDirection.ViewPortIfOutside:
763
direction = Direction.ViewPortIfOutside;
764
break;
765
default:
766
// illegal arguments
767
return null;
768
}
769
770
let unit = Unit.None;
771
switch (args.by) {
772
case RawUnit.Line:
773
unit = Unit.Line;
774
break;
775
case RawUnit.WrappedLine:
776
unit = Unit.WrappedLine;
777
break;
778
case RawUnit.Character:
779
unit = Unit.Character;
780
break;
781
case RawUnit.HalfLine:
782
unit = Unit.HalfLine;
783
break;
784
}
785
786
return {
787
direction: direction,
788
unit: unit,
789
select: (!!args.select),
790
value: (args.value || 1),
791
noHistory: (!!args.noHistory)
792
};
793
}
794
795
export interface ParsedArguments {
796
direction: Direction;
797
unit: Unit;
798
select: boolean;
799
value: number;
800
noHistory: boolean;
801
}
802
803
export interface SimpleMoveArguments {
804
direction: SimpleMoveDirection;
805
unit: Unit;
806
select: boolean;
807
value: number;
808
}
809
810
export const enum Direction {
811
Left,
812
Right,
813
Up,
814
Down,
815
PrevBlankLine,
816
NextBlankLine,
817
818
WrappedLineStart,
819
WrappedLineFirstNonWhitespaceCharacter,
820
WrappedLineColumnCenter,
821
WrappedLineEnd,
822
WrappedLineLastNonWhitespaceCharacter,
823
824
ViewPortTop,
825
ViewPortCenter,
826
ViewPortBottom,
827
828
ViewPortIfOutside,
829
}
830
831
export type SimpleMoveDirection = (
832
Direction.Left
833
| Direction.Right
834
| Direction.Up
835
| Direction.Down
836
| Direction.PrevBlankLine
837
| Direction.NextBlankLine
838
| Direction.WrappedLineStart
839
| Direction.WrappedLineFirstNonWhitespaceCharacter
840
| Direction.WrappedLineColumnCenter
841
| Direction.WrappedLineEnd
842
| Direction.WrappedLineLastNonWhitespaceCharacter
843
);
844
845
export type ViewportDirection = (
846
Direction.ViewPortTop
847
| Direction.ViewPortCenter
848
| Direction.ViewPortBottom
849
| Direction.ViewPortIfOutside
850
);
851
852
export const enum Unit {
853
None,
854
Line,
855
WrappedLine,
856
Character,
857
HalfLine,
858
}
859
860
}
861
862