diff --git a/Cargo.lock b/Cargo.lock index 76bff89..5458bf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1243,7 +1243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror", + "thiserror 2.0.9", "ucd-trie", ] @@ -1662,6 +1662,21 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "splash-rs" +version = "0.3.1" +source = "git+https://git.shadowfacts.net/shadowfacts/splash-rs.git#7f3b67c59a4753ae50367eba292c8717ccbb5542" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" + [[package]] name = "string_cache" version = "0.8.7" @@ -1744,13 +1759,33 @@ dependencies = [ "unic-segment", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.9", +] + +[[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", ] [[package]] @@ -1945,6 +1980,138 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +[[package]] +name = "tree-sitter" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2434c86ba59ed15af56039cc5bf1acf8ba76ce301e32ef08827388ef285ec5" +dependencies = [ + "cc", + "regex", + "regex-syntax", + "streaming-iterator", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-bash" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329a4d48623ac337d42b1df84e81a1c9dbb2946907c102ca72db158c1964a52e" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-c" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afd2b1bf1585dc2ef6d69e87d01db8adb059006649dd5f96f31aa789ee6e9c71" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-css" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad6489794d41350d12a7fbe520e5199f688618f43aace5443980d1ddcf1b29e" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-elixir" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7310aea06158653d18959123a64262bfbf122b7437899dea0c338654a51d3" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-highlight" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1df627d3607f08557788a3c85b85042d8ccd5155eb119afff5f0c7b67a40924" +dependencies = [ + "lazy_static", + "regex", + "streaming-iterator", + "thiserror 1.0.69", + "tree-sitter", +] + +[[package]] +name = "tree-sitter-html" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261b708e5d92061ede329babaaa427b819329a9d427a1d710abb0f67bbef63ee" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-java" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa6cbcdc8c679b214e616fd3300da67da0e492e066df01bcf5a5921a71e90d6" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-javascript" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf40bf599e0416c16c125c3cec10ee5ddc7d1bb8b0c60fa5c4de249ad34dc1b1" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-json" +version = "0.24.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d727acca406c0020cffc6cf35516764f36c8e3dc4408e5ebe2cb35a947ec471" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-language" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c199356c799a8945965bb5f2c55b2ad9d9aa7c4b4f6e587fe9dea0bc715e5f9c" + +[[package]] +name = "tree-sitter-objc" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca8bb556423fc176f0535e79d525f783a6684d3c9da81bf9d905303c129e1d2" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-rust" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4d64d449ca63e683c562c7743946a646671ca23947b9c925c0cfbe65051a4af" +dependencies = [ + "cc", + "tree-sitter-language", +] + [[package]] name = "tungstenite" version = "0.26.1" @@ -1959,7 +2126,7 @@ dependencies = [ "log", "rand", "sha1", - "thiserror", + "thiserror 2.0.9", "utf-8", ] @@ -2090,16 +2257,29 @@ dependencies = [ "notify", "once_cell", "pulldown-cmark", + "pulldown-cmark-escape", "regex", "rss", "serde", "serde_json", + "splash-rs", "tera", "tokio", "tokio-stream", "toml", "tower", "tower-http", + "tree-sitter-bash", + "tree-sitter-c", + "tree-sitter-css", + "tree-sitter-elixir", + "tree-sitter-highlight", + "tree-sitter-html", + "tree-sitter-java", + "tree-sitter-javascript", + "tree-sitter-json", + "tree-sitter-objc", + "tree-sitter-rust", "unicode-normalization", ] diff --git a/Cargo.toml b/Cargo.toml index 19f0446..4cdd027 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,13 +40,26 @@ markup5ever_rcdom = "0.3.0" notify = "7.0.0" once_cell = "1.20.2" pulldown-cmark = "0.12.2" +pulldown-cmark-escape = "0.11.0" regex = "1.11.1" rss = { version = "2.0.11", features = ["atom"] } serde = { version = "1.0", features = ["derive"] } +splash-rs = { version = "0.3.1", git = "https://git.shadowfacts.net/shadowfacts/splash-rs.git" } tera = "1.20.0" tokio = { version = "1.42.0", features = ["full"] } tokio-stream = "0.1.17" toml = "0.8.19" tower = { version = "0.5.2", features = ["steer", "util"] } tower-http = { version = "0.6.2", features = ["fs"] } +tree-sitter-bash = "0.23.3" +tree-sitter-c = "0.23.4" +tree-sitter-css = "0.23.2" +tree-sitter-elixir = "0.3.3" +tree-sitter-highlight = "0.24.6" +tree-sitter-html = "0.23.2" +tree-sitter-java = "0.23.5" +tree-sitter-javascript = "0.23.1" +tree-sitter-json = "0.24.8" +tree-sitter-objc = "3.0.2" +tree-sitter-rust = "0.23.2" unicode-normalization = "0.1.24" diff --git a/site_test/css/main.scss b/site_test/css/main.scss index f4b1d17..879347a 100644 --- a/site_test/css/main.scss +++ b/site_test/css/main.scss @@ -1,14 +1,17 @@ @import "normalize.scss"; @import "fonts.scss"; +@import "syntax-highlighting.scss"; $page-horizontal-margin: 2rem; $mobile-breakpoint: 480px; $container-max-width: 768px; +$link-color: #c21e1e; :root { - --background-color: #f8e7cf; + // solarized-base2 + --background-color: #eee8d5; --text-color: black; --secondary-text-color: #656565; - --link-color: blue; + --link-color: #c21e1e; --page-vertical-margin: 3rem; } @@ -55,6 +58,12 @@ code { font-size: 0.9em; } +pre { + overflow-x: scroll; + tab-size: 4; + word-wrap: normal; +} + blockquote { position: relative; font-style: italic; @@ -140,13 +149,20 @@ header { display: none; } } +} - &:hover + .sidenote { - color: black; +.footnote-reference:hover + .sidenote, +.sidenote:hover, +aside:not(.inline):hover { + color: black; + + a { + color: var(--link-color); } } -.sidenote { +.sidenote, +aside:not(.inline) { float: right; margin-right: -50%; width: 40%; @@ -160,6 +176,19 @@ header { margin: 20px 0; display: block; } + + a { + color: lighten($link-color, 20%); + transition: color 0.2s ease-in-out; + } +} + +aside:not(.inline) { + transform: translateY(-50%); + + p:first-child { + margin-top: 0; + } } .footnote { @@ -186,6 +215,20 @@ header { } } +aside.inline { + font-size: 1rem; + /* background-color: #d8d7fe; */ + background-color: lighten($link-color, 43%); + padding: 1rem; + + p:first-child { + margin-top: 0; + } + p:last-child { + margin-bottom: 0; + } +} + // 1.5 to account for -50% margin-right on .sidenote // plus page-horizontal-margin to effectively use that as the margin @media (min-width: calc(1.5 * $container-max-width + 2 * $page-horizontal-margin)) { @@ -197,7 +240,8 @@ header { display: inline; } } - .sidenote { + .sidenote, + aside:not(.inline) { display: block; } .footnote { diff --git a/site_test/css/syntax-highlighting.scss b/site_test/css/syntax-highlighting.scss new file mode 100644 index 0000000..e410535 --- /dev/null +++ b/site_test/css/syntax-highlighting.scss @@ -0,0 +1,67 @@ +:root { + --solarized-base01: #586e75; + --solarized-base00: #657b83; + /* --solarized-base0: #839496; */ + --solarized-base1: #93a1a1; + --solarized-base2: #eee8d5; + --solarized-base3: #fdf6e3; + --solarized-yellow: #b58900; + --solarized-orange: #cb4b16; + --solarized-red: #dc322f; + --solarized-blue: #268bd2; + --solarized-cyan: #2aa198; + --solarized-green: #859900; +} + +.highlight { + display: block; + overflow-x: auto; + padding: 0.5em; + color: var(--solarized-base01); + // darkened base2 + background: darken(#eee8d5, 4%); +} + +.highlight > code { + display: block; +} + +.hl-cmt { + color: var(--solarized-base00); + font-style: italic; +} + +.hl-kw, +.hl-const { + color: var(--solarized-green); +} + +.hl-punct-sp, +.hl-tag { + color: var(--solarized-blue); +} + +.hl-emb { + color: var(--solarized-base01); +} + +.hl-attr, +.hl-mod, +.hl-key, +.hl-prop { + color: var(--solarized-red); +} + +.hl-str { + color: var(--solarized-cyan); +} + +.hl-builtin, +.hl-type, +.hl-num { + color: var(--solarized-yellow); +} + +.hl-fn { + color: var(--solarized-orange); +} diff --git a/src.bak/generator/highlight/objc-highlight.scm b/src.bak/generator/highlight/objc-highlight.scm deleted file mode 100644 index 677afb4..0000000 --- a/src.bak/generator/highlight/objc-highlight.scm +++ /dev/null @@ -1,370 +0,0 @@ -[ - (comment) - (pragma) -] @comment - -[ - (self) - (super) -] @variable.builtin - -[ - (getter) - (setter) - (nonnull) - (nullable) - (null_resettable) - (unsafe_unretained) - (null_unspecified) - (direct) - (readwrite) - (readonly) - (strong) - (weak) - (copy) - (assign) - (retain) - (atomic) - (nonatomic) - (class) - (NS_NONATOMIC_IOSONLY) - (DISPATCH_QUEUE_REFERENCE_TYPE) -] @keyword - -[ - "@interface" - "@protocol" - "@property" - "@end" - "@implementation" - "@compatibility_alias" - "@autoreleasepool" - "@synchronized" - "@class" - "@synthesize" - "@dynamic" - "@defs" - "@try" - "@catch" - "@finally" - "@throw" - "@selector" - "@encode" - (private) - (public) - (protected) - (package) - (optional) - (required) - "NS_ENUM" - "NS_ERROR_ENUM" - "NS_OPTIONS" - "NS_SWIFT_NAME" - (type_qualifier) - (storage_class_specifier) - "NS_NOESCAPE" - "const" - "default" - "enum" - "extern" - "inline" - "static" - "struct" - "typedef" - "typeof" - "__typeof" - "__typeof__" - "_Atomic" - "union" - "volatile" - "goto" - "register" - "__covariant" - "__contravariant" - "__GENERICS" -] @keyword - -"sizeof" @keyword.operator -"return" @keyword.return - -[ - "while" - "for" - "do" - "continue" - "break" -] @keyword.repeat - -"#define" @constant.macro - -[ - "#if" - "#ifdef" - "#ifndef" - "#else" - "#elif" - "#endif" - (preproc_directive) -] @keyword - -"#include" @include -"#import" @include -"@import" @include - -[ - "=" - - "-" - "*" - "/" - "+" - "%" - - "~" - "|" - "&" - "^" - "<<" - ">>" - - "->" - - "<" - "<=" - ">=" - ">" - "==" - "!=" - - "!" - "&&" - "||" - - "-=" - "+=" - "*=" - "/=" - "%=" - "|=" - "&=" - "^=" - ">>=" - "<<=" - "--" - "++" - "@" -] @operator - -[ - "if" - "else" - "case" - "switch" -] @keyword.conditional - -(conditional_expression [ "?" ":" ] @keyword.conditional) - -[ - (true) - (false) - (YES) - (NO) -] @keyword.boolean - -[ "." ";" ":" "," ] @punctuation.delimiter - -"..." @punctuation.special - -[ "(" ")" "[" "]" "{" "}"] @punctuation.bracket - -[ - (string_literal) - (string_expression) - (system_lib_string) - (module_string) -] @string - -(escape_sequence) @string.escape - -(null) @constant.builtin -(nil) @constant.builtin -(number_literal) @number -(number_expression) @number -(char_literal) @character - -[ - (preproc_arg) - (preproc_defined) -] @function.macro - -; declarator: (identifier) @property -; (cast_expression value: (identifier) @property) - -; (((field_expression -; (field_identifier) @property)) @_parent -; (#not-has-parent? @_parent function_declarator call_expression)) - -; (((field_identifier) @property) -; (#has-ancestor? @property field_declaration) -; (#not-has-ancestor? @property function_declarator)) - -[ - (type_identifier) - (primitive_type) - (sized_type_specifier) - (type_descriptor) - (generics_type_reference) -] @type - -[ - (id) - (Class) - (Method) - (IMP) - (SEL) - (BOOL) - (instancetype) - (auto) -] @type.builtin - -(declaration (type_qualifier) @type) -(cast_expression type: (type_descriptor) @type) -(sizeof_expression value: (parenthesized_expression (identifier) @type)) - -(class_interface name: (identifier) @type.class) -(category_interface name: (identifier) @type.class) -(category_interface category: (identifier) @type.category) -(superclass_reference name: (identifier) @type.class) -(parameterized_class_type_arguments) @type.class -(class_implementation name: (identifier) @type.class) -(category_implementation name: (identifier) @type.class) -(compatibility_alias_declaration (identifier) @type.class) -(category_implementation category: (identifier) @type.category) -(class_forward_declaration name: (identifier) @type.class) -(protocol_forward_declaration name: (identifier) @type.protocol) -(protocol_declaration name: (identifier) @type.protocol) -(protocol_qualifiers name: (identifier) @type.protocol) -(protocol_expression (identifier) @type.protocol) - -;; Preproc def / undef -(preproc_def - name: (_) @constant) -(preproc_call - directive: (preproc_directive) @_u - argument: (_) @constant - (#eq? @_u "#undef")) - -(call_expression - function: (identifier) @function) -(field_expression - field: (field_identifier) @function) -(function_declarator - declarator: (identifier) @function) -(preproc_function_def - name: (identifier) @function.macro) -(selector_expression - name: (identifier) @function) - - -(method_declaration - selector: (identifier) @function) - -(method_declaration - (keyword_selector - (keyword_declarator - keyword: (identifier) @function))) - -(method_declaration - (keyword_selector - (keyword_declarator - name: (identifier) @function))) - -(message_expression - receiver: (field_expression - field: (field_identifier) @variable)) - -(method_definition - selector: (identifier) @function) - -(method_definition - (keyword_selector - (keyword_declarator - keyword: (identifier) @function))) - -(message_expression - selector: (identifier) @function) - -(method_definition - (keyword_selector - (keyword_declarator - name: (identifier) @property))) - -(message_expression - selector: (keyword_argument_list - (keyword_argument - keyword: (identifier) @function))) - -; (message_expression -; selector: (keyword_argument_list -; (keyword_argument -; argument: (identifier) @property))) - -(unary_expression argument: (identifier) @function) -(va_arg_expression) @function -; (va_arg_expression va_list: (identifier) @property) -; (enumerator name: (identifier) @property) - - -;; Parameters -(parameter_declaration - declarator: (identifier) @variable.parameter) - -(parameter_declaration - declarator: (pointer_declarator) @variable.parameter) - -(parameter_declaration - declarator: (pointer_declarator - declarator: (identifier) @variable.parameter)) - -(for_in_statement - loop: (identifier) @variable.parameter) - -; (dictionary_expression -; key:(_expression) @property) -; (dictionary_expression -; value: (_expression) @property) -; (array_expression -; (identifier) @property) -; (argument_list -; (identifier) @property) -; (expression_statement -; (identifier) @property) - -; (_expression (identifier) @property) - -[ - "__attribute" - "__attribute__" - "__cdecl" - "__clrcall" - "__stdcall" - "__fastcall" - "__thiscall" - "__vectorcall" - "_unaligned" - "__unaligned" - "__declspec" - "__unused" - "__builtin_available" - "@available" - (attribute_specifier) - (class_interface_attribute_sepcifier) - (method_variadic_arguments_attribute_specifier) -] @attribute - -(attribute_specifier) @attribute - -((identifier) @constant - (#match? @constant "^[A-Z][A-Z0-9_$]+$")) - -(ERROR) @error diff --git a/src/generator/markdown/highlight.rs b/src/generator/markdown/highlight.rs index 8a987bb..d2f8aab 100644 --- a/src/generator/markdown/highlight.rs +++ b/src/generator/markdown/highlight.rs @@ -1,6 +1,7 @@ -// use crate::generator::highlight::highlight; use pulldown_cmark::{CodeBlockKind, Event, Tag, TagEnd}; +use crate::generator::util::highlight::highlight; + pub fn new<'a, I: Iterator>>(iter: I) -> Highlight<'a, I> { Highlight { iter } } @@ -16,8 +17,7 @@ impl<'a, I: Iterator>> Iterator for Highlight<'a, I> { match self.iter.next() { Some(Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(label)))) => { let code = self.consume_code_block(); - // let mut highlighted = highlight(&code, &label); - let mut highlighted = "TODO".to_owned(); + let mut highlighted = highlight(&code, &label); highlighted.insert_str( 0, &format!("
", label),
diff --git a/src.bak/generator/highlight.rs b/src/generator/util/highlight.rs
similarity index 58%
rename from src.bak/generator/highlight.rs
rename to src/generator/util/highlight.rs
index 56178ec..a2281f4 100644
--- a/src.bak/generator/highlight.rs
+++ b/src/generator/util/highlight.rs
@@ -3,7 +3,7 @@ mod swift;
 mod tree_sitter;
 
 use once_cell::sync::Lazy;
-use pulldown_cmark::escape::escape_html;
+use pulldown_cmark_escape::escape_html_body_text;
 use tree_sitter::TreeSitterHighlighter;
 use tree_sitter_highlight::HighlightConfiguration;
 
@@ -17,7 +17,7 @@ pub fn highlight(code: &str, label: &str) -> String {
     match lang_highlighter(label) {
         None => {
             let mut buf = String::new();
-            escape_html(&mut buf, &code).unwrap();
+            escape_html_body_text(&mut buf, &code).expect("escaping");
             buf
         }
         Some(highlighter) => highlighter.highlight(&code),
@@ -25,39 +25,61 @@ pub fn highlight(code: &str, label: &str) -> String {
 }
 
 macro_rules! ts_config {
-    ($name:ident, $module:ident, $query:ident) => {
-        static $name: Lazy = Lazy::new(|| {
-            let mut config =
-                HighlightConfiguration::new($module::language(), $module::$query, "", "").unwrap();
+    ($static_name:ident, $module:ident, $name:literal, $query:ident) => {
+        static $static_name: Lazy = Lazy::new(|| {
+            let mut config = HighlightConfiguration::new(
+                $module::LANGUAGE.into(),
+                $name,
+                $module::$query,
+                "",
+                "",
+            )
+            .unwrap();
             config.configure(tree_sitter::HIGHLIGHT_NAMES);
             config
         });
     };
-    ($name:ident, $module:ident, $query:literal) => {
-        static $name: Lazy = Lazy::new(|| {
-            let mut config =
-                HighlightConfiguration::new($module::language(), include_str!($query), "", "")
-                    .unwrap();
+    ($static_name:ident, $module:ident, $name:literal, $query:literal) => {
+        static $static_name: Lazy = Lazy::new(|| {
+            let mut config = HighlightConfiguration::new(
+                $module::LANGUAGE.into(),
+                $name,
+                include_str!($query),
+                "",
+                "",
+            )
+            .unwrap();
             config.configure(tree_sitter::HIGHLIGHT_NAMES);
             config
         });
     };
 }
 
-ts_config!(BASH_CONFIG, tree_sitter_bash, HIGHLIGHTS_QUERY);
-ts_config!(C_CONFIG, tree_sitter_c, HIGHLIGHT_QUERY);
-ts_config!(CSS_CONFIG, tree_sitter_css, "highlight/css-highlight.scm");
-ts_config!(ELIXIR_CONFIG, tree_sitter_elixir, HIGHLIGHTS_QUERY);
-ts_config!(HTML_CONFIG, tree_sitter_html, HIGHLIGHTS_QUERY);
-ts_config!(JAVA_CONFIG, tree_sitter_java, HIGHLIGHT_QUERY);
-ts_config!(JS_CONFIG, tree_sitter_javascript, HIGHLIGHT_QUERY);
-ts_config!(JSON_CONFIG, tree_sitter_json, HIGHLIGHT_QUERY);
+ts_config!(BASH_CONFIG, tree_sitter_bash, "bash", HIGHLIGHT_QUERY);
+ts_config!(C_CONFIG, tree_sitter_c, "c", HIGHLIGHT_QUERY);
 ts_config!(
-    OBJC_CONFIG,
-    tree_sitter_objc,
-    "highlight/objc-highlight.scm"
+    CSS_CONFIG,
+    tree_sitter_css,
+    "css",
+    "highlight/css-highlight.scm"
 );
-ts_config!(RUST_CONFIG, tree_sitter_rust, HIGHLIGHT_QUERY);
+ts_config!(
+    ELIXIR_CONFIG,
+    tree_sitter_elixir,
+    "elixir",
+    HIGHLIGHTS_QUERY
+);
+ts_config!(HTML_CONFIG, tree_sitter_html, "html", HIGHLIGHTS_QUERY);
+ts_config!(JAVA_CONFIG, tree_sitter_java, "java", HIGHLIGHTS_QUERY);
+ts_config!(
+    JS_CONFIG,
+    tree_sitter_javascript,
+    "javascript",
+    HIGHLIGHT_QUERY
+);
+ts_config!(JSON_CONFIG, tree_sitter_json, "json", HIGHLIGHTS_QUERY);
+ts_config!(OBJC_CONFIG, tree_sitter_objc, "objc", HIGHLIGHTS_QUERY);
+ts_config!(RUST_CONFIG, tree_sitter_rust, "rust", HIGHLIGHTS_QUERY);
 
 fn lang_highlighter(name: &str) -> Option> {
     match name {
diff --git a/src.bak/generator/highlight/css-highlight.scm b/src/generator/util/highlight/css-highlight.scm
similarity index 100%
rename from src.bak/generator/highlight/css-highlight.scm
rename to src/generator/util/highlight/css-highlight.scm
diff --git a/src.bak/generator/highlight/shell.rs b/src/generator/util/highlight/shell.rs
similarity index 100%
rename from src.bak/generator/highlight/shell.rs
rename to src/generator/util/highlight/shell.rs
diff --git a/src.bak/generator/highlight/swift.rs b/src/generator/util/highlight/swift.rs
similarity index 100%
rename from src.bak/generator/highlight/swift.rs
rename to src/generator/util/highlight/swift.rs
diff --git a/src.bak/generator/highlight/tree_sitter.rs b/src/generator/util/highlight/tree_sitter.rs
similarity index 94%
rename from src.bak/generator/highlight/tree_sitter.rs
rename to src/generator/util/highlight/tree_sitter.rs
index 1ec75f1..cc055b8 100644
--- a/src.bak/generator/highlight/tree_sitter.rs
+++ b/src/generator/util/highlight/tree_sitter.rs
@@ -1,5 +1,5 @@
 use super::Highlighter;
-use pulldown_cmark::escape::escape_html;
+use pulldown_cmark_escape::escape_html_body_text;
 use std::fmt::Write;
 use tree_sitter_highlight::{Highlight as TSHighlight, HighlightConfiguration, HighlightEvent};
 
@@ -27,11 +27,11 @@ impl Highlighter for TreeSitterHighlighter {
             let event = highlight.unwrap();
             match event {
                 HighlightEvent::Source { start, end } => {
-                    escape_html(
+                    escape_html_body_text(
                         &mut buf,
                         std::str::from_utf8(&code.as_bytes()[start..end]).unwrap(),
                     )
-                    .unwrap();
+                    .expect("escaping");
                 }
                 HighlightEvent::HighlightStart(TSHighlight(idx)) => {
                     write!(
diff --git a/src/generator/util/mod.rs b/src/generator/util/mod.rs
index 73fb69b..9178b95 100644
--- a/src/generator/util/mod.rs
+++ b/src/generator/util/mod.rs
@@ -1,4 +1,5 @@
 pub mod file_watcher;
+pub mod highlight;
 pub mod one_more;
 pub mod slugify;
 pub mod templates;