diff --git a/src/context.zig b/src/context.zig
index e9b17cb..c499c90 100644
--- a/src/context.zig
+++ b/src/context.zig
@@ -84,6 +84,7 @@ pub const Value = union(enum) {
ctx: Ctx(Value),
alternative: Page.Alternative,
content_section: Page.ContentSection,
+ footnote: Page.Footnote,
build: *const Build,
git: Git,
asset: Asset,
@@ -174,6 +175,7 @@ pub const Value = union(enum) {
*const Page, *Page => .{ .page = v },
Page.Alternative => .{ .alternative = v },
Page.ContentSection => .{ .content_section = v },
+ Page.Footnote => .{ .footnote = v },
*const Build => .{ .build = v },
Git => .{ .git = v },
Ctx(Value) => .{ .ctx = v },
@@ -213,6 +215,7 @@ pub const Value = union(enum) {
[]const Page.Alternative => try Array.init(gpa, Page.Alternative, v),
[]Page.ContentSection => try Array.init(gpa, Page.ContentSection, v),
+ []Page.Footnote => try Array.init(gpa, Page.Footnote, v),
else => @compileError("TODO: implement Value.from for " ++ @typeName(@TypeOf(v))),
};
}
diff --git a/src/context/Page.zig b/src/context/Page.zig
index 48152bd..d40423d 100644
--- a/src/context/Page.zig
+++ b/src/context/Page.zig
@@ -150,6 +150,52 @@ pub const Alternative = struct {
};
};
};
+
+pub const Footnote = struct {
+ def_id: []const u8,
+ ref_ids: []const []const u8,
+
+ _page: *const Page,
+ _idx: usize,
+
+ pub const description =
+ \\A footnote from a page.
+ ;
+ pub const Fields = struct {
+ pub const def_id =
+ \\The ID for the footnote definition.
+ ;
+ pub const ref_ids =
+ \\The IDs of the footnote's references,
+ \\to be used for creating backlinks.
+ ;
+ };
+ pub const Builtins = struct {
+ pub const html = struct {
+ pub const signature: Signature = .{ .ret = .String };
+ pub const description =
+ \\Renders the footnote definition.
+ ;
+ pub const examples = "";
+ pub fn call(
+ f: Footnote,
+ gpa: Allocator,
+ args: []const Value,
+ ) !Value {
+ if (args.len != 0) return .{ .err = "expected 0 arguments" };
+
+ var buf = std.ArrayList(u8).init(gpa);
+ const ast = f._page._meta.ast orelse unreachable;
+ const node = ast.footnotes.values()[f._idx].node;
+
+ try render.html(gpa, ast, node, "", buf.writer());
+ return String.init(try buf.toOwnedSlice());
+ }
+ };
+ };
+ pub const dot = scripty.defaultDot(Footnote, Value, false);
+};
+
pub const dot = scripty.defaultDot(Page, Value, false);
pub const PassByRef = true;
@@ -996,6 +1042,53 @@ pub const Builtins = struct {
}
};
+ pub const @"footnotes?" = struct {
+ pub const signature: Signature = .{
+ .params = &.{},
+ .ret = .{ .Opt = .{ .Many = .Footnote } },
+ };
+ pub const description =
+ \\Returns a list of footnotes for the current page, if any exist.
+ ;
+ pub const examples =
+ \\
+ \\
+ \\ -
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ ;
+ pub fn call(
+ p: *const Page,
+ gpa: Allocator,
+ args: []const Value,
+ ) !Value {
+ const bad_arg = .{
+ .err = "expected 0 arguments",
+ };
+ if (args.len != 0) return bad_arg;
+
+ const ast = p._meta.ast.?;
+ if (ast.footnotes.count() == 0) {
+ return Optional.Null;
+ }
+ var _footnotes = try gpa.alloc(Footnote, ast.footnotes.count());
+ for (ast.footnotes.values(), 0..) |footnote, i| {
+ _footnotes[i] = .{
+ .def_id = footnote.def_id,
+ .ref_ids = footnote.ref_ids,
+ ._page = p,
+ ._idx = i,
+ };
+ }
+ return Optional.init(gpa, _footnotes);
+ }
+ };
+
pub const toc = struct {
pub const signature: Signature = .{ .ret = .String };
pub const description =
diff --git a/src/context/doctypes.zig b/src/context/doctypes.zig
index 2788953..0f4ee7a 100644
--- a/src/context/doctypes.zig
+++ b/src/context/doctypes.zig
@@ -36,6 +36,7 @@ pub const ScriptyParam = union(enum) {
Asset,
Alternative,
ContentSection,
+ Footnote,
Iterator,
Array,
String,
@@ -51,11 +52,12 @@ pub const ScriptyParam = union(enum) {
Opt: Base,
Many: Base,
- pub const Base = enum {
+ pub const Base = union(enum) {
Site,
Page,
Alternative,
ContentSection,
+ Footnote,
Iterator,
String,
Int,
@@ -63,6 +65,11 @@ pub const ScriptyParam = union(enum) {
Date,
KV,
any,
+ Many: Base2,
+
+ pub const Base2 = enum {
+ Footnote,
+ };
};
pub fn fromType(t: type) ScriptyParam {
@@ -76,6 +83,7 @@ pub const ScriptyParam = union(enum) {
superhtml.utils.Ctx(context.Value) => .Ctx,
context.Page.Alternative => .Alternative,
context.Page.ContentSection => .ContentSection,
+ context.Page.Footnote => .Footnote,
context.Asset => .Asset,
// context.Slice => .any,
context.Optional, ?*const context.Optional => .{ .Opt = .any },
@@ -90,6 +98,8 @@ pub const ScriptyParam = union(enum) {
context.Iterator => .Iterator,
?*context.Iterator => .{ .Opt = .Iterator },
[]const context.Page.Alternative => .{ .Many = .Alternative },
+ []const context.Page.Footnote => .{ .Many = .Footnote },
+ ?[]const context.Page.Footnote => .{ .Opt = .{ .Many = .Footnote } },
[]const u8 => .String,
?[]const u8 => .{ .Opt = .String },
[]const []const u8 => .{ .Many = .String },
@@ -106,14 +116,17 @@ pub const ScriptyParam = union(enum) {
comptime is_fn_param: bool,
) []const u8 {
switch (p) {
- .Many => |m| switch (m) {
- inline else => |mm| {
+ inline .Many => |m| switch (m) {
+ inline else => {
const dots = if (is_fn_param) "..." else "";
- return "[" ++ @tagName(mm) ++ dots ++ "]";
+ return "[" ++ @tagName(m) ++ dots ++ "]";
},
},
.Opt => |o| switch (o) {
- inline else => |oo| return "?" ++ @tagName(oo),
+ .Many => |om| switch (om) {
+ inline else => |omm| return "?[" ++ @tagName(omm) ++ "]",
+ },
+ inline else => return "?" ++ @tagName(o),
},
inline else => return @tagName(p),
}
@@ -123,21 +136,26 @@ pub const ScriptyParam = union(enum) {
comptime is_fn_param: bool,
) []const u8 {
switch (p) {
- .Many => |m| switch (m) {
- inline else => |mm| {
+ inline .Many => |m| switch (m) {
+ inline else => {
const dots = if (is_fn_param) "..." else "";
return std.fmt.comptimePrint(
\\[[{0s}]($link.ref("{0s}")){1s}]{2s}
, .{
- @tagName(mm), dots, if (is_fn_param or mm == .any) "" else
+ @tagName(m), dots, if (is_fn_param or m == .any) "" else
\\ *(see also [[any]]($link.ref("Array")))*
});
},
},
- .Opt => |o| switch (o) {
- inline else => |oo| return comptime std.fmt.comptimePrint(
+ inline .Opt => |o| switch (o) {
+ inline .Many => |om| switch (om) {
+ inline else => |omm| return comptime std.fmt.comptimePrint(
+ \\?[[{0s}]($link.ref("{0s}"))]
+ , .{@tagName(omm)}),
+ },
+ inline else => return comptime std.fmt.comptimePrint(
\\?[{0s}]($link.ref("{0s}"))
- , .{@tagName(oo)}),
+ , .{@tagName(o)}),
},
inline else => |_, t| return comptime std.fmt.comptimePrint(
\\[{0s}]($link.ref("{0s}"))
@@ -148,7 +166,7 @@ pub const ScriptyParam = union(enum) {
pub fn id(p: ScriptyParam) []const u8 {
switch (p) {
.Opt, .Many => |o| switch (o) {
- inline else => |oo| return @tagName(oo),
+ inline else => return @tagName(o),
},
inline else => return @tagName(p),
}
diff --git a/src/exes/docgen.zig b/src/exes/docgen.zig
index bb55290..291e4bf 100644
--- a/src/exes/docgen.zig
+++ b/src/exes/docgen.zig
@@ -42,7 +42,7 @@ pub fn main() !void {
\\---
\\
);
- try w.print("{}", .{ref});
+ try w.writeAll(std.fmt.comptimePrint("{}", .{ref}));
try buf_writer.flush();
}
diff --git a/src/render/html.zig b/src/render/html.zig
index 517ce4d..878534b 100644
--- a/src/render/html.zig
+++ b/src/render/html.zig
@@ -17,8 +17,7 @@ pub fn html(
path: []const u8,
w: anytype,
) !void {
- var it = Iter.init(ast.md.root);
- it.reset(start, .enter);
+ var it = Iter.init(start);
const full_page = start.n == ast.md.root.n;
@@ -162,9 +161,22 @@ pub fn html(
.enter => try w.print("
", .{}),
.exit => {},
},
+ .FOOTNOTE_REFERENCE => switch (ev.dir) {
+ .enter => {
+ const literal = node.literal().?;
+ const def_idx = ast.footnotes.getIndex(literal).?;
+ const footnote = ast.footnotes.values()[def_idx];
+ try w.print("", .{
+ footnote.def_id,
+ footnote.ref_ids[@intCast(node.footnoteRefIx() - 1)],
+ def_idx + 1,
+ });
+ },
+ .exit => {},
+ },
.FOOTNOTE_DEFINITION => switch (ev.dir) {
- .enter => @panic("TODO: FOOTNOTE_DEFINITION"),
- .exit => @panic("TODO: FOOTNOTE_DEFINITION"),
+ .enter => {},
+ .exit => {},
},
.HTML_INLINE => switch (ev.dir) {
.enter => try w.print(