Skip to content

Commit bed3e2f

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 abdc981 commit bed3e2f

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);
@@ -572,7 +607,7 @@ impl Renderer for HtmlHandlebars {
572607
debug!("Render template");
573608
let rendered = handlebars.render("index", &data)?;
574609

575-
let rendered = self.post_process(
610+
let rendered = self.post_process_common(
576611
rendered,
577612
&html_config.playground,
578613
&html_config.code,
@@ -783,9 +818,34 @@ fn make_data(
783818
Ok(data)
784819
}
785820

821+
/// Go through the rendered print page HTML,
822+
/// add path id prefix to all the elements id as well as footnote links.
823+
fn build_print_element_id(html: &str, print_page_id: &str) -> String {
824+
static ALL_ID: Lazy<Regex> = Lazy::new(|| Regex::new(r#"(<[^>]*?id=")([^"]+?)""#).unwrap());
825+
static FOOTNOTE_ID: Lazy<Regex> = Lazy::new(|| {
826+
Regex::new(
827+
r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""##,
828+
)
829+
.unwrap()
830+
});
831+
832+
let temp_html = ALL_ID.replace_all(html, |caps: &Captures<'_>| {
833+
format!("{}{}-{}\"", &caps[1], print_page_id, &caps[2])
834+
});
835+
836+
FOOTNOTE_ID
837+
.replace_all(&temp_html, |caps: &Captures<'_>| {
838+
format!("{}{}-{}\"", &caps[1], print_page_id, &caps[2])
839+
})
840+
.into_owned()
841+
}
842+
786843
/// Goes through the rendered HTML, making sure all header tags have
787844
/// an anchor respectively so people can link to sections directly.
788-
fn build_header_links(html: &str) -> String {
845+
///
846+
/// `print_page_id` should be set to the print page ID prefix when adjusting the
847+
/// print page.
848+
fn build_header_links(html: &str, print_page_id: Option<&str>) -> String {
789849
static BUILD_HEADER_LINKS: Lazy<Regex> = Lazy::new(|| {
790850
Regex::new(r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#).unwrap()
791851
});
@@ -814,21 +874,34 @@ fn build_header_links(html: &str) -> String {
814874
caps.get(2).map(|x| x.as_str().to_string()),
815875
caps.get(3).map(|x| x.as_str().to_string()),
816876
&mut id_counter,
877+
print_page_id,
817878
)
818879
})
819880
.into_owned()
820881
}
821882

822883
/// Insert a sinle link into a header, making sure each link gets its own
823884
/// unique ID by appending an auto-incremented number (if necessary).
885+
///
886+
/// For `print.html`, we will add a path id prefix.
824887
fn insert_link_into_header(
825888
level: usize,
826889
content: &str,
827890
id: Option<String>,
828891
classes: Option<String>,
829892
id_counter: &mut HashMap<String, usize>,
893+
print_page_id: Option<&str>,
830894
) -> String {
831-
let id = id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter));
895+
let id = if let Some(print_page_id) = print_page_id {
896+
let content_id = {
897+
#[allow(deprecated)]
898+
utils::id_from_content(content)
899+
};
900+
let with_prefix = format!("{} {}", print_page_id, content_id);
901+
id.unwrap_or_else(|| utils::unique_id_from_content(&with_prefix, id_counter))
902+
} else {
903+
id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter))
904+
};
832905
let classes = classes
833906
.map(|s| format!(" class=\"{s}\""))
834907
.unwrap_or_default();
@@ -1108,7 +1181,7 @@ mod tests {
11081181
];
11091182

11101183
for (src, should_be) in inputs {
1111-
let got = build_header_links(src);
1184+
let got = build_header_links(src, None);
11121185
assert_eq!(got, should_be);
11131186
}
11141187
}

0 commit comments

Comments
 (0)