Table of content
This commit is contained in:
parent
da6349e2f5
commit
1cd0e2ffc4
2 changed files with 107 additions and 8 deletions
|
@ -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, §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 += "<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;");
|
||||
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, "{{<"), "{{<");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue