diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs index 678e3f1..fcdcd5a 100644 --- a/src/compiler/compiler.rs +++ b/src/compiler/compiler.rs @@ -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 { + 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::
() { + if section.kind & section_kind::NO_TOC != 0 { + continue; + } + let last = last_matching(section.depth, §ions); + 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 += "
    "; + } + for _ in target..current { + result += "
"; + } + result + }; + result += "
"; + result += format!( + "{}", + 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!( + "
  • {}
  • ", + Compiler::refname(self.target(), section.title.as_str()), + Compiler::sanitize(self.target(), section.title.as_str()) + ) + .as_str(); + } else { + result += format!( + "
  • {}
  • ", + 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 += "
    "; + } + _ => 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#"
    "#.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;"); assert_eq!(Compiler::sanitize(Target::HTML, "\""), """); - assert_eq!(Compiler::sanitize_format(Target::HTML, "{<>&\"}"), "{<>&\"}"); - assert_eq!(Compiler::sanitize_format(Target::HTML, "{{<>}}"), "{{<>}}"); + assert_eq!( + Compiler::sanitize_format(Target::HTML, "{<>&\"}"), + "{<>&\"}" + ); + assert_eq!( + Compiler::sanitize_format(Target::HTML, "{{<>}}"), + "{{<>}}" + ); assert_eq!(Compiler::sanitize_format(Target::HTML, "{{<"), "{{<"); } } diff --git a/src/elements/section.rs b/src/elements/section.rs index d7f5b64..17f4d4b 100644 --- a/src/elements/section.rs +++ b/src/elements/section.rs @@ -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, + pub reference: Option, /// Style of the section - pub(self) style: Rc, + pub style: Rc, } impl Element for Section {