diff options
| author | Robert Günzler <r@gnzler.io> | 2026-09-23 22:09:26 +0200 |
|---|---|---|
| committer | Robert Günzler <r@gnzler.io> | 2026-10-02 10:39:47 +0200 |
| commit | 84b245cfb7a0ab1eff77cd4be7aa66d9089dc3ac (patch) | |
| tree | 478ef39ca26aefa89bb6193c6f65b1cf25ce0f77 | |
| parent | 7585af4ec6d41cff40a872d055bcb871a5057a85 (diff) | |
| download | site-84b245cfb7a0ab1eff77cd4be7aa66d9089dc3ac.tar.gz | |
support backrefs
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to '')
| -rw-r--r-- | links/main.css | 6 | ||||
| -rw-r--r-- | src/inc/meta.baseof.htm | 1 | ||||
| -rw-r--r-- | src/main.zig | 77 |
3 files changed, 83 insertions, 1 deletions
diff --git a/links/main.css b/links/main.css index 7467bf2..109d5b6 100644 --- a/links/main.css +++ b/links/main.css @@ -280,6 +280,12 @@ main .footnote-back { margin-left: 1ch; } +/* backrefs */ + +main section#backrefs ul { + list-style-type: none; +} + /* footer */ footer { diff --git a/src/inc/meta.baseof.htm b/src/inc/meta.baseof.htm index 462d279..6b47717 100644 --- a/src/inc/meta.baseof.htm +++ b/src/inc/meta.baseof.htm @@ -21,6 +21,7 @@ </header> <main> {[builtin.fragment]} + <section id="backrefs"><!-- no backrefs --></section> </main> <footer> Edited on 2026-04-08 <a href="https://git.sr.ht/~robertgzr/site/tree/master/src/inc/{[builtin.path]}" target="_blank">[src]</a> diff --git a/src/main.zig b/src/main.zig index 6529f8d..39106f4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -151,7 +151,11 @@ const Cli = struct { try b.build(io, gpa); } - fn gen(io: Io, gpa: Allocator, flags: FlagSet, args: [][]const u8) !void { + fn gen(io: Io, parent_gpa: Allocator, flags: FlagSet, args: [][]const u8) !void { + var arena = std.heap.ArenaAllocator.init(parent_gpa); + defer arena.deinit(); + const gpa = arena.allocator(); + if (flags.contains("--force")) FORCE = true; if (flags.contains("-f")) FORCE = true; @@ -203,6 +207,9 @@ const Cli = struct { var htmProgress = if (!DEBUG) progress.start("Generating html", work.items.len); defer if (!DEBUG) htmProgress.end(); + var outgoing: std.StringHashMapUnmanaged([][]const u8) = .empty; + defer outgoing.deinit(gpa); + for (work.items) |path| { const md_name = try std.fmt.allocPrint(gpa, "{s}.md", .{std.fs.path.stem(path)}); defer gpa.free(md_name); @@ -244,8 +251,76 @@ const Cli = struct { const b = try Builder.init(gpa, root_dir, source, target_dir); defer b.deinit(gpa); try b.build(io, gpa); + + // make a deep copy of the refs list + const refs = try b.refs.clone(gpa); + for (refs.items, 0..) |r, i| refs.items[i] = try gpa.dupe(u8, r); + // track the refs for this source file + try outgoing.put(gpa, std.fs.path.stem(source.path), refs.items); } + // create incoming refs from outgoing refs + var incoming: std.StringHashMapUnmanaged([][]const u8) = .empty; + try incoming.ensureTotalCapacity(gpa, outgoing.size); + defer incoming.deinit(gpa); + + var outIter = outgoing.iterator(); + while (outIter.next()) |entry| { + for (entry.value_ptr.*) |ref| { + const res = incoming.getOrPutAssumeCapacity(ref); + if (res.found_existing) { + res.value_ptr.* = try gpa.realloc(res.value_ptr.*, res.value_ptr.len + 1); + res.value_ptr.*[res.value_ptr.len - 1] = entry.key_ptr.*; + } else { + res.value_ptr.* = try gpa.alloc([]const u8, 1); + res.value_ptr.*[0] = entry.key_ptr.*; + } + } + } + + var inIter = incoming.iterator(); + while (inIter.next()) |entry| { + if (entry.value_ptr.len == 0) continue; + + const target_name = try std.fmt.allocPrint(gpa, "{s}.html", .{entry.key_ptr.*}); + defer gpa.free(target_name); + + log.info("{s}: backrefs n={d}", .{target_name, entry.value_ptr.len}); + + var target = try File.init(io, target_dir, target_name, null); + defer target.close(io); + + const BACKREFS = "<!-- no backrefs -->"; + const offset = std.mem.indexOf(u8, target.mmap.memory, BACKREFS) orelse continue; + const remainder = try gpa.dupe(u8, target.mmap.memory[offset + BACKREFS.len ..]); + defer gpa.free(remainder); + + // create the backref list + const buf = blk: { + var buf = Io.Writer.Allocating.init(gpa); + defer buf.deinit(); + try buf.writer.writeAll("<p>incoming:</p>"); + try buf.writer.writeByte('\n'); + try buf.writer.writeByte('\n'); + try buf.writer.writeAll("<nav><ul>"); + try buf.writer.writeByte('\n'); + for (entry.value_ptr.*) |back_ref| { + const back_ref_name = try std.fmt.allocPrint(gpa, "{s}.html", .{back_ref}); + const back_ref_path = try std.fs.path.join(gpa, &.{ "/", target_path, back_ref_name }); + try buf.writer.print( + \\<li><a href="{s}">{s}</a></li> + , .{ back_ref_path, back_ref }); + try buf.writer.writeByte('\n'); + } + try buf.writer.writeAll("</ul></nav>"); + try buf.writer.writeByte('\n'); + break :blk try buf.toOwnedSlice(); + }; + + // insert it into the target document + try target.fd.writePositionalAll(io, buf, offset); + try target.fd.writePositionalAll(io, remainder, offset + buf.len); + } } fn serve(io: Io, _: Allocator, _: FlagSet, args: [][]const u8) !void { |