diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 17:25:24 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 17:30:33 -0300 |
| commit | 1a33ed7221733b181726c7ad01e4ce190bcf1d05 (patch) | |
| tree | 7a3990fae122ca9e5dcaf5393686814530489a6d /src/look.zig | |
| parent | ff102e082105c78f4cc27487f550ee84d364c16c (diff) | |
| download | pardes-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.zig | 93 |
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(); |
