diff --git a/Cargo.toml b/Cargo.toml index 9451e2f2..17254dc9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ log = "0.3" env_logger = "0.3" toml = { version = "0.2", features = ["serde"] } open = "1.1" +regex = "0.1.80" # Watch feature notify = { version = "3.0", optional = true } diff --git a/src/lib.rs b/src/lib.rs index 0a59f9e7..49c56fac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,6 +74,7 @@ extern crate serde; extern crate serde_json; extern crate handlebars; extern crate pulldown_cmark; +extern crate regex; #[macro_use] extern crate log; pub mod book; diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index b122ed14..83b65bf0 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -3,7 +3,9 @@ use renderer::Renderer; use book::MDBook; use book::bookitem::BookItem; use {utils, theme}; +use regex::{Regex, Captures}; +use std::ascii::AsciiExt; use std::path::{Path, PathBuf}; use std::fs::{self, File}; use std::error::Error; @@ -91,6 +93,9 @@ impl Renderer for HtmlHandlebars { // Render the handlebars template with the data debug!("[*]: Render template"); let rendered = try!(handlebars.render("index", &data)); + + // create links for headers + let rendered = build_header_links(rendered); // Write to file let filename = Path::new(&ch.path).with_extension("html"); @@ -208,3 +213,37 @@ fn make_data(book: &MDBook) -> Result debug!("[*]: JSON constructed"); Ok(data) } + +fn build_header_links(mut html: String) -> String { + for header in &["h1", "h2", "h3", "h4", "h5"] { + let regex = Regex::new(&format!("<{h}>(.*?)", h=header)).unwrap(); + + html = regex.replace_all(&html, |caps: &Captures| { + let text = &caps[1]; + let mut id = text.to_string(); + let repl_sub = vec!["", "", "", "", + "", "", + "<", ">", "&", "'", """]; + for sub in repl_sub { + id = id.replace(sub, ""); + } + let id = id.chars().filter_map(|c| { + if c.is_alphanumeric() || c == '-' || c == '_' { + if c.is_ascii() { + Some(c.to_ascii_lowercase()) + } else { + Some(c) + } + } else if c.is_whitespace() && c.is_ascii() { + Some('-') + } else { + None + } + }).collect::(); + + format!("<{h}>{text}", h=header, id=id, text=text) + }); + } + + html +}