about summary refs log tree commit diff
path: root/src/main.zig
diff options
context:
space:
mode:
authorRobert Günzler <r@gnzler.io>2026-09-23 22:09:26 +0200
committerRobert Günzler <r@gnzler.io>2026-10-02 10:39:47 +0200
commit84b245cfb7a0ab1eff77cd4be7aa66d9089dc3ac (patch)
tree478ef39ca26aefa89bb6193c6f65b1cf25ce0f77 /src/main.zig
parent7585af4ec6d41cff40a872d055bcb871a5057a85 (diff)
downloadsite-84b245cfb7a0ab1eff77cd4be7aa66d9089dc3ac.tar.gz
support backrefs
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'src/main.zig')
-rw-r--r--src/main.zig77
1 files changed, 76 insertions, 1 deletions
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 {