summaryrefslogtreecommitdiff
path: root/src/generate_html.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/generate_html.rs')
-rw-r--r--src/generate_html.rs104
1 files changed, 78 insertions, 26 deletions
diff --git a/src/generate_html.rs b/src/generate_html.rs
index dca68f7..af48d2e 100644
--- a/src/generate_html.rs
+++ b/src/generate_html.rs
@@ -63,15 +63,31 @@ pub fn generate_html_redirect(path: &str) -> String {
pub fn get_html_head(page: &Page, website: &Website) -> String {
+ let mut include_default_head = true;
+ let mut html_head = String::new();
+ for block in &page.document.blocks {
+ if let markdown::Block::Fragment { language, content } = block {
+ if language == "override-html-head" {
+ html_head.push_str(content);
+ include_default_head = false;
+ }
+ if language == "embed-html-head" {
+ html_head.push_str(content);
+ }
+ }
+ }
+ if include_default_head {
+ html_head.insert_str(0, &website.get_config("html.head"));
+ }
let root = page.root();
- website.get_config("html.head")
+ html_head
.replace("href='/", &format!("href='{root}"))
.replace("src='/", &format!("src='{root}"))
}
pub fn get_table_of_contents(page: &Page) -> String {
- if page.headings.iter().filter(|h| h.level != Level::Heading3).count() < 3 {
+ if page.headings.len() < 3 {
return String::new();
}
let mut toc = String::from("<details><summary></summary><ul>\n");
@@ -94,7 +110,7 @@ pub fn get_table_of_contents(page: &Page) -> String {
pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Website) -> String {
- let from = &page.name;
+ let from = &page;
let mut html = String::new();
macro_rules! line_to_html {
@@ -112,7 +128,7 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
for block in &document.blocks {
match block {
Block::Heading { level, line } => {
- let id = make_url_safe(strip_appendix(&line_to_html!(line)));
+ let id = make_url_safe(strip_appendix(&line.to_string()));
match level {
Level::Heading1 => tag!("h1", line, format!("id='{id}'")),
Level::Heading2 => tag!("h2", line, format!("id='{id}'")),
@@ -176,6 +192,7 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
}
}
let label = sanitize_text(label, true);
+ let path = sanitize_text(&path, false);
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>"),
@@ -192,7 +209,15 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
"embed-html" => html!("{content}"),
"embed-css" => wrap!("style", html!("{content}")),
"embed-javascript"|"embed-js" => wrap!("script", html!("{content}")),
- "hidden"|"todo"|"embed-html-head" => (),
+ "embed-html-head"|"override-html-head" => (),
+ "hidden"|"todo" => (),
+ "poem" => wrap!("div", "class='poem'", for line in content.lines() {
+ let line = line.trim_end();
+ match line.is_empty() {
+ true => html!("<br>"),
+ false => html!("<p>{}</p>", sanitize_text(line, true)),
+ }
+ }),
"recipe" => {
let recipe = Recipe::parse(content);
html!("<div class='recipe'><ul>");
@@ -207,9 +232,9 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
warn!("Gallery on page {from:?} references nonexistent image {file:?}");
continue;
}
- let large = format!("{root}images/large/{file}");
- // let small = format!("{root}images/small/{file}");
- let thumb = format!("{root}images/thumb/{file}");
+ let large = sanitize_text(&format!("{root}images/large/{file}"), false);
+ // let small = sanitize_text(&format!("{root}images/small/{file}"), false);
+ let thumb = sanitize_text(&format!("{root}images/thumb/{file}"), false);
html!("<a href='{large}'><img src='{thumb}' /></a>");
}),
"gallery-nav" => wrap!("div", "class='gallery-nav'", for line in content.lines() {
@@ -219,7 +244,7 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
let image = image.trim();
let ParsedLink { path, class, label } = parse_internal_link(name, page, website);
if website.has_image(image) {
- let thumb = format!("{root}images/thumb/{image}");
+ let thumb = sanitize_text(&format!("{root}images/thumb/{image}"), false);
html!("<a href='{path}' class='{class}'><img src='{thumb}'/><p>{label}</p></a>")
} else {
warn!("Gallery-nav on page {from:?} references nonexistent image {image:?}");
@@ -228,11 +253,24 @@ pub fn document_to_html(document: &MarkdownDocument, page: &Page, website: &Webs
warn!("Gallery-nav on page {from:?} has line without a '::' separator");
}
}),
- _ => {
- html!("<pre class='{language}'>");
- html!("{}", sanitize_text(content, false));
- html!("</pre>");
- },
+ _ => wrap!("pre", format!("class='{language}'"), {
+ if let Some(i) = website.highlighters.languages.get(language) {
+ let mut source = String::new();
+ let highlighter = &website.highlighters.highlighters[*i];
+ for span in highlighter.highlight(content) {
+ if span.tag.is_empty() {
+ source.push_str(&sanitize_text(&span.text, false));
+ } else {
+ source.push_str(&format!("<span class='{}'>", span.tag.to_lowercase()));
+ source.push_str(&sanitize_text(&span.text, false));
+ source.push_str("</span>");
+ }
+ }
+ html!("{source}");
+ } else {
+ html!("{}", sanitize_text(content, false))
+ }
+ })
}
}
Block::Break => html!("<hr>"),
@@ -294,8 +332,11 @@ fn line_to_html(line: &Line, page: &Page, website: &Website) -> String {
let text = &sanitize_text(text, false); html.push_str(&format!("<code>{text}</code>")) }
Token::Math(text) => {
let text = &sanitize_text(text, false); html.push_str(&format!("<span class='math'>{text}</span>")) }
- Token::InternalLink(name) => {
- let ParsedLink { path, class, label } = parse_internal_link(name, page, website);
+ Token::InternalLink{ label: link_label, path } => {
+ let ParsedLink { path, class, mut label } = parse_internal_link(path, page, website);
+ if !link_label.is_empty() {
+ label = link_label.to_string();
+ }
html.push_str(&format!("<a href='{path}' class='{class}'>{label}</a>"))
}
Token::ExternalLink { label, path } => {
@@ -316,7 +357,7 @@ struct ParsedLink {
}
fn parse_internal_link(name: &str, page: &Page, website: &Website) -> ParsedLink {
- let from = &page.name;
+ let from = &page;
let (class, label, path) = match name.split_once('#') {
Some(("", heading)) => ("heading", heading, format!("#{}", strip_appendix(heading))),
Some((page, heading)) => ("page", heading, format!("{page}.html#{}", strip_appendix(heading))),
@@ -341,11 +382,12 @@ fn parse_internal_link(name: &str, page: &Page, website: &Website) -> ParsedLink
warn!("Page {from:?} contains link to nonexistent internal heading {heading:?}");
}
}
+ let path = url_encode(&path);
ParsedLink { path, class, label }
}
fn parse_external_link(label: &str, path: &str, page: &Page, website: &Website) -> ParsedLink {
- let from = &page.name;
+ let from = &page;
let mut path = path.to_owned();
let mut label = label.to_string();
let mut is_internal = true;
@@ -372,6 +414,7 @@ fn parse_external_link(label: &str, path: &str, page: &Page, website: &Website)
};
}
}
+ let path = url_encode(&path);
let label = sanitize_text(&label, true);
ParsedLink { path, class: "external", label }
}
@@ -398,17 +441,26 @@ fn sanitize_text(text: &str, fancy: bool) -> String {
},
'<' => output.push_str("&lt;"),
'>' => output.push_str("&gt;"),
- '"' if fancy => match prev.is_whitespace() {
- true => output.push('“'),
- false => output.push('”'),
+ '"' => match fancy {
+ true => match prev.is_whitespace() {
+ true => output.push('“'),
+ false => output.push('”'),
+ }
+ false => output.push_str("&#34;"),
},
- '\'' if fancy => match prev.is_whitespace() {
- true => output.push('‘'),
- false => output.push('’'),
+ '\'' => match fancy {
+ true => match prev.is_whitespace() {
+ true => output.push('‘'),
+ false => output.push('’'),
+ }
+ false => output.push_str("&#39;"),
},
'-' if fancy => match prev.is_whitespace() && next.is_whitespace() {
- true => output.push('—'),
- false => output.push('-'),
+ true => match i > 0 {
+ true => output.push('—'), // em-dash, for mid-sentence
+ false => output.push('–'), // en-dash, for start of line
+ }
+ false => output.push('-'), // regular dash, for mid-word
}
_ => output.push(*c),
}