From cd5c12ff211a70f9d7f086abcf537220f7023ae4 Mon Sep 17 00:00:00 2001 From: Quin Date: Sat, 5 Jul 2025 22:28:16 -0600 Subject: [PATCH 1/3] Add basic syntax highlighting support with syntect. Closes #5. --- htmlua-parser/Cargo.toml | 1 + htmlua-parser/src/render.rs | 89 +++++++++++++++++++++++++++++++++++-- htmlua-parser/src/serve.rs | 5 ++- 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/htmlua-parser/Cargo.toml b/htmlua-parser/Cargo.toml index c3cbe07..1e27f09 100644 --- a/htmlua-parser/Cargo.toml +++ b/htmlua-parser/Cargo.toml @@ -8,6 +8,7 @@ anyhow = "1.0.98" kuchikiki = "0.8.2" mlua = { version = "0.10.5", features = ["lua54", "vendored", "serialize"] } pulldown-cmark = "0.13.0" +syntect = "5.2.0" tendril = "0.4.3" diff --git a/htmlua-parser/src/render.rs b/htmlua-parser/src/render.rs index eb50c2c..396df4f 100644 --- a/htmlua-parser/src/render.rs +++ b/htmlua-parser/src/render.rs @@ -4,6 +4,13 @@ use anyhow::{Result, anyhow}; use kuchikiki::{NodeRef, traits::TendrilSink}; use mlua::{Lua, Table}; use pulldown_cmark::{Options, Parser, html}; +use syntect::{ + easy::HighlightLines, + highlighting::{Style, ThemeSet}, + html::{IncludeBackground, styled_line_to_highlighted_html}, + parsing::SyntaxSet, + util::LinesWithEndings, +}; fn create_htmlua_stdlib(l: &Lua, stdout: &Rc>) -> mlua::Result { let t = l.create_table()?; @@ -119,7 +126,9 @@ pub fn expand_template(document: NodeRef, component_path: &PathBuf, include_from item_path.push(include_path); let component_text = read_to_string(item_path)?; let new_doc = kuchikiki::parse_html().one(component_text); - let new_node = new_doc.select_first("body").map_err(|()| anyhow!("Failed to get body"))?; + let new_node = new_doc + .select_first("body") + .map_err(|()| anyhow!("Failed to get body"))?; let replaced_node = expand_template(new_node.as_node().clone(), component_path, Some(i.as_node()))?; i.as_node().insert_before(replaced_node); i.as_node().detach(); @@ -128,6 +137,49 @@ pub fn expand_template(document: NodeRef, component_path: &PathBuf, include_from Ok(document) } +pub fn process_syntax_highlighting(document: NodeRef) -> Result { + let ps = SyntaxSet::load_defaults_newlines(); + let ts = ThemeSet::load_defaults(); + let syntax_elements: Vec<_> = match document.select("syntaxhighlight") { + Ok(e) => e.collect(), + Err(()) => return Err(anyhow!("Unable to find syntaxhighlight elements")), + }; + for node in syntax_elements { + let attrs = match node.as_node().as_element() { + Some(e) => e.attributes.borrow(), + None => continue, + }; + let language = attrs.get("lang").unwrap_or("text"); + let theme_name = attrs.get("theme").unwrap_or("base16-ocean.dark"); + if let Some(text_node) = node.as_node().first_child() { + if let Some(code_text) = text_node.as_text() { + let syntax = ps + .find_syntax_by_extension(language) + .or_else(|| ps.find_syntax_by_name(language)) + .unwrap_or_else(|| ps.find_syntax_plain_text()); + let theme = &ts.themes[theme_name]; + let mut h = HighlightLines::new(syntax, theme); + let mut html_output = String::new(); + write!(html_output, r#"
"#)?;
+                html_output.push_str("");
+                for line in LinesWithEndings::from(&code_text.borrow()) {
+                    let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap();
+                    let escaped = styled_line_to_highlighted_html(&ranges[..], IncludeBackground::No).unwrap();
+                    html_output.push_str(&escaped);
+                }
+                html_output.push_str("
"); + let html_fragment = kuchikiki::parse_html().one(html_output); + for child in html_fragment.children() { + node.as_node().insert_before(child); + } + // Remove the original syntaxhighlight node. + node.as_node().detach(); + } + } + } + Ok(document) +} + #[cfg(test)] mod tests { use super::*; @@ -237,7 +289,6 @@ mod tests { let text = d.select_first("#inc1").unwrap().as_node().text_contents(); assert_eq!(text, "included1"); assert!(d.select_first("include").is_err()); - } #[test] @@ -292,7 +343,7 @@ mod tests { #[test] fn basic_markdown() { - let page = r#" + let page = r" @@ -311,11 +362,41 @@ This is a paragraph with **bold text** and *italic text*. - "#; + "; let document = kuchikiki::parse_html().one(page); let d = process_markdown(document).unwrap(); assert!(d.select_first("markdown").is_err()); assert!(d.select_first("p").is_ok()); assert!(d.select_first("ul").is_ok()); } + + #[test] + fn basic_syntax_highlighting() { + let page = r#" + + + + Syntax Highlighting Test + + +

Code Example

+ +fn main() { + println!("Hello, world!"); + let x = 42; + let y = "test"; +} + + + "#; + let document = kuchikiki::parse_html().one(page); + let d = process_syntax_highlighting(document).unwrap(); + assert!(d.select_first("syntaxhighlight").is_err()); + assert!(d.select_first("pre").is_ok()); + assert!(d.select_first("code").is_ok()); + let pre = d.select_first("pre").unwrap(); + let attrs = pre.attributes.borrow(); + assert_eq!(attrs.get("data-lang"), Some("rust")); + assert!(attrs.get("class").unwrap().contains("syntax-highlight")); + } } diff --git a/htmlua-parser/src/serve.rs b/htmlua-parser/src/serve.rs index eb9a043..5b63e3d 100644 --- a/htmlua-parser/src/serve.rs +++ b/htmlua-parser/src/serve.rs @@ -1,4 +1,4 @@ -use super::render::{execute_lua, expand_template, process_markdown}; +use super::render::{execute_lua, expand_template, process_markdown, process_syntax_highlighting}; use anyhow::Result; use kuchikiki::traits::TendrilSink; use std::{ @@ -16,6 +16,7 @@ pub fn serve_content(request_uri: &str) -> Result { let doc = kuchikiki::parse_html().one(page_string); let full_doc = expand_template(doc, &components, None)?; let markdown_doc = process_markdown(full_doc)?; - let executed_doc = execute_lua(markdown_doc)?; + let highlighted_doc = process_syntax_highlighting(markdown_doc)?; + let executed_doc = execute_lua(highlighted_doc)?; Ok(executed_doc.to_string()) } From 3f8616e8b08b7eeec94b9019fd3f4ca1216ecb7d Mon Sep 17 00:00:00 2001 From: Quin Date: Sat, 5 Jul 2025 22:28:26 -0600 Subject: [PATCH 2/3] Add basic syntax highlighting support with syntect. Closes #5. --- Cargo.lock | 305 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 300 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b13312c..5bfc908 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "anyhow" version = "1.0.98" @@ -14,6 +20,21 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[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" @@ -44,9 +65,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" dependencies = [ "shlex", ] @@ -63,6 +84,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "cssparser" version = "0.27.2" @@ -71,7 +101,7 @@ checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa", + "itoa 0.4.8", "matches", "phf 0.8.0", "proc-macro2", @@ -90,6 +120,15 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_more" version = "0.99.20" @@ -130,6 +169,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "erased-serde" version = "0.4.6" @@ -150,6 +195,22 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "futf" version = "0.1.5" @@ -206,6 +267,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + [[package]] name = "html5ever" version = "0.26.0" @@ -236,6 +303,7 @@ dependencies = [ "kuchikiki", "mlua", "pulldown-cmark", + "syntect", "tendril", ] @@ -250,7 +318,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown 0.15.4", ] [[package]] @@ -259,6 +337,12 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "kuchikiki" version = "0.8.2" @@ -267,7 +351,7 @@ checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" dependencies = [ "cssparser", "html5ever", - "indexmap", + "indexmap 1.9.3", "matches", "selectors", ] @@ -278,6 +362,12 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +[[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.9.4" @@ -351,6 +441,15 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "mlua" version = "0.10.5" @@ -394,6 +493,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.19" @@ -403,6 +508,34 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "onig" +version = "6.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" +dependencies = [ + "bitflags 2.9.1", + "libc", + "once_cell", + "onig_sys", +] + +[[package]] +name = "onig_sys" +version = "69.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "ordered-float" version = "2.10.1" @@ -552,6 +685,25 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plist" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "546b279bf0638ee811d9e47de2ca5b66575a543035d79fdf83959dd2f5c3b4c3" +dependencies = [ + "base64", + "indexmap 2.10.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -601,6 +753,15 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.40" @@ -700,6 +861,12 @@ dependencies = [ "bitflags 2.9.1", ] +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -734,6 +901,21 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[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 = "scopeguard" version = "1.2.0" @@ -796,6 +978,18 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa 1.0.15", + "memchr", + "ryu", + "serde", +] + [[package]] name = "servo_arc" version = "0.1.1" @@ -883,6 +1077,28 @@ dependencies = [ "unicode-ident", ] +[[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", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "walkdir", + "yaml-rust", +] + [[package]] name = "tendril" version = "0.4.3" @@ -900,6 +1116,57 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa 1.0.15", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "typeid" version = "1.0.3" @@ -930,6 +1197,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[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 = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -954,6 +1231,15 @@ dependencies = [ "winsafe", ] +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -1106,6 +1392,15 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[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 = "zerocopy" version = "0.8.26" From 3d50b12df4bd1e81248256893bd9a83498a0dae9 Mon Sep 17 00:00:00 2001 From: gyoder <70408179+gyoder@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:36:28 -0600 Subject: [PATCH 3/3] Added custom theme loading --- htmlua-parser/src/render.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/htmlua-parser/src/render.rs b/htmlua-parser/src/render.rs index 396df4f..55047fc 100644 --- a/htmlua-parser/src/render.rs +++ b/htmlua-parser/src/render.rs @@ -139,11 +139,16 @@ pub fn expand_template(document: NodeRef, component_path: &PathBuf, include_from pub fn process_syntax_highlighting(document: NodeRef) -> Result { let ps = SyntaxSet::load_defaults_newlines(); - let ts = ThemeSet::load_defaults(); + let mut ts = ThemeSet::load_defaults(); let syntax_elements: Vec<_> = match document.select("syntaxhighlight") { Ok(e) => e.collect(), Err(()) => return Err(anyhow!("Unable to find syntaxhighlight elements")), }; + if !syntax_elements.is_empty() { + // TODO: read from config + let themes = PathBuf::from("/var/www/htmlua/themes"); + ts.add_from_folder(themes)?; + } for node in syntax_elements { let attrs = match node.as_node().as_element() { Some(e) => e.attributes.borrow(), @@ -163,8 +168,8 @@ pub fn process_syntax_highlighting(document: NodeRef) -> Result { write!(html_output, r#"
"#)?;
                 html_output.push_str("");
                 for line in LinesWithEndings::from(&code_text.borrow()) {
-                    let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap();
-                    let escaped = styled_line_to_highlighted_html(&ranges[..], IncludeBackground::No).unwrap();
+                    let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps)?;
+                    let escaped = styled_line_to_highlighted_html(&ranges[..], IncludeBackground::No)?;
                     html_output.push_str(&escaped);
                 }
                 html_output.push_str("
");