62
*/
63
public static atomicPosition(lineContent: string, position: number, tabSize: number, direction: Direction): number {
65
>
66
>
// Get the 0-based visible column corresponding to the position, or return
67
>
// -1 if it is not in the initial whitespace.
68
>
const [prevTabStopPosition, prevTabStopVisibleColumn, visibleColumn] = AtomicTabMoveOperations.whitespaceVisibleColumn(lineContent, position, tabSize);
69
>
70
>
if (visibleColumn === -1) {
71
>
return -1;
72
>
}
73
>
74
>
// Is the output left or right of the current position. The case for nearest
75
>
// where it is the same as the current position is handled in the switch.
76
>
let left: boolean;
77
>
switch (direction) {
78
>
case Direction.Left:
79
>
left = true;
80
>
break;
81
>
case Direction.Right:
82
>
left = false;
83
>
break;
84
>
case Direction.Nearest:
85
>
// The code below assumes the output position is either left or right
86
>
// of the input position. If it is the same, return immediately.
87
>
if (visibleColumn % tabSize === 0) {
88
>
return position;
89
>
}
90
>
// Go to the nearest indentation.
91
>
left = visibleColumn % tabSize <= (tabSize / 2);
92
>
break;
93
>
}
94
>
95
>
// If going left, we can just use the info about the last tab stop position and
96
>
// last tab stop visible column that we computed in the first walk over the whitespace.
97
>
if (left) {
98
>
if (prevTabStopPosition === -1) {
99
>
return -1;
100
>
}
101
>
// If the direction is left, we need to keep scanning right to ensure
102
>
// that targetVisibleColumn + tabSize is before non-whitespace.
103
>
// This is so that when we press left at the end of a partial
104
>
// indentation it only goes one character. For example ' foo' with
105
>
// tabSize 4, should jump from position 6 to position 5, not 4.
106
>
let currentVisibleColumn = prevTabStopVisibleColumn;
107
>
for (let i = prevTabStopPosition; i < lineLength; ++i) {
108
>
if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) {
109
>
// It is a full indentation.
110
>
return prevTabStopPosition;
111
>
}
112
>
113
>
const chCode = lineContent.charCodeAt(i);
114
>
switch (chCode) {
115
>
case CharCode.Space:
116
>
currentVisibleColumn += 1;
117
>
break;
118
>
case CharCode.Tab:
119
>
currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize);
120
>
break;
121
>
default:
122
>
return -1;
123
>
}
124
>
}
125
>
if (currentVisibleColumn === prevTabStopVisibleColumn + tabSize) {
126
>
return prevTabStopPosition;
127
>
}
128
>
// It must have been a partial indentation.
129
>
return -1;
130
>
}
131
>
132
>
// We are going right.
133
>
const targetVisibleColumn = CursorColumns.nextRenderTabStop(visibleColumn, tabSize);
134
>
135
>
// We can just continue from where whitespaceVisibleColumn got to.
136
>
let currentVisibleColumn = visibleColumn;
137
>
for (let i = position; i < lineLength; i++) {
138
>
if (currentVisibleColumn === targetVisibleColumn) {
139
>
return i;
140
>
}
141
>
142
>
const chCode = lineContent.charCodeAt(i);
143
>
switch (chCode) {
144
>
case CharCode.Space:
145
>
currentVisibleColumn += 1;
146
>
break;
147
>
case CharCode.Tab:
148
>
currentVisibleColumn = CursorColumns.nextRenderTabStop(currentVisibleColumn, tabSize);
149
>
break;
150
>
default:
151
>
return -1;
152
>
}
153
>
}
154
>
// This condition handles when the target column is at the end of the line.
155
>
if (currentVisibleColumn === targetVisibleColumn) {
156
>
return lineLength;
157
>
}
158
>
return -1;
159
>
}
160
}