summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBen Bridle <bridle.benjamin@gmail.com>2025-01-21 18:38:19 +1300
committerBen Bridle <bridle.benjamin@gmail.com>2025-01-21 18:38:45 +1300
commite9f25c69e1e37d038ab05e7a69ea961799615141 (patch)
treea20ccdea194a6653738e1e7f280f0ab548862c41 /src
parent8936be41ffe3d658f44a42f59af4b2582c98571a (diff)
downloadtoaster-e9f25c69e1e37d038ab05e7a69ea961799615141.zip
Only transform quotes and dashes inside presentational text
Keep quotes and dashes unmodified in monospace line elements, math blocks, math elements, and fragment blocks.
Diffstat (limited to 'src')
-rw-r--r--src/generate_html.rs40
1 files changed, 21 insertions, 19 deletions
diff --git a/src/generate_html.rs b/src/generate_html.rs
index c0026fb..e531854 100644
--- a/src/generate_html.rs
+++ b/src/generate_html.rs
@@ -6,8 +6,8 @@ use recipe::*;
pub fn generate_html(document: &MarkdownDocument, page: &Page, website: &Website) -> String {
let root = page.root();
- let page_name = sanitize_text(&page.name);
- let site_name = sanitize_text(&website.name);
+ let page_name = sanitize_text(&page.name, true);
+ let site_name = sanitize_text(&website.name, true);
let mut parent_url = String::new();
for segment in &page.parents {
parent_url.push_str(&make_url_safe(segment)); parent_url.push('/');
@@ -51,7 +51,7 @@ pub fn generate_html(document: &MarkdownDocument, page: &Page, website: &Website
pub fn generate_html_redirect(path: &str) -> String {
- let path = sanitize_text(path);
+ let path = sanitize_text(path, false);
format!("\
<!DOCTYPE html>
<head>
@@ -75,7 +75,7 @@ pub fn get_table_of_contents(page: &Page) -> String {
return String::new();
}
let mut toc = String::from("<details><summary></summary><ul>\n");
- let site_name = sanitize_text(&page.name);
+ let site_name = sanitize_text(&page.name, true);
toc.push_str(&format!("<li class='l1'><a href='#title'>{site_name}</a></li>\n"));
for heading in &page.headings {
@@ -117,7 +117,7 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
Level::Heading3 => tag!("h3", line, format!("id='{}'", make_url_safe(&line_to_html!(line)))),
}
Block::Paragraph(line) => tag!("p", line),
- Block::Math(content) => html!("<div class='math'>{content}</div>"),
+ Block::Math(content) => html!("<div class='math'>{}</div>", sanitize_text(content, false)),
Block::List(lines) => wrap!("ul", for line in lines {
// Insert a <br> tag directly after the first untagged colon.
let mut depth = 0;
@@ -169,7 +169,7 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
None => warn!("Page {from:?} embeds nonexistent static file {path:?}"),
}
}
- let label = sanitize_text(label);
+ let label = sanitize_text(label, true);
match extension.to_lowercase().as_str() {
"jpg"|"jpeg"|"png"|"webp"|"gif"|"tiff" => html!(
"<figure><a href='{path}'><img src='{path}' alt='{label}' title='{label}' /></a></figure>"),
@@ -207,7 +207,7 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
}),
_ => {
html!("<pre class='{}'>", language);
- html!("{}", sanitize_text(content));
+ html!("{}", sanitize_text(content, false));
html!("</pre>");
},
}
@@ -258,15 +258,15 @@ fn line_to_html(line: &Line, page: &Page, website: &Website) -> String {
for line_element in &line.tokens {
match line_element {
Token::Normal(text) => {
- let text = &sanitize_text(text); html.push_str(text) }
+ let text = &sanitize_text(text, true); html.push_str(text) }
Token::Bold(text) => {
- let text = &sanitize_text(text); html.push_str(&format!("<b>{text}</b>")) }
+ let text = &sanitize_text(text, true); html.push_str(&format!("<b>{text}</b>")) }
Token::Italic(text) => {
- let text = &sanitize_text(text); html.push_str(&format!("<i>{text}</i>")) }
+ let text = &sanitize_text(text, true); html.push_str(&format!("<i>{text}</i>")) }
Token::Monospace(text) => {
- let text = &sanitize_text(text); html.push_str(&format!("<code>{text}</code>")) }
+ let text = &sanitize_text(text, false); html.push_str(&format!("<code>{text}</code>")) }
Token::Math(text) => {
- let text = &sanitize_text(text); html.push_str(&format!("<span class='math'>{text}</span>")) }
+ let text = &sanitize_text(text, false); html.push_str(&format!("<span class='math'>{text}</span>")) }
Token::InternalLink(name) => {
let (class, label, path) = match name.split_once('#') {
Some(("", heading)) => ("heading", heading, format!("#{heading}")),
@@ -275,8 +275,8 @@ fn line_to_html(line: &Line, page: &Page, website: &Website) -> String {
};
let mut path = make_url_safe(&path);
let label = match label.rsplit_once('/') {
- Some((_, label)) => sanitize_text(label.trim()),
- None => sanitize_text(label.trim()),
+ Some((_, label)) => sanitize_text(label.trim(), true),
+ None => sanitize_text(label.trim(), true),
};
// Check that the linked internal page exists.
if class == "page" {
@@ -321,7 +321,7 @@ fn line_to_html(line: &Line, page: &Page, website: &Website) -> String {
};
}
}
- let label = sanitize_text(&label);
+ let label = sanitize_text(&label, true);
html.push_str(&format!("<a href='{path}' class='external'>{label}</a>"));
}
}
@@ -331,8 +331,10 @@ fn line_to_html(line: &Line, page: &Page, website: &Website) -> String {
+
+
/// Replace each HTML-reserved character with an HTML-escaped character.
-fn sanitize_text(text: &str) -> String {
+fn sanitize_text(text: &str, fancy: bool) -> String {
let mut output = String::new();
let chars: Vec<char> = text.chars().collect();
for (i, c) in chars.iter().enumerate() {
@@ -352,15 +354,15 @@ fn sanitize_text(text: &str) -> String {
},
'<' => output.push_str("&lt;"),
'>' => output.push_str("&gt;"),
- '"' => match prev.is_whitespace() {
+ '"' if fancy => match prev.is_whitespace() {
true => output.push('“'),
false => output.push('”'),
},
- '\'' => match prev.is_whitespace() {
+ '\'' if fancy => match prev.is_whitespace() {
true => output.push('‘'),
false => output.push('’'),
},
- '-' => match prev.is_whitespace() && next.is_whitespace() {
+ '-' if fancy => match prev.is_whitespace() && next.is_whitespace() {
true => output.push('—'),
false => output.push('-'),
}