Skip to content

Commit 2670796

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 aa1750f commit 2670796

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,
@@ -561,7 +595,7 @@ impl Renderer for HtmlHandlebars {
561595
let rendered = handlebars.render("index", &data)?;
562596

563597
let rendered =
564-
self.post_process(rendered, &html_config.playground, ctx.config.rust.edition);
598+
self.post_process_print(rendered, &html_config.playground, ctx.config.rust.edition);
565599

566600
utils::fs::write_file(destination, "print.html", rendered.as_bytes())?;
567601
debug!("Creating print.html ✓");
@@ -761,9 +795,43 @@ fn make_data(
761795
Ok(data)
762796
}
763797

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

779-
insert_link_into_header(level, &caps[2], &mut id_counter)
847+
insert_link_into_header(level, &caps[2], &mut id_counter, path_id)
780848
})
781849
.into_owned()
782850
}
783851

784852
/// Insert a sinle link into a header, making sure each link gets its own
785853
/// unique ID by appending an auto-incremented number (if necessary).
854+
///
855+
/// For `print.html`, we will add a path id prefix.
786856
fn insert_link_into_header(
787857
level: usize,
788858
content: &str,
789859
id_counter: &mut HashMap<String, usize>,
860+
path_id: Option<&str>,
790861
) -> String {
791-
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+
};
792867

793868
format!(
794869
r##"<h{level} id="{id}"><a class="header" href="#{id}">{text}</a></h{level}>"##,
@@ -999,7 +1074,7 @@ mod tests {
9991074
];
10001075

10011076
for (src, should_be) in inputs {
1002-
let got = build_header_links(src);
1077+
let got = build_header_links(src, None);
10031078
assert_eq!(got, should_be);
10041079
}
10051080
}

0 commit comments

Comments
 (0)