Table of content

This commit is contained in:
ef3d0c3e 2024-08-28 12:12:38 +02:00
parent da6349e2f5
commit 1cd0e2ffc4
2 changed files with 107 additions and 8 deletions

View file

@ -9,6 +9,8 @@ use crate::document::document::CrossReference;
use crate::document::document::Document;
use crate::document::document::ElemReference;
use crate::document::variable::Variable;
use crate::elements::section::section_kind;
use crate::elements::section::Section;
use super::postprocess::PostProcess;
@ -226,6 +228,93 @@ impl<'a> Compiler<'a> {
result
}
pub fn toc(&self, document: &dyn Document) -> String {
let toc_title = if let Some(title) = document.get_variable("toc.title") {
title
} else {
return String::new();
};
let mut result = String::new();
let mut sections: Vec<(&Section, usize)> = vec![];
// Find last section with given depth
fn last_matching(depth: usize, sections: &Vec<(&Section, usize)>) -> Option<usize> {
for (idx, (section, _number)) in sections.iter().rev().enumerate() {
if section.depth < depth {
return None;
} else if section.depth == depth {
return Some(sections.len() - idx - 1);
}
}
None
}
let content_borrow = document.content().borrow();
for elem in content_borrow.iter() {
if let Some(section) = elem.downcast_ref::<Section>() {
if section.kind & section_kind::NO_TOC != 0 {
continue;
}
let last = last_matching(section.depth, &sections);
if let Some(last) = last {
if sections[last].0.kind & section_kind::NO_NUMBER != 0 {
sections.push((section, sections[last].1));
} else {
sections.push((section, sections[last].1 + 1))
}
} else {
sections.push((section, 1));
}
}
}
match self.target() {
Target::HTML => {
let match_depth = |current: usize, target: usize| -> String {
let mut result = String::new();
for _ in current..target {
result += "<ol>";
}
for _ in target..current {
result += "</ol>";
}
result
};
result += "<div class=\"toc\">";
result += format!(
"<span>{}</span>",
Compiler::sanitize(self.target(), toc_title.to_string())
)
.as_str();
let mut current_depth = 0;
for (section, number) in sections {
result += match_depth(current_depth, section.depth).as_str();
if section.kind & section_kind::NO_NUMBER != 0 {
result += format!(
"<li><a href=\"#{}\">{}</a></li>",
Compiler::refname(self.target(), section.title.as_str()),
Compiler::sanitize(self.target(), section.title.as_str())
)
.as_str();
} else {
result += format!(
"<li value=\"{number}\"><a href=\"#{}\">{}</a></li>",
Compiler::refname(self.target(), section.title.as_str()),
Compiler::sanitize(self.target(), section.title.as_str())
)
.as_str();
}
current_depth = section.depth;
}
match_depth(current_depth, 0);
result += "</div>";
}
_ => todo!(""),
}
result
}
pub fn compile(&self, document: &dyn Document) -> (CompiledDocument, PostProcess) {
let borrow = document.content().borrow();
@ -234,6 +323,10 @@ impl<'a> Compiler<'a> {
// Body
let mut body = r#"<div class="content">"#.to_string();
// Table of content
body += self.toc(document).as_str();
for i in 0..borrow.len() {
let elem = &borrow[i];
@ -378,8 +471,14 @@ mod tests {
assert_eq!(Compiler::sanitize(Target::HTML, "&lt;"), "&amp;lt;");
assert_eq!(Compiler::sanitize(Target::HTML, "\""), "&quot;");
assert_eq!(Compiler::sanitize_format(Target::HTML, "{<>&\"}"), "{<>&\"}");
assert_eq!(Compiler::sanitize_format(Target::HTML, "{{<>}}"), "{{&lt;&gt;}}");
assert_eq!(
Compiler::sanitize_format(Target::HTML, "{<>&\"}"),
"{<>&\"}"
);
assert_eq!(
Compiler::sanitize_format(Target::HTML, "{{<>}}"),
"{{&lt;&gt;}}"
);
assert_eq!(Compiler::sanitize_format(Target::HTML, "{{<"), "{{&lt;");
}
}

View file

@ -28,17 +28,17 @@ use super::reference::InternalReference;
#[derive(Debug)]
pub struct Section {
pub(self) location: Token,
pub location: Token,
/// Title of the section
pub(self) title: String,
pub title: String,
/// Depth i.e number of '#'
pub(self) depth: usize,
pub depth: usize,
/// [`section_kind`]
pub(self) kind: u8,
pub kind: u8,
/// Section reference name
pub(self) reference: Option<String>,
pub reference: Option<String>,
/// Style of the section
pub(self) style: Rc<section_style::SectionStyle>,
pub style: Rc<section_style::SectionStyle>,
}
impl Element for Section {