ep_vim 0.9.1 → 0.9.2
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 +2 -2
- package/static/js/index.js +267 -269
- package/static/js/index.test.js +3081 -0
- package/static/js/vim-core.js +1 -0
package/static/js/index.js
CHANGED
|
@@ -20,38 +20,40 @@ const {
|
|
|
20
20
|
getVisualSelection,
|
|
21
21
|
paragraphTextRange,
|
|
22
22
|
sentenceTextRange,
|
|
23
|
-
getFullText,
|
|
24
|
-
posToAbsolute,
|
|
25
|
-
absoluteToPos,
|
|
26
23
|
searchForward,
|
|
27
24
|
searchBackward,
|
|
28
25
|
} = require("./vim-core");
|
|
29
26
|
|
|
30
27
|
// --- State ---
|
|
31
28
|
|
|
32
|
-
let vimEnabled =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
29
|
+
let vimEnabled =
|
|
30
|
+
typeof localStorage !== "undefined" &&
|
|
31
|
+
localStorage.getItem("ep_vimEnabled") === "true";
|
|
32
|
+
|
|
33
|
+
const state = {
|
|
34
|
+
mode: "normal",
|
|
35
|
+
pendingKey: null,
|
|
36
|
+
pendingCount: null,
|
|
37
|
+
countBuffer: "",
|
|
38
|
+
register: null,
|
|
39
|
+
marks: {},
|
|
40
|
+
lastCharSearch: null,
|
|
41
|
+
visualAnchor: null,
|
|
42
|
+
visualCursor: null,
|
|
43
|
+
editorDoc: null,
|
|
44
|
+
currentRep: null,
|
|
45
|
+
desiredColumn: null,
|
|
46
|
+
lastCommand: null,
|
|
47
|
+
searchMode: false,
|
|
48
|
+
searchBuffer: "",
|
|
49
|
+
searchDirection: null,
|
|
50
|
+
lastSearch: null,
|
|
51
|
+
};
|
|
50
52
|
|
|
51
53
|
// --- Editor operations ---
|
|
52
54
|
|
|
53
55
|
const setRegister = (value) => {
|
|
54
|
-
register = value;
|
|
56
|
+
state.register = value;
|
|
55
57
|
const text = Array.isArray(value) ? value.join("\n") + "\n" : value;
|
|
56
58
|
if (navigator.clipboard) {
|
|
57
59
|
navigator.clipboard.writeText(text).catch(() => {});
|
|
@@ -80,33 +82,37 @@ const selectRange = (editorInfo, start, end) => {
|
|
|
80
82
|
};
|
|
81
83
|
|
|
82
84
|
const updateVisualSelection = (editorInfo, rep) => {
|
|
83
|
-
const vMode = mode === "visual-line" ? "line" : "char";
|
|
85
|
+
const vMode = state.mode === "visual-line" ? "line" : "char";
|
|
84
86
|
const [start, end] = getVisualSelection(
|
|
85
87
|
vMode,
|
|
86
|
-
visualAnchor,
|
|
87
|
-
visualCursor,
|
|
88
|
+
state.visualAnchor,
|
|
89
|
+
state.visualCursor,
|
|
88
90
|
rep,
|
|
89
91
|
);
|
|
90
|
-
|
|
92
|
+
if (vMode === "char") {
|
|
93
|
+
selectRange(editorInfo, start, [end[0], end[1] + 1]);
|
|
94
|
+
} else {
|
|
95
|
+
selectRange(editorInfo, start, end);
|
|
96
|
+
}
|
|
91
97
|
};
|
|
92
98
|
|
|
93
99
|
const clearEmptyLineCursor = () => {
|
|
94
|
-
if (!editorDoc) return;
|
|
95
|
-
const old = editorDoc.querySelector(".vim-empty-line-cursor");
|
|
100
|
+
if (!state.editorDoc) return;
|
|
101
|
+
const old = state.editorDoc.querySelector(".vim-empty-line-cursor");
|
|
96
102
|
if (old) old.classList.remove("vim-empty-line-cursor");
|
|
97
103
|
};
|
|
98
104
|
|
|
99
105
|
const scrollLineIntoView = (line) => {
|
|
100
|
-
if (!editorDoc) return;
|
|
101
|
-
const lineDiv = editorDoc.body.querySelectorAll("div")[line];
|
|
106
|
+
if (!state.editorDoc) return;
|
|
107
|
+
const lineDiv = state.editorDoc.body.querySelectorAll("div")[line];
|
|
102
108
|
if (lineDiv) lineDiv.scrollIntoView({ block: "nearest" });
|
|
103
109
|
};
|
|
104
110
|
|
|
105
111
|
const moveBlockCursor = (editorInfo, line, char) => {
|
|
106
112
|
clearEmptyLineCursor();
|
|
107
|
-
const lineText = currentRep ? getLineText(currentRep, line) : "";
|
|
108
|
-
if (lineText.length === 0 && editorDoc) {
|
|
109
|
-
const lineDiv = editorDoc.body.querySelectorAll("div")[line];
|
|
113
|
+
const lineText = state.currentRep ? getLineText(state.currentRep, line) : "";
|
|
114
|
+
if (lineText.length === 0 && state.editorDoc) {
|
|
115
|
+
const lineDiv = state.editorDoc.body.querySelectorAll("div")[line];
|
|
110
116
|
if (lineDiv) lineDiv.classList.add("vim-empty-line-cursor");
|
|
111
117
|
selectRange(editorInfo, [line, 0], [line, 0]);
|
|
112
118
|
} else {
|
|
@@ -116,7 +122,7 @@ const moveBlockCursor = (editorInfo, line, char) => {
|
|
|
116
122
|
};
|
|
117
123
|
|
|
118
124
|
const moveVisualCursor = (editorInfo, rep, line, char) => {
|
|
119
|
-
visualCursor = [line, char];
|
|
125
|
+
state.visualCursor = [line, char];
|
|
120
126
|
updateVisualSelection(editorInfo, rep);
|
|
121
127
|
scrollLineIntoView(line);
|
|
122
128
|
};
|
|
@@ -158,12 +164,13 @@ const applyLineOperator = (op, topLine, bottomLine, ctx) => {
|
|
|
158
164
|
return;
|
|
159
165
|
}
|
|
160
166
|
if (op === "c") {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
replaceRange(editorInfo, [topLine, 0], [topLine, text.length], "");
|
|
167
|
+
if (bottomLine > topLine) {
|
|
168
|
+
deleteLines(editorInfo, rep, topLine + 1, bottomLine);
|
|
164
169
|
}
|
|
170
|
+
const text = getLineText(rep, topLine);
|
|
171
|
+
replaceRange(editorInfo, [topLine, 0], [topLine, text.length], "");
|
|
165
172
|
moveCursor(editorInfo, topLine, 0);
|
|
166
|
-
mode = "insert";
|
|
173
|
+
state.mode = "insert";
|
|
167
174
|
return;
|
|
168
175
|
}
|
|
169
176
|
const cursorLine = deleteLines(editorInfo, rep, topLine, bottomLine);
|
|
@@ -186,7 +193,7 @@ const applyOperator = (op, start, end, ctx) => {
|
|
|
186
193
|
replaceRange(editorInfo, s, e, "");
|
|
187
194
|
if (op === "c") {
|
|
188
195
|
moveCursor(editorInfo, s[0], s[1]);
|
|
189
|
-
mode = "insert";
|
|
196
|
+
state.mode = "insert";
|
|
190
197
|
} else {
|
|
191
198
|
moveBlockCursor(editorInfo, s[0], s[1]);
|
|
192
199
|
}
|
|
@@ -229,28 +236,36 @@ const resolveTextObject = (key, type, line, lineText, char, rep) => {
|
|
|
229
236
|
};
|
|
230
237
|
|
|
231
238
|
const recordCommand = (key, count, param = null) => {
|
|
232
|
-
lastCommand = { key, count, param };
|
|
239
|
+
state.lastCommand = { key, count, param };
|
|
233
240
|
};
|
|
234
241
|
|
|
235
|
-
const registerMotion = (
|
|
242
|
+
const registerMotion = (
|
|
243
|
+
key,
|
|
244
|
+
getEndPos,
|
|
245
|
+
inclusive = false,
|
|
246
|
+
keepDesiredColumn = false,
|
|
247
|
+
) => {
|
|
236
248
|
commands.normal[key] = (ctx) => {
|
|
237
|
-
desiredColumn = null;
|
|
249
|
+
if (!keepDesiredColumn) state.desiredColumn = null;
|
|
238
250
|
const pos = getEndPos(ctx);
|
|
239
|
-
if (pos)
|
|
251
|
+
if (pos) {
|
|
252
|
+
const lineText = getLineText(ctx.rep, pos.line);
|
|
253
|
+
moveBlockCursor(ctx.editorInfo, pos.line, clampChar(pos.char, lineText));
|
|
254
|
+
}
|
|
240
255
|
};
|
|
241
256
|
commands["visual-char"][key] = (ctx) => {
|
|
242
|
-
desiredColumn = null;
|
|
257
|
+
if (!keepDesiredColumn) state.desiredColumn = null;
|
|
243
258
|
const pos = getEndPos(ctx);
|
|
244
259
|
if (pos) moveVisualCursor(ctx.editorInfo, ctx.rep, pos.line, pos.char);
|
|
245
260
|
};
|
|
246
261
|
commands["visual-line"][key] = (ctx) => {
|
|
247
|
-
desiredColumn = null;
|
|
262
|
+
if (!keepDesiredColumn) state.desiredColumn = null;
|
|
248
263
|
const pos = getEndPos(ctx);
|
|
249
264
|
if (pos) moveVisualCursor(ctx.editorInfo, ctx.rep, pos.line, pos.char);
|
|
250
265
|
};
|
|
251
266
|
for (const op of OPERATORS) {
|
|
252
267
|
commands.normal[op + key] = (ctx) => {
|
|
253
|
-
desiredColumn = null;
|
|
268
|
+
state.desiredColumn = null;
|
|
254
269
|
const pos = getEndPos(ctx);
|
|
255
270
|
if (pos) {
|
|
256
271
|
const endChar = inclusive ? pos.char + 1 : pos.char;
|
|
@@ -265,19 +280,19 @@ const parameterized = {};
|
|
|
265
280
|
|
|
266
281
|
const registerParamMotion = (key, getEndChar) => {
|
|
267
282
|
commands.normal[key] = () => {
|
|
268
|
-
pendingKey = key;
|
|
283
|
+
state.pendingKey = key;
|
|
269
284
|
};
|
|
270
285
|
commands["visual-char"][key] = () => {
|
|
271
|
-
pendingKey = key;
|
|
286
|
+
state.pendingKey = key;
|
|
272
287
|
};
|
|
273
288
|
commands["visual-line"][key] = () => {
|
|
274
|
-
pendingKey = key;
|
|
289
|
+
state.pendingKey = key;
|
|
275
290
|
};
|
|
276
291
|
parameterized[key] = (argKey, ctx) => {
|
|
277
|
-
lastCharSearch = { direction: key, target: argKey };
|
|
292
|
+
state.lastCharSearch = { direction: key, target: argKey };
|
|
278
293
|
const pos = getEndChar(argKey, ctx);
|
|
279
294
|
if (pos !== null) {
|
|
280
|
-
if (mode.startsWith("visual")) {
|
|
295
|
+
if (state.mode.startsWith("visual")) {
|
|
281
296
|
moveVisualCursor(ctx.editorInfo, ctx.rep, ctx.line, pos);
|
|
282
297
|
} else {
|
|
283
298
|
moveBlockCursor(ctx.editorInfo, ctx.line, pos);
|
|
@@ -288,10 +303,10 @@ const registerParamMotion = (key, getEndChar) => {
|
|
|
288
303
|
for (const op of OPERATORS) {
|
|
289
304
|
const combo = op + key;
|
|
290
305
|
commands.normal[combo] = () => {
|
|
291
|
-
pendingKey = combo;
|
|
306
|
+
state.pendingKey = combo;
|
|
292
307
|
};
|
|
293
308
|
parameterized[combo] = (argKey, ctx) => {
|
|
294
|
-
lastCharSearch = { direction: key, target: argKey };
|
|
309
|
+
state.lastCharSearch = { direction: key, target: argKey };
|
|
295
310
|
const pos = getEndChar(argKey, ctx);
|
|
296
311
|
if (pos !== null) {
|
|
297
312
|
const range = charMotionRange(key, ctx.char, pos);
|
|
@@ -321,10 +336,13 @@ const registerTextObject = (obj, getRange) => {
|
|
|
321
336
|
|
|
322
337
|
const getVisibleLineRange = (rep) => {
|
|
323
338
|
const totalLines = rep.lines.length();
|
|
324
|
-
if (!editorDoc)
|
|
325
|
-
|
|
339
|
+
if (!state.editorDoc) {
|
|
340
|
+
const mid = Math.floor((totalLines - 1) / 2);
|
|
341
|
+
return { top: 0, mid, bottom: totalLines - 1 };
|
|
342
|
+
}
|
|
343
|
+
const lineDivs = state.editorDoc.body.querySelectorAll("div");
|
|
326
344
|
const lineCount = Math.min(lineDivs.length, totalLines);
|
|
327
|
-
const frameEl = editorDoc.defaultView.frameElement;
|
|
345
|
+
const frameEl = state.editorDoc.defaultView.frameElement;
|
|
328
346
|
const iframeTop = frameEl ? frameEl.getBoundingClientRect().top : 0;
|
|
329
347
|
const outerViewportHeight = window.parent ? window.parent.innerHeight : 600;
|
|
330
348
|
let top = 0;
|
|
@@ -369,24 +387,40 @@ registerMotion("l", (ctx) => ({
|
|
|
369
387
|
char: clampChar(ctx.char + ctx.count, ctx.lineText),
|
|
370
388
|
}));
|
|
371
389
|
|
|
372
|
-
registerMotion(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
390
|
+
registerMotion(
|
|
391
|
+
"j",
|
|
392
|
+
(ctx) => {
|
|
393
|
+
if (state.desiredColumn === null) state.desiredColumn = ctx.char;
|
|
394
|
+
const newLine = clampLine(ctx.line + ctx.count, ctx.rep);
|
|
395
|
+
const newLineText = getLineText(ctx.rep, newLine);
|
|
396
|
+
return {
|
|
397
|
+
line: newLine,
|
|
398
|
+
char: clampChar(state.desiredColumn, newLineText),
|
|
399
|
+
};
|
|
400
|
+
},
|
|
401
|
+
false,
|
|
402
|
+
true,
|
|
403
|
+
);
|
|
378
404
|
|
|
379
|
-
registerMotion(
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
405
|
+
registerMotion(
|
|
406
|
+
"k",
|
|
407
|
+
(ctx) => {
|
|
408
|
+
if (state.desiredColumn === null) state.desiredColumn = ctx.char;
|
|
409
|
+
const newLine = clampLine(ctx.line - ctx.count, ctx.rep);
|
|
410
|
+
const newLineText = getLineText(ctx.rep, newLine);
|
|
411
|
+
return {
|
|
412
|
+
line: newLine,
|
|
413
|
+
char: clampChar(state.desiredColumn, newLineText),
|
|
414
|
+
};
|
|
415
|
+
},
|
|
416
|
+
false,
|
|
417
|
+
true,
|
|
418
|
+
);
|
|
385
419
|
|
|
386
420
|
registerMotion("w", (ctx) => {
|
|
387
421
|
let pos = ctx.char;
|
|
388
422
|
for (let i = 0; i < ctx.count; i++) pos = wordForward(ctx.lineText, pos);
|
|
389
|
-
return { line: ctx.line, char:
|
|
423
|
+
return { line: ctx.line, char: pos };
|
|
390
424
|
});
|
|
391
425
|
|
|
392
426
|
registerMotion("b", (ctx) => {
|
|
@@ -498,108 +532,65 @@ registerParamMotion("T", (key, ctx) => {
|
|
|
498
532
|
return pos !== -1 ? pos : null;
|
|
499
533
|
});
|
|
500
534
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
lastCharSearch.direction
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
ctx.count,
|
|
523
|
-
);
|
|
524
|
-
if (pos !== -1) moveBlockCursor(ctx.editorInfo, ctx.line, pos);
|
|
535
|
+
const registerCharRepeat = (key, getDirection) => {
|
|
536
|
+
const handler = (ctx) => {
|
|
537
|
+
if (!state.lastCharSearch) return;
|
|
538
|
+
const dir = getDirection(state.lastCharSearch.direction);
|
|
539
|
+
const pos = charSearchPos(
|
|
540
|
+
dir,
|
|
541
|
+
ctx.lineText,
|
|
542
|
+
ctx.char,
|
|
543
|
+
state.lastCharSearch.target,
|
|
544
|
+
ctx.count,
|
|
545
|
+
);
|
|
546
|
+
if (pos === -1) return;
|
|
547
|
+
if (state.mode.startsWith("visual")) {
|
|
548
|
+
moveVisualCursor(ctx.editorInfo, ctx.rep, ctx.line, pos);
|
|
549
|
+
} else {
|
|
550
|
+
moveBlockCursor(ctx.editorInfo, ctx.line, pos);
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
commands.normal[key] = handler;
|
|
554
|
+
commands["visual-char"][key] = handler;
|
|
555
|
+
commands["visual-line"][key] = handler;
|
|
525
556
|
};
|
|
526
557
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
lastCharSearch.target,
|
|
534
|
-
ctx.count,
|
|
535
|
-
);
|
|
536
|
-
if (pos !== -1) moveVisualCursor(ctx.editorInfo, ctx.rep, ctx.line, pos);
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
commands["visual-char"][","] = (ctx) => {
|
|
540
|
-
if (!lastCharSearch) return;
|
|
541
|
-
const opposite = { f: "F", F: "f", t: "T", T: "t" };
|
|
542
|
-
const dir = opposite[lastCharSearch.direction];
|
|
543
|
-
const pos = charSearchPos(
|
|
544
|
-
dir,
|
|
545
|
-
ctx.lineText,
|
|
546
|
-
ctx.char,
|
|
547
|
-
lastCharSearch.target,
|
|
548
|
-
ctx.count,
|
|
549
|
-
);
|
|
550
|
-
if (pos !== -1) moveVisualCursor(ctx.editorInfo, ctx.rep, ctx.line, pos);
|
|
558
|
+
const sameDirection = (dir) => dir;
|
|
559
|
+
const oppositeDirection = {
|
|
560
|
+
f: "F",
|
|
561
|
+
F: "f",
|
|
562
|
+
t: "T",
|
|
563
|
+
T: "t",
|
|
551
564
|
};
|
|
565
|
+
const reverseDirection = (dir) => oppositeDirection[dir];
|
|
552
566
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
const pos = charSearchPos(
|
|
556
|
-
lastCharSearch.direction,
|
|
557
|
-
ctx.lineText,
|
|
558
|
-
ctx.char,
|
|
559
|
-
lastCharSearch.target,
|
|
560
|
-
ctx.count,
|
|
561
|
-
);
|
|
562
|
-
if (pos !== -1) moveVisualCursor(ctx.editorInfo, ctx.rep, ctx.line, pos);
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
commands["visual-line"][","] = (ctx) => {
|
|
566
|
-
if (!lastCharSearch) return;
|
|
567
|
-
const opposite = { f: "F", F: "f", t: "T", T: "t" };
|
|
568
|
-
const dir = opposite[lastCharSearch.direction];
|
|
569
|
-
const pos = charSearchPos(
|
|
570
|
-
dir,
|
|
571
|
-
ctx.lineText,
|
|
572
|
-
ctx.char,
|
|
573
|
-
lastCharSearch.target,
|
|
574
|
-
ctx.count,
|
|
575
|
-
);
|
|
576
|
-
if (pos !== -1) moveVisualCursor(ctx.editorInfo, ctx.rep, ctx.line, pos);
|
|
577
|
-
};
|
|
567
|
+
registerCharRepeat(";", sameDirection);
|
|
568
|
+
registerCharRepeat(",", reverseDirection);
|
|
578
569
|
|
|
579
570
|
// --- Marks ---
|
|
580
571
|
|
|
581
572
|
commands.normal["m"] = () => {
|
|
582
|
-
pendingKey = "m";
|
|
573
|
+
state.pendingKey = "m";
|
|
583
574
|
};
|
|
584
575
|
parameterized["m"] = (key, ctx) => {
|
|
585
|
-
if (key >= "a" && key <= "z") marks[key] = [ctx.line, ctx.char];
|
|
576
|
+
if (key >= "a" && key <= "z") state.marks[key] = [ctx.line, ctx.char];
|
|
586
577
|
};
|
|
587
578
|
|
|
588
579
|
commands.normal["'"] = () => {
|
|
589
|
-
pendingKey = "'";
|
|
580
|
+
state.pendingKey = "'";
|
|
590
581
|
};
|
|
591
582
|
commands["visual-char"]["'"] = () => {
|
|
592
|
-
pendingKey = "'";
|
|
583
|
+
state.pendingKey = "'";
|
|
593
584
|
};
|
|
594
585
|
commands["visual-line"]["'"] = () => {
|
|
595
|
-
pendingKey = "'";
|
|
586
|
+
state.pendingKey = "'";
|
|
596
587
|
};
|
|
597
588
|
parameterized["'"] = (key, ctx) => {
|
|
598
|
-
if (!marks[key]) return;
|
|
599
|
-
const [markLine] = marks[key];
|
|
589
|
+
if (!state.marks[key]) return;
|
|
590
|
+
const [markLine] = state.marks[key];
|
|
600
591
|
const targetText = getLineText(ctx.rep, markLine);
|
|
601
592
|
const targetChar = firstNonBlank(targetText);
|
|
602
|
-
if (mode.startsWith("visual")) {
|
|
593
|
+
if (state.mode.startsWith("visual")) {
|
|
603
594
|
moveVisualCursor(ctx.editorInfo, ctx.rep, markLine, targetChar);
|
|
604
595
|
} else {
|
|
605
596
|
moveBlockCursor(ctx.editorInfo, markLine, targetChar);
|
|
@@ -607,18 +598,18 @@ parameterized["'"] = (key, ctx) => {
|
|
|
607
598
|
};
|
|
608
599
|
|
|
609
600
|
commands.normal["`"] = () => {
|
|
610
|
-
pendingKey = "`";
|
|
601
|
+
state.pendingKey = "`";
|
|
611
602
|
};
|
|
612
603
|
commands["visual-char"]["`"] = () => {
|
|
613
|
-
pendingKey = "`";
|
|
604
|
+
state.pendingKey = "`";
|
|
614
605
|
};
|
|
615
606
|
commands["visual-line"]["`"] = () => {
|
|
616
|
-
pendingKey = "`";
|
|
607
|
+
state.pendingKey = "`";
|
|
617
608
|
};
|
|
618
609
|
parameterized["`"] = (key, ctx) => {
|
|
619
|
-
if (!marks[key]) return;
|
|
620
|
-
const [markLine, markChar] = marks[key];
|
|
621
|
-
if (mode.startsWith("visual")) {
|
|
610
|
+
if (!state.marks[key]) return;
|
|
611
|
+
const [markLine, markChar] = state.marks[key];
|
|
612
|
+
if (state.mode.startsWith("visual")) {
|
|
622
613
|
moveVisualCursor(ctx.editorInfo, ctx.rep, markLine, markChar);
|
|
623
614
|
} else {
|
|
624
615
|
moveBlockCursor(ctx.editorInfo, markLine, markChar);
|
|
@@ -677,24 +668,24 @@ for (const op of OPERATORS) {
|
|
|
677
668
|
// --- Visual modes ---
|
|
678
669
|
|
|
679
670
|
commands.normal["v"] = ({ editorInfo, rep, line, char }) => {
|
|
680
|
-
visualAnchor = [line, char];
|
|
681
|
-
visualCursor = [line, char];
|
|
682
|
-
mode = "visual-char";
|
|
671
|
+
state.visualAnchor = [line, char];
|
|
672
|
+
state.visualCursor = [line, char];
|
|
673
|
+
state.mode = "visual-char";
|
|
683
674
|
updateVisualSelection(editorInfo, rep);
|
|
684
675
|
};
|
|
685
676
|
|
|
686
677
|
commands.normal["V"] = ({ editorInfo, rep, line }) => {
|
|
687
|
-
visualAnchor = [line, 0];
|
|
688
|
-
visualCursor = [line, 0];
|
|
689
|
-
mode = "visual-line";
|
|
678
|
+
state.visualAnchor = [line, 0];
|
|
679
|
+
state.visualCursor = [line, 0];
|
|
680
|
+
state.mode = "visual-line";
|
|
690
681
|
updateVisualSelection(editorInfo, rep);
|
|
691
682
|
};
|
|
692
683
|
|
|
693
684
|
for (const op of OPERATORS) {
|
|
694
685
|
commands["visual-line"][op] = (ctx) => {
|
|
695
|
-
const topLine = Math.min(visualAnchor[0], visualCursor[0]);
|
|
696
|
-
const bottomLine = Math.max(visualAnchor[0], visualCursor[0]);
|
|
697
|
-
mode = "normal";
|
|
686
|
+
const topLine = Math.min(state.visualAnchor[0], state.visualCursor[0]);
|
|
687
|
+
const bottomLine = Math.max(state.visualAnchor[0], state.visualCursor[0]);
|
|
688
|
+
state.mode = "normal";
|
|
698
689
|
applyLineOperator(op, topLine, bottomLine, ctx);
|
|
699
690
|
};
|
|
700
691
|
}
|
|
@@ -703,38 +694,39 @@ for (const op of OPERATORS) {
|
|
|
703
694
|
commands["visual-char"][op] = (ctx) => {
|
|
704
695
|
const [start, end] = getVisualSelection(
|
|
705
696
|
"char",
|
|
706
|
-
visualAnchor,
|
|
707
|
-
visualCursor,
|
|
697
|
+
state.visualAnchor,
|
|
698
|
+
state.visualCursor,
|
|
708
699
|
ctx.rep,
|
|
709
700
|
);
|
|
710
|
-
mode = "normal";
|
|
711
|
-
applyOperator(op, start, end, ctx);
|
|
701
|
+
state.mode = "normal";
|
|
702
|
+
applyOperator(op, start, [end[0], end[1] + 1], ctx);
|
|
712
703
|
};
|
|
713
704
|
}
|
|
714
705
|
|
|
715
706
|
commands["visual-char"]["~"] = (ctx) => {
|
|
716
707
|
const [start, end] = getVisualSelection(
|
|
717
708
|
"char",
|
|
718
|
-
visualAnchor,
|
|
719
|
-
visualCursor,
|
|
709
|
+
state.visualAnchor,
|
|
710
|
+
state.visualCursor,
|
|
720
711
|
ctx.rep,
|
|
721
712
|
);
|
|
722
|
-
const
|
|
713
|
+
const adjustedEnd = [end[0], end[1] + 1];
|
|
714
|
+
const text = getTextInRange(ctx.rep, start, adjustedEnd);
|
|
723
715
|
let toggled = "";
|
|
724
716
|
for (let i = 0; i < text.length; i++) {
|
|
725
717
|
const ch = text[i];
|
|
726
718
|
toggled += ch === ch.toLowerCase() ? ch.toUpperCase() : ch.toLowerCase();
|
|
727
719
|
}
|
|
728
|
-
replaceRange(ctx.editorInfo, start,
|
|
729
|
-
mode = "normal";
|
|
720
|
+
replaceRange(ctx.editorInfo, start, adjustedEnd, toggled);
|
|
721
|
+
state.mode = "normal";
|
|
730
722
|
moveBlockCursor(ctx.editorInfo, start[0], start[1]);
|
|
731
723
|
};
|
|
732
724
|
|
|
733
725
|
commands["visual-line"]["~"] = (ctx) => {
|
|
734
726
|
const [start, end] = getVisualSelection(
|
|
735
727
|
"line",
|
|
736
|
-
visualAnchor,
|
|
737
|
-
visualCursor,
|
|
728
|
+
state.visualAnchor,
|
|
729
|
+
state.visualCursor,
|
|
738
730
|
ctx.rep,
|
|
739
731
|
);
|
|
740
732
|
const text = getTextInRange(ctx.rep, start, end);
|
|
@@ -744,7 +736,7 @@ commands["visual-line"]["~"] = (ctx) => {
|
|
|
744
736
|
toggled += ch === ch.toLowerCase() ? ch.toUpperCase() : ch.toLowerCase();
|
|
745
737
|
}
|
|
746
738
|
replaceRange(ctx.editorInfo, start, end, toggled);
|
|
747
|
-
mode = "normal";
|
|
739
|
+
state.mode = "normal";
|
|
748
740
|
moveBlockCursor(ctx.editorInfo, start[0], start[1]);
|
|
749
741
|
};
|
|
750
742
|
|
|
@@ -755,13 +747,13 @@ commands.normal["u"] = ({ editorInfo }) => {
|
|
|
755
747
|
};
|
|
756
748
|
|
|
757
749
|
commands.normal["."] = (ctx) => {
|
|
758
|
-
if (!lastCommand) return;
|
|
759
|
-
const { key, count, param } = lastCommand;
|
|
750
|
+
if (!state.lastCommand) return;
|
|
751
|
+
const { key, count, param } = state.lastCommand;
|
|
760
752
|
if (param !== null && parameterized[key]) {
|
|
761
753
|
parameterized[key](param, ctx);
|
|
762
|
-
} else if (commands[mode] && commands[mode][key]) {
|
|
754
|
+
} else if (commands[state.mode] && commands[state.mode][key]) {
|
|
763
755
|
const newCtx = { ...ctx, count };
|
|
764
|
-
commands[mode][key](newCtx);
|
|
756
|
+
commands[state.mode][key](newCtx);
|
|
765
757
|
}
|
|
766
758
|
};
|
|
767
759
|
|
|
@@ -770,41 +762,41 @@ commands.normal["."] = (ctx) => {
|
|
|
770
762
|
commands.normal["i"] = ({ editorInfo, line, char }) => {
|
|
771
763
|
clearEmptyLineCursor();
|
|
772
764
|
moveCursor(editorInfo, line, char);
|
|
773
|
-
mode = "insert";
|
|
765
|
+
state.mode = "insert";
|
|
774
766
|
};
|
|
775
767
|
|
|
776
768
|
commands.normal["a"] = ({ editorInfo, line, char, lineText }) => {
|
|
777
769
|
clearEmptyLineCursor();
|
|
778
770
|
moveCursor(editorInfo, line, Math.min(char + 1, lineText.length));
|
|
779
|
-
mode = "insert";
|
|
771
|
+
state.mode = "insert";
|
|
780
772
|
};
|
|
781
773
|
|
|
782
774
|
commands.normal["A"] = ({ editorInfo, line, lineText }) => {
|
|
783
775
|
clearEmptyLineCursor();
|
|
784
776
|
moveCursor(editorInfo, line, lineText.length);
|
|
785
|
-
mode = "insert";
|
|
777
|
+
state.mode = "insert";
|
|
786
778
|
};
|
|
787
779
|
|
|
788
780
|
commands.normal["I"] = ({ editorInfo, line, lineText }) => {
|
|
789
781
|
clearEmptyLineCursor();
|
|
790
782
|
moveCursor(editorInfo, line, firstNonBlank(lineText));
|
|
791
|
-
mode = "insert";
|
|
783
|
+
state.mode = "insert";
|
|
792
784
|
};
|
|
793
785
|
|
|
794
786
|
commands["visual-char"]["i"] = () => {
|
|
795
|
-
pendingKey = "i";
|
|
787
|
+
state.pendingKey = "i";
|
|
796
788
|
};
|
|
797
789
|
|
|
798
790
|
commands["visual-char"]["a"] = () => {
|
|
799
|
-
pendingKey = "a";
|
|
791
|
+
state.pendingKey = "a";
|
|
800
792
|
};
|
|
801
793
|
|
|
802
794
|
commands["visual-line"]["i"] = () => {
|
|
803
|
-
pendingKey = "i";
|
|
795
|
+
state.pendingKey = "i";
|
|
804
796
|
};
|
|
805
797
|
|
|
806
798
|
commands["visual-line"]["a"] = () => {
|
|
807
|
-
pendingKey = "a";
|
|
799
|
+
state.pendingKey = "a";
|
|
808
800
|
};
|
|
809
801
|
|
|
810
802
|
commands.normal["o"] = ({ editorInfo, line, lineText }) => {
|
|
@@ -816,20 +808,20 @@ commands.normal["o"] = ({ editorInfo, line, lineText }) => {
|
|
|
816
808
|
"\n",
|
|
817
809
|
);
|
|
818
810
|
moveCursor(editorInfo, line + 1, 0);
|
|
819
|
-
mode = "insert";
|
|
811
|
+
state.mode = "insert";
|
|
820
812
|
};
|
|
821
813
|
|
|
822
814
|
commands.normal["O"] = ({ editorInfo, line }) => {
|
|
823
815
|
clearEmptyLineCursor();
|
|
824
816
|
replaceRange(editorInfo, [line, 0], [line, 0], "\n");
|
|
825
817
|
moveCursor(editorInfo, line, 0);
|
|
826
|
-
mode = "insert";
|
|
818
|
+
state.mode = "insert";
|
|
827
819
|
};
|
|
828
820
|
|
|
829
821
|
// --- More normal mode commands ---
|
|
830
822
|
|
|
831
823
|
commands.normal["r"] = () => {
|
|
832
|
-
pendingKey = "r";
|
|
824
|
+
state.pendingKey = "r";
|
|
833
825
|
};
|
|
834
826
|
parameterized["r"] = (key, { editorInfo, line, char, lineText, count }) => {
|
|
835
827
|
if (lineText.length > 0) {
|
|
@@ -855,14 +847,14 @@ commands.normal["x"] = ({ editorInfo, rep, line, char, lineText, count }) => {
|
|
|
855
847
|
};
|
|
856
848
|
|
|
857
849
|
commands.normal["p"] = ({ editorInfo, line, char, lineText, count }) => {
|
|
858
|
-
if (register !== null) {
|
|
859
|
-
if (typeof register === "string") {
|
|
850
|
+
if (state.register !== null) {
|
|
851
|
+
if (typeof state.register === "string") {
|
|
860
852
|
const insertPos = Math.min(char + 1, lineText.length);
|
|
861
|
-
const repeated = register.repeat(count);
|
|
853
|
+
const repeated = state.register.repeat(count);
|
|
862
854
|
replaceRange(editorInfo, [line, insertPos], [line, insertPos], repeated);
|
|
863
855
|
moveBlockCursor(editorInfo, line, insertPos);
|
|
864
856
|
} else {
|
|
865
|
-
const block = register.join("\n");
|
|
857
|
+
const block = state.register.join("\n");
|
|
866
858
|
const parts = [];
|
|
867
859
|
for (let i = 0; i < count; i++) parts.push(block);
|
|
868
860
|
const insertText = "\n" + parts.join("\n");
|
|
@@ -879,13 +871,13 @@ commands.normal["p"] = ({ editorInfo, line, char, lineText, count }) => {
|
|
|
879
871
|
};
|
|
880
872
|
|
|
881
873
|
commands.normal["P"] = ({ editorInfo, line, char, count }) => {
|
|
882
|
-
if (register !== null) {
|
|
883
|
-
if (typeof register === "string") {
|
|
884
|
-
const repeated = register.repeat(count);
|
|
874
|
+
if (state.register !== null) {
|
|
875
|
+
if (typeof state.register === "string") {
|
|
876
|
+
const repeated = state.register.repeat(count);
|
|
885
877
|
replaceRange(editorInfo, [line, char], [line, char], repeated);
|
|
886
878
|
moveBlockCursor(editorInfo, line, char);
|
|
887
879
|
} else {
|
|
888
|
-
const block = register.join("\n");
|
|
880
|
+
const block = state.register.join("\n");
|
|
889
881
|
const parts = [];
|
|
890
882
|
for (let i = 0; i < count; i++) parts.push(block);
|
|
891
883
|
const insertText = parts.join("\n") + "\n";
|
|
@@ -946,7 +938,7 @@ commands.normal["C"] = ({ editorInfo, line, char, lineText }) => {
|
|
|
946
938
|
setRegister(lineText.slice(char));
|
|
947
939
|
replaceRange(editorInfo, [line, char], [line, lineText.length], "");
|
|
948
940
|
moveCursor(editorInfo, line, char);
|
|
949
|
-
mode = "insert";
|
|
941
|
+
state.mode = "insert";
|
|
950
942
|
recordCommand("C", 1);
|
|
951
943
|
};
|
|
952
944
|
|
|
@@ -960,7 +952,7 @@ commands.normal["s"] = ({ editorInfo, rep, line, char, lineText, count }) => {
|
|
|
960
952
|
"",
|
|
961
953
|
);
|
|
962
954
|
moveCursor(editorInfo, line, char);
|
|
963
|
-
mode = "insert";
|
|
955
|
+
state.mode = "insert";
|
|
964
956
|
recordCommand("s", count);
|
|
965
957
|
};
|
|
966
958
|
|
|
@@ -969,35 +961,35 @@ commands.normal["S"] = ({ editorInfo, line, lineText }) => {
|
|
|
969
961
|
setRegister(lineText);
|
|
970
962
|
replaceRange(editorInfo, [line, 0], [line, lineText.length], "");
|
|
971
963
|
moveCursor(editorInfo, line, 0);
|
|
972
|
-
mode = "insert";
|
|
964
|
+
state.mode = "insert";
|
|
973
965
|
recordCommand("S", 1);
|
|
974
966
|
};
|
|
975
967
|
|
|
976
968
|
// --- Search ---
|
|
977
969
|
|
|
978
970
|
commands.normal["/"] = () => {
|
|
979
|
-
searchMode = true;
|
|
980
|
-
searchBuffer = "";
|
|
981
|
-
searchDirection = "/";
|
|
971
|
+
state.searchMode = true;
|
|
972
|
+
state.searchBuffer = "";
|
|
973
|
+
state.searchDirection = "/";
|
|
982
974
|
};
|
|
983
975
|
|
|
984
976
|
commands.normal["?"] = () => {
|
|
985
|
-
searchMode = true;
|
|
986
|
-
searchBuffer = "";
|
|
987
|
-
searchDirection = "?";
|
|
977
|
+
state.searchMode = true;
|
|
978
|
+
state.searchBuffer = "";
|
|
979
|
+
state.searchDirection = "?";
|
|
988
980
|
};
|
|
989
981
|
|
|
990
982
|
commands.normal["n"] = (ctx) => {
|
|
991
|
-
if (!lastSearch) return;
|
|
992
|
-
const { pattern, direction } = lastSearch;
|
|
983
|
+
if (!state.lastSearch) return;
|
|
984
|
+
const { pattern, direction } = state.lastSearch;
|
|
993
985
|
const searchFunc = direction === "/" ? searchForward : searchBackward;
|
|
994
986
|
const pos = searchFunc(ctx.rep, ctx.line, ctx.char + 1, pattern, ctx.count);
|
|
995
987
|
if (pos) moveBlockCursor(ctx.editorInfo, pos[0], pos[1]);
|
|
996
988
|
};
|
|
997
989
|
|
|
998
990
|
commands.normal["N"] = (ctx) => {
|
|
999
|
-
if (!lastSearch) return;
|
|
1000
|
-
const { pattern, direction } = lastSearch;
|
|
991
|
+
if (!state.lastSearch) return;
|
|
992
|
+
const { pattern, direction } = state.lastSearch;
|
|
1001
993
|
const searchFunc = direction === "/" ? searchBackward : searchForward;
|
|
1002
994
|
const pos = searchFunc(ctx.rep, ctx.line, ctx.char, pattern, ctx.count);
|
|
1003
995
|
if (pos) moveBlockCursor(ctx.editorInfo, pos[0], pos[1]);
|
|
@@ -1007,36 +999,36 @@ commands.normal["N"] = (ctx) => {
|
|
|
1007
999
|
|
|
1008
1000
|
const handleKey = (key, ctx) => {
|
|
1009
1001
|
if (key >= "1" && key <= "9") {
|
|
1010
|
-
countBuffer += key;
|
|
1002
|
+
state.countBuffer += key;
|
|
1011
1003
|
return true;
|
|
1012
1004
|
}
|
|
1013
|
-
if (key === "0" && countBuffer !== "") {
|
|
1014
|
-
countBuffer += key;
|
|
1005
|
+
if (key === "0" && state.countBuffer !== "") {
|
|
1006
|
+
state.countBuffer += key;
|
|
1015
1007
|
return true;
|
|
1016
1008
|
}
|
|
1017
1009
|
|
|
1018
|
-
if (countBuffer !== "") {
|
|
1019
|
-
pendingCount = parseInt(countBuffer, 10);
|
|
1020
|
-
countBuffer = "";
|
|
1010
|
+
if (state.countBuffer !== "") {
|
|
1011
|
+
state.pendingCount = parseInt(state.countBuffer, 10);
|
|
1012
|
+
state.countBuffer = "";
|
|
1021
1013
|
}
|
|
1022
|
-
ctx.count = pendingCount !== null ? pendingCount : 1;
|
|
1023
|
-
ctx.hasCount = pendingCount !== null;
|
|
1014
|
+
ctx.count = state.pendingCount !== null ? state.pendingCount : 1;
|
|
1015
|
+
ctx.hasCount = state.pendingCount !== null;
|
|
1024
1016
|
|
|
1025
|
-
if (pendingKey !== null && parameterized[pendingKey]) {
|
|
1026
|
-
const handler = parameterized[pendingKey];
|
|
1027
|
-
pendingKey = null;
|
|
1017
|
+
if (state.pendingKey !== null && parameterized[state.pendingKey]) {
|
|
1018
|
+
const handler = parameterized[state.pendingKey];
|
|
1019
|
+
state.pendingKey = null;
|
|
1028
1020
|
handler(key, ctx);
|
|
1029
|
-
pendingCount = null;
|
|
1021
|
+
state.pendingCount = null;
|
|
1030
1022
|
return true;
|
|
1031
1023
|
}
|
|
1032
1024
|
|
|
1033
|
-
const map = commands[mode];
|
|
1034
|
-
const seq = pendingKey !== null ? pendingKey + key : key;
|
|
1025
|
+
const map = commands[state.mode];
|
|
1026
|
+
const seq = state.pendingKey !== null ? state.pendingKey + key : key;
|
|
1035
1027
|
|
|
1036
1028
|
if (map[seq]) {
|
|
1037
|
-
pendingKey = null;
|
|
1029
|
+
state.pendingKey = null;
|
|
1038
1030
|
map[seq](ctx);
|
|
1039
|
-
if (pendingKey === null) pendingCount = null;
|
|
1031
|
+
if (state.pendingKey === null) state.pendingCount = null;
|
|
1040
1032
|
return true;
|
|
1041
1033
|
}
|
|
1042
1034
|
|
|
@@ -1044,25 +1036,25 @@ const handleKey = (key, ctx) => {
|
|
|
1044
1036
|
(k) => k.startsWith(seq) && k.length > seq.length,
|
|
1045
1037
|
);
|
|
1046
1038
|
if (isPrefix) {
|
|
1047
|
-
pendingKey = seq;
|
|
1039
|
+
state.pendingKey = seq;
|
|
1048
1040
|
return true;
|
|
1049
1041
|
}
|
|
1050
1042
|
|
|
1051
1043
|
if (
|
|
1052
|
-
pendingKey &&
|
|
1044
|
+
state.pendingKey &&
|
|
1053
1045
|
(key === "i" || key === "a") &&
|
|
1054
|
-
Object.keys(map).some((k) => k.startsWith(pendingKey + key))
|
|
1046
|
+
Object.keys(map).some((k) => k.startsWith(state.pendingKey + key))
|
|
1055
1047
|
) {
|
|
1056
|
-
pendingKey = pendingKey + key;
|
|
1048
|
+
state.pendingKey = state.pendingKey + key;
|
|
1057
1049
|
return true;
|
|
1058
1050
|
}
|
|
1059
1051
|
|
|
1060
1052
|
if (
|
|
1061
|
-
(mode === "visual-char" || mode === "visual-line") &&
|
|
1062
|
-
(pendingKey === "i" || pendingKey === "a")
|
|
1053
|
+
(state.mode === "visual-char" || state.mode === "visual-line") &&
|
|
1054
|
+
(state.pendingKey === "i" || state.pendingKey === "a")
|
|
1063
1055
|
) {
|
|
1064
|
-
const type = pendingKey;
|
|
1065
|
-
pendingKey = null;
|
|
1056
|
+
const type = state.pendingKey;
|
|
1057
|
+
state.pendingKey = null;
|
|
1066
1058
|
const range = resolveTextObject(
|
|
1067
1059
|
key,
|
|
1068
1060
|
type,
|
|
@@ -1072,15 +1064,15 @@ const handleKey = (key, ctx) => {
|
|
|
1072
1064
|
ctx.rep,
|
|
1073
1065
|
);
|
|
1074
1066
|
if (range) {
|
|
1075
|
-
visualAnchor = [range.startLine, range.startChar];
|
|
1076
|
-
visualCursor = [range.endLine, range.endChar];
|
|
1067
|
+
state.visualAnchor = [range.startLine, range.startChar];
|
|
1068
|
+
state.visualCursor = [range.endLine, range.endChar];
|
|
1077
1069
|
updateVisualSelection(ctx.editorInfo, ctx.rep);
|
|
1078
1070
|
}
|
|
1079
1071
|
return true;
|
|
1080
1072
|
}
|
|
1081
1073
|
|
|
1082
|
-
pendingKey = null;
|
|
1083
|
-
pendingCount = null;
|
|
1074
|
+
state.pendingKey = null;
|
|
1075
|
+
state.pendingCount = null;
|
|
1084
1076
|
return true;
|
|
1085
1077
|
};
|
|
1086
1078
|
|
|
@@ -1104,7 +1096,7 @@ exports.postAceInit = (_hookName, { ace }) => {
|
|
|
1104
1096
|
ace.callWithAce((aceTop) => {
|
|
1105
1097
|
const rep = aceTop.ace_getRep();
|
|
1106
1098
|
if (rep && rep.selStart) {
|
|
1107
|
-
currentRep = rep;
|
|
1099
|
+
state.currentRep = rep;
|
|
1108
1100
|
selectRange(aceTop, rep.selStart, [rep.selStart[0], rep.selStart[1] + 1]);
|
|
1109
1101
|
}
|
|
1110
1102
|
});
|
|
@@ -1119,57 +1111,57 @@ exports.aceKeyEvent = (_hookName, { evt, rep, editorInfo }) => {
|
|
|
1119
1111
|
(evt.key === "x" || evt.key === "c" || evt.key === "v" || evt.key === "r");
|
|
1120
1112
|
if (isBrowserShortcut) return false;
|
|
1121
1113
|
|
|
1122
|
-
currentRep = rep;
|
|
1123
|
-
if (!editorDoc) editorDoc = evt.target.ownerDocument;
|
|
1114
|
+
state.currentRep = rep;
|
|
1115
|
+
if (!state.editorDoc) state.editorDoc = evt.target.ownerDocument;
|
|
1124
1116
|
|
|
1125
1117
|
if (evt.key === "Escape") {
|
|
1126
|
-
desiredColumn = null;
|
|
1127
|
-
if (mode === "visual-line") {
|
|
1128
|
-
const line = Math.min(visualAnchor[0], visualCursor[0]);
|
|
1129
|
-
mode = "normal";
|
|
1118
|
+
state.desiredColumn = null;
|
|
1119
|
+
if (state.mode === "visual-line") {
|
|
1120
|
+
const line = Math.min(state.visualAnchor[0], state.visualCursor[0]);
|
|
1121
|
+
state.mode = "normal";
|
|
1130
1122
|
moveBlockCursor(editorInfo, line, 0);
|
|
1131
|
-
} else if (mode === "visual-char") {
|
|
1132
|
-
const [vLine, vChar] = visualCursor;
|
|
1133
|
-
mode = "normal";
|
|
1123
|
+
} else if (state.mode === "visual-char") {
|
|
1124
|
+
const [vLine, vChar] = state.visualCursor;
|
|
1125
|
+
state.mode = "normal";
|
|
1134
1126
|
moveBlockCursor(editorInfo, vLine, vChar);
|
|
1135
|
-
} else if (mode === "insert") {
|
|
1136
|
-
mode = "normal";
|
|
1127
|
+
} else if (state.mode === "insert") {
|
|
1128
|
+
state.mode = "normal";
|
|
1137
1129
|
const [curLine, curChar] = rep.selStart;
|
|
1138
1130
|
moveBlockCursor(editorInfo, curLine, Math.max(0, curChar - 1));
|
|
1139
1131
|
} else {
|
|
1140
|
-
mode = "normal";
|
|
1132
|
+
state.mode = "normal";
|
|
1141
1133
|
const [curLine, curChar] = rep.selStart;
|
|
1142
1134
|
moveBlockCursor(editorInfo, curLine, curChar);
|
|
1143
1135
|
}
|
|
1144
|
-
pendingKey = null;
|
|
1145
|
-
pendingCount = null;
|
|
1146
|
-
countBuffer = "";
|
|
1136
|
+
state.pendingKey = null;
|
|
1137
|
+
state.pendingCount = null;
|
|
1138
|
+
state.countBuffer = "";
|
|
1147
1139
|
evt.preventDefault();
|
|
1148
1140
|
return true;
|
|
1149
1141
|
}
|
|
1150
1142
|
|
|
1151
|
-
if (mode === "insert") return false;
|
|
1143
|
+
if (state.mode === "insert") return false;
|
|
1152
1144
|
|
|
1153
|
-
if (searchMode) {
|
|
1145
|
+
if (state.searchMode) {
|
|
1154
1146
|
if (evt.key === "Enter") {
|
|
1155
|
-
searchMode = false;
|
|
1156
|
-
const pattern = searchBuffer;
|
|
1157
|
-
lastSearch = { pattern, direction: searchDirection };
|
|
1147
|
+
state.searchMode = false;
|
|
1148
|
+
const pattern = state.searchBuffer;
|
|
1149
|
+
state.lastSearch = { pattern, direction: state.searchDirection };
|
|
1158
1150
|
const [curLine, curChar] = rep.selStart;
|
|
1159
1151
|
const searchFunc =
|
|
1160
|
-
searchDirection === "/" ? searchForward : searchBackward;
|
|
1152
|
+
state.searchDirection === "/" ? searchForward : searchBackward;
|
|
1161
1153
|
const pos = searchFunc(rep, curLine, curChar + 1, pattern);
|
|
1162
1154
|
if (pos) moveBlockCursor(editorInfo, pos[0], pos[1]);
|
|
1163
|
-
searchBuffer = "";
|
|
1155
|
+
state.searchBuffer = "";
|
|
1164
1156
|
evt.preventDefault();
|
|
1165
1157
|
return true;
|
|
1166
1158
|
} else if (evt.key === "Escape") {
|
|
1167
|
-
searchMode = false;
|
|
1168
|
-
searchBuffer = "";
|
|
1159
|
+
state.searchMode = false;
|
|
1160
|
+
state.searchBuffer = "";
|
|
1169
1161
|
evt.preventDefault();
|
|
1170
1162
|
return true;
|
|
1171
1163
|
} else if (evt.key.length === 1 && !evt.ctrlKey && !evt.metaKey) {
|
|
1172
|
-
searchBuffer += evt.key;
|
|
1164
|
+
state.searchBuffer += evt.key;
|
|
1173
1165
|
evt.preventDefault();
|
|
1174
1166
|
return true;
|
|
1175
1167
|
}
|
|
@@ -1177,8 +1169,8 @@ exports.aceKeyEvent = (_hookName, { evt, rep, editorInfo }) => {
|
|
|
1177
1169
|
}
|
|
1178
1170
|
|
|
1179
1171
|
const [line, char] =
|
|
1180
|
-
mode === "visual-line" || mode === "visual-char"
|
|
1181
|
-
? visualCursor
|
|
1172
|
+
state.mode === "visual-line" || state.mode === "visual-char"
|
|
1173
|
+
? state.visualCursor
|
|
1182
1174
|
: rep.selStart;
|
|
1183
1175
|
const lineText = rep.lines.atIndex(line).text;
|
|
1184
1176
|
const ctx = { rep, editorInfo, line, char, lineText };
|
|
@@ -1186,3 +1178,9 @@ exports.aceKeyEvent = (_hookName, { evt, rep, editorInfo }) => {
|
|
|
1186
1178
|
if (handled) evt.preventDefault();
|
|
1187
1179
|
return handled;
|
|
1188
1180
|
};
|
|
1181
|
+
|
|
1182
|
+
// Exports for testing
|
|
1183
|
+
exports._state = state;
|
|
1184
|
+
exports._handleKey = handleKey;
|
|
1185
|
+
exports._commands = commands;
|
|
1186
|
+
exports._parameterized = parameterized;
|