Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/cursor/cursorMoveCommands.ts
3294 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: any): boolean {
585
if (!types.isObject(arg)) {
586
return false;
587
}
588
589
const cursorMoveArg: RawArguments = arg;
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
return true;
608
};
609
610
export const metadata: ICommandMetadata = {
611
description: 'Move cursor to a logical position in the view',
612
args: [
613
{
614
name: 'Cursor move argument object',
615
description: `Property-value pairs that can be passed through this argument:
616
* 'to': A mandatory logical position value providing where to move the cursor.
617
\`\`\`
618
'left', 'right', 'up', 'down', 'prevBlankLine', 'nextBlankLine',
619
'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter'
620
'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter'
621
'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside'
622
\`\`\`
623
* 'by': Unit to move. Default is computed based on 'to' value.
624
\`\`\`
625
'line', 'wrappedLine', 'character', 'halfLine'
626
\`\`\`
627
* 'value': Number of units to move. Default is '1'.
628
* 'select': If 'true' makes the selection. Default is 'false'.
629
`,
630
constraint: isCursorMoveArgs,
631
schema: {
632
'type': 'object',
633
'required': ['to'],
634
'properties': {
635
'to': {
636
'type': 'string',
637
'enum': ['left', 'right', 'up', 'down', 'prevBlankLine', 'nextBlankLine', 'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter', 'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter', 'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside']
638
},
639
'by': {
640
'type': 'string',
641
'enum': ['line', 'wrappedLine', 'character', 'halfLine']
642
},
643
'value': {
644
'type': 'number',
645
'default': 1
646
},
647
'select': {
648
'type': 'boolean',
649
'default': false
650
}
651
}
652
}
653
}
654
]
655
};
656
657
/**
658
* Positions in the view for cursor move command.
659
*/
660
export const RawDirection = {
661
Left: 'left',
662
Right: 'right',
663
Up: 'up',
664
Down: 'down',
665
666
PrevBlankLine: 'prevBlankLine',
667
NextBlankLine: 'nextBlankLine',
668
669
WrappedLineStart: 'wrappedLineStart',
670
WrappedLineFirstNonWhitespaceCharacter: 'wrappedLineFirstNonWhitespaceCharacter',
671
WrappedLineColumnCenter: 'wrappedLineColumnCenter',
672
WrappedLineEnd: 'wrappedLineEnd',
673
WrappedLineLastNonWhitespaceCharacter: 'wrappedLineLastNonWhitespaceCharacter',
674
675
ViewPortTop: 'viewPortTop',
676
ViewPortCenter: 'viewPortCenter',
677
ViewPortBottom: 'viewPortBottom',
678
679
ViewPortIfOutside: 'viewPortIfOutside'
680
};
681
682
/**
683
* Units for Cursor move 'by' argument
684
*/
685
export const RawUnit = {
686
Line: 'line',
687
WrappedLine: 'wrappedLine',
688
Character: 'character',
689
HalfLine: 'halfLine'
690
};
691
692
/**
693
* Arguments for Cursor move command
694
*/
695
export interface RawArguments {
696
to: string;
697
select?: boolean;
698
by?: string;
699
value?: number;
700
}
701
702
export function parse(args: Partial<RawArguments>): ParsedArguments | null {
703
if (!args.to) {
704
// illegal arguments
705
return null;
706
}
707
708
let direction: Direction;
709
switch (args.to) {
710
case RawDirection.Left:
711
direction = Direction.Left;
712
break;
713
case RawDirection.Right:
714
direction = Direction.Right;
715
break;
716
case RawDirection.Up:
717
direction = Direction.Up;
718
break;
719
case RawDirection.Down:
720
direction = Direction.Down;
721
break;
722
case RawDirection.PrevBlankLine:
723
direction = Direction.PrevBlankLine;
724
break;
725
case RawDirection.NextBlankLine:
726
direction = Direction.NextBlankLine;
727
break;
728
case RawDirection.WrappedLineStart:
729
direction = Direction.WrappedLineStart;
730
break;
731
case RawDirection.WrappedLineFirstNonWhitespaceCharacter:
732
direction = Direction.WrappedLineFirstNonWhitespaceCharacter;
733
break;
734
case RawDirection.WrappedLineColumnCenter:
735
direction = Direction.WrappedLineColumnCenter;
736
break;
737
case RawDirection.WrappedLineEnd:
738
direction = Direction.WrappedLineEnd;
739
break;
740
case RawDirection.WrappedLineLastNonWhitespaceCharacter:
741
direction = Direction.WrappedLineLastNonWhitespaceCharacter;
742
break;
743
case RawDirection.ViewPortTop:
744
direction = Direction.ViewPortTop;
745
break;
746
case RawDirection.ViewPortBottom:
747
direction = Direction.ViewPortBottom;
748
break;
749
case RawDirection.ViewPortCenter:
750
direction = Direction.ViewPortCenter;
751
break;
752
case RawDirection.ViewPortIfOutside:
753
direction = Direction.ViewPortIfOutside;
754
break;
755
default:
756
// illegal arguments
757
return null;
758
}
759
760
let unit = Unit.None;
761
switch (args.by) {
762
case RawUnit.Line:
763
unit = Unit.Line;
764
break;
765
case RawUnit.WrappedLine:
766
unit = Unit.WrappedLine;
767
break;
768
case RawUnit.Character:
769
unit = Unit.Character;
770
break;
771
case RawUnit.HalfLine:
772
unit = Unit.HalfLine;
773
break;
774
}
775
776
return {
777
direction: direction,
778
unit: unit,
779
select: (!!args.select),
780
value: (args.value || 1)
781
};
782
}
783
784
export interface ParsedArguments {
785
direction: Direction;
786
unit: Unit;
787
select: boolean;
788
value: number;
789
}
790
791
export interface SimpleMoveArguments {
792
direction: SimpleMoveDirection;
793
unit: Unit;
794
select: boolean;
795
value: number;
796
}
797
798
export const enum Direction {
799
Left,
800
Right,
801
Up,
802
Down,
803
PrevBlankLine,
804
NextBlankLine,
805
806
WrappedLineStart,
807
WrappedLineFirstNonWhitespaceCharacter,
808
WrappedLineColumnCenter,
809
WrappedLineEnd,
810
WrappedLineLastNonWhitespaceCharacter,
811
812
ViewPortTop,
813
ViewPortCenter,
814
ViewPortBottom,
815
816
ViewPortIfOutside,
817
}
818
819
export type SimpleMoveDirection = (
820
Direction.Left
821
| Direction.Right
822
| Direction.Up
823
| Direction.Down
824
| Direction.PrevBlankLine
825
| Direction.NextBlankLine
826
| Direction.WrappedLineStart
827
| Direction.WrappedLineFirstNonWhitespaceCharacter
828
| Direction.WrappedLineColumnCenter
829
| Direction.WrappedLineEnd
830
| Direction.WrappedLineLastNonWhitespaceCharacter
831
);
832
833
export type ViewportDirection = (
834
Direction.ViewPortTop
835
| Direction.ViewPortCenter
836
| Direction.ViewPortBottom
837
| Direction.ViewPortIfOutside
838
);
839
840
export const enum Unit {
841
None,
842
Line,
843
WrappedLine,
844
Character,
845
HalfLine,
846
}
847
848
}
849
850