summaryrefslogtreecommitdiff
path: root/src/look.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 17:25:24 -0300
committerGabriel Schneider <[email protected]>2026-10-01 17:30:33 -0300
commit1a33ed7221733b181726c7ad01e4ce190bcf1d05 (patch)
tree7a3990fae122ca9e5dcaf5393686814530489a6d /src/look.zig
parentff102e082105c78f4cc27487f550ee84d364c16c (diff)
downloadpardes-1a33ed7221733b181726c7ad01e4ce190bcf1d05.tar.gz
pardes-1a33ed7221733b181726c7ad01e4ce190bcf1d05.zip
One Look path: Enter, Tab and a bare Look take what a click at the cursor takes, a diff line's address included, and an event reader is told of them as of a click
The keyboard expanded the cursor's word on its own (look.expandedSel), so a diff line's address (and a hunk line's code word, without its +/-) was the mouse's alone: Enter on `+line` looked for the text in the diff. Now exec.operandAt is the one expansion: pointerOperand is it with a gesture's selection borrowed, the Enter/Tab path and a bare Look (exec.currentOperand) are it from the cursor, and both dispatch through mouse.dispatchPointerBuiltin, so a pane with an event reader hears a key's look or exec as a click's. A test runs one diff line and one file:12:5: line through a B3 click, a B3 sweep, Enter, a bare Look, the look file and an event write-back, and asserts each lands in the same file, line and column. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/look.zig')
-rw-r--r--src/look.zig93
1 files changed, 93 insertions, 0 deletions
diff --git a/src/look.zig b/src/look.zig
index 115dabe1..a358b31a 100644
--- a/src/look.zig
+++ b/src/look.zig
@@ -2426,6 +2426,99 @@ test "a click, a 9P whole-line look and an empty event write-back take one expan
}
}
+test "every entry point to Look takes one expansion: a diff line and a file:12:5: line land in one place by each" {
+ if (!platform_has_fs) return;
+ const numbered = "l1\nl2\nl3\nl4\nl5\nl6\nl7\nl8\nl9\nl10\nl11\nline 12 here\nl13\n";
+ const diff_text =
+ \\diff --git a/src/x.zig b/src/x.zig
+ \\--- a/src/x.zig
+ \\+++ b/src/x.zig
+ \\@@ -11,2 +11,3 @@
+ \\ l11
+ \\-old
+ \\+line 12 here
+ \\
+ ;
+ const log_text = "src/x.zig:12:5: error: bad\n";
+ var fx: DiffFixture = undefined;
+ try fx.init(true, &.{.{ "src/x.zig", numbered }}, diff_text);
+ defer fx.tmp.cleanup();
+ const th = @import("ninep/testing.zig");
+ // `column` -1: wherever a click puts it, every other way the same.
+ const Case = struct { text: []const u8, name: []const u8, row: u16, col: u16, sweep: [2]u16, line: i32, column: i32 };
+ const cases = [_]Case{
+ // `+line 12 here` pointed at on its `+`: the hunk's new line 12.
+ .{ .text = diff_text, .name = "change.diff", .row = 6, .col = 0, .sweep = .{ 0, 12 }, .line = 11, .column = -1 },
+ // A compiler's place, its trailing `:` dropped: line 12, column 5.
+ .{ .text = log_text, .name = "build.log", .row = 0, .col = 2, .sweep = .{ 0, 14 }, .line = 11, .column = 4 },
+ };
+ for (cases) |case| {
+ const p = try Pardes.init(std.testing.allocator, .{ .cols = 100, .rows = 30 });
+ defer p.deinit();
+ const pane = try fx.open(p, case.text);
+ p.gpa.free(pane.file.?.path);
+ pane.file.?.path = try std.fs.path.join(p.gpa, &.{ fx.dir, case.name });
+ pane.tag_expanded = false;
+ var frame = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer frame.deinit();
+ const Entry = enum { click, sweep, enter, bare_look, look_file, event_write_back };
+ var column = case.column;
+ for (std.enums.values(Entry)) |entry| {
+ // From the pane, its cursor on the place, nothing selected,
+ // the target (once open) moved off it.
+ for (p.panes[1..]) |slot| if (slot) |other| {
+ other.body.cur_row = 0;
+ other.body.cur_col = 0;
+ };
+ p.active = 0;
+ pane.body.mode = .normal;
+ pane.body.vsel.active = false;
+ pane.body.msel.active = false;
+ pane.body.cur_row = case.row;
+ pane.body.cur_col = case.col;
+ pane.body.cur_pinned = true;
+ pane.file.?.scroll = 0;
+ p.sync();
+ _ = frame.reset(.retain_capacity);
+ _ = try p.render(frame.allocator());
+ const rect = p.rects[0];
+ const x0 = rect.x + config.GUTTER + panes.File.gutterWidth(pane);
+ const y = p.bodyTop(pane, rect) + case.row;
+ switch (entry) {
+ .click => {
+ p.update(.{ .mouse = .{ .button = .right, .kind = .press, .col = x0 + case.col, .row = y } });
+ p.update(.{ .mouse = .{ .button = .right, .kind = .release, .col = x0 + case.col, .row = y } });
+ },
+ .sweep => {
+ p.update(.{ .mouse = .{ .button = .right, .kind = .press, .col = x0 + case.sweep[0], .row = y } });
+ p.update(.{ .mouse = .{ .button = .right, .kind = .drag, .col = x0 + case.sweep[1], .row = y } });
+ p.update(.{ .mouse = .{ .button = .right, .kind = .release, .col = x0 + case.sweep[1], .row = y } });
+ },
+ .enter => p.update(.{ .key = .{ .cp = Key.enter } }),
+ .bare_look => _ = p.executeBuiltinLine(0, "Look"),
+ .look_file => {
+ const line = modal.lineSlice(case.text, case.row);
+ // A diff line written whole; a place, as a script writes it.
+ const written = if (std.mem.eql(u8, case.name, "change.diff")) line else line[case.sweep[0] .. case.sweep[1] + 1];
+ _ = th.wr(p, @intFromEnum(pardes.ctlfs.TopFile.look), written);
+ },
+ .event_write_back => {
+ const body = pane.file.?.content;
+ const text = pardes.ctlfs.events.expandAt(p, pane, body, modal.lineStartOffset(body, case.row) + case.col, true);
+ lookAt(p, 0, p.scratch.allocator().dupe(u8, text) catch unreachable);
+ },
+ }
+ const landed = p.panes[p.active].?;
+ const f = landed.file orelse return error.NothingOpened;
+ if (column < 0) column = landed.body.cur_col;
+ if (!std.mem.endsWith(u8, f.path, "/src/x.zig") or landed.body.cur_row != case.line or landed.body.cur_col != column) {
+ std.debug.print("{s} by {s}: at {s} {d}:{d}\n", .{ case.name, @tagName(entry), f.path, landed.body.cur_row, landed.body.cur_col });
+ return error.EntryPointsDiffer;
+ }
+ }
+ }
+}
+
test "Look outside a diff pane expands and looks as ever" {
const p = try Pardes.init(std.testing.allocator, .{ .cols = 100, .rows = 30 });
defer p.deinit();