Add toc; change minor things
This commit is contained in:
+270
-19
@@ -146,6 +146,151 @@ create_output_directories :: proc(dir_names: []string) {
|
||||
}
|
||||
}
|
||||
|
||||
html_escape :: proc(sb: ^strings.Builder, html: string) {
|
||||
strings.write_string(sb, html)
|
||||
}
|
||||
|
||||
render_html :: proc(sb: ^strings.Builder, root: ^md.Node) {
|
||||
iter := md.iter_new(root)
|
||||
defer md.iter_free(iter)
|
||||
should_write_slug: i32 = 0
|
||||
parent_is_li := false
|
||||
|
||||
for md.iter_next(iter) != .Done {
|
||||
node := md.iter_get_node(iter)
|
||||
event := md.iter_get_event_type(iter)
|
||||
|
||||
#partial switch node.type {
|
||||
case .Heading:
|
||||
level := md.node_get_heading_level(node)
|
||||
|
||||
if event == .Enter {
|
||||
should_write_slug = level
|
||||
} else {
|
||||
fmt.sbprintf(sb, "</h%d>\n", level)
|
||||
}
|
||||
|
||||
case .Paragraph:
|
||||
if parent_is_li {
|
||||
if event != .Enter do parent_is_li = false
|
||||
} else {
|
||||
if event == .Enter {
|
||||
strings.write_string(sb, "<p>")
|
||||
} else if event == .Exit {
|
||||
strings.write_string(sb, "</p>\n")
|
||||
}
|
||||
}
|
||||
|
||||
case .Emph:
|
||||
if event == .Enter {
|
||||
strings.write_string(sb, "<em>")
|
||||
} else {
|
||||
strings.write_string(sb, "</em>")
|
||||
}
|
||||
|
||||
case .Strong:
|
||||
if event == .Enter {
|
||||
strings.write_string(sb, "<strong>")
|
||||
} else {
|
||||
strings.write_string(sb, "</strong>")
|
||||
}
|
||||
|
||||
case .Code:
|
||||
strings.write_string(sb, "<code>")
|
||||
lit := string(md.node_get_literal(node))
|
||||
html_escape(sb, lit)
|
||||
strings.write_string(sb, "</code>")
|
||||
|
||||
case .Text:
|
||||
lit := string(md.node_get_literal(node))
|
||||
if should_write_slug > 0 {
|
||||
fmt.sbprintf(sb, "<h%d id='", should_write_slug)
|
||||
sb_write_slug(sb, lit)
|
||||
strings.write_string(sb, "'>")
|
||||
should_write_slug = 0
|
||||
}
|
||||
html_escape(sb, lit)
|
||||
|
||||
case .Soft_Break:
|
||||
strings.write_byte(sb, ' ')
|
||||
|
||||
case .Line_Break:
|
||||
strings.write_string(sb, "<br />\n")
|
||||
|
||||
case .Code_Block:
|
||||
fence_info := string(md.node_get_fence_info(node))
|
||||
i := strings.index_any(fence_info, " \t\r\n")
|
||||
|
||||
lang := fence_info
|
||||
if i != -1 {
|
||||
lang = fence_info[:i]
|
||||
}
|
||||
|
||||
strings.write_string(sb, "<pre class='language-")
|
||||
strings.write_string(sb, lang)
|
||||
strings.write_string(sb, "'><code>")
|
||||
html_escape(sb, string(md.node_get_literal(node)))
|
||||
strings.write_string(sb, "</code></pre>\n")
|
||||
|
||||
case .HTML_Inline:
|
||||
if event == .Enter {
|
||||
strings.write_string(sb, string(md.node_get_literal(node)))
|
||||
}
|
||||
|
||||
case .HTML_Block:
|
||||
if event == .Enter {
|
||||
strings.write_string(sb, string(md.node_get_literal(node)))
|
||||
}
|
||||
|
||||
case .Thematic_Break:
|
||||
if event == .Enter {
|
||||
strings.write_string(sb, "<hr />\n")
|
||||
}
|
||||
|
||||
case .List:
|
||||
if event == .Enter {
|
||||
if md.node_get_list_type(node) == .Bullet {
|
||||
strings.write_string(sb, "<ul>\n")
|
||||
} else {
|
||||
strings.write_string(sb, "<ol>\n")
|
||||
}
|
||||
} else {
|
||||
if md.node_get_list_type(node) == .Bullet {
|
||||
strings.write_string(sb, "</ul>\n")
|
||||
} else {
|
||||
strings.write_string(sb, "</ol>\n")
|
||||
}
|
||||
}
|
||||
|
||||
case .Item:
|
||||
if event == .Enter {
|
||||
parent_is_li = true
|
||||
strings.write_string(sb, "<li>")
|
||||
} else {
|
||||
strings.write_string(sb, "</li>\n")
|
||||
}
|
||||
|
||||
case .Link:
|
||||
if event == .Enter {
|
||||
fmt.sbprintf(sb,
|
||||
"<a href=\"%s\">",
|
||||
string(md.node_get_url(node)))
|
||||
} else {
|
||||
strings.write_string(sb, "</a>")
|
||||
}
|
||||
|
||||
case .Image:
|
||||
if event == .Enter {
|
||||
fmt.sbprintf(sb,
|
||||
"<img src=\"%s\" alt=\"",
|
||||
string(md.node_get_url(node)))
|
||||
} else {
|
||||
strings.write_string(sb, "\" />")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove_title :: proc(root: ^md.Node) {
|
||||
heading_state: enum {
|
||||
None,
|
||||
@@ -182,6 +327,91 @@ remove_title :: proc(root: ^md.Node) {
|
||||
//return title
|
||||
}
|
||||
|
||||
sb_indent :: proc(sb: ^strings.Builder, level: i32) {
|
||||
for i in 0..<level {
|
||||
strings.write_string(sb, " ")
|
||||
}
|
||||
}
|
||||
|
||||
sb_write_slug :: proc(sb: ^strings.Builder, text: string) {
|
||||
wrote_any := false
|
||||
pending_dash := false
|
||||
|
||||
for c in text {
|
||||
switch {
|
||||
case c >= 'A' && c <= 'Z':
|
||||
if pending_dash {
|
||||
strings.write_byte(sb, '-')
|
||||
pending_dash = false
|
||||
}
|
||||
strings.write_byte(sb, byte(c + ('a' - 'A')))
|
||||
wrote_any = true
|
||||
|
||||
case (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'):
|
||||
if pending_dash {
|
||||
strings.write_byte(sb, '-')
|
||||
pending_dash = false
|
||||
}
|
||||
strings.write_byte(sb, byte(c))
|
||||
wrote_any = true
|
||||
|
||||
case:
|
||||
if wrote_any {
|
||||
pending_dash = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_toc :: proc(sb: ^strings.Builder, root: ^md.Node) {
|
||||
entered_heading := false
|
||||
started := false
|
||||
iter := md.iter_new(root)
|
||||
defer md.iter_free(iter)
|
||||
current_level: i32 = 0
|
||||
for md.iter_next(iter) != .Done {
|
||||
event_type := md.iter_get_event_type(iter)
|
||||
node := md.iter_get_node(iter)
|
||||
|
||||
#partial switch node.type {
|
||||
case .Heading:
|
||||
level := md.node_get_heading_level(node)
|
||||
event_type := md.iter_get_event_type(iter)
|
||||
|
||||
if event_type == .Enter {
|
||||
entered_heading = true
|
||||
if !started {
|
||||
for i in 1..<level do strings.write_string(sb, "<ol>")
|
||||
started = true
|
||||
} else {
|
||||
for i in current_level..<level do strings.write_string(sb, "<ol>")
|
||||
if current_level > level do strings.write_string(sb, "</li>")
|
||||
for i in level..<current_level do strings.write_string(sb, "</ol></li>")
|
||||
if current_level == level do strings.write_string(sb, "</li>")
|
||||
}
|
||||
|
||||
strings.write_string(sb, "<li>")
|
||||
current_level = level
|
||||
}
|
||||
case .Text:
|
||||
if entered_heading {
|
||||
entered_heading = false
|
||||
text := string(md.node_get_literal(node))
|
||||
strings.write_string(sb, "<a href='#")
|
||||
sb_write_slug(sb, text)
|
||||
strings.write_string(sb, "'>")
|
||||
strings.write_string(sb, text)
|
||||
strings.write_string(sb, "</a>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if started {
|
||||
strings.write_string(sb, "</li>")
|
||||
for i in 1..<current_level do strings.write_string(sb, "</ol>")
|
||||
}
|
||||
}
|
||||
|
||||
// must free
|
||||
parse_json :: proc(file_path: string) -> json.Value {
|
||||
json_file_content, os_err := os.read_entire_file_from_path(file_path, context.temp_allocator)
|
||||
@@ -326,14 +556,29 @@ process_markdown :: proc(sb: ^strings.Builder, site: Page) {
|
||||
{.Validate_UTF8, .Unsafe},
|
||||
)
|
||||
remove_title(cmark_root)
|
||||
html := md.render_html(cmark_root, {.Unsafe})
|
||||
|
||||
output_file_name := fmt.tprint(md_name, ".html", sep = "")
|
||||
|
||||
dir, alloc_err3 := os.join_path(
|
||||
{OUTPUT_ROOT_NAME, markdown_dir, md_name},
|
||||
context.temp_allocator
|
||||
)
|
||||
if alloc_err3 != nil {
|
||||
fmt.println("allocator error:", alloc_err3)
|
||||
continue
|
||||
}
|
||||
os.make_directory(dir)
|
||||
|
||||
output_file_path, alloc_err2 := os.join_path(
|
||||
{OUTPUT_ROOT_NAME, markdown_dir, output_file_name},
|
||||
{OUTPUT_ROOT_NAME, markdown_dir, md_name, "index.html"},
|
||||
context.temp_allocator,
|
||||
)
|
||||
|
||||
if alloc_err2 != nil {
|
||||
fmt.println("allocator error:", alloc_err3)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
out_file, os_err2 := os.open(output_file_path, {.Write, .Create, .Trunc})
|
||||
if os_err2 != nil {
|
||||
fmt.eprintfln("failed to open output file '%s' (%s)", output_file_path, os_err2)
|
||||
@@ -353,10 +598,21 @@ process_markdown :: proc(sb: ^strings.Builder, site: Page) {
|
||||
|
||||
last_updated_date(sb, date)
|
||||
|
||||
block_begin(sb)
|
||||
defer block_end(sb)
|
||||
{
|
||||
block_begin(sb)
|
||||
defer block_end(sb)
|
||||
|
||||
fmt.sbprint(sb, html)
|
||||
strings.write_string(sb, "<h2>Contents</h2>")
|
||||
|
||||
generate_toc(sb, cmark_root)
|
||||
}
|
||||
strings.write_string(sb, "<hr>")
|
||||
{
|
||||
block_begin(sb)
|
||||
defer block_end(sb)
|
||||
|
||||
render_html(sb, cmark_root)
|
||||
}
|
||||
}
|
||||
|
||||
write_len: int
|
||||
@@ -410,10 +666,11 @@ create_toc_page :: proc(title, toc_path: string, toc_info: ^[dynamic][3]string,
|
||||
tr_begin(sb)
|
||||
fmt.sbprintf(
|
||||
sb,
|
||||
"<td><a href='%s.html'>%s</a></td><td><span class='table-last-updated' data-value='%s'>aarfsdf</span></td>",
|
||||
"<td><a href='%s'>%s</a></td><td><span class='table-last-updated' data-value='%s'>%s</span></td>",
|
||||
item[0],
|
||||
item[1],
|
||||
item[2],
|
||||
item[2],
|
||||
)
|
||||
tr_end(sb)
|
||||
}
|
||||
@@ -457,19 +714,13 @@ create_toc_page :: proc(title, toc_path: string, toc_info: ^[dynamic][3]string,
|
||||
}
|
||||
|
||||
last_updated_date :: proc(sb: ^strings.Builder, date: string) {
|
||||
fmt.sbprint(sb, "<span class='last-updated'></span>")
|
||||
fmt.sbprint(
|
||||
sb,
|
||||
"<script>",
|
||||
"const date = new Date('",
|
||||
date,
|
||||
"');",
|
||||
"document.querySelector('.last-updated').innerText = 'Last updated: ' + new Intl.DateTimeFormat(undefined, { dateStyle: 'long', timeStyle: 'short'}).format(date)",
|
||||
"</script>",
|
||||
sep = "",
|
||||
)
|
||||
|
||||
strings.write_string(sb, "<span class='last-updated'>Last updated: ")
|
||||
strings.write_string(sb, date)
|
||||
strings.write_string(sb, "</span>")
|
||||
|
||||
strings.write_string(sb, "<script>const date = new Date('")
|
||||
strings.write_string(sb, date)
|
||||
strings.write_string(sb, "'); document.querySelector('.last-updated').innerText = 'Last updated: ' + new Intl.DateTimeFormat(undefined, { dateStyle: 'long', timeStyle: 'short'}).format(date)</script>")
|
||||
}
|
||||
|
||||
nav :: proc(sb: ^strings.Builder, current_path: string, site: Page) {
|
||||
|
||||
Reference in New Issue
Block a user