Skip to content

Commit 251d359

Browse files
authored
Merge pull request #51 from andyfeller/af/handle-empty-lines
fix: handle empty context lines in unified diffs
2 parents cd0627a + a3180a5 commit 251d359

3 files changed

Lines changed: 178 additions & 2 deletions

File tree

__tests__/parse.spec.js

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,4 +517,180 @@ index 123..456 789
517517
expect(file2.newMode).toBe("789");
518518
expect(file2.chunks[0].changes.length).toBe(3);
519519
});
520+
521+
describe("empty context lines (suppressBlankEmpty)", function () {
522+
it("should parse empty context line between del and add", function () {
523+
// Simulates diff.suppressBlankEmpty=true: blank context line has no leading space
524+
const diff = `\
525+
diff --git a/file b/file
526+
index 123..456 789
527+
--- a/file
528+
+++ b/file
529+
@@ -1,3 +1,3 @@
530+
-old line
531+
+new line
532+
533+
keep this\
534+
`;
535+
const files = parse(diff);
536+
expect(files.length).toBe(1);
537+
const chunk = files[0].chunks[0];
538+
expect(chunk.changes.length).toBe(4);
539+
expect(chunk.changes[0].type).toBe("del");
540+
expect(chunk.changes[1].type).toBe("add");
541+
expect(chunk.changes[2].type).toBe("normal");
542+
expect(chunk.changes[2].content).toBe("");
543+
expect(chunk.changes[2].ln1).toBe(2);
544+
expect(chunk.changes[2].ln2).toBe(2);
545+
expect(chunk.changes[3].type).toBe("normal");
546+
expect(chunk.changes[3].content).toBe(" keep this");
547+
});
548+
549+
it("should parse empty context line at start of hunk", function () {
550+
const diff = `\
551+
diff --git a/file b/file
552+
index 123..456 789
553+
--- a/file
554+
+++ b/file
555+
@@ -1,2 +1,2 @@
556+
557+
-old
558+
+new\
559+
`;
560+
const files = parse(diff);
561+
expect(files.length).toBe(1);
562+
const chunk = files[0].chunks[0];
563+
expect(chunk.changes.length).toBe(3);
564+
expect(chunk.changes[0].type).toBe("normal");
565+
expect(chunk.changes[0].content).toBe("");
566+
expect(chunk.changes[1].type).toBe("del");
567+
expect(chunk.changes[2].type).toBe("add");
568+
});
569+
570+
it("should parse empty context line at end of hunk", function () {
571+
const diff = `\
572+
diff --git a/file b/file
573+
index 123..456 789
574+
--- a/file
575+
+++ b/file
576+
@@ -1,2 +1,2 @@
577+
-old
578+
+new
579+
\
580+
`;
581+
const files = parse(diff);
582+
expect(files.length).toBe(1);
583+
const chunk = files[0].chunks[0];
584+
expect(chunk.changes.length).toBe(3);
585+
expect(chunk.changes[0].type).toBe("del");
586+
expect(chunk.changes[1].type).toBe("add");
587+
expect(chunk.changes[2].type).toBe("normal");
588+
expect(chunk.changes[2].content).toBe("");
589+
});
590+
591+
it("should parse multiple consecutive empty context lines", function () {
592+
const diff = `\
593+
diff --git a/file b/file
594+
index 123..456 789
595+
--- a/file
596+
+++ b/file
597+
@@ -1,4 +1,4 @@
598+
-old
599+
+new
600+
601+
602+
keep\
603+
`;
604+
const files = parse(diff);
605+
expect(files.length).toBe(1);
606+
const chunk = files[0].chunks[0];
607+
expect(chunk.changes.length).toBe(5);
608+
expect(chunk.changes[0].type).toBe("del");
609+
expect(chunk.changes[1].type).toBe("add");
610+
expect(chunk.changes[2].type).toBe("normal");
611+
expect(chunk.changes[2].content).toBe("");
612+
expect(chunk.changes[3].type).toBe("normal");
613+
expect(chunk.changes[3].content).toBe("");
614+
expect(chunk.changes[4].type).toBe("normal");
615+
});
616+
617+
it("should not lose second hunk when first has empty context line", function () {
618+
const diff = `\
619+
diff --git a/file b/file
620+
index 123..456 789
621+
--- a/file
622+
+++ b/file
623+
@@ -1,2 +1,2 @@
624+
-old1
625+
+new1
626+
627+
@@ -10,1 +10,1 @@
628+
-old2
629+
+new2\
630+
`;
631+
const files = parse(diff);
632+
expect(files.length).toBe(1);
633+
expect(files[0].chunks.length).toBe(2);
634+
const chunk0 = files[0].chunks[0];
635+
expect(chunk0.changes.length).toBe(3);
636+
expect(chunk0.changes[2].type).toBe("normal");
637+
expect(chunk0.changes[2].content).toBe("");
638+
const chunk1 = files[0].chunks[1];
639+
expect(chunk1.oldStart).toBe(10);
640+
expect(chunk1.changes.length).toBe(2);
641+
expect(chunk1.changes[0].type).toBe("del");
642+
expect(chunk1.changes[1].type).toBe("add");
643+
});
644+
645+
it("should not lose second file when first has empty context line", function () {
646+
const diff = `\
647+
diff --git a/file1 b/file1
648+
index 123..456 789
649+
--- a/file1
650+
+++ b/file1
651+
@@ -1,2 +1,2 @@
652+
-old
653+
+new
654+
655+
diff --git a/file2 b/file2
656+
index 123..456 789
657+
--- a/file2
658+
+++ b/file2
659+
@@ -1,1 +1,1 @@
660+
-old2
661+
+new2\
662+
`;
663+
const files = parse(diff);
664+
expect(files.length).toBe(2);
665+
expect(files[0].from).toBe("file1");
666+
expect(files[0].chunks[0].changes.length).toBe(3);
667+
expect(files[1].from).toBe("file2");
668+
expect(files[1].chunks[0].changes.length).toBe(2);
669+
});
670+
671+
it("should still parse normal context lines with leading space", function () {
672+
// Regression guard: standard context lines (with leading space) must still work
673+
const diff = `\
674+
diff --git a/file b/file
675+
index 123..456 789
676+
--- a/file
677+
+++ b/file
678+
@@ -1,3 +1,3 @@
679+
context before
680+
-old
681+
+new
682+
context after\
683+
`;
684+
const files = parse(diff);
685+
expect(files.length).toBe(1);
686+
const chunk = files[0].chunks[0];
687+
expect(chunk.changes.length).toBe(4);
688+
expect(chunk.changes[0].type).toBe("normal");
689+
expect(chunk.changes[0].content).toBe(" context before");
690+
expect(chunk.changes[1].type).toBe("del");
691+
expect(chunk.changes[2].type).toBe("add");
692+
expect(chunk.changes[3].type).toBe("normal");
693+
expect(chunk.changes[3].content).toBe(" context after");
694+
});
695+
});
520696
});

0 commit comments

Comments
 (0)