Skip to content

Commit

Permalink
Render footnotes (#90)
Browse files Browse the repository at this point in the history
* Render footnotes

* More flexible footnote rendering

* Make footnotes optional and fix docgen

* Return correct type from footnotes?.call

* Fix incorrect footnotes? example
  • Loading branch information
brandondyck authored Dec 2, 2024
1 parent 5daeb84 commit cb8a17d
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 17 deletions.
3 changes: 3 additions & 0 deletions src/context.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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))),
};
}
Expand Down
93 changes: 93 additions & 0 deletions src/context/Page.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 =
\\<ctx :if="$page.footnotes?()">
\\ <ol :loop="$if">
\\ <li id="$loop.it.def_id">
\\ <ctx :html="$loop.it.html()"></ctx>
\\ <ctx :loop="$loop.it.ref_ids">
\\ <a href="$loop.it.prefix('#')" :html="$loop.idx"></a>
\\ </ctx>
\\ </li>
\\ </ol>
\\</ctx>
;
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 =
Expand Down
42 changes: 30 additions & 12 deletions src/context/doctypes.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub const ScriptyParam = union(enum) {
Asset,
Alternative,
ContentSection,
Footnote,
Iterator,
Array,
String,
Expand All @@ -51,18 +52,24 @@ 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,
Bool,
Date,
KV,
any,
Many: Base2,

pub const Base2 = enum {
Footnote,
};
};

pub fn fromType(t: type) ScriptyParam {
Expand All @@ -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 },
Expand All @@ -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 },
Expand All @@ -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),
}
Expand All @@ -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}"))
Expand All @@ -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),
}
Expand Down
2 changes: 1 addition & 1 deletion src/exes/docgen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub fn main() !void {
\\---
\\
);
try w.print("{}", .{ref});
try w.writeAll(std.fmt.comptimePrint("{}", .{ref}));
try buf_writer.flush();
}

Expand Down
20 changes: 16 additions & 4 deletions src/render/html.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -162,9 +161,22 @@ pub fn html(
.enter => try w.print("<hr>", .{}),
.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("<sup class=\"footnote-ref\"><a href=\"#{s}\" id=\"{s}\">{d}</a></sup>", .{
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(
Expand Down

0 comments on commit cb8a17d

Please sign in to comment.