Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render footnotes #90

Merged
merged 5 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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