mirror of
https://github.com/rust-lang/mdBook
synced 2024-12-14 06:42:35 +00:00
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
This commit is contained in:
parent
d609988264
commit
38b3516b60
3 changed files with 41 additions and 0 deletions
|
@ -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 }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<serde_json::Map<String, serde_json::Value>
|
|||
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}>", h=header)).unwrap();
|
||||
|
||||
html = regex.replace_all(&html, |caps: &Captures| {
|
||||
let text = &caps[1];
|
||||
let mut id = text.to_string();
|
||||
let repl_sub = vec!["<em>", "</em>", "<code>", "</code>",
|
||||
"<strong>", "</strong>",
|
||||
"<", ">", "&", "'", """];
|
||||
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::<String>();
|
||||
|
||||
format!("<a class=\"header\" href=\"#{id}\" name=\"{id}\"><{h}>{text}</{h}></a>", h=header, id=id, text=text)
|
||||
});
|
||||
}
|
||||
|
||||
html
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue