From 591d3c7baecc434527d02e5894ac0240d2fa1c8d Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 30 Sep 2026 21:10:00 -0300 Subject: :N on a PDF turns to page N, from the pane and from the root's look; @pN:N answers ok with no err; an address in a pane with no text names the pane A PDF pane took :N as a word to search for, and @p:N failed as "is no text to address" after the page had nothing to do with it, an err in the log for a look that meant something. A PDF's address is now its page: :N turns to it, one past the last is the has-no-page miss, and a byte offset or a pattern is refused saying a PDF's address is a page. The refusal for any other textless pane named an empty path when the look was a bare :addr; it names the image or @p now. Co-Authored-By: Claude Opus 5.5 --- src/look.zig | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'src/look.zig') diff --git a/src/look.zig b/src/look.zig index 3cd1faa5..4d23582b 100644 --- a/src/look.zig +++ b/src/look.zig @@ -1413,9 +1413,13 @@ fn lookAddress(p: *Pardes, id: usize, path: []const u8, expr: []const u8) void { } const pane = p.panes[target] orelse return; const f = pane.file orelse { + if (comptime pdf_enabled) if (pane.pdf) |*pv| return lookPdfPage(p, id, target, pv, expr); if (by_serial and pane.isTerminal()) return lookTerminalAddress(p, id, target, expr); + // Named as the pane is, the bare `:addr` (path empty) included. + var serial_name: [32]u8 = undefined; + const subject = if (pane.image) |iv| iv.path else if (path.len > 0) path else std.fmt.bufPrint(&serial_name, config.pane_addr ++ "{d}", .{pane.serial}) catch "the pane"; var cut: [miss_path_cap + "…".len]u8 = undefined; - return missed(p, id, "{s} is no text to address", .{missText(path, &cut)}); + return missed(p, id, "{s} is no text to address", .{missText(subject, &cut)}); }; if (!by_serial and path.len > 0 and (f.output != null or !std.mem.endsWith(u8, f.path, std.fs.path.basename(path)))) return; var a: Addr = .{ .text = f.content, .lim = null, .expr = expr }; @@ -1433,6 +1437,20 @@ fn lookAddress(p: *Pardes, id: usize, path: []const u8, expr: []const u8) void { p.active = target; } +/// A PDF's address is its page: `:N` turns to page N, one past its last a +/// miss. It has no text to hold a byte offset (`#N`) or a pattern: any +/// other address is refused with what it takes. +fn lookPdfPage(p: *Pardes, id: usize, target: usize, pv: anytype, expr: []const u8) void { + var cut: [miss_path_cap + "…".len]u8 = undefined; + const page = std.fmt.parseInt(usize, std.mem.trim(u8, expr, " \t"), 10) catch 0; + if (page == 0) return missed(p, id, "{s}: a PDF has no text to address; its address is a page, :N", .{missText(pv.path, &cut)}); + if (page > pv.page_count) return missedPage(p, id, pv.path, page); + const pane = p.panes[target].?; + clearNavigationSelection(p, pane); + if (pv.focusLocation(p.pdf_gpa, page, 0)) panes.Pdf.resetPageChrome(pane); + p.active = target; +} + /// `@p:
`: any address over the terminal's body as its /// logical lines read (the lines a +Search lists as `@p:`), /// from its cursor; the match is selected there, as a +Search row's is. @@ -1599,6 +1617,9 @@ pub fn lookAt(p: *Pardes, id: usize, operand_raw: []const u8) void { // acme's `file:addr` and `:addr` (look.c:450-543): an address after // the colon, evaluated in that file from its dot (openfile, look.c:675). if (addressed(trimmed)) |ad| return lookAddress(p, id, ad.path, ad.expr); + // `:N` on a PDF is its page N, never a search for the text. + if (comptime pdf_enabled) if (pane.hasPdf() and trimmed.len > 1 and trimmed[0] == ':') + return lookAddress(p, id, "", trimmed[1..]); const pl = parsePathLine(trimmed); // `file:3:0`: columns count from 1, as addr's do, and a zero is refused // in addr's words rather than taken as no column. -- cgit v1.3