Skip to content

Commit a921333

Browse files
committed
add absolute links support
1 parent bf258ee commit a921333

File tree

3 files changed

+42
-14
lines changed

3 files changed

+42
-14
lines changed

src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,8 @@ pub struct HtmlConfig {
522522
pub input_404: Option<String>,
523523
/// Absolute url to site, used to emit correct paths for the 404 page, which might be accessed in a deeply nested directory
524524
pub site_url: Option<String>,
525+
/// Prepend the `site_url` in links with absolute path.
526+
pub use_site_url_as_root: bool,
525527
/// The DNS subdomain or apex domain at which your book will be hosted. This
526528
/// string will be written to a file named CNAME in the root of your site,
527529
/// as required by GitHub Pages (see [*Managing a custom domain for your
@@ -568,6 +570,7 @@ impl Default for HtmlConfig {
568570
edit_url_template: None,
569571
input_404: None,
570572
site_url: None,
573+
use_site_url_as_root: false,
571574
cname: None,
572575
live_reload_endpoint: None,
573576
redirect: HashMap::new(),

src/renderer/html_handlebars/hbs_renderer.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,23 @@ impl HtmlHandlebars {
5252
}
5353

5454
let content = ch.content.clone();
55-
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes);
55+
let content = if ctx.html_config.use_site_url_as_root {
56+
utils::render_markdown_with_path(
57+
&content,
58+
ctx.html_config.curly_quotes,
59+
None,
60+
ctx.html_config.site_url.as_ref(),
61+
)
62+
} else {
63+
utils::render_markdown(&content, ctx.html_config.curly_quotes)
64+
};
5665

57-
let fixed_content =
58-
utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path));
66+
let fixed_content = utils::render_markdown_with_path(
67+
&ch.content,
68+
ctx.html_config.curly_quotes,
69+
Some(path),
70+
None,
71+
);
5972
if !ctx.is_index && ctx.html_config.print.page_break {
6073
// Add page break between chapters
6174
// 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

src/utils/mod.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ pub fn unique_id_from_content(content: &str, id_counter: &mut HashMap<String, us
9595
/// page go to the original location. Normal page rendering sets `path` to
9696
/// None. Ideally, print page links would link to anchors on the print page,
9797
/// but that is very difficult.
98-
fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> {
98+
fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>, abs_url: Option<&String>) -> Event<'a> {
9999
lazy_static! {
100100
static ref SCHEME_LINK: Regex = Regex::new(r"^[a-z][a-z0-9+.-]*:").unwrap();
101101
static ref MD_LINK: Regex = Regex::new(r"(?P<link>.*)\.md(?P<anchor>#.*)?").unwrap();
102102
}
103103

104-
fn fix<'a>(dest: CowStr<'a>, path: Option<&Path>) -> CowStr<'a> {
104+
fn fix<'a>(dest: CowStr<'a>, path: Option<&Path>, abs_url: Option<&String>) -> CowStr<'a> {
105105
if dest.starts_with('#') {
106106
// Fragment-only link.
107107
if let Some(path) = path {
@@ -138,12 +138,19 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> {
138138
} else {
139139
fixed_link.push_str(&dest);
140140
};
141-
return CowStr::from(fixed_link);
141+
if fixed_link.starts_with('/') {
142+
fixed_link = match abs_url {
143+
Some(abs_url) => format!("{}{}", abs_url.trim_end_matches('/'), &fixed_link),
144+
None => fixed_link,
145+
}
146+
.into();
147+
}
148+
return CowStr::from(format!("{}", fixed_link));
142149
}
143150
dest
144151
}
145152

146-
fn fix_html<'a>(html: CowStr<'a>, path: Option<&Path>) -> CowStr<'a> {
153+
fn fix_html<'a>(html: CowStr<'a>, path: Option<&Path>, abs_url: Option<&String>) -> CowStr<'a> {
147154
// This is a terrible hack, but should be reasonably reliable. Nobody
148155
// should ever parse a tag with a regex. However, there isn't anything
149156
// in Rust that I know of that is suitable for handling partial html
@@ -159,7 +166,7 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> {
159166

160167
HTML_LINK
161168
.replace_all(&html, |caps: &regex::Captures<'_>| {
162-
let fixed = fix(caps[2].into(), path);
169+
let fixed = fix(caps[2].into(), path, abs_url);
163170
format!("{}{}\"", &caps[1], fixed)
164171
})
165172
.into_owned()
@@ -168,19 +175,19 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> {
168175

169176
match event {
170177
Event::Start(Tag::Link(link_type, dest, title)) => {
171-
Event::Start(Tag::Link(link_type, fix(dest, path), title))
178+
Event::Start(Tag::Link(link_type, fix(dest, path, abs_url), title))
172179
}
173180
Event::Start(Tag::Image(link_type, dest, title)) => {
174-
Event::Start(Tag::Image(link_type, fix(dest, path), title))
181+
Event::Start(Tag::Image(link_type, fix(dest, path, abs_url), title))
175182
}
176-
Event::Html(html) => Event::Html(fix_html(html, path)),
183+
Event::Html(html) => Event::Html(fix_html(html, path, abs_url)),
177184
_ => event,
178185
}
179186
}
180187

181188
/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
182189
pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
183-
render_markdown_with_path(text, curly_quotes, None)
190+
render_markdown_with_path(text, curly_quotes, None, None)
184191
}
185192

186193
pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> {
@@ -195,12 +202,17 @@ pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_, '_> {
195202
Parser::new_ext(text, opts)
196203
}
197204

198-
pub fn render_markdown_with_path(text: &str, curly_quotes: bool, path: Option<&Path>) -> String {
205+
pub fn render_markdown_with_path(
206+
text: &str,
207+
curly_quotes: bool,
208+
path: Option<&Path>,
209+
abs_url: Option<&String>,
210+
) -> String {
199211
let mut s = String::with_capacity(text.len() * 3 / 2);
200212
let p = new_cmark_parser(text, curly_quotes);
201213
let events = p
202214
.map(clean_codeblock_headers)
203-
.map(|event| adjust_links(event, path));
215+
.map(|event| adjust_links(event, path, abs_url));
204216

205217
html::push_html(&mut s, events);
206218
s

0 commit comments

Comments
 (0)