Skip to content

Commit 713c825

Browse files
committed
Make print page (print.html) links link to anchors on the print page
Let all the anchors id on the print page to have a path id prefix to help locate. e.g. bar/foo.md#abc -> #bar-foo-abc Also append a dummy div to the start of the original page to make sure that original page links without an anchor can also be located. Fix to remove all the `./` in the normalized path id so that for "./foo/bar.html#abc" we still get "#foo-bar-abc" Add support for redirect link anchors in print page so that anchors can also be redirected, also handle URL redirect links on print page Handle all the elements id to add a path prefix, also make path id to all be the lower case Fix for print page footnote links by adding the path id prefix Signed-off-by: Hollow Man <[email protected]>
1 parent 8538309 commit 713c825

File tree

3 files changed

+281
-32
lines changed

3 files changed

+281
-32
lines changed

src/renderer/html_handlebars/hbs_renderer.rs

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,38 @@ impl HtmlHandlebars {
5454
let content = ch.content.clone();
5555
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes);
5656

57-
let fixed_content =
58-
utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path));
57+
let fixed_content = utils::render_markdown_with_path(
58+
&ch.content,
59+
ctx.html_config.curly_quotes,
60+
Some(path),
61+
ctx.html_config.redirect,
62+
);
5963
if !ctx.is_index && ctx.html_config.print.page_break {
6064
// Add page break between chapters
6165
// 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
6266
// Add both two CSS properties because of the compatibility issue
6367
print_content
6468
.push_str(r#"<div style="break-before: page; page-break-before: always;"></div>"#);
6569
}
66-
print_content.push_str(&fixed_content);
70+
let path_id = {
71+
let mut base = path.display().to_string();
72+
if base.ends_with(".md") {
73+
base.replace_range(base.len() - 3.., "");
74+
}
75+
&base
76+
.replace("/", "-")
77+
.replace("\\", "-")
78+
.to_ascii_lowercase()
79+
};
80+
81+
// We have to build header links in advance so that we can know the ranges
82+
// for the headers in one page.
83+
// Insert a dummy div to make sure that we can locate the specific page.
84+
print_content.push_str(&(format!(r#"<div id="{}"></div>"#, &path_id)));
85+
print_content.push_str(&build_header_links(
86+
&build_print_element_id(&fixed_content, &path_id),
87+
Some(path_id),
88+
));
6789

6890
// Update the context with data for this file
6991
let ctx_path = path
@@ -188,19 +210,31 @@ impl HtmlHandlebars {
188210
}
189211

190212
#[cfg_attr(feature = "cargo-clippy", allow(clippy::let_and_return))]
191-
fn post_process(
213+
fn post_process_print(
192214
&self,
193215
rendered: String,
194216
playground_config: &Playground,
195217
edition: Option<RustEdition>,
196218
) -> String {
197-
let rendered = build_header_links(&rendered);
198219
let rendered = fix_code_blocks(&rendered);
199220
let rendered = add_playground_pre(&rendered, playground_config, edition);
200221

201222
rendered
202223
}
203224

225+
#[cfg_attr(feature = "cargo-clippy", allow(clippy::let_and_return))]
226+
fn post_process(
227+
&self,
228+
rendered: String,
229+
playground_config: &Playground,
230+
edition: Option<RustEdition>,
231+
) -> String {
232+
let rendered = build_header_links(&rendered, None);
233+
let rendered = self.post_process_print(rendered, &playground_config, edition);
234+
235+
rendered
236+
}
237+
204238
fn copy_static_files(
205239
&self,
206240
destination: &Path,
@@ -560,7 +594,7 @@ impl Renderer for HtmlHandlebars {
560594
let rendered = handlebars.render("index", &data)?;
561595

562596
let rendered =
563-
self.post_process(rendered, &html_config.playground, ctx.config.rust.edition);
597+
self.post_process_print(rendered, &html_config.playground, ctx.config.rust.edition);
564598

565599
utils::fs::write_file(destination, "print.html", rendered.as_bytes())?;
566600
debug!("Creating print.html ✓");
@@ -760,9 +794,44 @@ fn make_data(
760794
Ok(data)
761795
}
762796

797+
/// Goes through part of the rendered print page HTML,
798+
/// add path id prefix to all the elements id as well as footnote links.
799+
fn build_print_element_id(html: &str, path_id: &str) -> String {
800+
let all_id = Regex::new(r#"(<[^>]*?id=")([^"]+?)""#).unwrap();
801+
let footnote_id = Regex::new(
802+
r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""##,
803+
)
804+
.unwrap();
805+
806+
if path_id.is_empty() {
807+
return html.to_string();
808+
}
809+
810+
let temp_html = all_id
811+
.replace_all(html, |caps: &Captures<'_>| {
812+
let mut fixed = String::new();
813+
fixed.push_str(&path_id);
814+
fixed.push_str("-");
815+
fixed.push_str(&caps[2]);
816+
format!("{}{}\"", &caps[1], fixed)
817+
})
818+
.into_owned();
819+
820+
footnote_id
821+
.replace_all(&temp_html, |caps: &Captures<'_>| {
822+
let mut fixed = String::new();
823+
fixed.push_str(&path_id);
824+
fixed.push_str("-");
825+
fixed.push_str(&caps[2]);
826+
format!("{}{}\"", &caps[1], fixed)
827+
})
828+
.into_owned()
829+
}
830+
763831
/// Goes through the rendered HTML, making sure all header tags have
764832
/// an anchor respectively so people can link to sections directly.
765-
fn build_header_links(html: &str) -> String {
833+
<<<<<<< HEAD
834+
fn build_header_links(html: &str, path_id: Option<&str>) -> String {
766835
lazy_static! {
767836
static ref BUILD_HEADER_LINKS: Regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();
768837
}
@@ -775,19 +844,26 @@ fn build_header_links(html: &str) -> String {
775844
.parse()
776845
.expect("Regex should ensure we only ever get numbers here");
777846

778-
insert_link_into_header(level, &caps[2], &mut id_counter)
847+
insert_link_into_header(level, &caps[2], &mut id_counter, path_id)
779848
})
780849
.into_owned()
781850
}
782851

783852
/// Insert a sinle link into a header, making sure each link gets its own
784853
/// unique ID by appending an auto-incremented number (if necessary).
854+
///
855+
/// For `print.html`, we will add a path id prefix.
785856
fn insert_link_into_header(
786857
level: usize,
787858
content: &str,
788859
id_counter: &mut HashMap<String, usize>,
860+
path_id: Option<&str>,
789861
) -> String {
790-
let id = utils::unique_id_from_content(content, id_counter);
862+
let id = if let Some(path_id) = path_id {
863+
utils::unique_id_from_content_with_path(content, id_counter, path_id)
864+
} else {
865+
utils::unique_id_from_content(content, id_counter)
866+
};
791867

792868
format!(
793869
r##"<h{level} id="{id}"><a class="header" href="#{id}">{text}</a></h{level}>"##,
@@ -998,7 +1074,7 @@ mod tests {
9981074
];
9991075

10001076
for (src, should_be) in inputs {
1001-
let got = build_header_links(src);
1077+
let got = build_header_links(src, None);
10021078
assert_eq!(got, should_be);
10031079
}
10041080
}

0 commit comments

Comments
 (0)