Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-diffs-moved.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hunkdiff": patch
---

Keep moved-line classification aligned when a diff hunk adds or removes a line whose content begins with `++` or `--`.
Binary file added docs/screenshots/moved-lines-after.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/moved-lines-before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions src/core/changeset/fromPatch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { describe, expect, test } from "bun:test";
import { changesetFromPatch } from "./fromPatch";

/**
* A hunk body line is always exactly one sign char plus its content, so a content line
* that happens to start with `++` or `--` (e.g. `++i;`, `--flag`) reads `+++…`/`---…`
* in the patch. File headers never appear inside a hunk, so content lines decide their
* own index in the per-side line arrays.
*/
const ADDITION_PLUS_PREFIX_PATCH = [
"diff --git a/add_plus.txt b/add_plus.txt",
"index 0000000..1111111 100644",
"--- a/add_plus.txt",
"+++ b/add_plus.txt",
"@@ -1,3 +1,4 @@",
" one",
"\u001b[1;35m-two\u001b[m",
"\u001b[32m+++ plus\u001b[m",
"\u001b[36m+three\u001b[m",
" four",
"",
].join("\n");

const DELETION_MINUS_PREFIX_PATCH = [
"diff --git a/del_minus.txt b/del_minus.txt",
"index 0000000..2222222 100644",
"--- a/del_minus.txt",
"+++ b/del_minus.txt",
"@@ -1,3 +1,3 @@",
" one",
"\u001b[1;35m--- flag\u001b[m",
"\u001b[36m+two\u001b[m",
" three",
"",
].join("\n");

describe("collectLineMoveKinds", () => {
test("indexes an added content line that starts with ++', not its following lines' move kind", () => {
const changeset = changesetFromPatch(ADDITION_PLUS_PREFIX_PATCH, "t", "probe", null);
const file = changeset.files.find((entry) => entry.path === "add_plus.txt")!;

expect(file.metadata.additionLines).toEqual(["one\n", "++ plus\n", "three\n", "four\n"]);
expect(file.lineMoveKinds?.additionLines).toEqual([undefined, undefined, "moved", undefined]);
});

test("indexes a deleted content line that starts with --', keeping its move kind", () => {
const changeset = changesetFromPatch(DELETION_MINUS_PREFIX_PATCH, "t", "probe", null);
const file = changeset.files.find((entry) => entry.path === "del_minus.txt")!;

expect(file.metadata.deletionLines).toEqual(["one\n", "-- flag\n", "three\n"]);
expect(file.lineMoveKinds?.deletionLines).toEqual([undefined, "moved", undefined]);
});
});
6 changes: 4 additions & 2 deletions src/core/changeset/fromPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,15 @@ function collectLineMoveKinds(patchText: string): DiffLineMoveKinds[] {
continue;
}

if (plainLine.startsWith("+") && !plainLine.startsWith("+++")) {
// Every hunk-body `+`/`-` line is content — a line whose content starts with `++`
// reads `+++…` just like a file header, but headers never appear inside a hunk.
if (plainLine.startsWith("+")) {
activeMoveKinds.additionLines[additionLineIndex] = movedLineKindFromAnsi(rawLine, "addition");
additionLineIndex += 1;
continue;
}

if (plainLine.startsWith("-") && !plainLine.startsWith("---")) {
if (plainLine.startsWith("-")) {
activeMoveKinds.deletionLines[deletionLineIndex] = movedLineKindFromAnsi(rawLine, "deletion");
deletionLineIndex += 1;
continue;
Expand Down
Loading