diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 18:18:47 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 3f6c259711252b57646f447de85e44c5239ebd0d (patch) | |
| tree | e75a1239f69a7b9ea0b3a7784315a0d71e8a15d2 /src/look.zig | |
| parent | 97464fad2d4f6a58608cf92729fd8395db89c7b4 (diff) | |
| download | pardes-3f6c259711252b57646f447de85e44c5239ebd0d.tar.gz pardes-3f6c259711252b57646f447de85e44c5239ebd0d.zip | |
A diff look names a file as the repository does: git's side prefix dropped by git's rules, a plain diff's names as written
In a git section (one with a `diff --git` line) the one-letter side prefix
git writes, `a/`/`b/` or with diff.mnemonicPrefix `c/`, `i/`, `w/`, `o/`,
is dropped from its paths only when both paths of that line carry one,
different ones: `--no-prefix` writes none, so its `a/x a/x` is a real
directory `a` and is kept. A plain `diff -u` never strips: its names are
used as written, past the timestamp. `/dev/null` stays itself.
The walker does this once (diff.Walk.side, diff.gitHeader), so the path a
look resolves, and the one a miss says, are the repository's name:
`Look: src/x.zig: no such file here`, `Look: lib/old.zig: deleted by this
diff`. The look no longer tries a stripped name beside the written one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/look.zig')
| -rw-r--r-- | src/look.zig | 76 |
1 files changed, 64 insertions, 12 deletions
diff --git a/src/look.zig b/src/look.zig index e22f5705..afb5b760 100644 --- a/src/look.zig +++ b/src/look.zig @@ -1527,9 +1527,11 @@ const DiffLook = union(enum) { none, done, at: []const u8 }; /// line names (diff.targetAt): the new file at a `@@` line's first new /// line, at a context or added line's own line, and at a removed line's /// place, the new line now standing where it was. `---`/`+++` open their -/// file. The path is the repository's (walking up from the diff's -/// directory to a `.git` or `.jj`), `a/` and `b/` dropped, else the -/// directory's own. A file that is not there opens nothing and says so. +/// file. The path is the repository's name (diff.Walk.side: git's side +/// prefixes dropped by git's rules, a plain diff's names as written), +/// found from the repository root (walking up from the diff's directory to +/// a `.git` or `.jj`), else the directory. A file that is not there opens +/// nothing and says so, by that name. /// /// Which line: the one a click just gave (`clicked`: exec.pointerOperand /// expanded to it and kept its row), else, for a look written over 9P, a @@ -1573,8 +1575,7 @@ fn diffLook(p: *Pardes, id: usize, pane: *Pane, operand: []const u8, clicked: bo } /// The file a diff names, from `base` (the diff's directory): tried in the -/// repository `base` is in, then in `base`, as git writes it (`b/x`, the -/// repository's `x`) and as written. +/// repository `base` is in, then in `base`. fn diffFile(base: []const u8, path: []const u8, out: *[4096]u8) ?[]const u8 { if (comptime !platform_has_fs) return null; if (path.len == 0) return null; @@ -1593,11 +1594,9 @@ fn diffFile(base: []const u8, path: []const u8, out: *[4096]u8) ?[]const u8 { } else null; for ([_]?[]const u8{ root, base }) |maybe| { const at = maybe orelse continue; - for ([_][]const u8{ diff.stripSide(path), path }) |name| { - var joined: [4096]u8 = undefined; - const full = std.fmt.bufPrint(&joined, "{s}/{s}", .{ at, name }) catch continue; - if (fs.resolveOs(full, out)) |found| if (!found.dir) return found.path; - } + var joined: [4096]u8 = undefined; + const full = std.fmt.bufPrint(&joined, "{s}/{s}", .{ at, path }) catch continue; + if (fs.resolveOs(full, out)) |found| if (!found.dir) return found.path; } return null; } @@ -2175,10 +2174,10 @@ test "Look on a diff line of a file that is not here says so and opens nothing" var after: usize = 0; for (p.panes) |slot| after += @intFromBool(slot != null); try std.testing.expectEqual(before, after); - try std.testing.expectEqualStrings("Look: b/src/elsewhere.zig: no such file here", pane.msg[0..pane.msg_len]); + try std.testing.expectEqualStrings("Look: src/elsewhere.zig: no such file here", pane.msg[0..pane.msg_len]); DiffFixture.click(p, pane, 11); try std.testing.expect(p.look_missed); - try std.testing.expectEqualStrings("Look: a/old.zig: deleted by this diff", pane.msg[0..pane.msg_len]); + try std.testing.expectEqualStrings("Look: old.zig: deleted by this diff", pane.msg[0..pane.msg_len]); } test "Look on plain diff -u output: names as written, timestamps cut, next to the diff" { @@ -2303,3 +2302,56 @@ test "Look outside a diff pane expands and looks as ever" { const operand = exec.pointerOperand(p, pane, pointed.sel); try std.testing.expect(operand.text == null or !std.mem.eql(u8, operand.text.?, "+const a = 1;")); } + +test "Look on a diff under each git prefix opens the repository's file, and a real a/ directory under --no-prefix" { + if (!platform_has_fs) return; + const Case = struct { text: []const u8, file: []const u8 }; + const cases = [_]Case{ + .{ .text = "diff --git a/src/x.zig b/src/x.zig\n--- a/src/x.zig\n+++ b/src/x.zig\n@@ -1,2 +1,2 @@\n-one\n+uno\n two\n", .file = "/src/x.zig" }, + .{ .text = "diff --git c/src/x.zig w/src/x.zig\n--- c/src/x.zig\n+++ w/src/x.zig\n@@ -1,2 +1,2 @@\n-one\n+uno\n two\n", .file = "/src/x.zig" }, + .{ .text = "diff --git i/src/x.zig w/src/x.zig\n--- i/src/x.zig\n+++ w/src/x.zig\n@@ -1,2 +1,2 @@\n-one\n+uno\n two\n", .file = "/src/x.zig" }, + // --no-prefix: `a/x.zig` is the file x.zig in the directory a, and + // a `x.zig` at the top is never taken for it. + .{ .text = "diff --git a/x.zig a/x.zig\n--- a/x.zig\n+++ a/x.zig\n@@ -1,2 +1,2 @@\n-one\n+uno\n two\n", .file = "/a/x.zig" }, + }; + for (cases) |c| { + var fx: DiffFixture = undefined; + try fx.init(true, &.{ .{ "src/x.zig", "uno\ntwo\n" }, .{ "a/x.zig", "uno\ntwo\n" }, .{ "x.zig", "top\n" } }, c.text); + defer fx.tmp.cleanup(); + const p = try Pardes.init(std.testing.allocator, .{ .cols = 100, .rows = 30 }); + defer p.deinit(); + const pane = try fx.open(p, c.text); + DiffFixture.click(p, pane, 6); // `two`, a context line: new line 2 + try std.testing.expect(!p.look_missed); + try expectAt(p, c.file, 2); + DiffFixture.click(p, pane, 1); // `---` + try std.testing.expect(!p.look_missed); + try std.testing.expect(std.mem.endsWith(u8, p.panes[p.active].?.file.?.path, c.file)); + } +} + +test "Look on a diff says a missing or deleted file by its repository name, a plain diff's as written" { + if (!platform_has_fs) return; + const git_text = "diff --git c/src/gone.zig w/src/gone.zig\n--- c/src/gone.zig\n+++ w/src/gone.zig\n@@ -1 +1 @@\n-a\n+b\n" ++ + "diff --git a/lib/old.zig b/lib/old.zig\ndeleted file mode 100644\n--- a/lib/old.zig\n+++ /dev/null\n@@ -1 +0,0 @@\n-a\n"; + const plain_text = "--- a/x.zig\t2026-09-30 10:00:00 +0000\n+++ b/x.zig\t2026-09-30 11:00:00 +0000\n@@ -1 +1 @@\n-a\n+b\n"; + var fx: DiffFixture = undefined; + try fx.init(true, &.{.{ "x.zig", "b\n" }}, git_text); + defer fx.tmp.cleanup(); + const p = try Pardes.init(std.testing.allocator, .{ .cols = 100, .rows = 30 }); + defer p.deinit(); + var pane = try fx.open(p, git_text); + DiffFixture.click(p, pane, 5); + try std.testing.expect(p.look_missed); + try std.testing.expectEqualStrings("Look: src/gone.zig: no such file here", pane.msg[0..pane.msg_len]); + DiffFixture.click(p, pane, 11); + try std.testing.expectEqualStrings("Look: lib/old.zig: deleted by this diff", pane.msg[0..pane.msg_len]); + DiffFixture.click(p, pane, 6); // `diff --git` of the deleted file + try std.testing.expectEqualStrings("Look: lib/old.zig: deleted by this diff", pane.msg[0..pane.msg_len]); + // A plain diff's `b/x.zig` is a path as written: there is no b/ here, + // and the x.zig at the top is not it. + pane = try fx.open(p, plain_text); + DiffFixture.click(p, pane, 4); + try std.testing.expect(p.look_missed); + try std.testing.expectEqualStrings("Look: b/x.zig: no such file here", pane.msg[0..pane.msg_len]); +} |
