Try to update handlebars
This commit is contained in:
parent
49c26a93f2
commit
33f437af9d
|
@ -220,12 +220,7 @@ impl HtmlHandlebars {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_hbs_helpers(&self, handlebars: &mut Handlebars, html_config: &HtmlConfig) {
|
fn register_hbs_helpers(&self, handlebars: &mut Handlebars, html_config: &HtmlConfig) {
|
||||||
handlebars.register_helper(
|
handlebars.register_helper("toc", Box::new(helpers::toc::toc));
|
||||||
"toc",
|
|
||||||
Box::new(helpers::toc::RenderToc {
|
|
||||||
no_section_label: html_config.no_section_label,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
handlebars.register_helper("previous", Box::new(helpers::navigation::previous));
|
||||||
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
handlebars.register_helper("next", Box::new(helpers::navigation::next));
|
||||||
}
|
}
|
||||||
|
@ -402,12 +397,12 @@ fn make_data(
|
||||||
json!(config.book.description.clone().unwrap_or_default()),
|
json!(config.book.description.clone().unwrap_or_default()),
|
||||||
);
|
);
|
||||||
data.insert("favicon".to_owned(), json!("favicon.png"));
|
data.insert("favicon".to_owned(), json!("favicon.png"));
|
||||||
|
|
||||||
if let Some(ref livereload) = html_config.livereload_url {
|
if let Some(ref livereload) = html_config.livereload_url {
|
||||||
data.insert("livereload".to_owned(), json!(livereload));
|
data.insert("livereload".to_owned(), json!(livereload));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add google analytics tag
|
if let Some(ref ga) = html.google_analytics {
|
||||||
if let Some(ref ga) = config.html_config().and_then(|html| html.google_analytics) {
|
|
||||||
data.insert("google_analytics".to_owned(), json!(ga));
|
data.insert("google_analytics".to_owned(), json!(ga));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,6 +410,10 @@ fn make_data(
|
||||||
data.insert("mathjax_support".to_owned(), json!(true));
|
data.insert("mathjax_support".to_owned(), json!(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if html.no_section_label {
|
||||||
|
data.insert("no_section_label".to_owned(), json!(true));
|
||||||
|
}
|
||||||
|
|
||||||
// Add check to see if there is an additional style
|
// Add check to see if there is an additional style
|
||||||
if !html.additional_css.is_empty() {
|
if !html.additional_css.is_empty() {
|
||||||
let mut css = Vec::new();
|
let mut css = Vec::new();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use handlebars::{Context, Handlebars, Helper, RenderContext, RenderError, Renderable};
|
use handlebars::{Context, Handlebars, Helper, RenderContext, RenderError, Renderable, HelperResult, Output};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use utils;
|
use utils;
|
||||||
|
@ -45,15 +45,15 @@ impl Target {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_chapter(rc: &mut RenderContext, target: Target) -> Result<Option<StringMap>, RenderError> {
|
fn find_chapter(cx: &Context, rc: &mut RenderContext, target: Target) -> Result<Option<StringMap>, RenderError> {
|
||||||
debug!("Get data from context");
|
debug!("Get data from context");
|
||||||
|
|
||||||
let chapters = rc.evaluate_absolute("chapters", true).and_then(|c| {
|
let chapters = rc.evaluate_absolute(cx, "chapters", true).and_then(|c| {
|
||||||
serde_json::value::from_value::<Vec<StringMap>>(c.clone())
|
serde_json::value::from_value::<Vec<StringMap>>(c.clone())
|
||||||
.map_err(|_| RenderError::new("Could not decode the JSON data"))
|
.map_err(|_| RenderError::new("Could not decode the JSON data"))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let base_path = rc.evaluate_absolute("path", true)?
|
let base_path = rc.evaluate_absolute(cx, "path", true)?
|
||||||
.as_str()
|
.as_str()
|
||||||
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
|
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
|
||||||
.replace("\"", "");
|
.replace("\"", "");
|
||||||
|
@ -80,16 +80,17 @@ fn find_chapter(rc: &mut RenderContext, target: Target) -> Result<Option<StringM
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(
|
fn render<'h: 'rc, 'rc>(
|
||||||
_h: &Helper,
|
h: &'h Handlebars,
|
||||||
r: &Handlebars,
|
cx: &'rc Context,
|
||||||
rc: &mut RenderContext,
|
rc: &'rc mut RenderContext<'h>,
|
||||||
chapter: &StringMap,
|
out: &mut Output,
|
||||||
) -> Result<(), RenderError> {
|
chapter: StringMap,
|
||||||
|
) -> HelperResult {
|
||||||
trace!("Creating BTreeMap to inject in context");
|
trace!("Creating BTreeMap to inject in context");
|
||||||
|
|
||||||
let mut context = BTreeMap::new();
|
let mut context = BTreeMap::new();
|
||||||
let base_path = rc.evaluate_absolute("path", false)?
|
let base_path = rc.evaluate_absolute(cx, "path", false)?
|
||||||
.as_str()
|
.as_str()
|
||||||
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
|
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
|
||||||
.replace("\"", "");
|
.replace("\"", "");
|
||||||
|
@ -117,31 +118,41 @@ fn render(
|
||||||
|
|
||||||
trace!("Render template");
|
trace!("Render template");
|
||||||
|
|
||||||
_h.template()
|
h.render_template()
|
||||||
.ok_or_else(|| RenderError::new("Error with the handlebars template"))
|
.ok_or_else(|| RenderError::new("Error with the handlebars template"))
|
||||||
.and_then(|t| {
|
.and_then(|t| {
|
||||||
let mut local_rc = rc.with_context(Context::wraps(&context)?);
|
let local_cx = Context::wraps(&context)?;
|
||||||
t.render(r, &mut local_rc)
|
t.render(r, &local_cx, rc, out)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn previous(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
|
pub fn previous<'h: 'rc, 'rc>(
|
||||||
|
h: &'h Handlebars,
|
||||||
|
cx: &'rc Context,
|
||||||
|
rc: &'rc mut RenderContext<'h>,
|
||||||
|
out: &mut Output
|
||||||
|
) -> HelperResult {
|
||||||
trace!("previous (handlebars helper)");
|
trace!("previous (handlebars helper)");
|
||||||
|
|
||||||
if let Some(previous) = find_chapter(rc, Target::Previous)? {
|
if let Some(previous) = find_chapter(cx, rc, Target::Previous)? {
|
||||||
render(_h, r, rc, &previous)?;
|
render(h, cx, rc, out, &previous)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next(_h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
|
pub fn next<'h: 'rc, 'rc>(
|
||||||
|
h: &'h Handlebars,
|
||||||
|
cx: &'rc Context,
|
||||||
|
rc: &'rc mut RenderContext<'h>,
|
||||||
|
out: &mut Output
|
||||||
|
) -> HelperResult {
|
||||||
trace!("next (handlebars helper)");
|
trace!("next (handlebars helper)");
|
||||||
|
|
||||||
if let Some(next) = find_chapter(rc, Target::Next)? {
|
if let Some(next) = find_chapter(cx, rc, Target::Next)? {
|
||||||
render(_h, r, rc, &next)?;
|
render(h, cx, rc, out, &next)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -3,142 +3,134 @@ use std::path::Path;
|
||||||
|
|
||||||
use utils;
|
use utils;
|
||||||
|
|
||||||
use handlebars::{Handlebars, Helper, HelperDef, RenderContext, RenderError};
|
use handlebars::{Helper, Handlebars, Context, RenderContext, Output, HelperResult, RenderError};
|
||||||
use pulldown_cmark::{html, Event, Parser, Tag};
|
use pulldown_cmark::{html, Event, Parser, Tag};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
// Handlebars helper to construct TOC
|
pub fn toc(h: &Helper, _: &Handlebars, cx: &Context, rc: &mut RenderContext, out: &mut Output) -> HelperResult {
|
||||||
#[derive(Clone, Copy)]
|
// get value from context data
|
||||||
pub struct RenderToc {
|
// rc.get_path() is current json parent path, you should always use it like this
|
||||||
pub no_section_label: bool,
|
// param is the key of value you want to display
|
||||||
}
|
let chapters = rc.evaluate_absolute(cx, "chapters", true).and_then(|c| {
|
||||||
|
serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.clone())
|
||||||
|
.map_err(|_| RenderError::new("Could not decode the JSON data"))
|
||||||
|
})?;
|
||||||
|
let current = rc.evaluate_absolute(cx, "path", true)?
|
||||||
|
.as_str()
|
||||||
|
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
|
||||||
|
.replace("\"", "");
|
||||||
|
let no_section_label = rc.evaluate_absolute(cx, "no_section_label", true)?.as_bool().unwrap_or(false);
|
||||||
|
|
||||||
impl HelperDef for RenderToc {
|
out.write("<ol class=\"chapter\">")?;
|
||||||
fn call(&self, _h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> 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_absolute("chapters", true).and_then(|c| {
|
|
||||||
serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.clone())
|
|
||||||
.map_err(|_| RenderError::new("Could not decode the JSON data"))
|
|
||||||
})?;
|
|
||||||
let current = rc.evaluate_absolute("path", true)?
|
|
||||||
.as_str()
|
|
||||||
.ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
|
|
||||||
.replace("\"", "");
|
|
||||||
|
|
||||||
rc.writer.write_all(b"<ol class=\"chapter\">")?;
|
let mut current_level = 1;
|
||||||
|
|
||||||
let mut current_level = 1;
|
for item in chapters {
|
||||||
|
// Spacer
|
||||||
|
if item.get("spacer").is_some() {
|
||||||
|
out.write("<li class=\"spacer\"></li>")?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for item in chapters {
|
let level = if let Some(s) = item.get("section") {
|
||||||
// Spacer
|
s.matches('.').count()
|
||||||
if item.get("spacer").is_some() {
|
} else {
|
||||||
rc.writer.write_all(b"<li class=\"spacer\"></li>")?;
|
1
|
||||||
continue;
|
};
|
||||||
|
|
||||||
|
if level > current_level {
|
||||||
|
while level > current_level {
|
||||||
|
out.write("<li>")?;
|
||||||
|
out.write("<ol class=\"section\">")?;
|
||||||
|
current_level += 1;
|
||||||
}
|
}
|
||||||
|
out.write("<li>")?;
|
||||||
let level = if let Some(s) = item.get("section") {
|
} else if level < current_level {
|
||||||
s.matches('.').count()
|
while level < current_level {
|
||||||
} else {
|
out.write("</ol>")?;
|
||||||
1
|
out.write("</li>")?;
|
||||||
};
|
current_level -= 1;
|
||||||
|
|
||||||
if level > current_level {
|
|
||||||
while level > current_level {
|
|
||||||
rc.writer.write_all(b"<li>")?;
|
|
||||||
rc.writer.write_all(b"<ol class=\"section\">")?;
|
|
||||||
current_level += 1;
|
|
||||||
}
|
|
||||||
rc.writer.write_all(b"<li>")?;
|
|
||||||
} else if level < current_level {
|
|
||||||
while level < current_level {
|
|
||||||
rc.writer.write_all(b"</ol>")?;
|
|
||||||
rc.writer.write_all(b"</li>")?;
|
|
||||||
current_level -= 1;
|
|
||||||
}
|
|
||||||
rc.writer.write_all(b"<li>")?;
|
|
||||||
} else {
|
|
||||||
rc.writer.write_all(b"<li")?;
|
|
||||||
if item.get("section").is_none() {
|
|
||||||
rc.writer.write_all(b" class=\"affix\"")?;
|
|
||||||
}
|
|
||||||
rc.writer.write_all(b">")?;
|
|
||||||
}
|
}
|
||||||
|
out.write("<li>")?;
|
||||||
|
} else {
|
||||||
|
out.write("<li")?;
|
||||||
|
if item.get("section").is_none() {
|
||||||
|
out.write(" class=\"affix\"")?;
|
||||||
|
}
|
||||||
|
out.write(">")?;
|
||||||
|
}
|
||||||
|
|
||||||
// Link
|
// Link
|
||||||
let path_exists = if let Some(path) = item.get("path") {
|
let path_exists = if let Some(path) = item.get("path") {
|
||||||
if !path.is_empty() {
|
if !path.is_empty() {
|
||||||
rc.writer.write_all(b"<a href=\"")?;
|
out.write("<a href=\"")?;
|
||||||
|
|
||||||
let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)"))
|
let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)"))
|
||||||
.with_extension("html")
|
.with_extension("html")
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
// Hack for windows who tends to use `\` as separator instead of `/`
|
// Hack for windows who tends to use `\` as separator instead of `/`
|
||||||
.replace("\\", "/");
|
.replace("\\", "/");
|
||||||
|
|
||||||
// Add link
|
// Add link
|
||||||
rc.writer
|
out.write(&utils::fs::path_to_root(¤t))?;
|
||||||
.write_all(&utils::fs::path_to_root(¤t).as_bytes())?;
|
out.write(&tmp)?;
|
||||||
rc.writer.write_all(tmp.as_bytes())?;
|
out.write("\"")?;
|
||||||
rc.writer.write_all(b"\"")?;
|
|
||||||
|
|
||||||
if path == ¤t {
|
if path == ¤t {
|
||||||
rc.writer.write_all(b" class=\"active\"")?;
|
out.write(" class=\"active\"")?;
|
||||||
}
|
|
||||||
|
|
||||||
rc.writer.write_all(b">")?;
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.write(">")?;
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
|
||||||
|
|
||||||
if !self.no_section_label {
|
|
||||||
// Section does not necessarily exist
|
|
||||||
if let Some(section) = item.get("section") {
|
|
||||||
rc.writer.write_all(b"<strong aria-hidden=\"true\">")?;
|
|
||||||
rc.writer.write_all(section.as_bytes())?;
|
|
||||||
rc.writer.write_all(b"</strong> ")?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(name) = item.get("name") {
|
if no_section_label {
|
||||||
// Render only inline code blocks
|
// Section does not necessarily exist
|
||||||
|
if let Some(section) = item.get("section") {
|
||||||
// filter all events that are not inline code blocks
|
out.write("<strong aria-hidden=\"true\">")?;
|
||||||
let parser = Parser::new(name).filter(|event| match *event {
|
out.write(section)?;
|
||||||
Event::Start(Tag::Code)
|
out.write("</strong> ")?;
|
||||||
| Event::End(Tag::Code)
|
|
||||||
| Event::InlineHtml(_)
|
|
||||||
| Event::Text(_) => true,
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// render markdown to html
|
|
||||||
let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
|
|
||||||
html::push_html(&mut markdown_parsed_name, parser);
|
|
||||||
|
|
||||||
// write to the handlebars template
|
|
||||||
rc.writer.write_all(markdown_parsed_name.as_bytes())?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if path_exists {
|
|
||||||
rc.writer.write_all(b"</a>")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc.writer.write_all(b"</li>")?;
|
|
||||||
}
|
|
||||||
while current_level > 1 {
|
|
||||||
rc.writer.write_all(b"</ol>")?;
|
|
||||||
rc.writer.write_all(b"</li>")?;
|
|
||||||
current_level -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc.writer.write_all(b"</ol>")?;
|
if let Some(name) = item.get("name") {
|
||||||
Ok(())
|
// Render only inline code blocks
|
||||||
|
|
||||||
|
// filter all events that are not inline code blocks
|
||||||
|
let parser = Parser::new(name).filter(|event| match *event {
|
||||||
|
Event::Start(Tag::Code)
|
||||||
|
| Event::End(Tag::Code)
|
||||||
|
| Event::InlineHtml(_)
|
||||||
|
| Event::Text(_) => true,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// render markdown to html
|
||||||
|
let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
|
||||||
|
html::push_html(&mut markdown_parsed_name, parser);
|
||||||
|
|
||||||
|
// write to the handlebars template
|
||||||
|
out.write(&markdown_parsed_name)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if path_exists {
|
||||||
|
out.write("</a>")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write("</li>")?;
|
||||||
}
|
}
|
||||||
|
while current_level > 1 {
|
||||||
|
out.write("</ol>")?;
|
||||||
|
out.write("</li>")?;
|
||||||
|
current_level -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write("</ol>")?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue