Skip to content

Commit edf3067

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 f4e1da6 commit edf3067

File tree

3 files changed

+335
-62
lines changed

3 files changed

+335
-62
lines changed

src/renderer/html_handlebars/hbs_renderer.rs

+84-8
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,38 @@ impl HtmlHandlebars {
5656

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

59-
let fixed_content =
60-
utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path));
59+
let printed_item = utils::render_markdown_with_path_and_redirects(
60+
&ch.content,
61+
ctx.html_config.curly_quotes,
62+
Some(path),
63+
&ctx.html_config.redirect,
64+
);
6165
if !ctx.is_index && ctx.html_config.print.page_break {
6266
// Add page break between chapters
6367
// 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
6468
// Add both two CSS properties because of the compatibility issue
6569
print_content
6670
.push_str(r#"<div style="break-before: page; page-break-before: always;"></div>"#);
6771
}
68-
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+
));
6991

7092
// Update the context with data for this file
7193
let ctx_path = path
@@ -210,7 +232,23 @@ impl HtmlHandlebars {
210232
code_config: &Code,
211233
edition: Option<RustEdition>,
212234
) -> String {
213-
let rendered = build_header_links(&rendered);
235+
let rendered = build_header_links(&rendered, None);
236+
let rendered = self.post_process_common(rendered, &playground_config, code_config, edition);
237+
238+
rendered
239+
}
240+
241+
/// Applies some post-processing to the HTML to apply some adjustments.
242+
///
243+
/// This common function is used for both normal chapters (via
244+
/// `post_process`) and the combined print page.
245+
fn post_process_common(
246+
&self,
247+
rendered: String,
248+
playground_config: &Playground,
249+
code_config: &Code,
250+
edition: Option<RustEdition>,
251+
) -> String {
214252
let rendered = fix_code_blocks(&rendered);
215253
let rendered = add_playground_pre(&rendered, playground_config, edition);
216254
let rendered = hide_lines(&rendered, code_config);
@@ -568,7 +606,7 @@ impl Renderer for HtmlHandlebars {
568606
debug!("Render template");
569607
let rendered = handlebars.render("index", &data)?;
570608

571-
let rendered = self.post_process(
609+
let rendered = self.post_process_common(
572610
rendered,
573611
&html_config.playground,
574612
&html_config.code,
@@ -779,9 +817,34 @@ fn make_data(
779817
Ok(data)
780818
}
781819

820+
/// Go through the rendered print page HTML,
821+
/// add path id prefix to all the elements id as well as footnote links.
822+
fn build_print_element_id(html: &str, print_page_id: &str) -> String {
823+
static ALL_ID: Lazy<Regex> = Lazy::new(|| Regex::new(r#"(<[^>]*?id=")([^"]+?)""#).unwrap());
824+
static FOOTNOTE_ID: Lazy<Regex> = Lazy::new(|| {
825+
Regex::new(
826+
r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""##,
827+
)
828+
.unwrap()
829+
});
830+
831+
let temp_html = ALL_ID.replace_all(html, |caps: &Captures<'_>| {
832+
format!("{}{}-{}\"", &caps[1], print_page_id, &caps[2])
833+
});
834+
835+
FOOTNOTE_ID
836+
.replace_all(&temp_html, |caps: &Captures<'_>| {
837+
format!("{}{}-{}\"", &caps[1], print_page_id, &caps[2])
838+
})
839+
.into_owned()
840+
}
841+
782842
/// Goes through the rendered HTML, making sure all header tags have
783843
/// an anchor respectively so people can link to sections directly.
784-
fn build_header_links(html: &str) -> String {
844+
///
845+
/// `print_page_id` should be set to the print page ID prefix when adjusting the
846+
/// print page.
847+
fn build_header_links(html: &str, print_page_id: Option<&str>) -> String {
785848
static BUILD_HEADER_LINKS: Lazy<Regex> = Lazy::new(|| {
786849
Regex::new(r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#).unwrap()
787850
});
@@ -810,21 +873,34 @@ fn build_header_links(html: &str) -> String {
810873
caps.get(2).map(|x| x.as_str().to_string()),
811874
caps.get(3).map(|x| x.as_str().to_string()),
812875
&mut id_counter,
876+
print_page_id,
813877
)
814878
})
815879
.into_owned()
816880
}
817881

818882
/// Insert a sinle link into a header, making sure each link gets its own
819883
/// unique ID by appending an auto-incremented number (if necessary).
884+
///
885+
/// For `print.html`, we will add a path id prefix.
820886
fn insert_link_into_header(
821887
level: usize,
822888
content: &str,
823889
id: Option<String>,
824890
classes: Option<String>,
825891
id_counter: &mut HashMap<String, usize>,
892+
print_page_id: Option<&str>,
826893
) -> String {
827-
let id = id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter));
894+
let id = if let Some(print_page_id) = print_page_id {
895+
let content_id = {
896+
#[allow(deprecated)]
897+
utils::id_from_content(content)
898+
};
899+
let with_prefix = format!("{} {}", print_page_id, content_id);
900+
id.unwrap_or_else(|| utils::unique_id_from_content(&with_prefix, id_counter))
901+
} else {
902+
id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter))
903+
};
828904
let classes = classes
829905
.map(|s| format!(" class=\"{s}\""))
830906
.unwrap_or_default();
@@ -1113,7 +1189,7 @@ mod tests {
11131189
];
11141190

11151191
for (src, should_be) in inputs {
1116-
let got = build_header_links(src);
1192+
let got = build_header_links(src, None);
11171193
assert_eq!(got, should_be);
11181194
}
11191195
}

0 commit comments

Comments
 (0)