Skip to content

Commit 63abbd1

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 db1a0bf commit 63abbd1

File tree

3 files changed

+357
-61
lines changed

3 files changed

+357
-61
lines changed

src/renderer/html_handlebars/hbs_renderer.rs

+80-7
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,11 @@ impl HtmlHandlebars {
5656

5757
let content = utils::render_markdown(&ch.content, ctx.html_config.smart_punctuation());
5858

59-
let fixed_content = utils::render_markdown_with_path(
59+
let printed_item = utils::render_markdown_with_path_and_redirects(
6060
&ch.content,
6161
ctx.html_config.smart_punctuation(),
6262
Some(path),
63+
&ctx.html_config.redirect,
6364
);
6465
if !ctx.is_index && ctx.html_config.print.page_break {
6566
// Add page break between chapters
@@ -68,7 +69,25 @@ impl HtmlHandlebars {
6869
print_content
6970
.push_str(r#"<div style="break-before: page; page-break-before: always;"></div>"#);
7071
}
71-
print_content.push_str(&fixed_content);
72+
let print_page_id = {
73+
let mut base = path.display().to_string();
74+
if base.ends_with(".md") {
75+
base.truncate(base.len() - 3);
76+
}
77+
&base
78+
.replace("/", "-")
79+
.replace("\\", "-")
80+
.to_ascii_lowercase()
81+
};
82+
83+
// We have to build header links in advance so that we can know the ranges
84+
// for the headers in one page.
85+
// Insert a dummy div to make sure that we can locate the specific page.
86+
print_content.push_str(&(format!(r#"<div id="{print_page_id}"></div>"#)));
87+
print_content.push_str(&build_header_links(
88+
&build_print_element_id(&printed_item, &print_page_id),
89+
Some(print_page_id),
90+
));
7291

7392
// Update the context with data for this file
7493
let ctx_path = path
@@ -214,7 +233,23 @@ impl HtmlHandlebars {
214233
code_config: &Code,
215234
edition: Option<RustEdition>,
216235
) -> String {
217-
let rendered = build_header_links(&rendered);
236+
let rendered = build_header_links(&rendered, None);
237+
let rendered = self.post_process_common(rendered, &playground_config, code_config, edition);
238+
239+
rendered
240+
}
241+
242+
/// Applies some post-processing to the HTML to apply some adjustments.
243+
///
244+
/// This common function is used for both normal chapters (via
245+
/// `post_process`) and the combined print page.
246+
fn post_process_common(
247+
&self,
248+
rendered: String,
249+
playground_config: &Playground,
250+
code_config: &Code,
251+
edition: Option<RustEdition>,
252+
) -> String {
218253
let rendered = fix_code_blocks(&rendered);
219254
let rendered = add_playground_pre(&rendered, playground_config, edition);
220255
let rendered = hide_lines(&rendered, code_config);
@@ -577,7 +612,7 @@ impl Renderer for HtmlHandlebars {
577612
debug!("Render template");
578613
let rendered = handlebars.render("index", &data)?;
579614

580-
let rendered = self.post_process(
615+
let rendered = self.post_process_common(
581616
rendered,
582617
&html_config.playground,
583618
&html_config.code,
@@ -800,9 +835,34 @@ fn make_data(
800835
Ok(data)
801836
}
802837

838+
/// Go through the rendered print page HTML,
839+
/// add path id prefix to all the elements id as well as footnote links.
840+
fn build_print_element_id(html: &str, print_page_id: &str) -> String {
841+
static ALL_ID: Lazy<Regex> = Lazy::new(|| Regex::new(r#"(<[^>]*?id=")([^"]+?)""#).unwrap());
842+
static FOOTNOTE_ID: Lazy<Regex> = Lazy::new(|| {
843+
Regex::new(
844+
r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""##,
845+
)
846+
.unwrap()
847+
});
848+
849+
let temp_html = ALL_ID.replace_all(html, |caps: &Captures<'_>| {
850+
format!("{}{}-{}\"", &caps[1], print_page_id, &caps[2])
851+
});
852+
853+
FOOTNOTE_ID
854+
.replace_all(&temp_html, |caps: &Captures<'_>| {
855+
format!("{}{}-{}\"", &caps[1], print_page_id, &caps[2])
856+
})
857+
.into_owned()
858+
}
859+
803860
/// Goes through the rendered HTML, making sure all header tags have
804861
/// an anchor respectively so people can link to sections directly.
805-
fn build_header_links(html: &str) -> String {
862+
///
863+
/// `print_page_id` should be set to the print page ID prefix when adjusting the
864+
/// print page.
865+
fn build_header_links(html: &str, print_page_id: Option<&str>) -> String {
806866
static BUILD_HEADER_LINKS: Lazy<Regex> = Lazy::new(|| {
807867
Regex::new(r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#).unwrap()
808868
});
@@ -831,21 +891,34 @@ fn build_header_links(html: &str) -> String {
831891
caps.get(2).map(|x| x.as_str().to_string()),
832892
caps.get(3).map(|x| x.as_str().to_string()),
833893
&mut id_counter,
894+
print_page_id,
834895
)
835896
})
836897
.into_owned()
837898
}
838899

839900
/// Insert a sinle link into a header, making sure each link gets its own
840901
/// unique ID by appending an auto-incremented number (if necessary).
902+
///
903+
/// For `print.html`, we will add a path id prefix.
841904
fn insert_link_into_header(
842905
level: usize,
843906
content: &str,
844907
id: Option<String>,
845908
classes: Option<String>,
846909
id_counter: &mut HashMap<String, usize>,
910+
print_page_id: Option<&str>,
847911
) -> String {
848-
let id = id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter));
912+
let id = if let Some(print_page_id) = print_page_id {
913+
let content_id = {
914+
#[allow(deprecated)]
915+
utils::id_from_content(content)
916+
};
917+
let with_prefix = format!("{} {}", print_page_id, content_id);
918+
id.unwrap_or_else(|| utils::unique_id_from_content(&with_prefix, id_counter))
919+
} else {
920+
id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter))
921+
};
849922
let classes = classes
850923
.map(|s| format!(" class=\"{s}\""))
851924
.unwrap_or_default();
@@ -1122,7 +1195,7 @@ mod tests {
11221195
];
11231196

11241197
for (src, should_be) in inputs {
1125-
let got = build_header_links(src);
1198+
let got = build_header_links(src, None);
11261199
assert_eq!(got, should_be);
11271200
}
11281201
}

0 commit comments

Comments
 (0)