Skip to content

Commit

Permalink
Make print page (print.html) links link to anchors on the print page
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
HollowMan6 committed Sep 30, 2023
1 parent 2d853bf commit 2dd22d5
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 31 deletions.
78 changes: 71 additions & 7 deletions src/renderer/html_handlebars/hbs_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,38 @@ impl HtmlHandlebars {
let content = ch.content.clone();
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes);

let fixed_content =
utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path));
let fixed_content = utils::render_markdown_with_path(
&ch.content,
ctx.html_config.curly_quotes,
Some(path),
ctx.html_config.redirect,
);
if !ctx.is_index && ctx.html_config.print.page_break {
// Add page break between chapters
// 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
// Add both two CSS properties because of the compatibility issue
print_content
.push_str(r#"<div style="break-before: page; page-break-before: always;"></div>"#);
}
print_content.push_str(&fixed_content);
let path_id = {
let mut base = path.display().to_string();
if base.ends_with(".md") {
base.replace_range(base.len() - 3.., "");
}
&base
.replace("/", "-")
.replace("\\", "-")
.to_ascii_lowercase()
};

// We have to build header links in advance so that we can know the ranges
// for the headers in one page.
// Insert a dummy div to make sure that we can locate the specific page.
print_content.push_str(&(format!(r#"<div id="{}"></div>"#, &path_id)));
print_content.push_str(&build_header_links(
&build_print_element_id(&fixed_content, &path_id),
Some(path_id),
));

// Update the context with data for this file
let ctx_path = path
Expand Down Expand Up @@ -211,7 +233,7 @@ impl HtmlHandlebars {
code_config: &Code,
edition: Option<RustEdition>,
) -> String {
let rendered = build_header_links(&rendered);
let rendered = build_header_links(&rendered, None);
let rendered = fix_code_blocks(&rendered);
let rendered = add_playground_pre(&rendered, playground_config, edition);
let rendered = hide_lines(&rendered, code_config);
Expand Down Expand Up @@ -809,9 +831,43 @@ fn make_data(
Ok(data)
}

/// Goes through part of the rendered print page HTML,
/// add path id prefix to all the elements id as well as footnote links.
fn build_print_element_id(html: &str, path_id: &str) -> String {
let all_id = Regex::new(r#"(<[^>]*?id=")([^"]+?)""#).unwrap();
let footnote_id = Regex::new(
r##"(<sup [^>]*?class="footnote-reference"[^>]*?>[^<]*?<a [^>]*?href="#)([^"]+?)""##,
)
.unwrap();

if path_id.is_empty() {
return html.to_string();
}

let temp_html = all_id
.replace_all(html, |caps: &Captures<'_>| {
let mut fixed = String::new();
fixed.push_str(&path_id);
fixed.push_str("-");
fixed.push_str(&caps[2]);
format!("{}{}\"", &caps[1], fixed)
})
.into_owned();

footnote_id
.replace_all(&temp_html, |caps: &Captures<'_>| {
let mut fixed = String::new();
fixed.push_str(&path_id);
fixed.push_str("-");
fixed.push_str(&caps[2]);
format!("{}{}\"", &caps[1], fixed)
})
.into_owned()
}

/// Goes through the rendered HTML, making sure all header tags have
/// an anchor respectively so people can link to sections directly.
fn build_header_links(html: &str) -> String {
fn build_header_links(html: &str, path_id: Option<&str>) -> String {
static BUILD_HEADER_LINKS: Lazy<Regex> = Lazy::new(|| {
Regex::new(r#"<h(\d)(?: id="([^"]+)")?(?: class="([^"]+)")?>(.*?)</h\d>"#).unwrap()
});
Expand Down Expand Up @@ -840,21 +896,29 @@ fn build_header_links(html: &str) -> String {
caps.get(2).map(|x| x.as_str().to_string()),
caps.get(3).map(|x| x.as_str().to_string()),
&mut id_counter,
path_id,
)
})
.into_owned()
}

/// Insert a sinle link into a header, making sure each link gets its own
/// unique ID by appending an auto-incremented number (if necessary).
///
/// For `print.html`, we will add a path id prefix.
fn insert_link_into_header(
level: usize,
content: &str,
id: Option<String>,
classes: Option<String>,
id_counter: &mut HashMap<String, usize>,
path_id: Option<&str>,
) -> String {
let id = id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter));
let id = if let Some(path_id) = path_id {
id.unwrap_or_else(|| utils::unique_id_from_content_with_path(content, id_counter, path_id))
} else {
id.unwrap_or_else(|| utils::unique_id_from_content(content, id_counter))
};
let classes = classes
.map(|s| format!(" class=\"{s}\""))
.unwrap_or_default();
Expand Down Expand Up @@ -1142,7 +1206,7 @@ mod tests {
];

for (src, should_be) in inputs {
let got = build_header_links(src);
let got = build_header_links(src, None);
assert_eq!(got, should_be);
}
}
Expand Down
Loading

0 comments on commit 2dd22d5

Please sign in to comment.