ep_vim 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/static/js/index.js +368 -736
package/package.json
CHANGED
package/static/js/index.js
CHANGED
|
@@ -28,6 +28,7 @@ let visualMode = null;
|
|
|
28
28
|
let visualAnchor = null;
|
|
29
29
|
let visualCursor = null;
|
|
30
30
|
let pendingKey = null;
|
|
31
|
+
let pendingOperator = null;
|
|
31
32
|
let pendingCount = null;
|
|
32
33
|
let countBuffer = "";
|
|
33
34
|
let register = null;
|
|
@@ -41,7 +42,7 @@ const QUOTE_CHARS = new Set(['"', "'"]);
|
|
|
41
42
|
const BRACKET_CHARS = new Set(["(", ")", "{", "}", "[", "]"]);
|
|
42
43
|
|
|
43
44
|
const textObjectRange = (key, lineText, char, type) => {
|
|
44
|
-
if (key === "w") return textWordRange(lineText, char);
|
|
45
|
+
if (key === "w") return textWordRange(lineText, char, type);
|
|
45
46
|
if (QUOTE_CHARS.has(key)) return textQuoteRange(lineText, char, key, type);
|
|
46
47
|
if (BRACKET_CHARS.has(key))
|
|
47
48
|
return textBracketRange(lineText, char, key, type);
|
|
@@ -53,7 +54,7 @@ const consumeCount = () => {
|
|
|
53
54
|
if (countBuffer !== "") {
|
|
54
55
|
pendingCount = parseInt(countBuffer, 10);
|
|
55
56
|
countBuffer = "";
|
|
56
|
-
} else if (pendingKey === null) {
|
|
57
|
+
} else if (pendingKey === null && pendingOperator === null) {
|
|
57
58
|
pendingCount = null;
|
|
58
59
|
}
|
|
59
60
|
};
|
|
@@ -129,8 +130,6 @@ const setVisualMode = (value) => {
|
|
|
129
130
|
}
|
|
130
131
|
};
|
|
131
132
|
|
|
132
|
-
// --- Visual mode helpers ---
|
|
133
|
-
|
|
134
133
|
const updateVisualSelection = (editorInfo, rep) => {
|
|
135
134
|
const [start, end] = getVisualSelection(
|
|
136
135
|
visualMode,
|
|
@@ -141,42 +140,9 @@ const updateVisualSelection = (editorInfo, rep) => {
|
|
|
141
140
|
selectRange(editorInfo, start, end);
|
|
142
141
|
};
|
|
143
142
|
|
|
144
|
-
// ---
|
|
145
|
-
|
|
146
|
-
const handleVisualKey = (rep, editorInfo, key) => {
|
|
147
|
-
const curLine = visualCursor[0];
|
|
148
|
-
const curChar = visualCursor[1];
|
|
149
|
-
const lineText = getLineText(rep, curLine);
|
|
150
|
-
|
|
151
|
-
if (key === "i" || key === "a") {
|
|
152
|
-
pendingKey = key;
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (pendingKey === "i" || pendingKey === "a") {
|
|
157
|
-
const range = textObjectRange(key, lineText, curChar, pendingKey);
|
|
158
|
-
pendingKey = null;
|
|
159
|
-
if (range) {
|
|
160
|
-
visualAnchor = [curLine, range.start];
|
|
161
|
-
visualCursor = [curLine, range.end];
|
|
162
|
-
setVisualMode("char");
|
|
163
|
-
updateVisualSelection(editorInfo, rep);
|
|
164
|
-
}
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (key >= "1" && key <= "9") {
|
|
169
|
-
countBuffer += key;
|
|
170
|
-
return true;
|
|
171
|
-
}
|
|
172
|
-
if (key === "0" && countBuffer !== "") {
|
|
173
|
-
countBuffer += key;
|
|
174
|
-
return true;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
consumeCount();
|
|
178
|
-
const count = getCount();
|
|
143
|
+
// --- Motion resolution (shared between normal and visual) ---
|
|
179
144
|
|
|
145
|
+
const resolveMotion = (key, line, char, lineText, rep, count) => {
|
|
180
146
|
if (
|
|
181
147
|
pendingKey === "f" ||
|
|
182
148
|
pendingKey === "F" ||
|
|
@@ -186,13 +152,12 @@ const handleVisualKey = (rep, editorInfo, key) => {
|
|
|
186
152
|
const direction = pendingKey;
|
|
187
153
|
pendingKey = null;
|
|
188
154
|
lastCharSearch = { direction, target: key };
|
|
189
|
-
const pos = charSearchPos(direction, lineText,
|
|
155
|
+
const pos = charSearchPos(direction, lineText, char, key, count);
|
|
190
156
|
if (pos !== -1) {
|
|
191
157
|
desiredColumn = null;
|
|
192
|
-
|
|
193
|
-
updateVisualSelection(editorInfo, rep);
|
|
158
|
+
return { line, char: pos };
|
|
194
159
|
}
|
|
195
|
-
return
|
|
160
|
+
return { line, char };
|
|
196
161
|
}
|
|
197
162
|
|
|
198
163
|
if (pendingKey === "'" || pendingKey === "`") {
|
|
@@ -203,97 +168,100 @@ const handleVisualKey = (rep, editorInfo, key) => {
|
|
|
203
168
|
desiredColumn = null;
|
|
204
169
|
if (jumpType === "'") {
|
|
205
170
|
const targetLineText = getLineText(rep, markLine);
|
|
206
|
-
|
|
207
|
-
} else {
|
|
208
|
-
visualCursor = [markLine, markChar];
|
|
171
|
+
return { line: markLine, char: firstNonBlank(targetLineText) };
|
|
209
172
|
}
|
|
210
|
-
|
|
173
|
+
return { line: markLine, char: markChar };
|
|
174
|
+
}
|
|
175
|
+
return { line, char };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (pendingKey === "g") {
|
|
179
|
+
pendingKey = null;
|
|
180
|
+
if (key === "g") {
|
|
181
|
+
desiredColumn = null;
|
|
182
|
+
if (pendingCount !== null) {
|
|
183
|
+
return { line: clampLine(pendingCount - 1, rep), char: 0 };
|
|
184
|
+
}
|
|
185
|
+
return { line: 0, char: 0 };
|
|
211
186
|
}
|
|
212
|
-
return true;
|
|
213
187
|
}
|
|
214
188
|
|
|
215
189
|
if (key === "h") {
|
|
216
190
|
desiredColumn = null;
|
|
217
|
-
|
|
218
|
-
updateVisualSelection(editorInfo, rep);
|
|
219
|
-
return true;
|
|
191
|
+
return { line, char: Math.max(0, char - count) };
|
|
220
192
|
}
|
|
221
193
|
|
|
222
194
|
if (key === "l") {
|
|
223
195
|
desiredColumn = null;
|
|
224
|
-
|
|
225
|
-
updateVisualSelection(editorInfo, rep);
|
|
226
|
-
return true;
|
|
196
|
+
return { line, char: clampChar(char + count, lineText) };
|
|
227
197
|
}
|
|
228
198
|
|
|
229
199
|
if (key === "j") {
|
|
230
|
-
if (desiredColumn === null)
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
const newLine = clampLine(curLine + count, rep);
|
|
200
|
+
if (desiredColumn === null) desiredColumn = char;
|
|
201
|
+
const newLine = clampLine(line + count, rep);
|
|
234
202
|
const newLineText = getLineText(rep, newLine);
|
|
235
|
-
|
|
236
|
-
updateVisualSelection(editorInfo, rep);
|
|
237
|
-
return true;
|
|
203
|
+
return { line: newLine, char: clampChar(desiredColumn, newLineText) };
|
|
238
204
|
}
|
|
239
205
|
|
|
240
206
|
if (key === "k") {
|
|
241
|
-
if (desiredColumn === null)
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
const newLine = clampLine(curLine - count, rep);
|
|
207
|
+
if (desiredColumn === null) desiredColumn = char;
|
|
208
|
+
const newLine = clampLine(line - count, rep);
|
|
245
209
|
const newLineText = getLineText(rep, newLine);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
210
|
+
return { line: newLine, char: clampChar(desiredColumn, newLineText) };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (key === "w") {
|
|
214
|
+
desiredColumn = null;
|
|
215
|
+
let pos = char;
|
|
216
|
+
for (let i = 0; i < count; i++) pos = wordForward(lineText, pos);
|
|
217
|
+
return { line, char: clampChar(pos, lineText) };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (key === "b") {
|
|
221
|
+
desiredColumn = null;
|
|
222
|
+
let pos = char;
|
|
223
|
+
for (let i = 0; i < count; i++) pos = wordBackward(lineText, pos);
|
|
224
|
+
return { line, char: pos };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (key === "e") {
|
|
228
|
+
desiredColumn = null;
|
|
229
|
+
let pos = char;
|
|
230
|
+
for (let i = 0; i < count; i++) pos = wordEnd(lineText, pos);
|
|
231
|
+
return { line, char: clampChar(pos, lineText) };
|
|
249
232
|
}
|
|
250
233
|
|
|
251
234
|
if (key === "0") {
|
|
252
235
|
desiredColumn = null;
|
|
253
|
-
|
|
254
|
-
updateVisualSelection(editorInfo, rep);
|
|
255
|
-
return true;
|
|
236
|
+
return { line, char: 0 };
|
|
256
237
|
}
|
|
257
238
|
|
|
258
239
|
if (key === "$") {
|
|
259
240
|
desiredColumn = null;
|
|
260
|
-
|
|
261
|
-
updateVisualSelection(editorInfo, rep);
|
|
262
|
-
return true;
|
|
241
|
+
return { line, char: clampChar(lineText.length - 1, lineText) };
|
|
263
242
|
}
|
|
264
243
|
|
|
265
244
|
if (key === "^") {
|
|
266
245
|
desiredColumn = null;
|
|
267
|
-
|
|
268
|
-
updateVisualSelection(editorInfo, rep);
|
|
269
|
-
return true;
|
|
246
|
+
return { line, char: firstNonBlank(lineText) };
|
|
270
247
|
}
|
|
271
248
|
|
|
272
|
-
if (key === "
|
|
249
|
+
if (key === "}") {
|
|
273
250
|
desiredColumn = null;
|
|
274
|
-
|
|
275
|
-
for (let i = 0; i < count; i++) pos = wordForward(lineText, pos);
|
|
276
|
-
visualCursor = [curLine, clampChar(pos, lineText)];
|
|
277
|
-
updateVisualSelection(editorInfo, rep);
|
|
278
|
-
return true;
|
|
251
|
+
return { line: paragraphForward(rep, line, count), char: 0 };
|
|
279
252
|
}
|
|
280
253
|
|
|
281
|
-
if (key === "
|
|
254
|
+
if (key === "{") {
|
|
282
255
|
desiredColumn = null;
|
|
283
|
-
|
|
284
|
-
for (let i = 0; i < count; i++) pos = wordBackward(lineText, pos);
|
|
285
|
-
visualCursor = [curLine, pos];
|
|
286
|
-
updateVisualSelection(editorInfo, rep);
|
|
287
|
-
return true;
|
|
256
|
+
return { line: paragraphBackward(rep, line, count), char: 0 };
|
|
288
257
|
}
|
|
289
258
|
|
|
290
|
-
if (key === "
|
|
259
|
+
if (key === "G") {
|
|
291
260
|
desiredColumn = null;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
return true;
|
|
261
|
+
if (pendingCount !== null) {
|
|
262
|
+
return { line: clampLine(pendingCount - 1, rep), char: 0 };
|
|
263
|
+
}
|
|
264
|
+
return { line: rep.lines.length() - 1, char: 0 };
|
|
297
265
|
}
|
|
298
266
|
|
|
299
267
|
if (key === ";") {
|
|
@@ -301,17 +269,16 @@ const handleVisualKey = (rep, editorInfo, key) => {
|
|
|
301
269
|
const pos = charSearchPos(
|
|
302
270
|
lastCharSearch.direction,
|
|
303
271
|
lineText,
|
|
304
|
-
|
|
272
|
+
char,
|
|
305
273
|
lastCharSearch.target,
|
|
306
274
|
count,
|
|
307
275
|
);
|
|
308
276
|
if (pos !== -1) {
|
|
309
277
|
desiredColumn = null;
|
|
310
|
-
|
|
311
|
-
updateVisualSelection(editorInfo, rep);
|
|
278
|
+
return { line, char: pos };
|
|
312
279
|
}
|
|
313
280
|
}
|
|
314
|
-
return
|
|
281
|
+
return { line, char };
|
|
315
282
|
}
|
|
316
283
|
|
|
317
284
|
if (key === ",") {
|
|
@@ -321,199 +288,139 @@ const handleVisualKey = (rep, editorInfo, key) => {
|
|
|
321
288
|
const pos = charSearchPos(
|
|
322
289
|
reverseDir,
|
|
323
290
|
lineText,
|
|
324
|
-
|
|
291
|
+
char,
|
|
325
292
|
lastCharSearch.target,
|
|
326
293
|
count,
|
|
327
294
|
);
|
|
328
295
|
if (pos !== -1) {
|
|
329
296
|
desiredColumn = null;
|
|
330
|
-
|
|
331
|
-
updateVisualSelection(editorInfo, rep);
|
|
297
|
+
return { line, char: pos };
|
|
332
298
|
}
|
|
333
299
|
}
|
|
334
|
-
return
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (key === "}") {
|
|
338
|
-
desiredColumn = null;
|
|
339
|
-
const target = paragraphForward(rep, curLine, count);
|
|
340
|
-
visualCursor = [target, 0];
|
|
341
|
-
updateVisualSelection(editorInfo, rep);
|
|
342
|
-
return true;
|
|
300
|
+
return { line, char };
|
|
343
301
|
}
|
|
344
302
|
|
|
345
|
-
if (key === "
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
visualCursor = [target, 0];
|
|
349
|
-
updateVisualSelection(editorInfo, rep);
|
|
350
|
-
return true;
|
|
303
|
+
if (key === "f" || key === "F" || key === "t" || key === "T") {
|
|
304
|
+
pendingKey = key;
|
|
305
|
+
return "pending";
|
|
351
306
|
}
|
|
352
307
|
|
|
353
|
-
if (key === "
|
|
354
|
-
pendingKey =
|
|
355
|
-
|
|
356
|
-
if (pendingCount !== null) {
|
|
357
|
-
visualCursor = [clampLine(pendingCount - 1, rep), curChar];
|
|
358
|
-
} else {
|
|
359
|
-
visualCursor = [rep.lines.length() - 1, curChar];
|
|
360
|
-
}
|
|
361
|
-
updateVisualSelection(editorInfo, rep);
|
|
362
|
-
return true;
|
|
308
|
+
if (key === "'" || key === "`") {
|
|
309
|
+
pendingKey = key;
|
|
310
|
+
return "pending";
|
|
363
311
|
}
|
|
364
312
|
|
|
365
313
|
if (key === "g") {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
desiredColumn = null;
|
|
369
|
-
visualCursor = [0, curChar];
|
|
370
|
-
updateVisualSelection(editorInfo, rep);
|
|
371
|
-
} else {
|
|
372
|
-
pendingKey = "g";
|
|
373
|
-
}
|
|
374
|
-
return true;
|
|
314
|
+
pendingKey = "g";
|
|
315
|
+
return "pending";
|
|
375
316
|
}
|
|
376
317
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
318
|
+
return null;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// --- Apply motion (mode-aware cursor placement) ---
|
|
322
|
+
|
|
323
|
+
const applyMotion = (editorInfo, rep, newLine, newChar) => {
|
|
324
|
+
if (visualMode !== null) {
|
|
325
|
+
visualCursor = [newLine, newChar];
|
|
326
|
+
updateVisualSelection(editorInfo, rep);
|
|
327
|
+
} else {
|
|
328
|
+
moveBlockCursor(editorInfo, newLine, newChar);
|
|
380
329
|
}
|
|
381
330
|
|
|
382
|
-
if (
|
|
383
|
-
|
|
384
|
-
|
|
331
|
+
if (editorDoc) {
|
|
332
|
+
const lineDiv = editorDoc.body.querySelectorAll("div")[newLine];
|
|
333
|
+
if (lineDiv) lineDiv.scrollIntoView({ block: "nearest" });
|
|
385
334
|
}
|
|
335
|
+
};
|
|
386
336
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
337
|
+
// --- Line deletion helper ---
|
|
338
|
+
|
|
339
|
+
const deleteLines = (editorInfo, rep, topLine, bottomLine) => {
|
|
340
|
+
const totalLines = rep.lines.length();
|
|
341
|
+
if (bottomLine === totalLines - 1 && topLine > 0) {
|
|
342
|
+
const prevLineLen = getLineText(rep, topLine - 1).length;
|
|
343
|
+
replaceRange(
|
|
344
|
+
editorInfo,
|
|
345
|
+
[topLine - 1, prevLineLen],
|
|
346
|
+
[bottomLine, getLineText(rep, bottomLine).length],
|
|
347
|
+
"",
|
|
393
348
|
);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
replaceRange(editorInfo, start, end, toggled);
|
|
401
|
-
setVisualMode(null);
|
|
402
|
-
moveBlockCursor(editorInfo, start[0], start[1]);
|
|
403
|
-
return true;
|
|
349
|
+
return topLine - 1;
|
|
350
|
+
}
|
|
351
|
+
if (bottomLine < totalLines - 1) {
|
|
352
|
+
replaceRange(editorInfo, [topLine, 0], [bottomLine + 1, 0], "");
|
|
353
|
+
return topLine;
|
|
404
354
|
}
|
|
355
|
+
replaceRange(
|
|
356
|
+
editorInfo,
|
|
357
|
+
[0, 0],
|
|
358
|
+
[bottomLine, getLineText(rep, bottomLine).length],
|
|
359
|
+
"",
|
|
360
|
+
);
|
|
361
|
+
return 0;
|
|
362
|
+
};
|
|
405
363
|
|
|
406
|
-
|
|
407
|
-
const [start] = getVisualSelection(
|
|
408
|
-
visualMode,
|
|
409
|
-
visualAnchor,
|
|
410
|
-
visualCursor,
|
|
411
|
-
rep,
|
|
412
|
-
);
|
|
364
|
+
// --- Operator application ---
|
|
413
365
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
366
|
+
const applyCharOperator = (operator, start, end, editorInfo, rep) => {
|
|
367
|
+
if (start[0] === end[0]) {
|
|
368
|
+
const lineText = getLineText(rep, start[0]);
|
|
369
|
+
setRegister(lineText.slice(start[1], end[1]));
|
|
370
|
+
} else {
|
|
371
|
+
setRegister(getTextInRange(rep, start, end));
|
|
372
|
+
}
|
|
373
|
+
if (operator === "y") {
|
|
374
|
+
moveBlockCursor(editorInfo, start[0], start[1]);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
replaceRange(editorInfo, start, end, "");
|
|
378
|
+
if (operator === "c") {
|
|
379
|
+
moveCursor(editorInfo, start[0], start[1]);
|
|
380
|
+
setInsertMode(true);
|
|
381
|
+
} else {
|
|
382
|
+
const newLineText = getLineText(rep, start[0]);
|
|
383
|
+
moveBlockCursor(editorInfo, start[0], clampChar(start[1], newLineText));
|
|
384
|
+
}
|
|
385
|
+
};
|
|
426
386
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
387
|
+
const applyLineOperator = (
|
|
388
|
+
operator,
|
|
389
|
+
topLine,
|
|
390
|
+
bottomLine,
|
|
391
|
+
editorInfo,
|
|
392
|
+
rep,
|
|
393
|
+
char,
|
|
394
|
+
) => {
|
|
395
|
+
const lines = [];
|
|
396
|
+
for (let i = topLine; i <= bottomLine; i++) {
|
|
397
|
+
lines.push(getLineText(rep, i));
|
|
398
|
+
}
|
|
399
|
+
setRegister(lines);
|
|
400
|
+
if (operator === "y") {
|
|
435
401
|
moveBlockCursor(editorInfo, topLine, 0);
|
|
436
|
-
return
|
|
402
|
+
return;
|
|
437
403
|
}
|
|
438
|
-
|
|
439
|
-
if (key === "d" || key === "c") {
|
|
440
|
-
const enterInsert = key === "c";
|
|
441
|
-
const [start, end] = getVisualSelection(
|
|
442
|
-
visualMode,
|
|
443
|
-
visualAnchor,
|
|
444
|
-
visualCursor,
|
|
445
|
-
rep,
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
if (visualMode === "char") {
|
|
449
|
-
setRegister(getTextInRange(rep, start, end));
|
|
450
|
-
replaceRange(editorInfo, start, end, "");
|
|
451
|
-
if (enterInsert) {
|
|
452
|
-
moveCursor(editorInfo, start[0], start[1]);
|
|
453
|
-
setVisualMode(null);
|
|
454
|
-
setInsertMode(true);
|
|
455
|
-
} else {
|
|
456
|
-
setVisualMode(null);
|
|
457
|
-
moveBlockCursor(editorInfo, start[0], start[1]);
|
|
458
|
-
}
|
|
459
|
-
return true;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
const topLine = start[0];
|
|
463
|
-
const bottomLine = Math.max(visualAnchor[0], visualCursor[0]);
|
|
464
|
-
const totalLines = rep.lines.length();
|
|
465
|
-
const lines = [];
|
|
404
|
+
if (operator === "c") {
|
|
466
405
|
for (let i = topLine; i <= bottomLine; i++) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
setRegister(lines);
|
|
470
|
-
|
|
471
|
-
if (enterInsert) {
|
|
472
|
-
for (let i = topLine; i <= bottomLine; i++) {
|
|
473
|
-
const text = getLineText(rep, i);
|
|
474
|
-
replaceRange(editorInfo, [topLine, 0], [topLine, text.length], "");
|
|
475
|
-
}
|
|
476
|
-
moveCursor(editorInfo, topLine, 0);
|
|
477
|
-
setVisualMode(null);
|
|
478
|
-
setInsertMode(true);
|
|
479
|
-
return true;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (bottomLine === totalLines - 1 && topLine > 0) {
|
|
483
|
-
const prevLineLen = getLineText(rep, topLine - 1).length;
|
|
484
|
-
replaceRange(
|
|
485
|
-
editorInfo,
|
|
486
|
-
[topLine - 1, prevLineLen],
|
|
487
|
-
[bottomLine, getLineText(rep, bottomLine).length],
|
|
488
|
-
"",
|
|
489
|
-
);
|
|
490
|
-
moveBlockCursor(editorInfo, topLine - 1, 0);
|
|
491
|
-
} else if (bottomLine < totalLines - 1) {
|
|
492
|
-
replaceRange(editorInfo, [topLine, 0], [bottomLine + 1, 0], "");
|
|
493
|
-
moveBlockCursor(editorInfo, topLine, 0);
|
|
494
|
-
} else {
|
|
495
|
-
replaceRange(
|
|
496
|
-
editorInfo,
|
|
497
|
-
[0, 0],
|
|
498
|
-
[bottomLine, getLineText(rep, bottomLine).length],
|
|
499
|
-
"",
|
|
500
|
-
);
|
|
501
|
-
moveBlockCursor(editorInfo, 0, 0);
|
|
406
|
+
const text = getLineText(rep, i);
|
|
407
|
+
replaceRange(editorInfo, [topLine, 0], [topLine, text.length], "");
|
|
502
408
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
return
|
|
409
|
+
moveCursor(editorInfo, topLine, 0);
|
|
410
|
+
setInsertMode(true);
|
|
411
|
+
return;
|
|
506
412
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
413
|
+
const cursorLine = deleteLines(editorInfo, rep, topLine, bottomLine);
|
|
414
|
+
const newLineText = getLineText(rep, cursorLine);
|
|
415
|
+
moveBlockCursor(editorInfo, cursorLine, clampChar(char, newLineText));
|
|
510
416
|
};
|
|
511
417
|
|
|
512
|
-
// ---
|
|
418
|
+
// --- Unified key handler ---
|
|
513
419
|
|
|
514
|
-
const
|
|
515
|
-
const
|
|
516
|
-
const
|
|
420
|
+
const handleKey = (rep, editorInfo, key) => {
|
|
421
|
+
const inVisual = visualMode !== null;
|
|
422
|
+
const line = inVisual ? visualCursor[0] : rep.selStart[0];
|
|
423
|
+
const char = inVisual ? visualCursor[1] : rep.selStart[1];
|
|
517
424
|
const lineText = getLineText(rep, line);
|
|
518
425
|
|
|
519
426
|
if (key >= "1" && key <= "9") {
|
|
@@ -528,6 +435,8 @@ const handleNormalKey = (rep, editorInfo, key) => {
|
|
|
528
435
|
consumeCount();
|
|
529
436
|
const count = getCount();
|
|
530
437
|
|
|
438
|
+
// --- Normal-only pending states: r + char, m + letter ---
|
|
439
|
+
|
|
531
440
|
if (pendingKey === "r") {
|
|
532
441
|
pendingKey = null;
|
|
533
442
|
if (lineText.length > 0) {
|
|
@@ -537,306 +446,190 @@ const handleNormalKey = (rep, editorInfo, key) => {
|
|
|
537
446
|
return true;
|
|
538
447
|
}
|
|
539
448
|
|
|
540
|
-
if (
|
|
541
|
-
pendingKey === "f" ||
|
|
542
|
-
pendingKey === "F" ||
|
|
543
|
-
pendingKey === "t" ||
|
|
544
|
-
pendingKey === "T"
|
|
545
|
-
) {
|
|
546
|
-
const direction = pendingKey;
|
|
547
|
-
pendingKey = null;
|
|
548
|
-
lastCharSearch = { direction, target: key };
|
|
549
|
-
const pos = charSearchPos(direction, lineText, char, key, count);
|
|
550
|
-
if (pos !== -1) {
|
|
551
|
-
desiredColumn = null;
|
|
552
|
-
moveBlockCursor(editorInfo, line, pos);
|
|
553
|
-
}
|
|
554
|
-
return true;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
if (
|
|
558
|
-
pendingKey === "df" ||
|
|
559
|
-
pendingKey === "dF" ||
|
|
560
|
-
pendingKey === "dt" ||
|
|
561
|
-
pendingKey === "dT"
|
|
562
|
-
) {
|
|
563
|
-
const motion = pendingKey[1];
|
|
449
|
+
if (pendingKey === "m") {
|
|
564
450
|
pendingKey = null;
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
if (pos !== -1) {
|
|
568
|
-
const range = charMotionRange(motion, char, pos);
|
|
569
|
-
if (range) {
|
|
570
|
-
setRegister(lineText.slice(range.start, range.end));
|
|
571
|
-
replaceRange(editorInfo, [line, range.start], [line, range.end], "");
|
|
572
|
-
const newLineText = getLineText(rep, line);
|
|
573
|
-
moveBlockCursor(editorInfo, line, clampChar(range.start, newLineText));
|
|
574
|
-
}
|
|
451
|
+
if (key >= "a" && key <= "z") {
|
|
452
|
+
marks[key] = [line, char];
|
|
575
453
|
}
|
|
576
454
|
return true;
|
|
577
455
|
}
|
|
578
456
|
|
|
579
|
-
|
|
580
|
-
const range = textObjectRange(key, lineText, char, pendingKey[1]);
|
|
581
|
-
pendingKey = null;
|
|
582
|
-
if (range) {
|
|
583
|
-
setRegister(lineText.slice(range.start, range.end));
|
|
584
|
-
replaceRange(editorInfo, [line, range.start], [line, range.end], "");
|
|
585
|
-
const newLineText = getLineText(rep, line);
|
|
586
|
-
moveBlockCursor(editorInfo, line, clampChar(range.start, newLineText));
|
|
587
|
-
}
|
|
588
|
-
return true;
|
|
589
|
-
}
|
|
457
|
+
// --- Operator-pending: resolve target ---
|
|
590
458
|
|
|
591
|
-
if (
|
|
592
|
-
|
|
459
|
+
if (pendingOperator !== null) {
|
|
460
|
+
const op = pendingOperator;
|
|
593
461
|
|
|
594
|
-
if (key ===
|
|
595
|
-
|
|
596
|
-
const
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
601
|
-
setRegister(deletedLines);
|
|
602
|
-
if (lastDeleteLine === lineCount - 1 && line > 0) {
|
|
603
|
-
const prevLineText = getLineText(rep, line - 1);
|
|
604
|
-
replaceRange(
|
|
605
|
-
editorInfo,
|
|
606
|
-
[line - 1, prevLineText.length],
|
|
607
|
-
[lastDeleteLine, getLineText(rep, lastDeleteLine).length],
|
|
608
|
-
"",
|
|
609
|
-
);
|
|
610
|
-
moveBlockCursor(editorInfo, line - 1, clampChar(char, prevLineText));
|
|
611
|
-
} else if (lineCount > deleteCount) {
|
|
612
|
-
replaceRange(editorInfo, [line, 0], [lastDeleteLine + 1, 0], "");
|
|
613
|
-
const newLineText = getLineText(rep, line);
|
|
614
|
-
moveBlockCursor(editorInfo, line, clampChar(char, newLineText));
|
|
615
|
-
} else {
|
|
616
|
-
replaceRange(
|
|
617
|
-
editorInfo,
|
|
618
|
-
[0, 0],
|
|
619
|
-
[lastDeleteLine, getLineText(rep, lastDeleteLine).length],
|
|
620
|
-
"",
|
|
621
|
-
);
|
|
622
|
-
moveBlockCursor(editorInfo, 0, 0);
|
|
623
|
-
}
|
|
624
|
-
return true;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
if (key === "i") {
|
|
628
|
-
pendingKey = "di";
|
|
629
|
-
return true;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
if (key === "f" || key === "F" || key === "t" || key === "T") {
|
|
633
|
-
pendingKey = "d" + key;
|
|
462
|
+
if (key === op) {
|
|
463
|
+
pendingOperator = null;
|
|
464
|
+
const lineCount = rep.lines.length();
|
|
465
|
+
const opCount = Math.min(count, lineCount - line);
|
|
466
|
+
const lastLine = line + opCount - 1;
|
|
467
|
+
applyLineOperator(op, line, lastLine, editorInfo, rep, char);
|
|
634
468
|
return true;
|
|
635
469
|
}
|
|
636
470
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
const
|
|
642
|
-
moveBlockCursor(editorInfo, line, clampChar(range.start, newLineText));
|
|
643
|
-
}
|
|
644
|
-
return true;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
if (
|
|
648
|
-
pendingKey === "yf" ||
|
|
649
|
-
pendingKey === "yF" ||
|
|
650
|
-
pendingKey === "yt" ||
|
|
651
|
-
pendingKey === "yT"
|
|
652
|
-
) {
|
|
653
|
-
const motion = pendingKey[1];
|
|
654
|
-
pendingKey = null;
|
|
655
|
-
const pos = charSearchPos(motion, lineText, char, key, count);
|
|
656
|
-
if (pos !== -1) {
|
|
657
|
-
const range = charMotionRange(motion, char, pos);
|
|
658
|
-
if (range) {
|
|
659
|
-
setRegister(lineText.slice(range.start, range.end));
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
return true;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
if (
|
|
666
|
-
pendingKey === "cf" ||
|
|
667
|
-
pendingKey === "cF" ||
|
|
668
|
-
pendingKey === "ct" ||
|
|
669
|
-
pendingKey === "cT"
|
|
670
|
-
) {
|
|
671
|
-
const motion = pendingKey[1];
|
|
672
|
-
pendingKey = null;
|
|
673
|
-
const pos = charSearchPos(motion, lineText, char, key, count);
|
|
674
|
-
if (pos !== -1) {
|
|
675
|
-
const range = charMotionRange(motion, char, pos);
|
|
471
|
+
if (pendingKey === "i" || pendingKey === "a") {
|
|
472
|
+
const type = pendingKey;
|
|
473
|
+
pendingKey = null;
|
|
474
|
+
pendingOperator = null;
|
|
475
|
+
const range = textObjectRange(key, lineText, char, type);
|
|
676
476
|
if (range) {
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
477
|
+
applyCharOperator(
|
|
478
|
+
op,
|
|
479
|
+
[line, range.start],
|
|
480
|
+
[line, range.end],
|
|
481
|
+
editorInfo,
|
|
482
|
+
rep,
|
|
483
|
+
);
|
|
681
484
|
}
|
|
682
|
-
}
|
|
683
|
-
return true;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
if (pendingKey === "ci" || pendingKey === "ca") {
|
|
687
|
-
const range = textObjectRange(key, lineText, char, pendingKey[1]);
|
|
688
|
-
pendingKey = null;
|
|
689
|
-
if (range) {
|
|
690
|
-
setRegister(lineText.slice(range.start, range.end));
|
|
691
|
-
replaceRange(editorInfo, [line, range.start], [line, range.end], "");
|
|
692
|
-
moveCursor(editorInfo, line, range.start);
|
|
693
|
-
setInsertMode(true);
|
|
694
|
-
}
|
|
695
|
-
return true;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
if (pendingKey === "c") {
|
|
699
|
-
pendingKey = null;
|
|
700
|
-
|
|
701
|
-
if (key === "c") {
|
|
702
|
-
setRegister(lineText);
|
|
703
|
-
replaceRange(editorInfo, [line, 0], [line, lineText.length], "");
|
|
704
|
-
moveCursor(editorInfo, line, 0);
|
|
705
|
-
setInsertMode(true);
|
|
706
|
-
return true;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
if (key === "i") {
|
|
710
|
-
pendingKey = "ci";
|
|
711
|
-
return true;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
if (key === "f" || key === "F" || key === "t" || key === "T") {
|
|
715
|
-
pendingKey = "c" + key;
|
|
716
485
|
return true;
|
|
717
486
|
}
|
|
718
487
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
if (key === "y") {
|
|
742
|
-
const yankCount = Math.min(count, lineCount - line);
|
|
743
|
-
const lastYankLine = line + yankCount - 1;
|
|
744
|
-
const yankedLines = [];
|
|
745
|
-
for (let i = line; i <= lastYankLine; i++) {
|
|
746
|
-
yankedLines.push(getLineText(rep, i));
|
|
488
|
+
if (
|
|
489
|
+
pendingKey === "f" ||
|
|
490
|
+
pendingKey === "F" ||
|
|
491
|
+
pendingKey === "t" ||
|
|
492
|
+
pendingKey === "T"
|
|
493
|
+
) {
|
|
494
|
+
const direction = pendingKey;
|
|
495
|
+
pendingKey = null;
|
|
496
|
+
pendingOperator = null;
|
|
497
|
+
lastCharSearch = { direction, target: key };
|
|
498
|
+
const pos = charSearchPos(direction, lineText, char, key, count);
|
|
499
|
+
if (pos !== -1) {
|
|
500
|
+
const range = charMotionRange(direction, char, pos);
|
|
501
|
+
if (range) {
|
|
502
|
+
applyCharOperator(
|
|
503
|
+
op,
|
|
504
|
+
[line, range.start],
|
|
505
|
+
[line, range.end],
|
|
506
|
+
editorInfo,
|
|
507
|
+
rep,
|
|
508
|
+
);
|
|
509
|
+
}
|
|
747
510
|
}
|
|
748
|
-
setRegister(yankedLines);
|
|
749
511
|
return true;
|
|
750
512
|
}
|
|
751
513
|
|
|
752
|
-
if (key === "i") {
|
|
753
|
-
pendingKey =
|
|
514
|
+
if (key === "i" || key === "a") {
|
|
515
|
+
pendingKey = key;
|
|
754
516
|
return true;
|
|
755
517
|
}
|
|
756
518
|
|
|
757
519
|
if (key === "f" || key === "F" || key === "t" || key === "T") {
|
|
758
|
-
pendingKey =
|
|
520
|
+
pendingKey = key;
|
|
759
521
|
return true;
|
|
760
522
|
}
|
|
761
523
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
marks[key] = [line, char];
|
|
773
|
-
}
|
|
774
|
-
return true;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
if (pendingKey === "'" || pendingKey === "`") {
|
|
778
|
-
const jumpType = pendingKey;
|
|
779
|
-
pendingKey = null;
|
|
780
|
-
if (key >= "a" && key <= "z" && marks[key]) {
|
|
781
|
-
const [markLine, markChar] = marks[key];
|
|
782
|
-
desiredColumn = null;
|
|
783
|
-
if (jumpType === "'") {
|
|
784
|
-
const targetLineText = getLineText(rep, markLine);
|
|
785
|
-
moveBlockCursor(editorInfo, markLine, firstNonBlank(targetLineText));
|
|
786
|
-
} else {
|
|
787
|
-
moveBlockCursor(editorInfo, markLine, markChar);
|
|
788
|
-
}
|
|
524
|
+
pendingOperator = null;
|
|
525
|
+
const range = motionRange(key, char, lineText, count);
|
|
526
|
+
if (range && range.end > range.start) {
|
|
527
|
+
applyCharOperator(
|
|
528
|
+
op,
|
|
529
|
+
[line, range.start],
|
|
530
|
+
[line, range.end],
|
|
531
|
+
editorInfo,
|
|
532
|
+
rep,
|
|
533
|
+
);
|
|
789
534
|
}
|
|
790
535
|
return true;
|
|
791
536
|
}
|
|
792
537
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
538
|
+
// --- Text object in visual mode (i/a + object key) ---
|
|
539
|
+
|
|
540
|
+
if (inVisual && (pendingKey === "i" || pendingKey === "a")) {
|
|
541
|
+
const type = pendingKey;
|
|
542
|
+
pendingKey = null;
|
|
543
|
+
const range = textObjectRange(key, lineText, char, type);
|
|
544
|
+
if (range) {
|
|
545
|
+
visualAnchor = [line, range.start];
|
|
546
|
+
visualCursor = [line, range.end];
|
|
547
|
+
setVisualMode("char");
|
|
548
|
+
updateVisualSelection(editorInfo, rep);
|
|
549
|
+
}
|
|
796
550
|
return true;
|
|
797
551
|
}
|
|
798
552
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
553
|
+
// --- Motions (shared between normal and visual) ---
|
|
554
|
+
|
|
555
|
+
const motion = resolveMotion(key, line, char, lineText, rep, count);
|
|
556
|
+
if (motion === "pending") return true;
|
|
557
|
+
if (motion) {
|
|
558
|
+
applyMotion(editorInfo, rep, motion.line, motion.char);
|
|
802
559
|
return true;
|
|
803
560
|
}
|
|
804
561
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
562
|
+
// --- Operators (d/c/y) ---
|
|
563
|
+
|
|
564
|
+
if (key === "d" || key === "c" || key === "y") {
|
|
565
|
+
if (inVisual) {
|
|
566
|
+
if (visualMode === "char") {
|
|
567
|
+
const [start, end] = getVisualSelection(
|
|
568
|
+
visualMode,
|
|
569
|
+
visualAnchor,
|
|
570
|
+
visualCursor,
|
|
571
|
+
rep,
|
|
572
|
+
);
|
|
573
|
+
setVisualMode(null);
|
|
574
|
+
applyCharOperator(key, start, end, editorInfo, rep);
|
|
575
|
+
} else {
|
|
576
|
+
const topLine = Math.min(visualAnchor[0], visualCursor[0]);
|
|
577
|
+
const bottomLine = Math.max(visualAnchor[0], visualCursor[0]);
|
|
578
|
+
setVisualMode(null);
|
|
579
|
+
applyLineOperator(key, topLine, bottomLine, editorInfo, rep, 0);
|
|
580
|
+
}
|
|
581
|
+
return true;
|
|
808
582
|
}
|
|
809
|
-
|
|
810
|
-
const newLineText = getLineText(rep, newLine);
|
|
811
|
-
moveBlockCursor(editorInfo, newLine, clampChar(desiredColumn, newLineText));
|
|
583
|
+
pendingOperator = key;
|
|
812
584
|
return true;
|
|
813
585
|
}
|
|
814
586
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
587
|
+
// --- Visual-mode specific ---
|
|
588
|
+
|
|
589
|
+
if (inVisual) {
|
|
590
|
+
if (key === "i" || key === "a") {
|
|
591
|
+
pendingKey = key;
|
|
592
|
+
return true;
|
|
818
593
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
594
|
+
|
|
595
|
+
if (key === "~") {
|
|
596
|
+
const [start, end] = getVisualSelection(
|
|
597
|
+
visualMode,
|
|
598
|
+
visualAnchor,
|
|
599
|
+
visualCursor,
|
|
600
|
+
rep,
|
|
601
|
+
);
|
|
602
|
+
const text = getTextInRange(rep, start, end);
|
|
603
|
+
let toggled = "";
|
|
604
|
+
for (let i = 0; i < text.length; i++) {
|
|
605
|
+
const ch = text[i];
|
|
606
|
+
toggled +=
|
|
607
|
+
ch === ch.toLowerCase() ? ch.toUpperCase() : ch.toLowerCase();
|
|
608
|
+
}
|
|
609
|
+
replaceRange(editorInfo, start, end, toggled);
|
|
610
|
+
setVisualMode(null);
|
|
611
|
+
moveBlockCursor(editorInfo, start[0], start[1]);
|
|
612
|
+
return true;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
pendingKey = null;
|
|
616
|
+
return false;
|
|
823
617
|
}
|
|
824
618
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
619
|
+
// --- Normal-mode only commands ---
|
|
620
|
+
|
|
621
|
+
if (key === "Y") {
|
|
622
|
+
setRegister([lineText]);
|
|
828
623
|
return true;
|
|
829
624
|
}
|
|
830
625
|
|
|
831
|
-
if (key === "
|
|
832
|
-
|
|
833
|
-
moveBlockCursor(editorInfo, line, clampChar(lineText.length - 1, lineText));
|
|
626
|
+
if (key === "r") {
|
|
627
|
+
if (lineText.length > 0) pendingKey = "r";
|
|
834
628
|
return true;
|
|
835
629
|
}
|
|
836
630
|
|
|
837
|
-
if (key === "
|
|
838
|
-
|
|
839
|
-
moveBlockCursor(editorInfo, line, firstNonBlank(lineText));
|
|
631
|
+
if (key === "m") {
|
|
632
|
+
pendingKey = "m";
|
|
840
633
|
return true;
|
|
841
634
|
}
|
|
842
635
|
|
|
@@ -850,22 +643,6 @@ const handleNormalKey = (rep, editorInfo, key) => {
|
|
|
850
643
|
return true;
|
|
851
644
|
}
|
|
852
645
|
|
|
853
|
-
if (key === "w") {
|
|
854
|
-
desiredColumn = null;
|
|
855
|
-
let pos = char;
|
|
856
|
-
for (let i = 0; i < count; i++) pos = wordForward(lineText, pos);
|
|
857
|
-
moveBlockCursor(editorInfo, line, clampChar(pos, lineText));
|
|
858
|
-
return true;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
if (key === "b") {
|
|
862
|
-
desiredColumn = null;
|
|
863
|
-
let pos = char;
|
|
864
|
-
for (let i = 0; i < count; i++) pos = wordBackward(lineText, pos);
|
|
865
|
-
moveBlockCursor(editorInfo, line, pos);
|
|
866
|
-
return true;
|
|
867
|
-
}
|
|
868
|
-
|
|
869
646
|
if (key === "o") {
|
|
870
647
|
replaceRange(
|
|
871
648
|
editorInfo,
|
|
@@ -937,70 +714,8 @@ const handleNormalKey = (rep, editorInfo, key) => {
|
|
|
937
714
|
return true;
|
|
938
715
|
}
|
|
939
716
|
|
|
940
|
-
if (key === "G") {
|
|
941
|
-
desiredColumn = null;
|
|
942
|
-
if (pendingCount !== null) {
|
|
943
|
-
moveBlockCursor(editorInfo, clampLine(pendingCount - 1, rep), 0);
|
|
944
|
-
} else {
|
|
945
|
-
moveBlockCursor(editorInfo, lineCount - 1, 0);
|
|
946
|
-
}
|
|
947
|
-
return true;
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
if (key === "g") {
|
|
951
|
-
if (pendingKey === "g") {
|
|
952
|
-
pendingKey = null;
|
|
953
|
-
desiredColumn = null;
|
|
954
|
-
moveBlockCursor(editorInfo, 0, 0);
|
|
955
|
-
} else {
|
|
956
|
-
pendingKey = "g";
|
|
957
|
-
}
|
|
958
|
-
return true;
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
if (key === "r") {
|
|
962
|
-
if (lineText.length > 0) {
|
|
963
|
-
pendingKey = "r";
|
|
964
|
-
}
|
|
965
|
-
return true;
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
if (key === "f" || key === "F" || key === "t" || key === "T") {
|
|
969
|
-
pendingKey = key;
|
|
970
|
-
return true;
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
if (key === "m") {
|
|
974
|
-
pendingKey = "m";
|
|
975
|
-
return true;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
if (key === "'" || key === "`") {
|
|
979
|
-
pendingKey = key;
|
|
980
|
-
return true;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
if (key === "d") {
|
|
984
|
-
pendingKey = "d";
|
|
985
|
-
return true;
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
if (key === "c") {
|
|
989
|
-
pendingKey = "c";
|
|
990
|
-
return true;
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
if (key === "y") {
|
|
994
|
-
pendingKey = "y";
|
|
995
|
-
return true;
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
if (key === "Y") {
|
|
999
|
-
setRegister([lineText]);
|
|
1000
|
-
return true;
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
717
|
if (key === "J") {
|
|
718
|
+
const lineCount = rep.lines.length();
|
|
1004
719
|
const joins = Math.min(count, lineCount - 1 - line);
|
|
1005
720
|
let cursorChar = lineText.length;
|
|
1006
721
|
for (let i = 0; i < joins; i++) {
|
|
@@ -1079,65 +794,7 @@ const handleNormalKey = (rep, editorInfo, key) => {
|
|
|
1079
794
|
return true;
|
|
1080
795
|
}
|
|
1081
796
|
|
|
1082
|
-
if (key === ";") {
|
|
1083
|
-
if (lastCharSearch) {
|
|
1084
|
-
const pos = charSearchPos(
|
|
1085
|
-
lastCharSearch.direction,
|
|
1086
|
-
lineText,
|
|
1087
|
-
char,
|
|
1088
|
-
lastCharSearch.target,
|
|
1089
|
-
count,
|
|
1090
|
-
);
|
|
1091
|
-
if (pos !== -1) {
|
|
1092
|
-
desiredColumn = null;
|
|
1093
|
-
moveBlockCursor(editorInfo, line, pos);
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
return true;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
if (key === ",") {
|
|
1100
|
-
if (lastCharSearch) {
|
|
1101
|
-
const opposite = { f: "F", F: "f", t: "T", T: "t" };
|
|
1102
|
-
const reverseDir = opposite[lastCharSearch.direction];
|
|
1103
|
-
const pos = charSearchPos(
|
|
1104
|
-
reverseDir,
|
|
1105
|
-
lineText,
|
|
1106
|
-
char,
|
|
1107
|
-
lastCharSearch.target,
|
|
1108
|
-
count,
|
|
1109
|
-
);
|
|
1110
|
-
if (pos !== -1) {
|
|
1111
|
-
desiredColumn = null;
|
|
1112
|
-
moveBlockCursor(editorInfo, line, pos);
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
return true;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
if (key === "}") {
|
|
1119
|
-
desiredColumn = null;
|
|
1120
|
-
const target = paragraphForward(rep, line, count);
|
|
1121
|
-
moveBlockCursor(editorInfo, target, 0);
|
|
1122
|
-
return true;
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
if (key === "{") {
|
|
1126
|
-
desiredColumn = null;
|
|
1127
|
-
const target = paragraphBackward(rep, line, count);
|
|
1128
|
-
moveBlockCursor(editorInfo, target, 0);
|
|
1129
|
-
return true;
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
797
|
pendingKey = null;
|
|
1133
|
-
|
|
1134
|
-
if (key === "e") {
|
|
1135
|
-
let pos = char;
|
|
1136
|
-
for (let i = 0; i < count; i++) pos = wordEnd(lineText, pos);
|
|
1137
|
-
moveBlockCursor(editorInfo, line, clampChar(pos, lineText));
|
|
1138
|
-
return true;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
798
|
return false;
|
|
1142
799
|
};
|
|
1143
800
|
|
|
@@ -1176,71 +833,6 @@ exports.aceKeyEvent = (_hookName, { evt, rep, editorInfo }) => {
|
|
|
1176
833
|
setInsertMode(insertMode);
|
|
1177
834
|
}
|
|
1178
835
|
|
|
1179
|
-
if (visualMode !== null && pendingKey !== null) {
|
|
1180
|
-
const handled = handleVisualKey(rep, editorInfo, evt.key);
|
|
1181
|
-
evt.preventDefault();
|
|
1182
|
-
return handled || true;
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
if (!insertMode && visualMode === null && pendingKey !== null) {
|
|
1186
|
-
const handled = handleNormalKey(rep, editorInfo, evt.key);
|
|
1187
|
-
evt.preventDefault();
|
|
1188
|
-
return handled || true;
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
if (
|
|
1192
|
-
!insertMode &&
|
|
1193
|
-
visualMode !== null &&
|
|
1194
|
-
(evt.key === "i" || evt.key === "a")
|
|
1195
|
-
) {
|
|
1196
|
-
const handled = handleVisualKey(rep, editorInfo, evt.key);
|
|
1197
|
-
evt.preventDefault();
|
|
1198
|
-
return handled || true;
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
if (!insertMode && evt.key === "i") {
|
|
1202
|
-
const [line, char] = rep.selStart;
|
|
1203
|
-
desiredColumn = null;
|
|
1204
|
-
moveCursor(editorInfo, line, char);
|
|
1205
|
-
setVisualMode(null);
|
|
1206
|
-
setInsertMode(true);
|
|
1207
|
-
evt.preventDefault();
|
|
1208
|
-
return true;
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
if (!insertMode && evt.key === "a") {
|
|
1212
|
-
const [line, char] = rep.selStart;
|
|
1213
|
-
const lineText = getLineText(rep, line);
|
|
1214
|
-
desiredColumn = null;
|
|
1215
|
-
moveCursor(editorInfo, line, Math.min(char + 1, lineText.length));
|
|
1216
|
-
setVisualMode(null);
|
|
1217
|
-
setInsertMode(true);
|
|
1218
|
-
evt.preventDefault();
|
|
1219
|
-
return true;
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
if (!insertMode && evt.key === "A") {
|
|
1223
|
-
const [line] = rep.selStart;
|
|
1224
|
-
const lineText = getLineText(rep, line);
|
|
1225
|
-
desiredColumn = null;
|
|
1226
|
-
moveCursor(editorInfo, line, lineText.length);
|
|
1227
|
-
setVisualMode(null);
|
|
1228
|
-
setInsertMode(true);
|
|
1229
|
-
evt.preventDefault();
|
|
1230
|
-
return true;
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
if (!insertMode && evt.key === "I") {
|
|
1234
|
-
const [line] = rep.selStart;
|
|
1235
|
-
const lineText = getLineText(rep, line);
|
|
1236
|
-
desiredColumn = null;
|
|
1237
|
-
moveCursor(editorInfo, line, firstNonBlank(lineText));
|
|
1238
|
-
setVisualMode(null);
|
|
1239
|
-
setInsertMode(true);
|
|
1240
|
-
evt.preventDefault();
|
|
1241
|
-
return true;
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
836
|
if (evt.key === "Escape") {
|
|
1245
837
|
if (insertMode) {
|
|
1246
838
|
setInsertMode(false);
|
|
@@ -1254,43 +846,83 @@ exports.aceKeyEvent = (_hookName, { evt, rep, editorInfo }) => {
|
|
|
1254
846
|
}
|
|
1255
847
|
countBuffer = "";
|
|
1256
848
|
pendingKey = null;
|
|
849
|
+
pendingOperator = null;
|
|
1257
850
|
pendingCount = null;
|
|
1258
851
|
desiredColumn = null;
|
|
1259
852
|
evt.preventDefault();
|
|
1260
853
|
return true;
|
|
1261
854
|
}
|
|
1262
855
|
|
|
1263
|
-
if (
|
|
1264
|
-
const [line] = rep.selStart;
|
|
1265
|
-
visualAnchor = [line, 0];
|
|
1266
|
-
visualCursor = [line, 0];
|
|
1267
|
-
setVisualMode("line");
|
|
1268
|
-
updateVisualSelection(editorInfo, rep);
|
|
1269
|
-
evt.preventDefault();
|
|
1270
|
-
return true;
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
if (!insertMode && visualMode === null && evt.key === "v") {
|
|
1274
|
-
const [line, char] = rep.selStart;
|
|
1275
|
-
visualAnchor = [line, char];
|
|
1276
|
-
visualCursor = [line, char];
|
|
1277
|
-
setVisualMode("char");
|
|
1278
|
-
updateVisualSelection(editorInfo, rep);
|
|
1279
|
-
evt.preventDefault();
|
|
1280
|
-
return true;
|
|
1281
|
-
}
|
|
856
|
+
if (insertMode) return false;
|
|
1282
857
|
|
|
1283
|
-
if (
|
|
1284
|
-
const handled =
|
|
858
|
+
if (pendingKey !== null || pendingOperator !== null) {
|
|
859
|
+
const handled = handleKey(rep, editorInfo, evt.key);
|
|
1285
860
|
evt.preventDefault();
|
|
1286
861
|
return handled || true;
|
|
1287
862
|
}
|
|
1288
863
|
|
|
1289
|
-
if (
|
|
1290
|
-
|
|
864
|
+
if (visualMode === null) {
|
|
865
|
+
if (evt.key === "i") {
|
|
866
|
+
const [line, char] = rep.selStart;
|
|
867
|
+
desiredColumn = null;
|
|
868
|
+
moveCursor(editorInfo, line, char);
|
|
869
|
+
setInsertMode(true);
|
|
870
|
+
evt.preventDefault();
|
|
871
|
+
return true;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (evt.key === "a") {
|
|
875
|
+
const [line, char] = rep.selStart;
|
|
876
|
+
const lineText = getLineText(rep, line);
|
|
877
|
+
desiredColumn = null;
|
|
878
|
+
moveCursor(editorInfo, line, Math.min(char + 1, lineText.length));
|
|
879
|
+
setInsertMode(true);
|
|
880
|
+
evt.preventDefault();
|
|
881
|
+
return true;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (evt.key === "A") {
|
|
885
|
+
const [line] = rep.selStart;
|
|
886
|
+
const lineText = getLineText(rep, line);
|
|
887
|
+
desiredColumn = null;
|
|
888
|
+
moveCursor(editorInfo, line, lineText.length);
|
|
889
|
+
setInsertMode(true);
|
|
890
|
+
evt.preventDefault();
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (evt.key === "I") {
|
|
895
|
+
const [line] = rep.selStart;
|
|
896
|
+
const lineText = getLineText(rep, line);
|
|
897
|
+
desiredColumn = null;
|
|
898
|
+
moveCursor(editorInfo, line, firstNonBlank(lineText));
|
|
899
|
+
setInsertMode(true);
|
|
900
|
+
evt.preventDefault();
|
|
901
|
+
return true;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if (evt.key === "V") {
|
|
905
|
+
const [line] = rep.selStart;
|
|
906
|
+
visualAnchor = [line, 0];
|
|
907
|
+
visualCursor = [line, 0];
|
|
908
|
+
setVisualMode("line");
|
|
909
|
+
updateVisualSelection(editorInfo, rep);
|
|
910
|
+
evt.preventDefault();
|
|
911
|
+
return true;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (evt.key === "v") {
|
|
915
|
+
const [line, char] = rep.selStart;
|
|
916
|
+
visualAnchor = [line, char];
|
|
917
|
+
visualCursor = [line, char];
|
|
918
|
+
setVisualMode("char");
|
|
919
|
+
updateVisualSelection(editorInfo, rep);
|
|
920
|
+
evt.preventDefault();
|
|
921
|
+
return true;
|
|
922
|
+
}
|
|
1291
923
|
}
|
|
1292
924
|
|
|
1293
|
-
const handled =
|
|
925
|
+
const handled = handleKey(rep, editorInfo, evt.key);
|
|
1294
926
|
evt.preventDefault();
|
|
1295
927
|
return handled || true;
|
|
1296
928
|
};
|