Compare commits

..

2 commits

Author SHA1 Message Date
6da4a7712b add: toc support 2025-08-14 12:26:20 -04:00
fc2df000c0 fix: details docs 2025-08-14 03:16:23 -04:00

View file

@ -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("[/]", "<br />"),
))),
)))),
)))),
)))
.replace("$per", "%");
@ -811,7 +811,7 @@ pub fn parse_page(input: &str) -> String {
/// Parse the markdown syntax for the expandable `<details>` 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.
///
@ -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!("<h{hc} id=\"{id}\">{htext}</h{hc}>\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
}