From 38b3516b60bd839cba4efd07a9ae588bf8e4589a Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 16 Feb 2017 01:39:13 -0500 Subject: [PATCH] Implement links in section headers. This project already had a transitive dependency on regex; let's use it. This isn't the most efficient solution, but it should be fine. It ends up doing five full scans of the text. There's probably an easier way but I'm mostly just trying to get this to work for now. This also implements the same algorithm that rustdoc does for generating the name for the link. Fixes #204 --- Cargo.toml | 1 + src/lib.rs | 1 + src/renderer/html_handlebars/hbs_renderer.rs | 39 ++++++++++++++++++++ 3 files changed, 41 insertions(+) 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 +}