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.
@@ -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
+ });