summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-30 21:38:57 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:17 -0300
commit988b288b870dd0165f09b991839c2c37fcd3fd62 (patch)
tree6552dc29f322c6ec6ee89b3ffdcc811389305e27 /src
parent68dd567218505838814d73473460a0c0ff8e97b3 (diff)
downloadpardes-988b288b870dd0165f09b991839c2c37fcd3fd62.tar.gz
pardes-988b288b870dd0165f09b991839c2c37fcd3fd62.zip
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 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/CHANGELOG.md8
-rw-r--r--src/diff.zig32
-rw-r--r--src/exec.zig12
-rw-r--r--src/look.zig273
-rw-r--r--src/pardes.zig4
5 files changed, 130 insertions, 199 deletions
diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md
index ae796a4d..a1caffb4 100644
--- a/src/CHANGELOG.md
+++ b/src/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## Unreleased
+
+- A look on a diff line is the look of the `path:line` it names, exactly as
+ if that text were selected by hand: the same resolution, placement and
+ errors. A patch cached outside any checkout opens the file the way a
+ hand-selected path would, and the diff's own path finding and its
+ messages are gone.
+
## 0.0.12
- A selection deep in a very long line no longer slows every frame: a 5 MB
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);
}
diff --git a/src/exec.zig b/src/exec.zig
index c673aab2..ea20f969 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -170,8 +170,9 @@ pub fn pointerOperand(p: *Pardes, pane: *Pane, clicked: Pane.Sel) PointerOperand
return result;
}
// In a diff, a file or hunk header, and a hunk line pointed at on its
- // `+`/`-`/` ` prefix, are looked at whole: the place the line names
- // (look.diffLook). A hunk line's code is words, looked at as ever:
+ // `+`/`-`/` ` prefix, expand to the whole line (the hover shows it) and
+ // the look is of the address it names (look.diffAddress), a regular
+ // look from there. A hunk line's code is words, looked at as ever:
// the code's, the prefix never one of them.
var word_from: usize = 0;
if (clicked.r0 >= pane.tag_rows and clicked.r0 == clicked.r1 and clicked.c0 == clicked.c1 and row >= 0) diff: {
@@ -179,7 +180,9 @@ pub fn pointerOperand(p: *Pardes, pane: *Pane, clicked: Pane.Sel) PointerOperand
const at: usize = @intCast(row);
if (at >= lines.len) break :diff;
const line = lines[at];
- const target = @import("diff.zig").targetAt(lines, at, pane.file == null) orelse break :diff;
+ var address_buf: [4096 + 32]u8 = undefined;
+ const address = look.diffAddress(pane, lines, at, &address_buf) orelse break :diff;
+ const target = @import("diff.zig").targetAt(lines, at, pane.file == null).?;
const on_prefix = col == 0 and line.len > 0 and std.mem.indexOfScalar(u8, "+- ", line[0]) != null;
switch (target.kind) {
.context, .added, .removed => if (!on_prefix) {
@@ -188,8 +191,7 @@ pub fn pointerOperand(p: *Pardes, pane: *Pane, clicked: Pane.Sel) PointerOperand
},
else => {},
}
- result.text = line;
- p.diff_look = .{ .serial = pane.serial, .row = at };
+ result.text = p.scratch.allocator().dupe(u8, address) catch break :diff;
const hi = std.math.cast(i32, line.len) orelse return result;
if (hi == 0) return result;
var expanded = clicked;
diff --git a/src/look.zig b/src/look.zig
index 36b91545..3cd1faa5 100644
--- a/src/look.zig
+++ b/src/look.zig
@@ -1539,82 +1539,26 @@ pub fn diffLines(p: *Pardes, pane: *Pane) ?[]const []const u8 {
return p.diff_output.lines(p, pane);
}
-const DiffLook = union(enum) { none, done, at: []const u8 };
-
-/// A look in a diff pane at one of its lines opens the file and line that
-/// 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 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
-/// line the text is the whole of, the cursor's first, then the first after
-/// it. A word on a hunk line is looked at as ever.
-fn diffLook(p: *Pardes, id: usize, pane: *Pane, operand: []const u8, clicked: bool, buf: []u8) DiffLook {
- const text = std.mem.trimEnd(u8, operand, "\r\n");
- if (std.mem.trim(u8, text, " \t").len == 0) return .none;
- const lines = diffLines(p, pane) orelse return .none;
- const row: usize = row: {
- if (p.diff_look) |at| if (at.serial == pane.serial and at.row < lines.len and
- std.mem.eql(u8, std.mem.trimEnd(u8, lines[at.row], "\r"), text))
- {
- p.diff_look = null;
- break :row at.row;
- };
- if (clicked) return .none;
- const cur: usize = @intCast(@max(0, pane.body.cur_row));
- for (0..lines.len) |k| {
- const i = (cur + k) % lines.len;
- if (std.mem.eql(u8, std.mem.trimEnd(u8, lines[i], "\r"), text)) break :row i;
- }
- return .none;
- };
- const target = diff.targetAt(lines, row, pane.file == null) orelse return .none;
- const base = if (pane.file) |f| std.fs.path.dirname(f.path) orelse "/" else pane.cwdSlice();
- var realbuf: [4096]u8 = undefined;
- const found = diffFile(base, target.path, &realbuf) orelse {
- var cut: [miss_path_cap + "…".len]u8 = undefined;
- if (target.old_side)
- missed(p, id, "{s}: deleted by this diff", .{missText(target.path, &cut)})
- else
- missed(p, id, "{s}: no such file here", .{missText(target.path, &cut)});
- return .done;
- };
- const place = if (target.line > 0)
- std.fmt.bufPrint(buf, "{s}:{d}", .{ found, target.line }) catch return .none
- else
- std.fmt.bufPrint(buf, "{s}", .{found}) catch return .none;
- return .{ .at = place };
+/// The look address a diff line names (diff.targetAt): `path` for a
+/// `diff --git`, `---` or `+++` line, `path:N` for a `@@` line (its first
+/// new line) and a hunk line (its new line; a removed line's, the new line
+/// now standing where it was). The look of it is a regular one, as of that
+/// text selected by hand. Null for a line no part of a file section.
+pub fn diffAddress(pane: *const Pane, lines: []const []const u8, row: usize, buf: []u8) ?[]const u8 {
+ const target = diff.targetAt(lines, row, pane.file == null) orelse return null;
+ return target.address(buf);
}
-/// The file a diff names, from `base` (the diff's directory): tried in the
-/// 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;
- if (path[0] == '/') {
- const found = fs.resolveOs(path, out) orelse return null;
- return if (found.dir) null else found.path;
- }
- var root_buf: [4096]u8 = undefined;
- var dir: ?[]const u8 = base;
- const root: ?[]const u8 = while (dir) |d| : (dir = std.fs.path.dirname(d)) {
- var probe: [4096]u8 = undefined;
- const git = std.fmt.bufPrint(&probe, "{s}/.git", .{d}) catch break null;
- if (fs.resolveOs(git, &root_buf) != null) break d;
- const jj = std.fmt.bufPrint(&probe, "{s}/.jj", .{d}) catch break null;
- if (fs.resolveOs(jj, &root_buf) != null) break d;
- } else null;
- for ([_]?[]const u8{ root, base }) |maybe| {
- const at = maybe orelse continue;
- 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;
+/// A look written over 9P of a whole line of a diff pane (the cursor's line
+/// first, then the first after it): its address, as a click there makes.
+fn diffLineAddress(p: *Pardes, pane: *Pane, operand: []const u8, buf: []u8) ?[]const u8 {
+ const text = std.mem.trimEnd(u8, operand, "\r\n");
+ if (std.mem.trim(u8, text, " \t").len == 0) return null;
+ const lines = diffLines(p, pane) orelse return null;
+ const cur: usize = @intCast(@max(0, pane.body.cur_row));
+ for (0..lines.len) |k| {
+ const i = (cur + k) % lines.len;
+ if (std.mem.eql(u8, std.mem.trimEnd(u8, lines[i], "\r"), text)) return diffAddress(pane, lines, i, buf);
}
return null;
}
@@ -1639,24 +1583,15 @@ pub fn lookAt(p: *Pardes, id: usize, operand_raw: []const u8) void {
p.noteHaptic(.look);
noteLookSource(p, id);
const command = std.mem.trim(u8, operand, " \t\r\n");
- // A click on a diff line (a context line ` Del` included) is a look at
- // the place it names, never the word run.
+ // A whole line of a diff written over 9P (a context line ` Del`
+ // included) is a look at the address it names, never the word run.
var diff_buf: [4096 + 32]u8 = undefined;
- const looked: []const u8 = looked: {
- switch (diffLook(p, id, pane, operand_raw, true, &diff_buf)) {
- .none => {},
- .done => return,
- .at => |place| break :looked place,
- }
+ const looked: []const u8 = diffLineAddress(p, pane, operand_raw, &diff_buf) orelse looked: {
if (config.commandWord(command)) |cmd| {
if (exec.execute(p, id, cmd)) |dst| p.active = dst;
return;
}
- switch (diffLook(p, id, pane, operand_raw, false, &diff_buf)) {
- .none => break :looked operand,
- .done => return,
- .at => |place| break :looked place,
- }
+ break :looked operand;
};
// The operand may borrow selected terminal/PDF text released below.
const txt = p.scratch.allocator().dupe(u8, looked) catch return;
@@ -2084,13 +2019,14 @@ const DiffFixture = struct {
return pane;
}
- /// A right click's look at `row` of the diff pane (slot 0).
+ /// A right click's look at `row` of the diff pane (slot 0): the look
+ /// of the address that line names.
fn click(p: *Pardes, diff_pane: *Pane, row: usize) void {
p.active = 0;
const lines = diffLines(p, diff_pane).?;
- p.diff_look = .{ .serial = diff_pane.serial, .row = row };
+ var buf: [4096]u8 = undefined;
p.look_missed = false;
- lookAt(p, 0, lines[row]);
+ lookAt(p, 0, diffAddress(diff_pane, lines, row, &buf).?);
}
};
@@ -2149,11 +2085,9 @@ test "Look on each kind of diff line opens the new file at the line it names" {
try std.testing.expect(std.mem.endsWith(u8, p.panes[p.active].?.file.?.path, "/src/a.zig"));
try std.testing.expect(!p.look_missed);
- // No click: a 9P look writes a line, found after the cursor, or the
- // text is on the cursor's line.
+ // No click: a 9P look writes a whole line, found from the cursor on.
p.active = 0;
pane.body.cur_row = 0;
- p.diff_look = null;
lookAt(p, 0, "+line 12");
try expectAt(p, "/src/a.zig", 12);
// A word on a hunk line is a word, looked for in the diff as ever.
@@ -2168,43 +2102,6 @@ test "Look on each kind of diff line opens the new file at the line it names" {
try std.testing.expectEqual(@as(usize, 0), p.active);
}
-test "Look on a diff line of a file that is not here says so and opens nothing" {
- if (!platform_has_fs) return;
- const diff_text =
- \\diff --git a/src/elsewhere.zig b/src/elsewhere.zig
- \\--- a/src/elsewhere.zig
- \\+++ b/src/elsewhere.zig
- \\@@ -1 +1 @@
- \\-const a = 1;
- \\+const a = 2;
- \\diff --git a/old.zig b/old.zig
- \\deleted file mode 100644
- \\--- a/old.zig
- \\+++ /dev/null
- \\@@ -1 +0,0 @@
- \\-const gone = 1;
- \\
- ;
- var fx: DiffFixture = undefined;
- try fx.init(true, &.{}, diff_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, diff_text);
- var before: usize = 0;
- for (p.panes) |slot| before += @intFromBool(slot != null);
- DiffFixture.click(p, pane, 5);
- try std.testing.expect(p.look_missed);
- try std.testing.expectEqual(@as(usize, 0), p.active);
- var after: usize = 0;
- for (p.panes) |slot| after += @intFromBool(slot != null);
- try std.testing.expectEqual(before, after);
- 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: 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" {
if (!platform_has_fs) return;
const diff_text = "--- old/x.zig\t2026-09-30 10:00:00.000000000 +0000\n" ++
@@ -2250,22 +2147,22 @@ test "Look on a diff: headers and a hunk line's prefix are the whole line, hover
pane.tag_expanded = false;
// `word`: what the pointer there expands to when it is not the whole
// line; `line` 0: that word is looked at in the diff, nothing opened.
- const Point = struct { row: u16, col: u16, line: usize, word: ?[]const u8 = null };
+ const Point = struct { row: u16, col: u16, line: usize, word: ?[]const u8 = null, addr: []const u8 = "" };
const points = [_]Point{
- .{ .row = 0, .col = 0, .line = 1 }, // `diff`: the new side
- .{ .row = 0, .col = 35, .line = 1 }, // on b/src/new.zig
- .{ .row = 4, .col = 1, .line = 1 }, // `---`: renamed, so the new name
- .{ .row = 4, .col = 5, .line = 1 }, // the `a/` of the old name
- .{ .row = 5, .col = 0, .line = 1 }, // `+++`
- .{ .row = 5, .col = 4, .line = 1 }, // its `b/`
- .{ .row = 5, .col = 10, .line = 1 }, // its path
- .{ .row = 6, .col = 0, .line = 2 }, // the first `@@`
- .{ .row = 6, .col = 9, .line = 2 }, // `+2,2`
- .{ .row = 6, .col = 13, .line = 2 }, // the second `@@`
- .{ .row = 6, .col = 20, .line = 2 }, // the function after it
- .{ .row = 7, .col = 0, .line = 2 }, // a context line's ` `
- .{ .row = 8, .col = 0, .line = 3 }, // a removed line's `-`: where it was
- .{ .row = 9, .col = 0, .line = 3 }, // an added line's `+`
+ .{ .row = 0, .col = 0, .line = 1, .addr = "src/new.zig" }, // `diff`: the new side
+ .{ .row = 0, .col = 35, .line = 1, .addr = "src/new.zig" }, // on b/src/new.zig
+ .{ .row = 4, .col = 1, .line = 1, .addr = "src/new.zig" }, // `---`: renamed, so the new name
+ .{ .row = 4, .col = 5, .line = 1, .addr = "src/new.zig" }, // the `a/` of the old name
+ .{ .row = 5, .col = 0, .line = 1, .addr = "src/new.zig" }, // `+++`
+ .{ .row = 5, .col = 4, .line = 1, .addr = "src/new.zig" }, // its `b/`
+ .{ .row = 5, .col = 10, .line = 1, .addr = "src/new.zig" }, // its path
+ .{ .row = 6, .col = 0, .line = 2, .addr = "src/new.zig:2" }, // the first `@@`
+ .{ .row = 6, .col = 9, .line = 2, .addr = "src/new.zig:2" }, // `+2,2`
+ .{ .row = 6, .col = 13, .line = 2, .addr = "src/new.zig:2" }, // the second `@@`
+ .{ .row = 6, .col = 20, .line = 2, .addr = "src/new.zig:2" }, // the function after it
+ .{ .row = 7, .col = 0, .line = 2, .addr = "src/new.zig:2" }, // a context line's ` `
+ .{ .row = 8, .col = 0, .line = 3, .addr = "src/new.zig:3" }, // a removed line's `-`: where it was
+ .{ .row = 9, .col = 0, .line = 3, .addr = "src/new.zig:3" }, // an added line's `+`
.{ .row = 9, .col = 3, .line = 0, .word = "three" }, // its code: a word
.{ .row = 7, .col = 2, .line = 0, .word = "two" },
};
@@ -2291,7 +2188,8 @@ test "Look on a diff: headers and a hunk line's prefix are the whole line, hover
try std.testing.expectEqualStrings(word, operand.text.?);
try std.testing.expectEqualStrings(word, lines[at.row][@intCast(span.lo)..@intCast(span.hi)]);
} else {
- try std.testing.expectEqualStrings(lines[at.row], operand.text.?);
+ // The hover shows the line; the look is of the address it names.
+ try std.testing.expectEqualStrings(at.addr, operand.text.?);
try std.testing.expectEqual(@as(i32, 0), span.lo);
try std.testing.expectEqual(@as(i32, @intCast(lines[at.row].len)), span.hi);
}
@@ -2355,28 +2253,65 @@ test "Look on a diff under each git prefix opens the repository's file, and a re
}
}
-test "Look on a diff says a missing or deleted file by its repository name, a plain diff's as written" {
+
+test "Look on a diff line is the look of the address it names, selected by hand: a cached patch, the checkout elsewhere" {
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]);
+ const io = std.testing.io;
+ const patch = "diff --git a/entry/src/entry.rs b/entry/src/entry.rs\n--- a/entry/src/entry.rs\n+++ b/entry/src/entry.rs\n" ++
+ "@@ -1,2 +1,2 @@\n-fn old() {}\n+fn new() {}\n fn kept() {}\n";
+ // The review tool's cache, with no checkout in it.
+ var cache: DiffFixture = undefined;
+ try cache.init(false, &.{}, patch);
+ defer cache.tmp.cleanup();
+ // The checkout, elsewhere, with a file of it open in the session.
+ var checkout = std.testing.tmpDir(.{});
+ defer checkout.cleanup();
+ try checkout.dir.createDir(io, ".git", .default_dir);
+ try checkout.dir.createDirPath(io, "entry/src");
+ try checkout.dir.writeFile(io, .{ .sub_path = "entry/src/entry.rs", .data = "fn new() {}\nfn kept() {}\n" });
+ try checkout.dir.writeFile(io, .{ .sub_path = "Cargo.toml", .data = "[workspace]\n" });
+ var path_buf: [4096]u8 = undefined;
+ const cargo = path_buf[0..try checkout.dir.realPathFile(io, "Cargo.toml", &path_buf)];
+
+ const Seen = struct { active_path: []u8, row: i32, missed: bool, msg: []u8, panes: usize };
+ var seen: [2]Seen = undefined;
+ for (0..2) |way| {
+ const p = try Pardes.init(std.testing.allocator, .{ .cols = 100, .rows = 30 });
+ defer p.deinit();
+ const pane = try cache.open(p, patch);
+ // The checkout's file, opened by a look from the diff pane.
+ lookAt(p, 0, cargo);
+ p.update(.tick);
+ try std.testing.expect(std.mem.endsWith(u8, p.panes[p.active].?.file.?.path, "Cargo.toml"));
+ p.active = 0;
+ p.update(.tick);
+ p.look_missed = false;
+ if (way == 0) {
+ DiffFixture.click(p, pane, 6); // ` fn kept() {}`, new line 2
+ } else {
+ lookAt(p, 0, "entry/src/entry.rs:2"); // the same, selected by hand
+ }
+ const shown = p.panes[p.active].?;
+ var n: usize = 0;
+ for (p.panes) |slot| n += @intFromBool(slot != null);
+ seen[way] = .{
+ .active_path = try std.testing.allocator.dupe(u8, if (shown.file) |f| f.path else ""),
+ .row = shown.body.cur_row,
+ .missed = p.look_missed,
+ .msg = try std.testing.allocator.dupe(u8, pane.msg[0..pane.msg_len]),
+ .panes = n,
+ };
+ }
+ defer for (seen) |s| {
+ std.testing.allocator.free(s.active_path);
+ std.testing.allocator.free(s.msg);
+ };
+ try std.testing.expectEqualStrings(seen[1].active_path, seen[0].active_path);
+ try std.testing.expectEqual(seen[1].row, seen[0].row);
+ try std.testing.expectEqual(seen[1].missed, seen[0].missed);
+ try std.testing.expectEqualStrings(seen[1].msg, seen[0].msg);
+ try std.testing.expectEqual(seen[1].panes, seen[0].panes);
+ // And it works: the checkout's file, at the line.
+ try std.testing.expect(std.mem.endsWith(u8, seen[0].active_path, "/entry/src/entry.rs"));
+ try std.testing.expectEqual(@as(i32, 1), seen[0].row);
}
diff --git a/src/pardes.zig b/src/pardes.zig
index fbc19b8a..f479f44d 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -4376,10 +4376,6 @@ pub const Pardes = struct {
chord_hold: bool = false,
/// The look just run found nothing (look.zig missed).
look_missed: bool = false,
- /// The diff line a right click just expanded to, by row: a look at its
- /// text goes to the place that row names, not the first equal line
- /// (look.zig diffLook).
- diff_look: ?struct { serial: u32, row: usize } = null,
look_hover_wait: ?look.LookHoverWait = null,
/// The place a look-hover last found nothing to open: the pointer
/// resting there does not ask again every frame (look.noteLookHover).