Skip to content

Commit f234c82

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 d1c7112 commit f234c82

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
@@ -57,10 +57,11 @@ impl HtmlHandlebars {
5757

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

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

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

467-
let rendered = self.post_process(
502+
let rendered = self.post_process_common(
468503
rendered,
469504
&html_config.playground,
470505
&html_config.code,
@@ -660,9 +695,34 @@ fn make_data(
660695
Ok(data)
661696
}
662697

698+
/// Go through the rendered print page HTML,
699+
/// add path id prefix to all the elements id as well as footnote links.
700+
fn build_print_element_id(html: &str, print_page_id: &str) -> String {
701+
static ALL_ID: Lazy<Regex> = Lazy::new(|| Regex::new(r#"(<[^>]*?id=")([^"]+?)""#).unwrap());
702+
static FOOTNOTE_ID: Lazy<Regex> = Lazy::new(|| {
703+
Regex::new(
704+
r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""##,
705+
)
706+
.unwrap()
707+
});
708+
709+
let temp_html = ALL_ID.replace_all(html, |caps: &Captures<'_>| {
710+
format!("{}{}-{}\"", &caps[1], print_page_id, &caps[2])
711+
});
712+
713+
FOOTNOTE_ID
714+
.replace_all(&temp_html, |caps: &Captures<'_>| {
715+
format!("{}{}-{}\"", &caps[1], print_page_id, &caps[2])
716+
})
717+
.into_owned()
718+
}
719+
663720
/// Goes through the rendered HTML, making sure all header tags have
664721
/// an anchor respectively so people can link to sections directly.
665-
fn build_header_links(html: &str) -> String {
722+
///
723+
/// `print_page_id` should be set to the print page ID prefix when adjusting the
724+
/// print page.
725+
fn build_header_links(html: &str, print_page_id: Option<&str>) -> String {
666726
static BUILD_HEADER_LINKS: Lazy<Regex> = Lazy::new(|| {
667727
Regex::new(r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#).unwrap()
668728
});
@@ -691,21 +751,34 @@ fn build_header_links(html: &str) -> String {
691751
caps.get(2).map(|x| x.as_str().to_string()),
692752
caps.get(3).map(|x| x.as_str().to_string()),
693753
&mut id_counter,
754+
print_page_id,
694755
)
695756
})
696757
.into_owned()
697758
}
698759

699760
/// Insert a sinle link into a header, making sure each link gets its own
700761
/// unique ID by appending an auto-incremented number (if necessary).
762+
///
763+
/// For `print.html`, we will add a path id prefix.
701764
fn insert_link_into_header(
702765
level: usize,
703766
content: &str,
704767
id: Option<String>,
705768
classes: Option<String>,
706769
id_counter: &mut HashMap<String, usize>,
770+
print_page_id: Option<&str>,
707771
) -> String {
708-
let id = id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter));
772+
let id = if let Some(print_page_id) = print_page_id {
773+
let content_id = {
774+
#[allow(deprecated)]
775+
utils::id_from_content(content)
776+
};
777+
let with_prefix = format!("{} {}", print_page_id, content_id);
778+
id.unwrap_or_else(|| utils::unique_id_from_content(&with_prefix, id_counter))
779+
} else {
780+
id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter))
781+
};
709782
let classes = classes
710783
.map(|s| format!(" class=\"{s}\""))
711784
.unwrap_or_default();
@@ -982,7 +1055,7 @@ mod tests {
9821055
];
9831056

9841057
for (src, should_be) in inputs {
985-
let got = build_header_links(src);
1058+
let got = build_header_links(src, None);
9861059
assert_eq!(got, should_be);
9871060
}
9881061
}

0 commit comments

Comments
 (0)