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 --- test/pdf.zig | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'test/pdf.zig') diff --git a/test/pdf.zig b/test/pdf.zig index fbd580b3..fe084cc1 100644 --- a/test/pdf.zig +++ b/test/pdf.zig @@ -407,6 +407,51 @@ test "Recent keeps a PDF as it keeps a file: open, then closed at its page" { try std.testing.expect(std.mem.indexOf(u8, out.written(), try std.fmt.bufPrint(&want, "{s}:1:1 open\n", .{path})) != null); } +test "`:N` on a PDF turns to page N, from the pane and from the root's look, and `@pN:N` logs no err" { + if (!pdf_enabled or platform == .web) return; + const gpa = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + const fixture = try pdf_impl.makeOutlineTestPdf(gpa); + defer gpa.free(fixture); + try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "outline.pdf", .data = fixture }); + var path_buf: [256]u8 = undefined; + const path = try std.fmt.bufPrint(&path_buf, ".zig-cache/tmp/{s}/outline.pdf", .{tmp.sub_path}); + const p = try Pardes.init(gpa, .{ .file = path, .cols = 80, .rows = 28 }); + defer p.deinit(); + const doc = p.panes[0].?; + const pages = doc.pdf.?.page_count; + try std.testing.expect(pages >= 2); + const failures = p.fs.failures; + pardes.look.lookAt(p, 0, ":2"); + try std.testing.expect(!p.look_missed); + try std.testing.expectEqual(@as(usize, 1), doc.pdf.?.page); + var line: [64]u8 = undefined; + pardes.look.lookAt(p, 0, try std.fmt.bufPrint(&line, "@p{d}:1", .{doc.serial})); + try std.testing.expect(!p.look_missed); + try std.testing.expectEqual(@as(usize, 0), doc.pdf.?.page); + try std.testing.expectEqual(failures, p.fs.failures); + // Over 9P too: the root's look at `@pN:2` answers ok and logs no err. + const ctlfs = pardes.ctlfs; + const written = p.serveFs(.{ .tag = 1, .op = .write, .node = @intFromEnum(ctlfs.TopFile.look), .data = try std.fmt.bufPrint(&line, "@p{d}:2\n", .{doc.serial}) }); + try std.testing.expectEqual(ctlfs.Status.ok, written.status); + try std.testing.expectEqual(@as(usize, 1), doc.pdf.?.page); + const log = @intFromEnum(ctlfs.TopFile.log); + const h = p.serveFs(.{ .tag = 2, .op = .open, .node = log }).handle; + const record = p.fsPayload(p.serveFs(.{ .tag = 3, .op = .read, .node = log, .handle = h, .size = 1 << 16 })); + try std.testing.expect(std.mem.indexOf(u8, record, "\nerr ") == null); + _ = p.serveFs(.{ .tag = 4, .op = .release, .node = log, .handle = h }); + while (p.nextEffect()) |_| {} + // A byte offset or a pattern is no PDF address: refused with words. + pardes.look.lookAt(p, 0, ":#3"); + try std.testing.expect(p.look_missed); + try std.testing.expect(std.mem.indexOf(u8, doc.msg[0..doc.msg_len], "its address is a page, :N") != null); + p.look_missed = false; + pardes.look.lookAt(p, 0, try std.fmt.bufPrint(&line, ":{d}", .{pages + 1})); + try std.testing.expect(p.look_missed); + try std.testing.expectEqual(@as(usize, 1), doc.pdf.?.page); // where the 9P look left it +} + test "a look at a PDF's page past its last is a miss, never the last page" { if (!pdf_enabled or platform == .web) return; const gpa = std.testing.allocator; -- cgit v1.3