Skip to content

Commit

Permalink
Merge branch 'main' into make-comrak-ext-options-non-exhaustive
Browse files Browse the repository at this point in the history
  • Loading branch information
CosmicHorrorDev authored May 31, 2023
2 parents bdb030e + a87860c commit aaaa634
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
target
comrak-*
.vscode
.idea
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ exclude = [
resolver = "2"
edition = "2018"

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[profile.release]
lto = true

Expand Down
21 changes: 14 additions & 7 deletions src/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,9 @@ impl<'o> HtmlFormatter<'o> {
self.footnote_ix += 1;
self.output.write_all(b"<li")?;
self.render_sourcepos(node)?;
writeln!(self.output, " id=\"fn-{}\">", nfd.name)?;
self.output.write_all(b" id=\"fn-")?;
self.escape_href(nfd.name.as_bytes())?;
self.output.write_all(b"\">")?;
} else {
if self.put_footnote_backref(nfd)? {
self.output.write_all(b"\n")?;
Expand All @@ -962,10 +964,13 @@ impl<'o> HtmlFormatter<'o> {
if nfr.ref_num > 1 {
ref_id = format!("{}-{}", ref_id, nfr.ref_num);
}
write!(
self.output, " class=\"footnote-ref\"><a href=\"#fn-{}\" id=\"{}\" data-footnote-ref>{}</a></sup>",
nfr.name, ref_id, nfr.ix,
)?;

self.output
.write_all(b" class=\"footnote-ref\"><a href=\"#fn-")?;
self.escape_href(nfr.name.as_bytes())?;
self.output.write_all(b"\" id=\"")?;
self.escape_href(ref_id.as_bytes())?;
write!(self.output, "\" data-footnote-ref>{}</a></sup>", nfr.ix)?;
}
}
NodeValue::TaskItem(symbol) => {
Expand Down Expand Up @@ -1018,10 +1023,12 @@ impl<'o> HtmlFormatter<'o> {
write!(self.output, " ")?;
}

self.output.write_all(b"<a href=\"#fnref-")?;
self.escape_href(nfd.name.as_bytes())?;
write!(
self.output,
"<a href=\"#fnref-{}{}\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"{}{}\" aria-label=\"Back to reference {}{}\">↩{}</a>",
nfd.name, ref_suffix, self.footnote_ix, ref_suffix, self.footnote_ix, ref_suffix, superscript
"{}\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"{}{}\" aria-label=\"Back to reference {}{}\">↩{}</a>",
ref_suffix, self.footnote_ix, ref_suffix, self.footnote_ix, ref_suffix, superscript
)?;
}
Ok(true)
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
//! # }
//! ```
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(
missing_docs,
missing_debug_implementations,
Expand Down
2 changes: 1 addition & 1 deletion src/parser/inlines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1197,7 +1197,7 @@ impl<'a, 'r, 'o, 'd, 'i, 'c, 'subj> Subject<'a, 'r, 'o, 'd, 'i, 'c, 'subj> {
}

// Need to normalize both to lookup in refmap and to call callback
let lab = strings::normalize_label(&lab);
let lab = strings::normalize_label(&lab, false);
let mut reff = if found_label {
self.refmap.lookup(&lab)
} else {
Expand Down
14 changes: 8 additions & 6 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,8 @@ pub struct ComrakExtensionOptions {
pub front_matter_delimiter: Option<String>,

#[cfg(feature = "shortcodes")]
/// Available if "shortcodes" feature is enabled. Phrases wrapped inside of ':' blocks will be
/// replaced with emojis.
#[cfg_attr(docsrs, doc(cfg(feature = "shortcodes")))]
/// Phrases wrapped inside of ':' blocks will be replaced with emojis.
///
/// ```
/// # use comrak::{markdown_to_html, ComrakOptions};
Expand Down Expand Up @@ -1788,11 +1788,11 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
NodeValue::FootnoteDefinition(ref nfd) => {
node.detach();
map.insert(
strings::normalize_label(&nfd.name),
strings::normalize_label(&nfd.name, false),
FootnoteDefinition {
ix: None,
node,
name: strings::normalize_label(&nfd.name),
name: strings::normalize_label(&nfd.name, true),
total_references: 0,
},
);
Expand All @@ -1814,7 +1814,8 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
let mut replace = None;
match ast.value {
NodeValue::FootnoteReference(ref mut nfr) => {
if let Some(ref mut footnote) = map.get_mut(&nfr.name) {
let normalized = strings::normalize_label(&nfr.name, false);
if let Some(ref mut footnote) = map.get_mut(&normalized) {
let ix = match footnote.ix {
Some(ix) => ix,
None => {
Expand All @@ -1826,6 +1827,7 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
footnote.total_references += 1;
nfr.ref_num = footnote.total_references;
nfr.ix = ix;
nfr.name = strings::normalize_label(&footnote.name, true);
} else {
replace = Some(nfr.name.clone());
}
Expand Down Expand Up @@ -2026,7 +2028,7 @@ impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
}
}

lab = strings::normalize_label(&lab);
lab = strings::normalize_label(&lab, false);
if !lab.is_empty() {
subj.refmap.map.entry(lab).or_insert(Reference {
url: String::from_utf8(strings::clean_url(url)).unwrap(),
Expand Down
1 change: 1 addition & 0 deletions src/plugins/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Plugins for enhancing the default implementation of comrak can be defined in this module.
#[cfg(feature = "syntect")]
#[cfg_attr(docsrs, doc(cfg(feature = "syntect")))]
pub mod syntect;
34 changes: 24 additions & 10 deletions src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,23 +237,25 @@ pub fn is_blank(s: &[u8]) -> bool {
true
}

pub fn normalize_label(i: &str) -> String {
pub fn normalize_label(i: &str, preserve_case: bool) -> String {
// trim_slice only removes bytes from start and end that match isspace();
// result is UTF-8.
let i = unsafe { str::from_utf8_unchecked(trim_slice(i.as_bytes())) };

let mut v = String::with_capacity(i.len());
let mut last_was_whitespace = false;
for c in i.chars() {
for e in c.to_lowercase() {
if e.is_whitespace() {
if !last_was_whitespace {
last_was_whitespace = true;
v.push(' ');
}
if c.is_whitespace() {
if !last_was_whitespace {
last_was_whitespace = true;
v.push(' ');
}
} else {
last_was_whitespace = false;
if preserve_case {
v.push(c);
} else {
last_was_whitespace = false;
v.push(e);
v.push_str(&c.to_lowercase().to_string());
}
}
}
Expand Down Expand Up @@ -308,7 +310,7 @@ pub fn trim_start_match<'s>(s: &'s str, pat: &str) -> &'s str {

#[cfg(test)]
pub mod tests {
use super::{normalize_code, split_off_front_matter};
use super::{normalize_code, normalize_label, split_off_front_matter};

#[test]
fn normalize_code_handles_lone_newline() {
Expand Down Expand Up @@ -341,4 +343,16 @@ pub mod tests {
Some(("!@#\r\n\r\nfoo: \n!@# \r\nquux\n!@#\r\n\n", "\nYes!\n"))
);
}

#[test]
fn normalize_label_lowercase() {
assert_eq!(normalize_label(" Foo\u{A0}BAR ", false), "foo bar");
assert_eq!(normalize_label(" FooİBAR ", false), "fooi\u{307}bar");
}

#[test]
fn normalize_label_preserve() {
assert_eq!(normalize_label(" Foo\u{A0}BAR ", true), "Foo BAR");
assert_eq!(normalize_label(" FooİBAR ", true), "FooİBAR");
}
}
44 changes: 44 additions & 0 deletions src/tests/footnotes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,50 @@ fn footnote_with_superscript() {
);
}

#[test]
fn footnote_escapes_name() {
html_opts!(
[extension.footnotes],
concat!(
"Here is a footnote reference.[^😄ref]\n",
"\n",
"[^😄ref]: Here is the footnote.\n",
),
concat!(
"<p>Here is a footnote reference.<sup class=\"footnote-ref\"><a href=\"#fn-%F0%9F%98%84ref\" id=\"fnref-%F0%9F%98%84ref\" data-footnote-ref>1</a></sup></p>\n",
"<section class=\"footnotes\" data-footnotes>\n",
"<ol>\n",
"<li id=\"fn-%F0%9F%98%84ref\">\n",
"<p>Here is the footnote. <a href=\"#fnref-%F0%9F%98%84ref\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a></p>\n",
"</li>\n",
"</ol>\n",
"</section>\n"
),
);
}

#[test]
fn footnote_case_insensitive_and_case_preserving() {
html_opts!(
[extension.footnotes],
concat!(
"Here is a footnote reference.[^AB] and [^ab]\n",
"\n",
"[^aB]: Here is the footnote.\n",
),
concat!(
"<p>Here is a footnote reference.<sup class=\"footnote-ref\"><a href=\"#fn-aB\" id=\"fnref-aB\" data-footnote-ref>1</a></sup> and <sup class=\"footnote-ref\"><a href=\"#fn-aB\" id=\"fnref-aB-2\" data-footnote-ref>1</a></sup></p>\n",
"<section class=\"footnotes\" data-footnotes>\n",
"<ol>\n",
"<li id=\"fn-aB\">\n",
"<p>Here is the footnote. <a href=\"#fnref-aB\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\">↩</a> <a href=\"#fnref-aB-2\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1-2\" aria-label=\"Back to reference 1-2\">↩<sup class=\"footnote-ref\">2</sup></a></p>\n",
"</li>\n",
"</ol>\n",
"</section>\n"
),
);
}

#[test]
fn sourcepos() {
assert_ast_match!(
Expand Down

0 comments on commit aaaa634

Please sign in to comment.