diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 08:22:47 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 08:53:11 -0300 |
| commit | f3070c04d3eaa0e0d3965478781c2d0b456446bf (patch) | |
| tree | 56cc5dd8138f7fd83755133798253ec2220ecd32 | |
| parent | c5c9768bed2368c6a341d0400b13b3428c138b6e (diff) | |
| download | pardes-f3070c04d3eaa0e0d3965478781c2d0b456446bf.tar.gz pardes-f3070c04d3eaa0e0d3965478781c2d0b456446bf.zip | |
A PDF place `:P:H` whose search hit H is not on page P, or with no search, is a miss with an err, as a page past the last is
The second number of a PDF place is a hit of the pane's search (its
search rows are `file.pdf:P:H`), and one that named no hit turned to
page P silently. pdf State.hitExists asks the pane's search for page P:
a hit it does not have, or any hit with no search, is now `<file> has
no search hit H on page P`, on the pane itself (`:P:H`) and from
anywhere (`file.pdf:P:H`); a search row's hit is always there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
| -rw-r--r-- | src/look.zig | 26 | ||||
| -rw-r--r-- | src/pdf_view.zig | 10 | ||||
| -rw-r--r-- | test/pdf.zig | 10 |
3 files changed, 41 insertions, 5 deletions
diff --git a/src/look.zig b/src/look.zig index 11c77760..0f5103ba 100644 --- a/src/look.zig +++ b/src/look.zig @@ -1260,6 +1260,20 @@ fn missedPage(p: *Pardes, id: usize, path: []const u8, page: usize) void { missed(p, id, "{s} has no page {d}", .{ missText(path, &cut), page }); } +/// `file.pdf:P:H` names hit H of the pane's search on page P: none there, +/// or no search, is a miss as a page past the last is. +fn missedHit(p: *Pardes, id: usize, path: []const u8, page: usize, hit: usize) void { + var cut: [miss_path_cap + "…".len]u8 = undefined; + missed(p, id, "{s} has no search hit {d} on page {d}", .{ missText(path, &cut), hit, page }); +} + +/// The open PDF pane showing `path`, if one does. +fn pdfPaneOf(p: *Pardes, path: []const u8) ?*panes.Pdf.State { + if (comptime !pdf_enabled) return null; + for (p.panes) |slot| if (slot) |pane| if (pane.pdf) |*pv| if (std.mem.eql(u8, pv.path, path)) return pv; + return null; +} + fn missedLine(p: *Pardes, id: usize, path: []const u8, line: anytype) void { var cut: [miss_path_cap + "…".len]u8 = undefined; missed(p, id, "{s} has no line {d}", .{ missText(path, &cut), line }); @@ -1505,6 +1519,7 @@ fn lookPdfPage(p: *Pardes, id: usize, target: usize, pv: anytype, expr: []const return missed(p, id, no_text, .{missText(pv.path, &cut)}) else 0; if (page == 0) return missedPage(p, id, pv.path, 0); if (page > pv.page_count) return missedPage(p, id, pv.path, page); + if (!pv.hitExists(p.pdf_gpa, page, hit)) return missedHit(p, id, pv.path, page, hit); const pane = p.panes[target].?; clearNavigationSelection(p, pane); if (pv.focusLocation(p.pdf_gpa, page, hit)) panes.Pdf.resetPageChrome(pane); @@ -1761,8 +1776,10 @@ pub fn lookAt(p: *Pardes, id: usize, operand_raw: []const u8) void { // A PDF's page past its last is a miss, as a line past a file's // end is: never the last page in its place (opening one, the // same: Pdf.openPane's .refuse). - if (comptime pdf_enabled) if (target.pdf) |pv| if (t.at.line > pv.page_count) - return missedPage(p, id, pv.path, t.at.line); + if (comptime pdf_enabled) if (target.pdf) |*pv| { + if (t.at.line > pv.page_count) return missedPage(p, id, pv.path, t.at.line); + if (!pv.hitExists(p.pdf_gpa, @max(1, t.at.line), t.at.col)) return missedHit(p, id, pv.path, @max(1, t.at.line), t.at.col); + }; const before = p.active; clearNavigationSelection(p, target); focusPaneLine(p, t.id, t.at, .center); @@ -1791,6 +1808,11 @@ pub fn lookAt(p: *Pardes, id: usize, operand_raw: []const u8) void { }, .file => |target| { if (comptime pdf_enabled) if (target.kind == .pdf) { + // A hit of a search: one of the open pane's, checked by + // focusPaneByPath's pane; a PDF not open has none. + if (target.at.col > 0) if (pdfPaneOf(p, target.path)) |pv| { + if (!pv.hitExists(p.pdf_gpa, @max(1, target.at.line), target.at.col)) return missedHit(p, id, target.path, @max(1, target.at.line), target.at.col); + } else return missedHit(p, id, target.path, @max(1, target.at.line), target.at.col); if (focusPaneByPath(p, target.path, target.at)) return; const free = p.freeSlot() orelse return p.reportError(id, "look", error.NoPaneSlots); // Said with its path and why, as an image that cannot be read diff --git a/src/pdf_view.zig b/src/pdf_view.zig index b307d1f3..d5769ad3 100644 --- a/src/pdf_view.zig +++ b/src/pdf_view.zig @@ -547,6 +547,16 @@ pub const State = if (enabled) struct { state.search_results = results; } + /// Whether page `page` (one-based) holds search hit `hit` (one-based) of + /// the pane's search: none asked, no hit is there. + pub fn hitExists(state: *@This(), gpa: std.mem.Allocator, page: usize, hit: usize) bool { + if (hit == 0) return true; + if (state.search_query.len == 0 or page == 0 or page > state.page_count) return false; + var results = state.document.search(gpa, page - 1, state.search_query) catch return false; + defer results.deinit(gpa); + return hit <= results.hit_count; + } + pub fn ensureOutline(state: *@This(), gpa: std.mem.Allocator) ?*const pdf.Outline { if (!state.outline_tried) { state.outline_tried = true; diff --git a/test/pdf.zig b/test/pdf.zig index a1be4075..00d678d4 100644 --- a/test/pdf.zig +++ b/test/pdf.zig @@ -492,12 +492,16 @@ test "`:N` on a PDF turns to page N, from the pane and from the root's look, and try std.testing.expect(std.mem.indexOf(u8, record, "\nerr ") == null); _ = p.serveFs(.{ .tag = 4, .op = .release, .node = log, .handle = h }); while (p.nextEffect()) |_| {} - // `:N:H` on the pane is its page N, as `file.pdf:N:H` is. + // `:N:H` on the pane is hit H of its search on page N, as `file.pdf:N:H` + // (a search row) is: with no search there is no hit 3, a miss that + // turns no page. p.look_missed = false; pardes.look.lookAt(p, 0, ":1"); pardes.look.lookAt(p, 0, ":2:3"); - try std.testing.expect(!p.look_missed); - try std.testing.expectEqual(@as(usize, 1), doc.pdf.?.page); + try std.testing.expect(p.look_missed); + try std.testing.expect(std.mem.indexOf(u8, doc.msg[0..doc.msg_len], "has no search hit 3 on page 2") != null); + try std.testing.expectEqual(@as(usize, 0), doc.pdf.?.page); + pardes.look.lookAt(p, 0, ":2"); // Page 0 is no page: the has-no-page miss. p.look_missed = false; pardes.look.lookAt(p, 0, ":0"); |
