From c1a0af3cf65ae3371e336ca29ea42c02c15ba9fb Mon Sep 17 00:00:00 2001 From: joaofreires Date: Sat, 7 May 2022 16:22:24 -0300 Subject: [PATCH] add absolute links support --- src/config.rs | 3 ++ src/renderer/html_handlebars/hbs_renderer.rs | 19 +++++++++-- src/utils/mod.rs | 34 +++++++++++++------- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/config.rs b/src/config.rs index 6b8f1414..26b00b4b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -516,6 +516,8 @@ pub struct HtmlConfig { pub input_404: Option, /// Absolute url to site, used to emit correct paths for the 404 page, which might be accessed in a deeply nested directory pub site_url: Option, + /// Prepend the `site_url` in links with absolute path. + pub use_site_url_as_root: bool, /// The DNS subdomain or apex domain at which your book will be hosted. This /// string will be written to a file named CNAME in the root of your site, /// as required by GitHub Pages (see [*Managing a custom domain for your @@ -562,6 +564,7 @@ impl Default for HtmlConfig { edit_url_template: None, input_404: None, site_url: None, + use_site_url_as_root: false, cname: None, live_reload_endpoint: None, redirect: HashMap::new(), diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index 9c126feb..154564ce 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -55,10 +55,23 @@ impl HtmlHandlebars { } let content = ch.content.clone(); - let content = utils::render_markdown(&content, ctx.html_config.curly_quotes); + let content = if ctx.html_config.use_site_url_as_root { + utils::render_markdown_with_path( + &content, + ctx.html_config.curly_quotes, + None, + ctx.html_config.site_url.as_ref(), + ) + } else { + utils::render_markdown(&content, ctx.html_config.curly_quotes) + }; - let fixed_content = - utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path)); + let fixed_content = utils::render_markdown_with_path( + &ch.content, + ctx.html_config.curly_quotes, + Some(path), + None, + ); if !ctx.is_index && ctx.html_config.print.page_break { // Add page break between chapters // See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ab1a1bcd..13bc07bf 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -96,13 +96,13 @@ pub fn unique_id_from_content(content: &str, id_counter: &mut HashMap(event: Event<'a>, path: Option<&Path>) -> Event<'a> { +fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>, abs_url: Option<&String>) -> Event<'a> { lazy_static! { static ref SCHEME_LINK: Regex = Regex::new(r"^[a-z][a-z0-9+.-]*:").unwrap(); static ref MD_LINK: Regex = Regex::new(r"(?P.*)\.md(?P#.*)?").unwrap(); } - fn fix<'a>(dest: CowStr<'a>, path: Option<&Path>) -> CowStr<'a> { + fn fix<'a>(dest: CowStr<'a>, path: Option<&Path>, abs_url: Option<&String>) -> CowStr<'a> { if dest.starts_with('#') { // Fragment-only link. if let Some(path) = path { @@ -139,12 +139,19 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> { } else { fixed_link.push_str(&dest); }; - return CowStr::from(fixed_link); + if fixed_link.starts_with('/') { + fixed_link = match abs_url { + Some(abs_url) => format!("{}{}", abs_url.trim_end_matches('/'), &fixed_link), + None => fixed_link, + } + .into(); + } + return CowStr::from(format!("{}", fixed_link)); } dest } - fn fix_html<'a>(html: CowStr<'a>, path: Option<&Path>) -> CowStr<'a> { + fn fix_html<'a>(html: CowStr<'a>, path: Option<&Path>, abs_url: Option<&String>) -> CowStr<'a> { // This is a terrible hack, but should be reasonably reliable. Nobody // should ever parse a tag with a regex. However, there isn't anything // in Rust that I know of that is suitable for handling partial html @@ -160,7 +167,7 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> { HTML_LINK .replace_all(&html, |caps: ®ex::Captures<'_>| { - let fixed = fix(caps[2].into(), path); + let fixed = fix(caps[2].into(), path, abs_url); format!("{}{}\"", &caps[1], fixed) }) .into_owned() @@ -169,19 +176,19 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> { match event { Event::Start(Tag::Link(link_type, dest, title)) => { - Event::Start(Tag::Link(link_type, fix(dest, path), title)) + Event::Start(Tag::Link(link_type, fix(dest, path, abs_url), title)) } Event::Start(Tag::Image(link_type, dest, title)) => { - Event::Start(Tag::Image(link_type, fix(dest, path), title)) + Event::Start(Tag::Image(link_type, fix(dest, path, abs_url), title)) } - Event::Html(html) => Event::Html(fix_html(html, path)), + Event::Html(html) => Event::Html(fix_html(html, path, abs_url)), _ => event, } } /// Wrapper around the pulldown-cmark parser for rendering markdown to HTML. pub fn render_markdown(text: &str, curly_quotes: bool) -> String { - render_markdown_with_path(text, curly_quotes, None) + render_markdown_with_path(text, curly_quotes, None, None) } pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> { @@ -196,12 +203,17 @@ pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> { Parser::new_ext(text, opts) } -pub fn render_markdown_with_path(text: &str, curly_quotes: bool, path: Option<&Path>) -> String { +pub fn render_markdown_with_path( + text: &str, + curly_quotes: bool, + path: Option<&Path>, + abs_url: Option<&String>, +) -> String { let mut s = String::with_capacity(text.len() * 3 / 2); let p = new_cmark_parser(text, curly_quotes); let events = p .map(clean_codeblock_headers) - .map(|event| adjust_links(event, path)) + .map(|event| adjust_links(event, path, abs_url)) .flat_map(|event| { let (a, b) = wrap_tables(event); a.into_iter().chain(b)