diff options
author | metamuffin <metamuffin@disroot.org> | 2024-05-04 01:23:33 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-05-04 01:23:50 +0200 |
commit | 4ea388dc7dab5dd7f784c180abafe59c39bb884d (patch) | |
tree | 60371b9c2d3f1cb8942dc9b650a6d9a61624ad3f | |
parent | a78ba6bc689d9d3eed2c0f49cd075ceee045c95f (diff) | |
download | metamuffin-website-4ea388dc7dab5dd7f784c180abafe59c39bb884d.tar metamuffin-website-4ea388dc7dab5dd7f784c180abafe59c39bb884d.tar.bz2 metamuffin-website-4ea388dc7dab5dd7f784c180abafe59c39bb884d.tar.zst |
syntax highlight code blocks and inline math
-rw-r--r-- | Cargo.lock | 199 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/blog/mod.rs | 96 |
3 files changed, 284 insertions, 13 deletions
@@ -264,12 +264,27 @@ dependencies = [ ] [[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] name = "binascii" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" [[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -369,6 +384,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] name = "crossbeam-utils" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -505,6 +529,16 @@ dependencies = [ ] [[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -900,6 +934,12 @@ dependencies = [ ] [[package]] +name = "latex2mathml" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678cf5bdb3ba63a264e6e0c9eee36538ca1d2da0afa4dd801c1f96309e710765" + +[[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -912,6 +952,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] name = "linux-raw-sys" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1012,10 +1067,12 @@ dependencies = [ "futures", "include_dir", "iso8601", + "latex2mathml", "log", "markdown", "markup", "rocket", + "syntect", "tokio", ] @@ -1126,6 +1183,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] +name = "onig" +version = "6.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +dependencies = [ + "bitflags 1.3.2", + "libc", + "once_cell", + "onig_sys", +] + +[[package]] +name = "onig_sys" +version = "69.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1202,6 +1281,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plist" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4a0cfc5fb21a09dc6af4bf834cf10d4a32fccd9e2ea468c4b1751a097487aa" +dependencies = [ + "base64", + "indexmap 1.9.3", + "line-wrap", + "quick-xml", + "serde", + "time", +] + +[[package]] name = "polling" version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1244,6 +1343,15 @@ dependencies = [ ] [[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + +[[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1356,6 +1464,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" [[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] name = "rocket" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1483,6 +1597,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1623,6 +1752,28 @@ dependencies = [ ] [[package]] +name = "syntect" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" +dependencies = [ + "bincode", + "bitflags 1.3.2", + "flate2", + "fnv", + "once_cell", + "onig", + "plist", + "regex-syntax 0.8.3", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "walkdir", + "yaml-rust", +] + +[[package]] name = "tempfile" version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1637,6 +1788,26 @@ dependencies = [ ] [[package]] +name = "thiserror" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "thread_local" version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1904,6 +2075,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2020,6 +2201,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2240,6 +2430,15 @@ dependencies = [ ] [[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] name = "yansi" version = "1.0.0-rc" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -16,3 +16,5 @@ anyhow = "1.0.82" markup = "0.15.0" markdown = "1.0.0-alpha.17" chrono = "0.4.38" +syntect = "5.2.0" +latex2mathml = "0.2.3" diff --git a/src/blog/mod.rs b/src/blog/mod.rs index b301171..49fbd4b 100644 --- a/src/blog/mod.rs +++ b/src/blog/mod.rs @@ -8,8 +8,14 @@ use crate::uri; use anyhow::anyhow; pub use atom::r_blog_atom; use atom::rocket_uri_macro_r_blog_atom; +use latex2mathml::DisplayStyle; +use markdown::mdast::Node; +use markup::{raw, DynRender}; use rocket::{get, response::Redirect}; use std::{path::PathBuf, str::FromStr}; +use syntect::highlighting::ThemeSet; +use syntect::html::highlighted_html_for_string; +use syntect::parsing::SyntaxSet; use tokio::fs::read_to_string; pub const ARTICLE_ROOT: &'static str = "./blog/articles"; @@ -49,28 +55,92 @@ pub async fn r_blog_article(name: &str) -> MyResult<DynScaffold<'static>> { .join(PathBuf::new().with_file_name(name).with_extension("md")); let a = article_metadata(apath.clone()).await?; let text = read_to_string(apath).await?; - let html = markdown::to_html_with_options( + let ast = markdown::to_mdast( &text, - &markdown::Options { - parse: markdown::ParseOptions { - constructs: markdown::Constructs { - math_flow: true, - math_text: true, - ..Default::default() - }, - ..Default::default() - }, - compile: markdown::CompileOptions { + &markdown::ParseOptions { + constructs: markdown::Constructs { + math_flow: true, + math_text: true, ..Default::default() }, + ..Default::default() }, ) - .map_err(|e| anyhow!("the server had trouble compiling markdown: {e}"))?; + .map_err(|e| anyhow!("the server had trouble parsing markdown: {e}"))?; + Ok(Scaffold { title: a.title, content: markup::new! { - @markup::raw(&html) + @node_to_render(&ast) p{i{ "Article written by metamuffin, text licenced under CC BY-ND 4.0, non-trivial code blocks under GPL-3.0-only except where indicated otherwise" }} }, }) } + +fn node_to_render<'a>(node: &'a Node) -> DynRender<'a> { + match node { + Node::Text(s) => markup::new!(@s.value), + Node::Paragraph(el) => markup::new!(p { @for e in &el.children { @node_to_render(e) } }), + Node::List(list) => markup::new!(ul { @for e in &list.children { @node_to_render(e) } }), + Node::Root(el) => markup::new!(article { @for e in &el.children { @node_to_render(e) } }), + Node::ListItem(el) => markup::new!(li { @for e in &el.children { @node_to_render(e) } }), + Node::Emphasis(el) => markup::new!(i { @for e in &el.children { @node_to_render(e) } }), + Node::Strong(el) => markup::new!(strong { @for e in &el.children { @node_to_render(e) } }), + Node::Html(html) => markup::new!(@raw(&html.value)), + Node::Link(l) => { + markup::new!(a[href=&l.url, alt=&l.title] { @for e in &l.children { @node_to_render(e) } }) + } + Node::Break(_) => markup::new!(br;), + Node::InlineCode(s) => markup::new!(code { @s.value }), + Node::Delete(_) => markup::new!("todo1"), + Node::FootnoteReference(_) => markup::new!("todo3"), + Node::FootnoteDefinition(_) => markup::new!("todo4"), + Node::Image(_) => markup::new!("todo5"), + Node::ImageReference(_) => markup::new!("todo6"), + Node::LinkReference(_) => markup::new!("todo8"), + Node::BlockQuote(_) => markup::new!("todo10"), + Node::Table(_) => markup::new!("todo12"), + Node::ThematicBreak(_) => markup::new!("todo13"), + Node::TableRow(_) => markup::new!("todo14"), + Node::TableCell(_) => markup::new!("todo15"), + Node::Definition(_) => markup::new!("todo16"), + Node::Toml(_) + | Node::Yaml(_) + | Node::MdxjsEsm(_) + | Node::MdxJsxFlowElement(_) + | Node::MdxJsxTextElement(_) + | Node::MdxTextExpression(_) + | Node::MdxFlowExpression(_) => markup::new!("unsupported"), + Node::Heading(h) => { + let inner = markup::new!(@for e in &h.children { @node_to_render(e) }); + match h.depth { + 1 => markup::new!(h2{@inner}), + 2 => markup::new!(h3{@inner}), + 3 => markup::new!(h4{@inner}), + 4 => markup::new!(h5{@inner}), + 5 => markup::new!(h6{@inner}), + 6 => markup::new!(h6{@inner}), + _ => unreachable!(), + } + } + Node::Code(code) => { + let theme = &ThemeSet::load_defaults().themes["base16-ocean.dark"]; + let syntax = &SyntaxSet::load_defaults_newlines(); + let lang = syntax + .find_syntax_by_extension(&code.lang.to_owned().unwrap_or("txt".to_owned())) + .unwrap_or_else(|| syntax.find_syntax_by_extension("txt").unwrap()); + let html = highlighted_html_for_string(&code.value, syntax, lang, theme).unwrap(); + markup::new!(@raw(&html)) + } + Node::Math(s) => { + let mathml = latex2mathml::latex_to_mathml(&s.value, DisplayStyle::Block) + .expect("invalid block math"); + markup::new!(@raw(&mathml)) + } + Node::InlineMath(s) => { + let mathml = latex2mathml::latex_to_mathml(&s.value, DisplayStyle::Inline) + .expect("invalid inline math"); + markup::new!(@raw(&mathml)) + } + } +} |