summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pdf_view.zig15
-rw-r--r--test/pdf.zig37
2 files changed, 51 insertions, 1 deletions
diff --git a/src/pdf_view.zig b/src/pdf_view.zig
index 09c06356..b307d1f3 100644
--- a/src/pdf_view.zig
+++ b/src/pdf_view.zig
@@ -2248,7 +2248,14 @@ pub fn lookSection(
std.fmt.bufPrint(&joined_path, "{s}{s}{s}", .{ output_dir, separator, path }) catch return true;
if (!std.mem.eql(u8, owner.state.path, target_path)) return true;
- const destination = owner.state.sectionDestination(core.pdf_gpa, at.col - 1) orelse return true;
+ // A row is `file:PAGE:SECTION`, its first number the page as in every
+ // other location: a section there is none, or one on another page, is
+ // a miss said, never a silent jump.
+ var said: [160]u8 = undefined;
+ const destination = owner.state.sectionDestination(core.pdf_gpa, at.col - 1) orelse
+ return failSection(core, output_id, std.fmt.bufPrint(&said, "PdfSections: {s} has no section {d}", .{ std.fs.path.basename(owner.state.path), at.col }) catch "PdfSections: no such section");
+ if (destination == .internal and destination.internal.page + 1 != at.line)
+ return failSection(core, output_id, std.fmt.bufPrint(&said, "PdfSections: section {d} of {s} is on page {d}, not {d}", .{ at.col, std.fs.path.basename(owner.state.path), destination.internal.page + 1, at.line }) catch "PdfSections: that section is on another page");
switch (destination) {
.internal => |internal| {
look.clearNavigationSelection(core, owner.pane);
@@ -2261,6 +2268,12 @@ pub fn lookSection(
return true;
}
+fn failSection(core: *pardes.Pardes, id: usize, text: []const u8) bool {
+ core.look_missed = true;
+ core.reportFailure(id, text);
+ return true;
+}
+
pub fn resetPageChrome(pane: *pardes.Pane) void {
pane.body.cur_row = 0;
pane.body.cur_col = 0;
diff --git a/test/pdf.zig b/test/pdf.zig
index d1dd5bb0..d91f50a7 100644
--- a/test/pdf.zig
+++ b/test/pdf.zig
@@ -315,6 +315,43 @@ test "PDF dump fallback remains a byte-preserving file" {
try std.testing.expectEqualStrings(" Keep Del", pane.tag.own.?);
}
+test "a +PdfSections row's first number is the page: a section not there, or on another page, is a miss said" {
+ 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();
+ panes.Pdf.openSections(p, 0);
+ const out_id = p.panes[0].?.search_pane orelse return error.MissingPdfSectionsOutput;
+ const out = p.panes[out_id].?;
+ const row = std.mem.sliceTo(out.file.?.content, '\n');
+ const place = row[0 .. std.mem.indexOfScalar(u8, row, ' ') orelse row.len]; // outline.pdf:P:1
+ var fields = std.mem.splitScalar(u8, place, ':');
+ _ = fields.next();
+ const page = try std.fmt.parseInt(usize, fields.next().?, 10);
+ var line: [128]u8 = undefined;
+ // As listed: found, no miss.
+ p.look_missed = false;
+ pardes.look.lookAt(p, out_id, place);
+ try std.testing.expect(!p.look_missed);
+ // The section on another page than the row says, and one not there.
+ for ([_][]const u8{ try std.fmt.bufPrint(&line, "outline.pdf:{d}:1", .{page + 1}), "outline.pdf:1:999" }) |bad| {
+ var copy: [128]u8 = undefined;
+ @memcpy(copy[0..bad.len], bad);
+ p.look_missed = false;
+ const failures = p.fs.failures;
+ pardes.look.lookAt(p, out_id, copy[0..bad.len]);
+ try std.testing.expect(p.look_missed);
+ try std.testing.expect(p.fs.failures != failures);
+ }
+}
+
test "PdfSections Look follows the exact owning PDF, not an equal path" {
if (!pdf_enabled or platform == .web) return;
const gpa = std.testing.allocator;