summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 06:47:48 -0300
committerGabriel Schneider <[email protected]>2026-10-01 07:16:43 -0300
commit24dbdf73f92cd0a4b5ba130abe182cbcf9b3e472 (patch)
tree78d7594b6d0325c3671296327c8c7eda826702bd /src
parent5d66ae0b2864dea14c2e3906bfc5c93a727c5518 (diff)
downloadpardes-24dbdf73f92cd0a4b5ba130abe182cbcf9b3e472.tar.gz
pardes-24dbdf73f92cd0a4b5ba130abe182cbcf9b3e472.zip
`@pN:` looks at pane N, its trailing colon dropped as any look's is, and a line past the end is said one way: `<file>:N has no line N`, for `:N` as for `file:N`
lookAt dropped a trailing `:` from what it read but resolved the target from the text as written, so `@p33:` was `no match for "@p33:"`. And `:99999` said `address out of range` while `file:99999` said `has no line 99999`: a bare line number past the end now says the latter too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/look.zig14
-rw-r--r--src/ninep/ctl.zig16
2 files changed, 29 insertions, 1 deletions
diff --git a/src/look.zig b/src/look.zig
index 3ae0578c..c77119c5 100644
--- a/src/look.zig
+++ b/src/look.zig
@@ -1467,6 +1467,16 @@ fn lookAddress(p: *Pardes, id: usize, path: []const u8, expr: []const u8) void {
// the address in goes again, the miss said from the pane it came from.
const opened_here = open == null and target != id;
const r = a.address(ninep_pane.dotOf(pane)) orelse {
+ // A line past the end says so as `file:N` does, whichever way it
+ // was written: `<file>:N has no line N`.
+ if (std.fmt.parseInt(usize, expr, 10)) |line| {
+ var placed: [miss_path_cap + "…".len + 24]u8 = undefined;
+ const place = std.fmt.bufPrint(&placed, "{s}:{d}", .{ name, line }) catch name;
+ if (!opened_here) return missedLine(p, target, place, line);
+ p.removePane(target, null) catch {};
+ if (p.panes[id] != null) p.active = id;
+ return missedLine(p, id, place, line);
+ } else |_| {}
if (opened_here) return closeAndMiss(p, id, target, name, a.err);
return missed(p, target, "{s}: {s}", .{ name, a.err });
};
@@ -1719,7 +1729,9 @@ pub fn lookAt(p: *Pardes, id: usize, operand_raw: []const u8) void {
if (comptime pdf_enabled) if (pane.hasPdf()) clearNavigationSelection(p, pane);
if (comptime pdf_enabled) if (panes.Pdf.lookSection(p, id, pl.path, pl.at)) return;
var realbuf: [4096]u8 = undefined;
- const found = resolveLookTarget(p, id, txt, &realbuf);
+ // What is looked for is the place without its trailing `:`, `@p33:`
+ // and `a.txt:` as much as `a.txt:2:`.
+ const found = resolveLookTarget(p, id, trimmed, &realbuf);
// A line past the end of an open pane is a miss (the target's line is
// already clamped, so the one written is asked).
if (found == .pane and found.pane.id < MAX_PANES) if (p.panes[found.pane.id]) |shown| {
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 1bf916a9..70ed51a7 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -4149,3 +4149,19 @@ test "Edit on an image pane is refused as a data write there is, EINVAL, before
try testing.expectEqualStrings(data.reply.ename, edit.reply.ename);
try testing.expectEqual(E.INVAL, wr(p, Node.of(doc.serial, .name), "/tmp/other.ppm\n").errno());
}
+
+test "`@pN:` looks at pane N as `a.txt:` looks at a.txt, and a line past the end is said one way, `:N` or `file:N`" {
+ const p = try withFile(testing.allocator, "one\ntwo\n");
+ defer p.deinit();
+ const s = serialOf(p);
+ var buf: [64]u8 = undefined;
+ p.look_missed = false;
+ pardes.look.lookAt(p, 0, try std.fmt.bufPrint(&buf, "@p{d}:", .{s}));
+ try testing.expect(!p.look_missed);
+ const f = pane_files.fileOf(p.panes[0].?).?;
+ for ([_][]const u8{ ":99999", try std.fmt.bufPrint(&buf, "{s}:99999", .{f.path}) }) |looked| {
+ pardes.look.lookAt(p, 0, looked);
+ const said = p.panes[0].?.msg[0..p.panes[0].?.msg_len];
+ try testing.expect(std.mem.endsWith(u8, said, "/test.txt:99999 has no line 99999"));
+ }
+}