From 988b288b870dd0165f09b991839c2c37fcd3fd62 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 30 Sep 2026 21:38:57 -0300 Subject: A diff line looks up the path and line it names, as any look does The look on a diff line was its own resolver: a walk up to .git/.jj from the diff's directory, its own "no such file here" and "deleted by this diff". A review tool's cached patch, in no repository, missed every file though the session's terminals sat in the checkout. Now a diff line only expands and transforms. Its expansion (what the hover shows) is the whole line for a header, a `@@` line, or a hunk line pointed at on its prefix, and its text is the look address the line names (look.diffAddress): `path` for `diff --git`/`---`/`+++`, `path:c` for `@@ -a,b +c,d @@`, `path:N` for a hunk line's new line (a removed line's, the new line now where it was). That text goes through the regular look exactly as if selected by hand: same resolution, placement and errors. A 9P look of a whole diff line takes the same transform. Kept: the line classification, hunk arithmetic, git prefix stripping and timestamp cutting (diff.zig). Gone: the repo-root walk, its messages, Target.old_side and deletesAhead, and the click's row stash (Pardes.diff_look). A test compares a B3 on a cached patch's line with a look of the hand-selected `entry/src/entry.rs:2` in the same session. Co-Authored-By: Claude Opus 5.5 --- src/diff.zig | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) (limited to 'src/diff.zig') diff --git a/src/diff.zig b/src/diff.zig index e2b4afd4..362695d5 100644 --- a/src/diff.zig +++ b/src/diff.zig @@ -331,22 +331,14 @@ pub const Target = struct { path: []const u8, /// From 1; 0 for the file alone (a `---`/`+++` line). line: usize, - /// The section deletes the file, and this is its old side. - old_side: bool = false, -}; -/// Whether the section `row` is in deletes its file by a line still ahead -/// of it (`deleted file mode`, `+++ /dev/null`): its `diff --git` and `---` -/// lines come first, and are about a file this diff deletes all the same. -fn deletesAhead(lines: []const []const u8, row: usize) bool { - for (lines[row + 1 ..]) |raw| { - const l = std.mem.trimEnd(u8, raw, "\r"); - if (std.mem.startsWith(u8, l, "diff ") or std.mem.startsWith(u8, l, "@@")) return false; - if (std.mem.startsWith(u8, l, "deleted file mode") or std.mem.eql(u8, l, "+++ /dev/null") or - (std.mem.startsWith(u8, l, "+++ /dev/null") and l.len > 13 and (l[13] == '\t' or l[13] == ' '))) return true; + /// The look address for it, as one would select it by hand: `path` + /// or `path:line`. + pub fn address(t: Target, buf: []u8) ?[]const u8 { + if (t.line == 0) return std.fmt.bufPrint(buf, "{s}", .{t.path}) catch null; + return std.fmt.bufPrint(buf, "{s}:{d}", .{ t.path, t.line }) catch null; } - return false; -} +}; /// What a look at `lines[row]` opens, or null for a line that is no part /// of a file section (a commit message, `index ...`). The whole line is @@ -374,7 +366,7 @@ pub fn targetAt(lines: []const []const u8, row: usize, continued_rows: bool) ?Ta .other, .meta, .no_newline => { // `diff --git a/x b/y`: the file, as its `+++` would. if (got.kind == .meta and std.mem.startsWith(u8, line, "diff ") and w.git_path.len > 0) - return .{ .kind = got.kind, .path = w.git_path, .line = 0, .old_side = deletesAhead(lines, row) }; + return .{ .kind = got.kind, .path = w.git_path, .line = 0 }; return null; }, .old_path, .new_path => { @@ -388,18 +380,18 @@ pub fn targetAt(lines: []const []const u8, row: usize, continued_rows: bool) ?Ta if (!isDevNull(new) and (isDevNull(own) or !std.mem.eql(u8, new, own))) return .{ .kind = got.kind, .path = new, .line = 0 }; } - if (!isDevNull(own)) return .{ .kind = got.kind, .path = own, .line = 0, .old_side = got.kind == .old_path and (w.deleted() or deletesAhead(lines, row)) }; + if (!isDevNull(own)) return .{ .kind = got.kind, .path = own, .line = 0 }; if (w.path().len == 0) return null; - return .{ .kind = got.kind, .path = w.path(), .line = 0, .old_side = w.deleted() }; + return .{ .kind = got.kind, .path = w.path(), .line = 0 }; }, .hunk => { if (w.path().len == 0) return null; - if (w.deleted()) return .{ .kind = got.kind, .path = w.path(), .line = @max(1, got.old), .old_side = true }; + if (w.deleted()) return .{ .kind = got.kind, .path = w.path(), .line = @max(1, got.old) }; return .{ .kind = got.kind, .path = w.path(), .line = @max(1, got.new) }; }, .context, .added, .removed => { if (w.path().len == 0) return null; - if (w.deleted()) return .{ .kind = got.kind, .path = w.path(), .line = @max(1, got.old), .old_side = true }; + if (w.deleted()) return .{ .kind = got.kind, .path = w.path(), .line = @max(1, got.old) }; var at = got.new; // Gone from the end of the file: nothing stands after it. if (got.kind == .removed and w.new_count > 0 and at >= w.new_start + w.new_count) @@ -454,7 +446,6 @@ test "diff every line of a section that deletes its file is about a file this di if (row < 15) continue; // the deleting section: `diff --git a/old.py` on const t = targetAt(&lines, row, false) orelse continue; try testing.expectEqualStrings("old.py", t.path); - try testing.expect(t.old_side); _ = line; rows += 1; } @@ -505,7 +496,6 @@ test "diff a look on each kind of line names the new file and its line" { const gone = targetAt(&lines, 21, false).?; try testing.expectEqualStrings("old.py", gone.path); try testing.expectEqual(@as(usize, 2), gone.line); - try testing.expect(gone.old_side); try testing.expect(targetAt(&lines, 1, false) == null); } -- cgit v1.3