Footnotes

This commit is contained in:
2026-07-14 18:59:50 +02:00
parent 3f680b5e22
commit 91671995d3
5 changed files with 120 additions and 3 deletions
+6 -3
View File
@@ -1,7 +1,7 @@
! Heading
!! Smolr heading
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam blandit dolor nibh, vitae tempor enim pretium non. Sed in condimentum turpis. Vivamus placerat molestie ullamcorper. Maecenas nec risus nibh. Nullam vel vestibulum sapien. Integer ante erat, aliquam id tellus in, malesuada finibus nunc. Sed porttitor ante orci, eget molestie nibh posuere nec.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam blandit dolor nibh, vitae tempor enim pretium non. Sed in condimentum turpis. Vivamus placerat molestie ullamcorper. Maecenas nec risus nibh. Nullam vel vestibulum sapien. Integer ante erat, aliquam id tellus in, malesuada finibus nunc. Sed porttitor ante orci, eget molestie nibh posuere nec.
#[Link on the right](http://example.com) >>
(http://example.com)[Example.com]# <<
@@ -26,12 +26,12 @@ The **VARIABLE** is $(variable)
- List item 1
. List item 2
1. Ordered list item
1. Ordered list item [#1]
> This is a sample blockquote
> Bon apetit!
Text under a blockquote
Text under a blockquote [#named]
**This *is* bold *and* italics *mixed** TOGETHER*
@@ -40,3 +40,6 @@ Text under a blockquote
| This << | is >> | a ^^ |
| table | row | with |
| some | ***content*** | in |
[&1] Ordered list item 2 not included
[&named] This is the definition for a named footnote
+19
View File
@@ -5,7 +5,12 @@ import "core:fmt"
print_html :: proc(token: ^Token) {
switch token.type {
case TokenType.Root:
has_footnotes := false
for child in token.children {
if child.type == .FootnoteDef && !has_footnotes {
fmt.print("<hr class=\"footnotes-sep\">\n")
has_footnotes = true
}
print_html(child)
}
@@ -168,6 +173,20 @@ print_html :: proc(token: ^Token) {
}
fmt.print("</div>")
case TokenType.FootnoteRef:
if id, ok := token.value.(string); ok {
fmt.printf("<a href=\"#fn-%s\" id=\"ref-%s\"><sup>%s</sup></a>", id, id, id)
}
case TokenType.FootnoteDef:
if id, ok := token.value.(string); ok {
fmt.printf("<div class=\"footnote\" id=\"fn-%s\"><a href=\"#ref-%s\">^</a> ", id, id)
for child in token.children {
print_html(child)
}
fmt.print("</div>\n")
}
case TokenType.BreakLine:
fmt.print("<br>\n")
+4
View File
@@ -55,6 +55,10 @@ print_token :: proc(token: ^Token, depth: int = 0) {
type_str = "Right"
case TokenType.Middle:
type_str = "Middle"
case TokenType.FootnoteDef:
type_str = "FootnoteDef"
case TokenType.FootnoteRef:
type_str = "FootnoteRef"
}
value_str := ""
+78
View File
@@ -24,6 +24,8 @@ TokenType :: enum {
Left,
Right,
Middle,
FootnoteDef,
FootnoteRef,
}
TokenValue :: union {
@@ -141,6 +143,18 @@ parse_header :: proc(line: string) -> (int, string) {
return lvl, line[lvl + 1:]
}
parse_footnote_def :: proc(line: string) -> (string, string, bool) {
if strings.has_prefix(line, "[&") {
idx := strings.index_byte(line, ']')
if idx != -1 {
id := line[2:idx]
text := strings.trim_space(line[idx + 1:])
return id, text, true
}
}
return "", "", false
}
StyleToggle :: struct {
amount: int,
type: TokenType,
@@ -240,6 +254,46 @@ pass_through_children :: proc(token: ^Token) {
parse_inline_style(token, '-', []StyleToggle{{2, .Strikethrough}})
}
parse_inline_footnotes :: proc(token: ^Token) {
if token.children != nil {
for i := 0; i < len(token.children); i += 1 {
parse_inline_footnotes(token.children[i])
}
}
if token.type == .Text {
str, ok := token.value.(string)
if !ok {
return
}
start_idx := strings.index(str, "[#")
if start_idx == -1 {
return
}
end_idx := strings.index_byte(str[start_idx:], ']')
if end_idx == -1 {
return
}
end_idx += start_idx
id := str[start_idx + 2:end_idx]
token.value = nil
if start_idx > 0 {
push_token(token, .Text, str[:start_idx])
}
push_token(token, .FootnoteRef, id)
if end_idx + 1 < len(str) {
rem := push_token(token, .Text, str[end_idx + 1:])
parse_inline_footnotes(rem)
}
}
}
add_breaklines :: proc(token: ^Token) {
if token.children == nil {
return
@@ -293,6 +347,13 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
continue
}
if id, ftext, ok := parse_footnote_def(line); ok {
current_token = push_token(&root, TokenType.FootnoteDef, id)
push_text(current_token, ftext)
current_token = &root
continue
}
if strings.has_prefix(text, "> ") {
if current_token.type != TokenType.Blockquote {
current_token = push_token(&root, TokenType.Blockquote, nil)
@@ -381,7 +442,24 @@ parse :: proc(lines: []string, allocator := context.allocator) -> Token {
for token in root.children {
pass_through_children(token)
parse_inline_footnotes(token)
}
// FOURTH PASS
footnotes: [dynamic]^Token
new_root_children: [dynamic]^Token
for child in root.children {
if child.type == .FootnoteDef {
append(&footnotes, child)
} else {
append(&new_root_children, child)
}
}
for child in footnotes {
append(&new_root_children, child)
}
root.children = new_root_children
return root
}
+13
View File
@@ -59,3 +59,16 @@ tr.table-sep td {
padding: 0;
height: 0;
}
hr.footnotes-sep {
border: 0;
border-top: 1px solid #444;
margin: 2em 0 1em 0;
}
.footnote {
font-size: 14px;
color: #a0a0a0;
margin-bottom: 0.5em;
}
.footnote a {
text-decoration: none;
}