From fc2df000c04d653d32201f55c8d9291afe5eaeb8 Mon Sep 17 00:00:00 2001 From: trisua Date: Thu, 14 Aug 2025 03:16:23 -0400 Subject: [PATCH 1/2] fix: details docs --- src/markdown.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/markdown.rs b/src/markdown.rs index aba7642..b4d500d 100644 --- a/src/markdown.rs +++ b/src/markdown.rs @@ -811,7 +811,7 @@ pub fn parse_page(input: &str) -> String { /// Parse the markdown syntax for the expandable `
` element. /// /// Similar to the [`parse_page`] page definitions, details elements are denoted -/// with two ampersand symbols. The opening line should look like `&& [summary]; [open?]`. +/// with two ampersand symbols. The opening line should look like `&& [summary]`. /// /// The block is closed with a line of exactly two ampersand symbols. /// From 6da4a7712bb2080871e0006188216872b25adb0d Mon Sep 17 00:00:00 2001 From: trisua Date: Thu, 14 Aug 2025 12:26:20 -0400 Subject: [PATCH 2/2] add: toc support --- src/markdown.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/src/markdown.rs b/src/markdown.rs index b4d500d..3bc93d5 100644 --- a/src/markdown.rs +++ b/src/markdown.rs @@ -3,9 +3,9 @@ use std::collections::HashSet; pub fn render_markdown(input: &str) -> String { let html = tetratto_shared::markdown::render_markdown_dirty(&parse_page(&parse_details( &parse_text_color(&parse_highlight(&parse_link(&parse_image( - &parse_image_size(&parse_underline(&parse_comment( + &parse_image_size(&parse_toc(&parse_underline(&parse_comment( &input.replace("[/]", "
"), - ))), + )))), )))), ))) .replace("$per", "%"); @@ -883,3 +883,88 @@ pub fn parse_details(input: &str) -> String { output } + +/// Get the list of headers needed for [`parse_toc`]. +pub fn get_toc_list(input: &str) -> (String, String) { + let mut output = String::new(); + let mut toc = String::new(); + let mut in_pre = false; + + for line in input.split("\n") { + if line.starts_with("```") { + in_pre = !in_pre; + output.push_str(&format!("{line}\n")); + continue; + } + + if in_pre { + output.push_str(&format!("{line}\n")); + continue; + } + + // not in pre + if line.starts_with("#") { + // get heading count + let mut hc = 0; + + for x in line.chars() { + if x != '#' { + break; + } + + hc += 1; + } + + // add heading with id + let x = line.replacen(&"#".repeat(hc), "", 1); + let htext = x.trim(); + + let id = htext.to_lowercase().replace(" ", "_"); + output.push_str(&format!("{htext}\n")); + + // add heading to toc + toc += &format!("{}- [{htext}](#{id})\n", " ".repeat(hc)); + + // ... + continue; + } + + // otherwise + output.push_str(&format!("{line}\n")); + } + + (toc, output) +} + +/// Parse the `[toc]` table-of-contents syntax. +pub fn parse_toc(input: &str) -> String { + let (toc_list, new_input) = get_toc_list(input); + + let mut output = String::new(); + let mut in_pre = false; + + for line in new_input.split("\n") { + if line.starts_with("```") { + in_pre = !in_pre; + output.push_str(&format!("{line}\n")); + continue; + } + + if in_pre { + output.push_str(&format!("{line}\n")); + continue; + } + + // not in pre + if line.len() == 5 && line.to_lowercase() == "[toc]" { + // add toc + output.push_str(&toc_list); + continue; + } + + // otherwise + output.push_str(&format!("{line}\n")); + } + + output +}