From d5ecc05345e02679c9bd3a506d626d19d316134a Mon Sep 17 00:00:00 2001 From: Jan-Bulthuis Date: Sun, 19 Jul 2026 14:02:00 +0200 Subject: [PATCH] feat: setup for tree-sitter syntax highlighting --- Cargo.lock | 171 +++++++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 5 ++ src/main.rs | 7 +++ src/syntax.rs | 48 ++++++++++++++ src/typst.rs | 10 ++- 5 files changed, 233 insertions(+), 8 deletions(-) create mode 100644 src/syntax.rs diff --git a/Cargo.lock b/Cargo.lock index cad2d68..e16991e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anstream" version = "1.0.0" @@ -100,6 +106,15 @@ dependencies = [ "object", ] +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -585,6 +600,12 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "displaydoc" version = "0.2.6" @@ -733,6 +754,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -842,8 +869,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d99fc21d493812643aae86d53b7bbd02f376434a90317e8a790bc209fdd6605e" dependencies = [ "bytemuck", - "foldhash", - "hashbrown", + "foldhash 0.2.0", + "hashbrown 0.17.1", "log", "peniko", "png", @@ -872,13 +899,24 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ - "foldhash", + "foldhash 0.2.0", ] [[package]] @@ -1341,7 +1379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.17.1", "rayon", "serde", "serde_core", @@ -1385,18 +1423,32 @@ dependencies = [ "clap", "flate2", "itertools", + "ropey", "tar", "time", "tokio", "tower-http", "tracing", "tracing-subscriber", + "tree-house", + "tree-house-bindings", "typst", "typst-html", "typst-kit", + "typst-library", + "typst-utils", "ureq", ] +[[package]] +name = "kstring" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b609e7ca5ea38f093c20a4a102335b247221c9643b7a6bc3510f196f99499a9e" +dependencies = [ + "static_assertions", +] + [[package]] name = "kurbo" version = "0.13.1" @@ -1421,6 +1473,16 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libm" version = "0.2.16" @@ -1822,6 +1884,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -1974,6 +2046,19 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-cursor" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0497c781d2f982ae8284d2932aee6a877e58a4541daa5e8fadc18cc75c23a61d" +dependencies = [ + "log", + "memchr", + "regex-automata", + "regex-syntax", + "ropey", +] + [[package]] name = "regex-syntax" version = "0.8.11" @@ -2000,6 +2085,16 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85cd47a33a4510b1424fe796498e174c6a9cf94e606460ef022a19f3e4ff85e" +[[package]] +name = "ropey" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" +dependencies = [ + "smallvec", + "str_indices", +] + [[package]] name = "roxmltree" version = "0.21.1" @@ -2073,6 +2168,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + [[package]] name = "rustybuzz" version = "0.20.1" @@ -2308,6 +2409,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str_indices" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" + [[package]] name = "strict-num" version = "0.1.1" @@ -2715,6 +2828,38 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tree-house" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333476442882205ab249a8ced263ae53db3d0797c727cc3424836a6fb221d7b1" +dependencies = [ + "arc-swap", + "hashbrown 0.15.5", + "kstring", + "once_cell", + "pretty_assertions", + "regex", + "regex-cursor", + "ropey", + "slab", + "tree-house-bindings", + "unicode-width", +] + +[[package]] +name = "tree-house-bindings" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5d0eed0db98578618e598b8f6f413f7c172bade1f12f8242d71c38dd5deb0f" +dependencies = [ + "cc", + "libloading", + "regex-cursor", + "ropey", + "thiserror", +] + [[package]] name = "ttf-parser" version = "0.25.1" @@ -3155,6 +3300,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" +[[package]] +name = "unicode-width" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -3282,7 +3433,7 @@ dependencies = [ "bytemuck", "fearless_simd", "guillotiere", - "hashbrown", + "hashbrown 0.17.1", "log", "peniko", "png", @@ -3299,7 +3450,7 @@ dependencies = [ "bytemuck", "fearless_simd", "guillotiere", - "hashbrown", + "hashbrown 0.17.1", "log", "peniko", "png", @@ -3315,7 +3466,7 @@ checksum = "6d8ded630e8316bb94a55881256506d1f3b9947b5f66db8a7d32ca7ba02decd0" dependencies = [ "bytemuck", "glifo", - "hashbrown", + "hashbrown 0.17.1", "png", "vello_common 0.0.8", ] @@ -3546,6 +3697,12 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yoke" version = "0.8.3" diff --git a/Cargo.toml b/Cargo.toml index 21930db..e68c0fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,8 @@ typst-kit = "0.15.1" typst = "0.15.1" ureq = "3.3.0" clap = { version = "4.6.2", features = ["derive"] } +typst-library = "0.15.1" +typst-utils = "0.15.1" +tree-house = "0.4.0" +tree-house-bindings = { version = "0.3.2", features = ["ropey"] } +ropey = "1.6.1" diff --git a/src/main.rs b/src/main.rs index 1cbe184..e1c01db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ use tracing_subscriber::FmtSubscriber; use crate::typst::TypstContext; +mod syntax; mod typst; #[derive(Clone)] @@ -59,6 +60,10 @@ struct AppConfig { #[arg(short = 'd', long, value_name = "DIR", default_value = "/tmp/karnaugh")] packages_root: PathBuf, + /// The directory where the syntax files are stored. + #[arg(short = 'g', long, value_name = "DIR", default_value = None)] + syntax_root: Option, + /// The log level to use #[arg(short = 'l', long, value_name = "LEVEL", default_value_t = Level::INFO)] log_level: Level, @@ -94,6 +99,8 @@ async fn main() { .with_max_level(state.config.log_level) .init(); + syntax::init(&state.config); + // TODO: Replace with config option let mut favicon = state.config.get_full_assets_path(); favicon.push(PathBuf::from("favicon.svg")); diff --git a/src/syntax.rs b/src/syntax.rs new file mode 100644 index 0000000..c450ef4 --- /dev/null +++ b/src/syntax.rs @@ -0,0 +1,48 @@ +use std::{path::PathBuf, sync::OnceLock}; + +use tracing::{info, warn}; +use typst::foundations::{Func, NativeFunc, func}; + +use crate::AppConfig; + +static REGISTRY: OnceLock = OnceLock::new(); + +struct LanguageRegistry {} + +impl LanguageRegistry { + fn new(syntax_root: &PathBuf) -> Self { + Self {} + } +} + +pub fn init(config: &AppConfig) { + match &config.syntax_root { + Some(syntax_root) => { + info!("Loading syntax files"); + REGISTRY.get_or_init(|| LanguageRegistry::new(syntax_root)); + } + None => { + warn!( + "No syntax root has been configured, improved tree-sitter highlighting will not be possible." + ) + } + } +} + +pub fn highlight_func() -> Func { + highlight::func() +} + +#[func] +fn highlight( + text: String, + #[named] + #[default(false)] + block: bool, + #[named] + #[default(None)] + lang: Option, +) -> String { + let rev = text.chars().rev(); + rev.collect() +} diff --git a/src/typst.rs b/src/typst.rs index 9f87e86..be3074e 100644 --- a/src/typst.rs +++ b/src/typst.rs @@ -5,7 +5,7 @@ use tracing::info; use typst::{ Library, LibraryExt, World, diag::{FileError, FileResult, PackageError, PackageResult, SourceDiagnostic}, - foundations::{Bytes, Datetime, Duration}, + foundations::{Bytes, Datetime, Dict, Duration, Value}, syntax::{FileId, RootedPath, Source, VirtualPath, VirtualRoot, package::PackageSpec}, text::{Font, FontBook}, utils::LazyHash, @@ -23,7 +23,15 @@ pub struct TypstContext { impl TypstContext { pub fn new(config: AppConfig) -> Self { + let mut inputs = Dict::new(); + if config.syntax_root.is_some() { + inputs.insert( + "highlight".into(), + Value::Func(crate::syntax::highlight_func()), + ); + } let library = Library::builder() + .with_inputs(inputs) .with_features([typst::Feature::Html].into_iter().collect()) .build(); let library = LazyHash::new(library);