summaryrefslogtreecommitdiffstats
path: root/vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs')
-rw-r--r--vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs203
1 files changed, 203 insertions, 0 deletions
diff --git a/vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs b/vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs
new file mode 100644
index 000000000..0884d30ad
--- /dev/null
+++ b/vendor/mdbook/src/renderer/html_handlebars/helpers/toc.rs
@@ -0,0 +1,203 @@
+use std::path::Path;
+use std::{cmp::Ordering, collections::BTreeMap};
+
+use crate::utils;
+use crate::utils::bracket_escape;
+
+use handlebars::{Context, Handlebars, Helper, HelperDef, Output, RenderContext, RenderError};
+
+// Handlebars helper to construct TOC
+#[derive(Clone, Copy)]
+pub struct RenderToc {
+ pub no_section_label: bool,
+}
+
+impl HelperDef for RenderToc {
+ fn call<'reg: 'rc, 'rc>(
+ &self,
+ _h: &Helper<'reg, 'rc>,
+ _r: &'reg Handlebars<'_>,
+ ctx: &'rc Context,
+ rc: &mut RenderContext<'reg, 'rc>,
+ out: &mut dyn Output,
+ ) -> Result<(), RenderError> {
+ // get value from context data
+ // rc.get_path() is current json parent path, you should always use it like this
+ // param is the key of value you want to display
+ let chapters = rc.evaluate(ctx, "@root/chapters").and_then(|c| {
+ serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.as_json().clone())
+ .map_err(|_| RenderError::new("Could not decode the JSON data"))
+ })?;
+ let current_path = rc
+ .evaluate(ctx, "@root/path")?
+ .as_json()
+ .as_str()
+ .ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
+ .replace('\"', "");
+
+ let current_section = rc
+ .evaluate(ctx, "@root/section")?
+ .as_json()
+ .as_str()
+ .map(str::to_owned)
+ .unwrap_or_default();
+
+ let fold_enable = rc
+ .evaluate(ctx, "@root/fold_enable")?
+ .as_json()
+ .as_bool()
+ .ok_or_else(|| RenderError::new("Type error for `fold_enable`, bool expected"))?;
+
+ let fold_level = rc
+ .evaluate(ctx, "@root/fold_level")?
+ .as_json()
+ .as_u64()
+ .ok_or_else(|| RenderError::new("Type error for `fold_level`, u64 expected"))?;
+
+ out.write("<ol class=\"chapter\">")?;
+
+ let mut current_level = 1;
+ // The "index" page, which has this attribute set, is supposed to alias the first chapter in
+ // the book, i.e. the first link. There seems to be no easy way to determine which chapter
+ // the "index" is aliasing from within the renderer, so this is used instead to force the
+ // first link to be active. See further below.
+ let mut is_first_chapter = ctx.data().get("is_index").is_some();
+
+ for item in chapters {
+ // Spacer
+ if item.get("spacer").is_some() {
+ out.write("<li class=\"spacer\"></li>")?;
+ continue;
+ }
+
+ let (section, level) = if let Some(s) = item.get("section") {
+ (s.as_str(), s.matches('.').count())
+ } else {
+ ("", 1)
+ };
+
+ let is_expanded =
+ if !fold_enable || (!section.is_empty() && current_section.starts_with(section)) {
+ // Expand if folding is disabled, or if the section is an
+ // ancestor or the current section itself.
+ true
+ } else {
+ // Levels that are larger than this would be folded.
+ level - 1 < fold_level as usize
+ };
+
+ match level.cmp(&current_level) {
+ Ordering::Greater => {
+ while level > current_level {
+ out.write("<li>")?;
+ out.write("<ol class=\"section\">")?;
+ current_level += 1;
+ }
+ write_li_open_tag(out, is_expanded, false)?;
+ }
+ Ordering::Less => {
+ while level < current_level {
+ out.write("</ol>")?;
+ out.write("</li>")?;
+ current_level -= 1;
+ }
+ write_li_open_tag(out, is_expanded, false)?;
+ }
+ Ordering::Equal => {
+ write_li_open_tag(out, is_expanded, item.get("section").is_none())?;
+ }
+ }
+
+ // Part title
+ if let Some(title) = item.get("part") {
+ out.write("<li class=\"part-title\">")?;
+ out.write(&bracket_escape(title))?;
+ out.write("</li>")?;
+ continue;
+ }
+
+ // Link
+ let path_exists = if let Some(path) =
+ item.get("path")
+ .and_then(|p| if p.is_empty() { None } else { Some(p) })
+ {
+ out.write("<a href=\"")?;
+
+ let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)"))
+ .with_extension("html")
+ .to_str()
+ .unwrap()
+ // Hack for windows who tends to use `\` as separator instead of `/`
+ .replace('\\', "/");
+
+ // Add link
+ out.write(&utils::fs::path_to_root(&current_path))?;
+ out.write(&tmp)?;
+ out.write("\"")?;
+
+ if path == &current_path || is_first_chapter {
+ is_first_chapter = false;
+ out.write(" class=\"active\"")?;
+ }
+
+ out.write(">")?;
+ true
+ } else {
+ out.write("<div>")?;
+ false
+ };
+
+ if !self.no_section_label {
+ // Section does not necessarily exist
+ if let Some(section) = item.get("section") {
+ out.write("<strong aria-hidden=\"true\">")?;
+ out.write(section)?;
+ out.write("</strong> ")?;
+ }
+ }
+
+ if let Some(name) = item.get("name") {
+ out.write(&bracket_escape(name))?
+ }
+
+ if path_exists {
+ out.write("</a>")?;
+ } else {
+ out.write("</div>")?;
+ }
+
+ // Render expand/collapse toggle
+ if let Some(flag) = item.get("has_sub_items") {
+ let has_sub_items = flag.parse::<bool>().unwrap_or_default();
+ if fold_enable && has_sub_items {
+ out.write("<a class=\"toggle\"><div>❱</div></a>")?;
+ }
+ }
+ out.write("</li>")?;
+ }
+ while current_level > 1 {
+ out.write("</ol>")?;
+ out.write("</li>")?;
+ current_level -= 1;
+ }
+
+ out.write("</ol>")?;
+ Ok(())
+ }
+}
+
+fn write_li_open_tag(
+ out: &mut dyn Output,
+ is_expanded: bool,
+ is_affix: bool,
+) -> Result<(), std::io::Error> {
+ let mut li = String::from("<li class=\"chapter-item ");
+ if is_expanded {
+ li.push_str("expanded ");
+ }
+ if is_affix {
+ li.push_str("affix ");
+ }
+ li.push_str("\">");
+ out.write(&li)
+}