Skip to content

Commit c25736d

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 6a0d601 commit c25736d

File tree

3 files changed

+280
-32
lines changed

3 files changed

+280
-32
lines changed

src/renderer/html_handlebars/hbs_renderer.rs

Lines changed: 85 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,
@@ -554,7 +588,7 @@ impl Renderer for HtmlHandlebars {
554588
let rendered = handlebars.render("index", &data)?;
555589

556590
let rendered =
557-
self.post_process(rendered, &html_config.playground, ctx.config.rust.edition);
591+
self.post_process_print(rendered, &html_config.playground, ctx.config.rust.edition);
558592

559593
utils::fs::write_file(destination, "print.html", rendered.as_bytes())?;
560594
debug!("Creating print.html ✓");
@@ -754,9 +788,43 @@ fn make_data(
754788
Ok(data)
755789
}
756790

791+
/// Goes through part of the rendered print page HTML,
792+
/// add path id prefix to all the elements id as well as footnote links.
793+
fn build_print_element_id(html: &str, path_id: &str) -> String {
794+
let all_id = Regex::new(r#"(<[^>]*?id=")([^"]+?)""#).unwrap();
795+
let footnote_id = Regex::new(
796+
r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""##,
797+
)
798+
.unwrap();
799+
800+
if path_id.is_empty() {
801+
return html.to_string();
802+
}
803+
804+
let temp_html = all_id
805+
.replace_all(html, |caps: &Captures<'_>| {
806+
let mut fixed = String::new();
807+
fixed.push_str(&path_id);
808+
fixed.push_str("-");
809+
fixed.push_str(&caps[2]);
810+
format!("{}{}\"", &caps[1], fixed)
811+
})
812+
.into_owned();
813+
814+
footnote_id
815+
.replace_all(&temp_html, |caps: &Captures<'_>| {
816+
let mut fixed = String::new();
817+
fixed.push_str(&path_id);
818+
fixed.push_str("-");
819+
fixed.push_str(&caps[2]);
820+
format!("{}{}\"", &caps[1], fixed)
821+
})
822+
.into_owned()
823+
}
824+
757825
/// Goes through the rendered HTML, making sure all header tags have
758826
/// an anchor respectively so people can link to sections directly.
759-
fn build_header_links(html: &str) -> String {
827+
fn build_header_links(html: &str, path_id: Option<&str>) -> String {
760828
let regex = Regex::new(r"<h(\d)>(.*?)</h\d>").unwrap();
761829
let mut id_counter = HashMap::new();
762830

@@ -766,19 +834,26 @@ fn build_header_links(html: &str) -> String {
766834
.parse()
767835
.expect("Regex should ensure we only ever get numbers here");
768836

769-
insert_link_into_header(level, &caps[2], &mut id_counter)
837+
insert_link_into_header(level, &caps[2], &mut id_counter, path_id)
770838
})
771839
.into_owned()
772840
}
773841

774842
/// Insert a sinle link into a header, making sure each link gets its own
775843
/// unique ID by appending an auto-incremented number (if necessary).
844+
///
845+
/// For `print.html`, we will add a path id prefix.
776846
fn insert_link_into_header(
777847
level: usize,
778848
content: &str,
779849
id_counter: &mut HashMap<String, usize>,
850+
path_id: Option<&str>,
780851
) -> String {
781-
let id = utils::unique_id_from_content(content, id_counter);
852+
let id = if let Some(path_id) = path_id {
853+
utils::unique_id_from_content_with_path(content, id_counter, path_id)
854+
} else {
855+
utils::unique_id_from_content(content, id_counter)
856+
};
782857

783858
format!(
784859
r##"<h{level} id="{id}"><a class="header" href="#{id}">{text}</a></h{level}>"##,
@@ -982,7 +1057,7 @@ mod tests {
9821057
];
9831058

9841059
for (src, should_be) in inputs {
985-
let got = build_header_links(src);
1060+
let got = build_header_links(src, None);
9861061
assert_eq!(got, should_be);
9871062
}
9881063
}

0 commit comments

Comments
 (0)