ep_vim 0.7.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/README.md +10 -3
- package/package.json +6 -6
- package/static/js/index.js +959 -825
- package/static/js/index.test.js +3081 -0
- package/static/js/vim-core.js +79 -0
- package/static/js/vim-core.test.js +118 -0
package/static/js/vim-core.js
CHANGED
|
@@ -13,6 +13,7 @@ const getLineText = (rep, line) => rep.lines.atIndex(line).text;
|
|
|
13
13
|
|
|
14
14
|
const firstNonBlank = (lineText) => {
|
|
15
15
|
let i = 0;
|
|
16
|
+
if (!lineText) return 0;
|
|
16
17
|
while (i < lineText.length && isWhitespace(lineText[i])) i++;
|
|
17
18
|
return i;
|
|
18
19
|
};
|
|
@@ -301,6 +302,79 @@ const textBracketRange = (lineText, char, bracket, type) => {
|
|
|
301
302
|
return null;
|
|
302
303
|
};
|
|
303
304
|
|
|
305
|
+
const getFullText = (rep) => {
|
|
306
|
+
const lines = [];
|
|
307
|
+
for (let i = 0; i < rep.lines.length(); i++) {
|
|
308
|
+
lines.push(getLineText(rep, i));
|
|
309
|
+
}
|
|
310
|
+
return lines.join("\n");
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const posToAbsolute = (rep, line, char) => {
|
|
314
|
+
let pos = 0;
|
|
315
|
+
for (let i = 0; i < line; i++) {
|
|
316
|
+
pos += getLineText(rep, i).length + 1;
|
|
317
|
+
}
|
|
318
|
+
return pos + char;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const absoluteToPos = (rep, absPos) => {
|
|
322
|
+
let pos = 0;
|
|
323
|
+
const totalLines = rep.lines.length();
|
|
324
|
+
for (let i = 0; i < totalLines; i++) {
|
|
325
|
+
const lineLen = getLineText(rep, i).length;
|
|
326
|
+
if (pos + lineLen + 1 > absPos) {
|
|
327
|
+
return [i, absPos - pos];
|
|
328
|
+
}
|
|
329
|
+
pos += lineLen + 1;
|
|
330
|
+
}
|
|
331
|
+
return [totalLines - 1, getLineText(rep, totalLines - 1).length];
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
const searchForward = (rep, fromLine, fromChar, pattern, count = 1) => {
|
|
335
|
+
if (!pattern || pattern.length === 0) return null;
|
|
336
|
+
const text = getFullText(rep);
|
|
337
|
+
let currentPos = posToAbsolute(rep, fromLine, fromChar);
|
|
338
|
+
let matchPos = -1;
|
|
339
|
+
for (let i = 0; i < count; i++) {
|
|
340
|
+
matchPos = text.indexOf(pattern, currentPos);
|
|
341
|
+
if (matchPos !== -1) {
|
|
342
|
+
currentPos = matchPos + pattern.length;
|
|
343
|
+
} else {
|
|
344
|
+
matchPos = text.indexOf(pattern);
|
|
345
|
+
if (matchPos !== -1) {
|
|
346
|
+
currentPos = matchPos + pattern.length;
|
|
347
|
+
} else {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return absoluteToPos(rep, matchPos);
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const searchBackward = (rep, fromLine, fromChar, pattern, count = 1) => {
|
|
356
|
+
if (!pattern || pattern.length === 0) return null;
|
|
357
|
+
const text = getFullText(rep);
|
|
358
|
+
let currentPos = posToAbsolute(rep, fromLine, fromChar);
|
|
359
|
+
let matchPos = -1;
|
|
360
|
+
for (let i = 0; i < count; i++) {
|
|
361
|
+
if (currentPos > 0) {
|
|
362
|
+
matchPos = text.lastIndexOf(pattern, currentPos - 1);
|
|
363
|
+
if (matchPos !== -1) {
|
|
364
|
+
currentPos = matchPos;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
matchPos = text.lastIndexOf(pattern);
|
|
369
|
+
if (matchPos !== -1) {
|
|
370
|
+
currentPos = matchPos;
|
|
371
|
+
} else {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return absoluteToPos(rep, matchPos);
|
|
376
|
+
};
|
|
377
|
+
|
|
304
378
|
const offsetToPos = (rep, offset) => {
|
|
305
379
|
const totalLines = rep.lines.length();
|
|
306
380
|
for (let i = 0; i < totalLines; i++) {
|
|
@@ -465,4 +539,9 @@ module.exports = {
|
|
|
465
539
|
matchingBracketPos,
|
|
466
540
|
paragraphTextRange,
|
|
467
541
|
sentenceTextRange,
|
|
542
|
+
getFullText,
|
|
543
|
+
posToAbsolute,
|
|
544
|
+
absoluteToPos,
|
|
545
|
+
searchForward,
|
|
546
|
+
searchBackward,
|
|
468
547
|
};
|
|
@@ -24,6 +24,11 @@ const {
|
|
|
24
24
|
paragraphForward,
|
|
25
25
|
paragraphBackward,
|
|
26
26
|
getTextInRange,
|
|
27
|
+
getFullText,
|
|
28
|
+
posToAbsolute,
|
|
29
|
+
absoluteToPos,
|
|
30
|
+
searchForward,
|
|
31
|
+
searchBackward,
|
|
27
32
|
} = require("./vim-core");
|
|
28
33
|
|
|
29
34
|
const makeRep = (lines) => ({
|
|
@@ -674,3 +679,116 @@ describe("paragraphBackward", () => {
|
|
|
674
679
|
assert.equal(paragraphBackward(rep, 5, 1), 2);
|
|
675
680
|
});
|
|
676
681
|
});
|
|
682
|
+
|
|
683
|
+
describe("getFullText", () => {
|
|
684
|
+
it("joins lines with newlines", () => {
|
|
685
|
+
const rep = makeRep(["hello", "world", "test"]);
|
|
686
|
+
assert.equal(getFullText(rep), "hello\nworld\ntest");
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it("handles single line", () => {
|
|
690
|
+
const rep = makeRep(["hello"]);
|
|
691
|
+
assert.equal(getFullText(rep), "hello");
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
it("handles empty lines", () => {
|
|
695
|
+
const rep = makeRep(["hello", "", "world"]);
|
|
696
|
+
assert.equal(getFullText(rep), "hello\n\nworld");
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
describe("posToAbsolute and absoluteToPos", () => {
|
|
701
|
+
const rep = makeRep(["hello", "world", "test"]);
|
|
702
|
+
|
|
703
|
+
it("converts position to absolute and back", () => {
|
|
704
|
+
assert.deepEqual(absoluteToPos(rep, posToAbsolute(rep, 0, 0)), [0, 0]);
|
|
705
|
+
assert.deepEqual(absoluteToPos(rep, posToAbsolute(rep, 0, 3)), [0, 3]);
|
|
706
|
+
assert.deepEqual(absoluteToPos(rep, posToAbsolute(rep, 1, 0)), [1, 0]);
|
|
707
|
+
assert.deepEqual(absoluteToPos(rep, posToAbsolute(rep, 1, 2)), [1, 2]);
|
|
708
|
+
assert.deepEqual(absoluteToPos(rep, posToAbsolute(rep, 2, 4)), [2, 4]);
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it("converts line start positions correctly", () => {
|
|
712
|
+
assert.equal(posToAbsolute(rep, 0, 0), 0);
|
|
713
|
+
assert.equal(posToAbsolute(rep, 1, 0), 6);
|
|
714
|
+
assert.equal(posToAbsolute(rep, 2, 0), 12);
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
describe("searchForward", () => {
|
|
719
|
+
const rep = makeRep(["hello world", "foo hello bar", "hello"]);
|
|
720
|
+
|
|
721
|
+
it("finds pattern forward from position", () => {
|
|
722
|
+
const pos = searchForward(rep, 0, 0, "hello");
|
|
723
|
+
assert.deepEqual(pos, [0, 0]);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
it("finds next occurrence after cursor", () => {
|
|
727
|
+
const pos = searchForward(rep, 0, 6, "hello");
|
|
728
|
+
assert.deepEqual(pos, [1, 4]);
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it("wraps to beginning when pattern not found after cursor", () => {
|
|
732
|
+
const pos = searchForward(rep, 2, 5, "hello");
|
|
733
|
+
assert.deepEqual(pos, [0, 0]);
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it("finds pattern across lines", () => {
|
|
737
|
+
const pos = searchForward(rep, 0, 0, "world");
|
|
738
|
+
assert.deepEqual(pos, [0, 6]);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
it("returns null for empty pattern", () => {
|
|
742
|
+
assert.equal(searchForward(rep, 0, 0, ""), null);
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
it("returns null when pattern not found", () => {
|
|
746
|
+
assert.equal(searchForward(rep, 0, 0, "xyz"), null);
|
|
747
|
+
});
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
describe("searchBackward", () => {
|
|
751
|
+
const rep = makeRep(["hello world", "foo hello bar", "hello"]);
|
|
752
|
+
|
|
753
|
+
it("finds pattern backward from position", () => {
|
|
754
|
+
const pos = searchBackward(rep, 2, 5, "hello");
|
|
755
|
+
assert.deepEqual(pos, [2, 0]);
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
it("finds previous occurrence before cursor", () => {
|
|
759
|
+
const pos = searchBackward(rep, 2, 0, "hello");
|
|
760
|
+
assert.deepEqual(pos, [1, 4]);
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
it("wraps to end when pattern not found before cursor", () => {
|
|
764
|
+
const pos = searchBackward(rep, 0, 0, "hello");
|
|
765
|
+
assert.deepEqual(pos, [2, 0]);
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
it("returns null for empty pattern", () => {
|
|
769
|
+
assert.equal(searchBackward(rep, 2, 5, ""), null);
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
it("returns null when pattern not found", () => {
|
|
773
|
+
assert.equal(searchBackward(rep, 2, 5, "xyz"), null);
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
it("searches multiple times with count", () => {
|
|
777
|
+
const pos = searchBackward(rep, 2, 5, "hello", 2);
|
|
778
|
+
assert.deepEqual(pos, [1, 4]);
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
describe("searchForward and searchBackward with count", () => {
|
|
783
|
+
const rep = makeRep(["aa", "aa", "aa", "aa"]);
|
|
784
|
+
|
|
785
|
+
it("searchForward finds nth occurrence", () => {
|
|
786
|
+
const pos = searchForward(rep, 0, 0, "aa", 3);
|
|
787
|
+
assert.deepEqual(pos, [2, 0]);
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
it("searchBackward finds nth occurrence backward", () => {
|
|
791
|
+
const pos = searchBackward(rep, 3, 2, "aa", 2);
|
|
792
|
+
assert.deepEqual(pos, [2, 0]);
|
|
793
|
+
});
|
|
794
|
+
});
|