summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 07:23:58 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commita63185292e80beac3848f4f6395c61d941f5144f (patch)
treee097eb6675db1af9a098e647a5b9f33c161c7ba4
parent97780909cbe0f4c62dfb9b012bff3fe7e7ec32f9 (diff)
downloadpardes-a63185292e80beac3848f4f6395c61d941f5144f.tar.gz
pardes-a63185292e80beac3848f4f6395c61d941f5144f.zip
A look that goes to another pane leaves the looking pane's dot alone
Every look that found something cleared the looker's selection first, so `@p5:2` or `f.txt:2` written to a pane's look collapsed its dot to q1-1. Only a look that stays in the pane starts from a clean selection now; one that goes elsewhere takes away just the click's highlight, and a miss changes nothing, as acme's look does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--src/look.zig15
-rw-r--r--src/ninep/ctl.zig24
-rw-r--r--test/gui-goldens.txt6
-rw-r--r--test/panes.zig9
4 files changed, 44 insertions, 10 deletions
diff --git a/src/look.zig b/src/look.zig
index 92e711fa..2f712fae 100644
--- a/src/look.zig
+++ b/src/look.zig
@@ -1381,9 +1381,16 @@ pub fn lookAt(p: *Pardes, id: usize, operand: []const u8) void {
// already clamped, so the one written is asked).
if (found == .pane and found.pane.id < MAX_PANES) if (p.panes[found.pane.id]) |shown| if (shown.file) |*f| if (pl.at.line > panes.File.nlines(p.gpa, f))
return missedLine(p, id, trimmed, pl.at.line);
- // A miss changes nothing, the selection included; anything else starts
- // from a clean one.
- if (found != .none) clearNavigationSelection(p, pane);
+ // A miss changes nothing, the selection included; a look that stays in
+ // this pane starts from a clean one. One that goes to another pane
+ // leaves this one's dot alone, as acme's does: only the click's own
+ // highlight goes.
+ if (found == .pane and found.pane.id == id)
+ clearNavigationSelection(p, pane)
+ else if (found != .none) {
+ for (0..pane.sel.len) |slot| pane.clearPointerSelection(slot);
+ pane.sel = @splat(.{});
+ }
switch (found) {
.none => {
// A raw terminal's clicks are its program's; a look written over
@@ -1717,6 +1724,6 @@ test "Look ignores missing and out of bounds pane addresses" {
lookAt(p, 0, operand);
try std.testing.expectEqual(@as(usize, 0), p.active);
try std.testing.expectEqual(@as(i32, 0), pane.body.cur_row);
- try std.testing.expect(!pane.body.vsel.active);
+ try std.testing.expect(pane.body.vsel.active); // a miss changes nothing, the selection included
}
}
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 709f017f..b8f73c6f 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -2024,6 +2024,30 @@ test "@p<serial>:<address> takes any sam address; a miss and an unknown serial a
try testing.expectEqualStrings("", rd(p, root, 0, 64).bytes);
}
+test "a look that goes to another pane leaves the looking pane's dot alone" {
+ const p = try withFile(testing.allocator, "alpha bravo\ncharlie\n");
+ defer p.deinit();
+ const source = serialOf(p);
+ const other = try th.newPane(p);
+ _ = wr(p, Node.of(other, .body), "one\ntwo\nthree\n");
+ var tmp = testing.tmpDir(.{});
+ defer tmp.cleanup();
+ try tmp.dir.writeFile(testing.io, .{ .sub_path = "f.txt", .data = "f one\nf two\n" });
+ var dir_buf: [4096]u8 = undefined;
+ const dir = dir_buf[0..try tmp.dir.realPath(testing.io, &dir_buf)];
+ var line: [4200]u8 = undefined;
+ for ([_][]const u8{ "@p{d}:2", "@p{d}:/three/", "{s}/f.txt:2" }) |form| {
+ @import("pane.zig").setDot(p.panes[p.paneBySerial(source).?].?, .{ .q0 = 6, .q1 = 11 });
+ const text = if (form[0] == '@')
+ (if (std.mem.eql(u8, form, "@p{d}:2")) try std.fmt.bufPrint(&line, "@p{d}:2\n", .{other}) else try std.fmt.bufPrint(&line, "@p{d}:/three/\n", .{other}))
+ else
+ try std.fmt.bufPrint(&line, "{s}/f.txt:2\n", .{dir});
+ try testing.expectEqual(Status.ok, wr(p, Node.of(source, .look), text).reply.status);
+ try testing.expect(p.active != p.paneBySerial(source).?);
+ try testing.expectEqualStrings(" 6 11 ", rd(p, Node.of(source, .dot), 0, 64).bytes);
+ }
+}
+
test "@p<terminal>:<address> takes any address over the terminal's lines, selecting the match" {
const p = try th.withTerm(testing.allocator);
defer p.deinit();
diff --git a/test/gui-goldens.txt b/test/gui-goldens.txt
index 8266abcc..9c380a3c 100644
--- a/test/gui-goldens.txt
+++ b/test/gui-goldens.txt
@@ -6,9 +6,9 @@
06-tag-focus-selection a781c95cebb8c47a5c5168e1b7f9747f44a0aad043cc4eec1338bb1f08d0a340
07-prompt-notice 03bdefcd99ccbf4849d2c2f6b561cee6c24263a1be70d55ecb9550a815506e0e
08-wide-glyph-edges c57df963702fb1747262c783542013cf2c5cb6dc5d001040c0006ced5a8d5aa6
-09-collapsed-focus-tint 807bd7621d54948b84cf2965ef463d3928d050259e8ee2d27472b1d3d378f91e
-10-tagbottom 03086852528d9c8a76f5926a7e7ee371200da617db279f42cbbb6825b5f41fce
-12-multiline-tag cd12edf85d9f472fc79aac91669f5d37619e0cc0ceedd1fe22d100faa127b685
+09-collapsed-focus-tint 8fc6bc5646669023946a3b595249a83aa95f7bfb5b43c18aa35bb971a9278a7c
+10-tagbottom d5b7e7c162c91a3beb15e1219266bf5c5b12a5c22ddf532730d456d15ccec479
+12-multiline-tag bd2bc9a5952cb6e78d3e38c37134ebe475d8fd582dbd6b3394c6e886be8cfde7
13-image f9412d7bbe0ebdb53bc62145c51ad3028d2f07cc3e9d2e52a3aeb89de7db6da3
14-theme-ink da6369bd141a2d71aabb91854e82da88026926e0c931d89947645c090896b5d3
15-theme-acme-light ca5252f5117ac8d6c679cef917c9f316c9ca311fc7a8dcbac2796b4fc04d3b64
diff --git a/test/panes.zig b/test/panes.zig
index 42dd6b77..3d2821aa 100644
--- a/test/panes.zig
+++ b/test/panes.zig
@@ -4853,7 +4853,7 @@ const LookResetTests = struct {
try std.testing.expectEqualStrings("needle", selected(pane));
}
- test "Look clears both source and existing destination selections before focus" {
+ test "Look clears the source's click highlight, not its dot, and the destination's selections before focus" {
const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 30 });
defer p.deinit();
const source = try p.setTestFile("source\nlines\n");
@@ -4872,9 +4872,12 @@ const LookResetTests = struct {
try seed(destination, "previous destination");
pardes.look.lookAt(p, 0, source.pointer_selections[0].?.raw_text.?);
try std.testing.expectEqual(id, p.active);
- try cleared(source);
+ // The source keeps its dot (acme's look leaves it); the click's
+ // highlight goes.
+ for (source.sel) |selection| try std.testing.expectEqual(.none, selection.state);
+ for (source.pointer_selections) |selection| try std.testing.expect(selection == null);
+ try std.testing.expect(source.body.vsel.active);
try cleared(destination);
- try std.testing.expect(!source.body.vsel.active);
if (range) {
try std.testing.expectEqual(@as(i32, 1), destination.body.cur_row);
try std.testing.expectEqualStrings("rav", selected(destination));