fix: outlines without titles no longer include the first list level
This commit is contained in:
parent
4fc4922256
commit
03d3a31d71
1 changed files with 78 additions and 18 deletions
94
src/lib.rs
94
src/lib.rs
|
|
@ -126,10 +126,24 @@ pub fn compile_article(
|
||||||
.map_err(|e| format_typst_compile_error(e, &full_source_str, index_byte_start, &index_source))?;
|
.map_err(|e| format_typst_compile_error(e, &full_source_str, index_byte_start, &index_source))?;
|
||||||
|
|
||||||
let mut outline = EcoString::new();
|
let mut outline = EcoString::new();
|
||||||
let mut curr_level = 1;
|
let mut curr_level = 1u32;
|
||||||
parse_outline(&mut doc.root, &mut outline, &mut curr_level, include_title);
|
let mut ul_depth = 0u32;
|
||||||
for i in (1..curr_level).rev() {
|
let mut title_h2_pending = !include_title;
|
||||||
outline.push_str(" ".repeat(i as usize - 1).as_str());
|
let mut first_outline_heading = true;
|
||||||
|
parse_outline(
|
||||||
|
&mut doc.root,
|
||||||
|
&mut outline,
|
||||||
|
&mut curr_level,
|
||||||
|
&mut ul_depth,
|
||||||
|
include_title,
|
||||||
|
&mut title_h2_pending,
|
||||||
|
&mut first_outline_heading,
|
||||||
|
);
|
||||||
|
// `ul_depth` counts open `<ul>` tags; it can diverge from `curr_level - 1` when the first
|
||||||
|
// outline heading uses lazy depth (skip title). Always drain by `ul_depth`, not `curr_level`.
|
||||||
|
while ul_depth > 0 {
|
||||||
|
ul_depth -= 1;
|
||||||
|
outline.push_str(" ".repeat(ul_depth as usize).as_str());
|
||||||
outline.push_str("</ul>\n");
|
outline.push_str("</ul>\n");
|
||||||
}
|
}
|
||||||
fs::write(&outline_file, outline.as_bytes()).map_err(|e| {
|
fs::write(&outline_file, outline.as_bytes()).map_err(|e| {
|
||||||
|
|
@ -179,19 +193,50 @@ pub fn compile_article(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn heading_level_from_tag(tag: &str) -> Option<u32> {
|
||||||
|
match tag {
|
||||||
|
"<h2>" => Some(2),
|
||||||
|
"<h3>" => Some(3),
|
||||||
|
"<h4>" => Some(4),
|
||||||
|
"<h5>" => Some(5),
|
||||||
|
"<h6>" => Some(6),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_outline(
|
fn parse_outline(
|
||||||
elem: &mut HtmlElement,
|
elem: &mut HtmlElement,
|
||||||
outline: &mut EcoString,
|
outline: &mut EcoString,
|
||||||
curr_level: &mut u32,
|
curr_level: &mut u32,
|
||||||
|
ul_depth: &mut u32,
|
||||||
include_title: bool,
|
include_title: bool,
|
||||||
|
title_h2_pending: &mut bool,
|
||||||
|
first_outline_heading: &mut bool,
|
||||||
) {
|
) {
|
||||||
if matches!(
|
let tag = elem.tag.to_string();
|
||||||
elem.tag.to_string().as_str(),
|
let tag_ref = tag.as_str();
|
||||||
"<h2>" | "<h3>" | "<h4>" | "<h5>" | "<h6>"
|
|
||||||
) {
|
if let Some(level) = heading_level_from_tag(tag_ref) {
|
||||||
if !include_title && elem.tag.to_string().as_str() == "<h2>" {
|
if !include_title && *title_h2_pending {
|
||||||
|
*title_h2_pending = false;
|
||||||
|
if tag_ref == "<h2>" {
|
||||||
|
for child in elem.children.make_mut().iter_mut() {
|
||||||
|
if let HtmlNode::Element(e) = child {
|
||||||
|
parse_outline(
|
||||||
|
e,
|
||||||
|
outline,
|
||||||
|
curr_level,
|
||||||
|
ul_depth,
|
||||||
|
include_title,
|
||||||
|
title_h2_pending,
|
||||||
|
first_outline_heading,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
*title_h2_pending = false;
|
||||||
|
|
||||||
let mut header_text = EcoString::new();
|
let mut header_text = EcoString::new();
|
||||||
|
|
||||||
|
|
@ -218,21 +263,26 @@ fn parse_outline(
|
||||||
.trim_matches('-')
|
.trim_matches('-')
|
||||||
.replace("--", "-");
|
.replace("--", "-");
|
||||||
|
|
||||||
let level: u32 = elem.tag.to_string().chars().nth(2).unwrap() as u32 - '0' as u32;
|
if *first_outline_heading {
|
||||||
|
*first_outline_heading = false;
|
||||||
|
*curr_level = level.saturating_sub(1);
|
||||||
|
}
|
||||||
|
|
||||||
while level > *curr_level {
|
while level > *curr_level {
|
||||||
*curr_level += 1;
|
outline.push_str(" ".repeat(*ul_depth as usize).as_str());
|
||||||
outline.push_str(" ".repeat(*curr_level as usize - 2).as_str());
|
|
||||||
outline.push_str("<ul>\n");
|
outline.push_str("<ul>\n");
|
||||||
|
*ul_depth += 1;
|
||||||
|
*curr_level += 1;
|
||||||
}
|
}
|
||||||
while level < *curr_level {
|
while level < *curr_level && *ul_depth > 0 {
|
||||||
outline.push_str(" ".repeat(*curr_level as usize - 2).as_str());
|
|
||||||
outline.push_str("</ul>\n");
|
|
||||||
*curr_level -= 1;
|
*curr_level -= 1;
|
||||||
|
*ul_depth -= 1;
|
||||||
|
outline.push_str(" ".repeat(*ul_depth as usize).as_str());
|
||||||
|
outline.push_str("</ul>\n");
|
||||||
}
|
}
|
||||||
*curr_level = level;
|
*curr_level = level;
|
||||||
|
|
||||||
outline.push_str(" ".repeat(*curr_level as usize - 1).as_str());
|
outline.push_str(" ".repeat(*ul_depth as usize).as_str());
|
||||||
outline.push_str(
|
outline.push_str(
|
||||||
format!(
|
format!(
|
||||||
"<li><a href=\"#{}\">{}</a></li>\n",
|
"<li><a href=\"#{}\">{}</a></li>\n",
|
||||||
|
|
@ -247,7 +297,17 @@ fn parse_outline(
|
||||||
|
|
||||||
for child in elem.children.make_mut().iter_mut() {
|
for child in elem.children.make_mut().iter_mut() {
|
||||||
match child {
|
match child {
|
||||||
HtmlNode::Element(e) => parse_outline(e, outline, curr_level, include_title),
|
HtmlNode::Element(e) => {
|
||||||
|
parse_outline(
|
||||||
|
e,
|
||||||
|
outline,
|
||||||
|
curr_level,
|
||||||
|
ul_depth,
|
||||||
|
include_title,
|
||||||
|
title_h2_pending,
|
||||||
|
first_outline_heading,
|
||||||
|
);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue