Compare commits
10
Commits
0.1.5
...
bfc908f075
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfc908f075 | ||
|
|
255d6e9611 | ||
|
|
08504aac28 | ||
|
|
cac6d0ae31 | ||
|
|
c1057be18e | ||
|
|
06173555f6 | ||
|
|
308dcf2911 | ||
|
|
d5ecc05345 | ||
|
|
853efc406d | ||
|
|
035acb614f |
No files matched your search
Generated
+164
-7
@@ -32,6 +32,12 @@ dependencies = [
|
|||||||
"alloc-no-stdlib",
|
"alloc-no-stdlib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -100,6 +106,15 @@ dependencies = [
|
|||||||
"object",
|
"object",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arc-swap"
|
||||||
|
version = "1.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b"
|
||||||
|
dependencies = [
|
||||||
|
"rustversion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "arrayref"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@@ -585,6 +600,12 @@ version = "0.5.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diff"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@@ -733,6 +754,12 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -842,8 +869,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d99fc21d493812643aae86d53b7bbd02f376434a90317e8a790bc209fdd6605e"
|
checksum = "d99fc21d493812643aae86d53b7bbd02f376434a90317e8a790bc209fdd6605e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"foldhash",
|
"foldhash 0.2.0",
|
||||||
"hashbrown",
|
"hashbrown 0.17.1",
|
||||||
"log",
|
"log",
|
||||||
"peniko",
|
"peniko",
|
||||||
"png",
|
"png",
|
||||||
@@ -872,13 +899,24 @@ dependencies = [
|
|||||||
"zerocopy",
|
"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]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.17.1"
|
version = "0.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"foldhash",
|
"foldhash 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1341,7 +1379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown 0.17.1",
|
||||||
"rayon",
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
@@ -1385,18 +1423,32 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"flate2",
|
"flate2",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"ropey",
|
||||||
"tar",
|
"tar",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"tree-house",
|
||||||
|
"tree-house-bindings",
|
||||||
"typst",
|
"typst",
|
||||||
"typst-html",
|
"typst-html",
|
||||||
"typst-kit",
|
"typst-kit",
|
||||||
|
"typst-library",
|
||||||
|
"typst-utils",
|
||||||
"ureq",
|
"ureq",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kstring"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b609e7ca5ea38f093c20a4a102335b247221c9643b7a6bc3510f196f99499a9e"
|
||||||
|
dependencies = [
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kurbo"
|
name = "kurbo"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
@@ -1421,6 +1473,16 @@ version = "0.2.186"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
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]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
@@ -1822,6 +1884,16 @@ dependencies = [
|
|||||||
"zerocopy",
|
"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]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.20+deprecated"
|
version = "0.5.20+deprecated"
|
||||||
@@ -1974,6 +2046,19 @@ dependencies = [
|
|||||||
"regex-syntax",
|
"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]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.11"
|
version = "0.8.11"
|
||||||
@@ -2000,6 +2085,16 @@ version = "3.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c85cd47a33a4510b1424fe796498e174c6a9cf94e606460ef022a19f3e4ff85e"
|
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]]
|
[[package]]
|
||||||
name = "roxmltree"
|
name = "roxmltree"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
@@ -2073,6 +2168,12 @@ dependencies = [
|
|||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustybuzz"
|
name = "rustybuzz"
|
||||||
version = "0.20.1"
|
version = "0.20.1"
|
||||||
@@ -2308,6 +2409,18 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"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]]
|
[[package]]
|
||||||
name = "strict-num"
|
name = "strict-num"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -2715,6 +2828,38 @@ dependencies = [
|
|||||||
"tracing-log",
|
"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]]
|
[[package]]
|
||||||
name = "ttf-parser"
|
name = "ttf-parser"
|
||||||
version = "0.25.1"
|
version = "0.25.1"
|
||||||
@@ -3155,6 +3300,12 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
|
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unsafe-libyaml"
|
name = "unsafe-libyaml"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
@@ -3282,7 +3433,7 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"fearless_simd",
|
"fearless_simd",
|
||||||
"guillotiere",
|
"guillotiere",
|
||||||
"hashbrown",
|
"hashbrown 0.17.1",
|
||||||
"log",
|
"log",
|
||||||
"peniko",
|
"peniko",
|
||||||
"png",
|
"png",
|
||||||
@@ -3299,7 +3450,7 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"fearless_simd",
|
"fearless_simd",
|
||||||
"guillotiere",
|
"guillotiere",
|
||||||
"hashbrown",
|
"hashbrown 0.17.1",
|
||||||
"log",
|
"log",
|
||||||
"peniko",
|
"peniko",
|
||||||
"png",
|
"png",
|
||||||
@@ -3315,7 +3466,7 @@ checksum = "6d8ded630e8316bb94a55881256506d1f3b9947b5f66db8a7d32ca7ba02decd0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"glifo",
|
"glifo",
|
||||||
"hashbrown",
|
"hashbrown 0.17.1",
|
||||||
"png",
|
"png",
|
||||||
"vello_common 0.0.8",
|
"vello_common 0.0.8",
|
||||||
]
|
]
|
||||||
@@ -3546,6 +3697,12 @@ dependencies = [
|
|||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yansi"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
|||||||
@@ -23,3 +23,8 @@ typst-kit = "0.15.1"
|
|||||||
typst = "0.15.1"
|
typst = "0.15.1"
|
||||||
ureq = "3.3.0"
|
ureq = "3.3.0"
|
||||||
clap = { version = "4.6.2", features = ["derive"] }
|
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"
|
||||||
Generated
+58
-1
@@ -18,7 +18,42 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"helix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"rust-overlay": "rust-overlay"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1784219418,
|
||||||
|
"narHash": "sha256-yfNBfV6HWAQU/xZXwrFoedsSYsDmCDIlTHRxAZI6teY=",
|
||||||
|
"owner": "helix-editor",
|
||||||
|
"repo": "helix",
|
||||||
|
"rev": "f6f3eb1fe4a73933466f30175843059a4381a985",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "helix-editor",
|
||||||
|
"repo": "helix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775036866,
|
||||||
|
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1784120854,
|
"lastModified": 1784120854,
|
||||||
"narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=",
|
"narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=",
|
||||||
@@ -37,7 +72,29 @@
|
|||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": "nixpkgs"
|
"helix": "helix",
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"helix",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775358767,
|
||||||
|
"narHash": "sha256-f2eC+WIfhjevCPQILuV08i/kmKZzYZpUvkom/33VxCA=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "20fd44bc663daa53a2575e01293e24e681d62244",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
helix.url = "github:helix-editor/helix";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
@@ -9,6 +10,7 @@
|
|||||||
self,
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
flake-utils,
|
flake-utils,
|
||||||
|
helix,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
flake-utils.lib.eachDefaultSystem (
|
flake-utils.lib.eachDefaultSystem (
|
||||||
@@ -19,6 +21,39 @@
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
|
packages.syntax =
|
||||||
|
let
|
||||||
|
grammars = pkgs.callPackage "${helix}/grammars.nix" { };
|
||||||
|
in
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
name = "syntax";
|
||||||
|
phases = [
|
||||||
|
"buildPhase"
|
||||||
|
"installPhase"
|
||||||
|
];
|
||||||
|
buildPhase = ''
|
||||||
|
cp -r ${helix}/runtime/queries/* ./
|
||||||
|
chmod u+w ./* -R
|
||||||
|
for so in ${grammars}/*.so; do
|
||||||
|
name=$(basename "$so" .so)
|
||||||
|
if [ -d "./$name" ]; then
|
||||||
|
cp "$so" "./$name/$name.so"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp -r ./* $out/
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
packages.themes = pkgs.stdenv.mkDerivation {
|
||||||
|
name = "themes";
|
||||||
|
phases = [ "installPhase" ];
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp ${helix}/runtime/themes/*.toml $out/
|
||||||
|
'';
|
||||||
|
};
|
||||||
packages.karnaugh = pkgs.rustPlatform.buildRustPackage (final: {
|
packages.karnaugh = pkgs.rustPlatform.buildRustPackage (final: {
|
||||||
pname = "karnaugh";
|
pname = "karnaugh";
|
||||||
version = "0.1.5";
|
version = "0.1.5";
|
||||||
|
|||||||
+19
-1
@@ -25,6 +25,8 @@ use tracing_subscriber::FmtSubscriber;
|
|||||||
|
|
||||||
use crate::typst::TypstContext;
|
use crate::typst::TypstContext;
|
||||||
|
|
||||||
|
mod syntax;
|
||||||
|
mod themes;
|
||||||
mod typst;
|
mod typst;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -59,6 +61,19 @@ struct AppConfig {
|
|||||||
#[arg(short = 'd', long, value_name = "DIR", default_value = "/tmp/karnaugh")]
|
#[arg(short = 'd', long, value_name = "DIR", default_value = "/tmp/karnaugh")]
|
||||||
packages_root: PathBuf,
|
packages_root: PathBuf,
|
||||||
|
|
||||||
|
/// The directory where the syntax files are stored.
|
||||||
|
#[arg(short = 'g', long, value_name = "DIR", default_value = None)]
|
||||||
|
syntax_root: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// The directory where Helix theme `.toml` files are stored.
|
||||||
|
#[arg(short = 't', long, value_name = "DIR", default_value = None)]
|
||||||
|
themes_root: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Grammars to load, separated by commas.
|
||||||
|
/// Useful to avoid the memory required to load all languages.
|
||||||
|
#[arg(short = 'L', long = "lang", value_name = "NAME", value_delimiter = ',')]
|
||||||
|
languages: Vec<String>,
|
||||||
|
|
||||||
/// The log level to use
|
/// The log level to use
|
||||||
#[arg(short = 'l', long, value_name = "LEVEL", default_value_t = Level::INFO)]
|
#[arg(short = 'l', long, value_name = "LEVEL", default_value_t = Level::INFO)]
|
||||||
log_level: Level,
|
log_level: Level,
|
||||||
@@ -94,6 +109,9 @@ async fn main() {
|
|||||||
.with_max_level(state.config.log_level)
|
.with_max_level(state.config.log_level)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
|
syntax::init(&state.config);
|
||||||
|
themes::init(&state.config);
|
||||||
|
|
||||||
// TODO: Replace with config option
|
// TODO: Replace with config option
|
||||||
let mut favicon = state.config.get_full_assets_path();
|
let mut favicon = state.config.get_full_assets_path();
|
||||||
favicon.push(PathBuf::from("favicon.svg"));
|
favicon.push(PathBuf::from("favicon.svg"));
|
||||||
@@ -117,7 +135,7 @@ async fn main() {
|
|||||||
info!("Serving Karnaugh on socket {}", socket);
|
info!("Serving Karnaugh on socket {}", socket);
|
||||||
let listener = tokio::net::TcpListener::bind(socket)
|
let listener = tokio::net::TcpListener::bind(socket)
|
||||||
.await
|
.await
|
||||||
.expect(&format!("Failed to bind to socket {}", socket));
|
.unwrap_or_else(|_| panic!("Failed to bind to socket {}", socket));
|
||||||
axum::serve(listener, app)
|
axum::serve(listener, app)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to serve site");
|
.expect("Failed to serve site");
|
||||||
|
|||||||
+265
@@ -0,0 +1,265 @@
|
|||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
error::Error,
|
||||||
|
fs,
|
||||||
|
path::Path,
|
||||||
|
sync::OnceLock,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use ropey::Rope;
|
||||||
|
use tracing::{info, warn};
|
||||||
|
use tree_house::{
|
||||||
|
InjectionLanguageMarker, Language, LanguageConfig, LanguageLoader, Syntax,
|
||||||
|
highlighter::{Highlight, HighlightEvent, Highlighter},
|
||||||
|
read_query,
|
||||||
|
};
|
||||||
|
use tree_house_bindings::Grammar;
|
||||||
|
use typst::{
|
||||||
|
foundations::{Content, Func, NativeElement, NativeFunc, func},
|
||||||
|
text::TextElem,
|
||||||
|
};
|
||||||
|
use typst_html::{HtmlAttr, HtmlElem, attr, tag};
|
||||||
|
|
||||||
|
use crate::AppConfig;
|
||||||
|
|
||||||
|
static REGISTRY: OnceLock<LanguageRegistry> = OnceLock::new();
|
||||||
|
|
||||||
|
/// Class names used in highlighted code.
|
||||||
|
const HIGHLIGHT_NAMES: &[&str] = &[
|
||||||
|
"attribute",
|
||||||
|
"comment",
|
||||||
|
"comment.block",
|
||||||
|
"comment.line",
|
||||||
|
"constant",
|
||||||
|
"constant.builtin",
|
||||||
|
"constructor",
|
||||||
|
"function",
|
||||||
|
"function.builtin",
|
||||||
|
"function.macro",
|
||||||
|
"function.method",
|
||||||
|
"keyword",
|
||||||
|
"keyword.control",
|
||||||
|
"keyword.operator",
|
||||||
|
"label",
|
||||||
|
"namespace",
|
||||||
|
"number",
|
||||||
|
"operator",
|
||||||
|
"property",
|
||||||
|
"punctuation",
|
||||||
|
"punctuation.bracket",
|
||||||
|
"punctuation.delimiter",
|
||||||
|
"special",
|
||||||
|
"string",
|
||||||
|
"string.special",
|
||||||
|
"tag",
|
||||||
|
"type",
|
||||||
|
"type.builtin",
|
||||||
|
"variable",
|
||||||
|
"variable.builtin",
|
||||||
|
"variable.parameter",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Get the longest matching highlight name for a given scope.
|
||||||
|
fn highlight_for_scope(scope: &str) -> Option<Highlight> {
|
||||||
|
let mut candidate = scope;
|
||||||
|
loop {
|
||||||
|
if let Some(i) = HIGHLIGHT_NAMES.iter().position(|&n| n == candidate) {
|
||||||
|
return Some(Highlight::new(i as u32));
|
||||||
|
}
|
||||||
|
match candidate.rfind('.') {
|
||||||
|
Some(dot) => candidate = &candidate[..dot],
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LanguageRegistry {
|
||||||
|
by_name: HashMap<String, Language>,
|
||||||
|
configs: Vec<LanguageConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanguageRegistry {
|
||||||
|
/// Loads grammars from `syntax_root`. When `languages` is non-empty, only
|
||||||
|
/// grammars with those names are loaded; otherwise every grammar found is.
|
||||||
|
fn new(syntax_root: &Path, languages: &[String]) -> Self {
|
||||||
|
let filter: Option<HashSet<&str>> =
|
||||||
|
(!languages.is_empty()).then(|| languages.iter().map(String::as_str).collect());
|
||||||
|
|
||||||
|
let mut by_name = HashMap::new();
|
||||||
|
let mut configs = Vec::new();
|
||||||
|
|
||||||
|
for entry in fs::read_dir(syntax_root).into_iter().flatten().flatten() {
|
||||||
|
let name = entry.file_name().to_string_lossy().into_owned();
|
||||||
|
if filter.as_ref().is_some_and(|f| !f.contains(name.as_str())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let so_path = entry.path().join(format!("{name}.so"));
|
||||||
|
if !so_path.exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match load_language(&name, &entry.path(), syntax_root) {
|
||||||
|
Ok(config) => {
|
||||||
|
by_name.insert(name, Language::new(configs.len() as u32));
|
||||||
|
configs.push(config);
|
||||||
|
}
|
||||||
|
Err(e) => warn!("Failed to load grammar {name}: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn about explicitly requested grammars that were not found.
|
||||||
|
for lang in languages {
|
||||||
|
if !by_name.contains_key(lang) {
|
||||||
|
warn!("Requested grammar {lang} was not found in the syntax root");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Loaded {} syntax highlighting grammars", configs.len());
|
||||||
|
Self { by_name, configs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_language(
|
||||||
|
name: &str,
|
||||||
|
lang_dir: &Path,
|
||||||
|
syntax_root: &Path,
|
||||||
|
) -> Result<LanguageConfig, Box<dyn Error>> {
|
||||||
|
let grammar = unsafe { Grammar::new(name, &lang_dir.join(format!("{name}.so")))? };
|
||||||
|
|
||||||
|
let read = |kind: &str| {
|
||||||
|
read_query(name, |lang| {
|
||||||
|
fs::read_to_string(syntax_root.join(lang).join(format!("{kind}.scm")))
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let config = LanguageConfig::new(
|
||||||
|
grammar,
|
||||||
|
&read("highlights"),
|
||||||
|
&read("injections"),
|
||||||
|
&read("locals"),
|
||||||
|
)?;
|
||||||
|
config.configure(highlight_for_scope);
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LanguageLoader for LanguageRegistry {
|
||||||
|
fn language_for_marker(&self, marker: InjectionLanguageMarker<'_>) -> Option<Language> {
|
||||||
|
match marker {
|
||||||
|
InjectionLanguageMarker::Name(name) => self.by_name.get(name).copied(),
|
||||||
|
InjectionLanguageMarker::Match(text) => {
|
||||||
|
let name: Cow<str> = text.into();
|
||||||
|
self.by_name.get(name.as_ref()).copied()
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_config(&self, lang: Language) -> Option<&LanguageConfig> {
|
||||||
|
self.configs.get(lang.idx())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(config: &AppConfig) {
|
||||||
|
match &config.syntax_root {
|
||||||
|
Some(syntax_root) => {
|
||||||
|
info!("Loading syntax files from {}", syntax_root.display());
|
||||||
|
REGISTRY.get_or_init(|| LanguageRegistry::new(syntax_root, &config.languages));
|
||||||
|
}
|
||||||
|
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>,
|
||||||
|
) -> Content {
|
||||||
|
let highlighted = highlight_code(&text, lang.as_deref()).unwrap_or(TextElem::packed(text));
|
||||||
|
|
||||||
|
if block {
|
||||||
|
HtmlElem::new(tag::pre)
|
||||||
|
.with_body(Some(highlighted))
|
||||||
|
.with_attr(HtmlAttr::constant("class"), "code-block")
|
||||||
|
.pack()
|
||||||
|
} else {
|
||||||
|
HtmlElem::new(tag::code)
|
||||||
|
.with_body(Some(highlighted))
|
||||||
|
.with_attr(HtmlAttr::constant("class"), "code-inline")
|
||||||
|
.pack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Highlights `source` as `lang`.
|
||||||
|
fn highlight_code(source: &str, lang: Option<&str>) -> Option<Content> {
|
||||||
|
let registry = REGISTRY.get()?;
|
||||||
|
let language = *registry.by_name.get(lang?)?;
|
||||||
|
|
||||||
|
let rope = Rope::from_str(source);
|
||||||
|
let slice = rope.slice(..);
|
||||||
|
let syntax = Syntax::new(slice, language, Duration::from_millis(500), registry).ok()?;
|
||||||
|
let mut hl = Highlighter::new(&syntax, slice, registry, 0..source.len() as u32);
|
||||||
|
|
||||||
|
let src = source.as_bytes();
|
||||||
|
let src_len = source.len() as u32;
|
||||||
|
let mut pos: u32 = 0;
|
||||||
|
let mut classes = String::new();
|
||||||
|
let mut nodes = Vec::<Content>::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let next = hl.next_event_offset().min(src_len);
|
||||||
|
|
||||||
|
if next > pos {
|
||||||
|
let text = std::str::from_utf8(&src[pos as usize..next as usize]).ok()?;
|
||||||
|
let node = TextElem::packed(text);
|
||||||
|
nodes.push(if classes.is_empty() {
|
||||||
|
node
|
||||||
|
} else {
|
||||||
|
HtmlElem::new(tag::span)
|
||||||
|
.with_attr(attr::class, classes.clone())
|
||||||
|
.with_body(Some(node))
|
||||||
|
.pack()
|
||||||
|
});
|
||||||
|
pos = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if pos >= src_len {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (event, highlights) = hl.advance();
|
||||||
|
let base = match event {
|
||||||
|
// Replace the active highlights
|
||||||
|
HighlightEvent::Refresh => String::new(),
|
||||||
|
// Extend the active highlights
|
||||||
|
HighlightEvent::Push => classes.clone(),
|
||||||
|
};
|
||||||
|
classes = highlights.fold(base, |mut acc, h| {
|
||||||
|
let name = HIGHLIGHT_NAMES[h.idx()].replace('.', "-");
|
||||||
|
if !acc.is_empty() {
|
||||||
|
acc.push(' ');
|
||||||
|
}
|
||||||
|
acc.push_str("hl-");
|
||||||
|
acc.push_str(&name);
|
||||||
|
acc
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Content::sequence(nodes))
|
||||||
|
}
|
||||||
+300
@@ -0,0 +1,300 @@
|
|||||||
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
error::Error,
|
||||||
|
fs,
|
||||||
|
path::Path,
|
||||||
|
sync::OnceLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
use toml::{Table, Value};
|
||||||
|
use tracing::{info, warn};
|
||||||
|
use typst::{
|
||||||
|
foundations::{Content, Func, NativeElement, NativeFunc, func},
|
||||||
|
text::TextElem,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::AppConfig;
|
||||||
|
|
||||||
|
static REGISTRY: OnceLock<ThemeRegistry> = OnceLock::new();
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ThemeRegistry {
|
||||||
|
themes: HashMap<String, Theme>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThemeRegistry {
|
||||||
|
fn new(theme_root: &Path) -> Self {
|
||||||
|
let mut helix_themes = HashMap::new();
|
||||||
|
for entry in fs::read_dir(theme_root).into_iter().flatten().flatten() {
|
||||||
|
let name = entry
|
||||||
|
.path()
|
||||||
|
.file_prefix()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
if let Ok(text) = fs::read_to_string(entry.path())
|
||||||
|
&& let Ok(toml) = toml::from_str::<Table>(&text)
|
||||||
|
{
|
||||||
|
helix_themes.insert(name, toml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve inheritance
|
||||||
|
while helix_themes
|
||||||
|
.values()
|
||||||
|
.any(|table| table.contains_key("inherits"))
|
||||||
|
{
|
||||||
|
let old_themes = helix_themes.clone();
|
||||||
|
old_themes
|
||||||
|
.keys()
|
||||||
|
.filter(|name| old_themes.get(*name).unwrap().contains_key("inherits"))
|
||||||
|
.for_each(|name| {
|
||||||
|
let parent_name = old_themes
|
||||||
|
.get(name)
|
||||||
|
.unwrap()
|
||||||
|
.get("inherits")
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.unwrap();
|
||||||
|
let parent = old_themes.get(parent_name).unwrap();
|
||||||
|
if !parent.contains_key("inherits") {
|
||||||
|
let table = helix_themes.get_mut(name).unwrap();
|
||||||
|
parent.into_iter().for_each(|(k, v)| {
|
||||||
|
if k == "palette" {
|
||||||
|
let parent_palette = v.as_table();
|
||||||
|
let child_palette = table
|
||||||
|
.entry(k.clone())
|
||||||
|
.or_insert_with(|| Value::Table(Table::new()))
|
||||||
|
.as_table_mut();
|
||||||
|
if let (Some(parent_palette), Some(child_palette)) =
|
||||||
|
(parent_palette, child_palette)
|
||||||
|
{
|
||||||
|
for (pk, pv) in parent_palette {
|
||||||
|
child_palette
|
||||||
|
.entry(pk.clone())
|
||||||
|
.or_insert_with(|| pv.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !table.contains_key(k) {
|
||||||
|
table.insert(k.clone(), v.clone());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
table.remove("inherits");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let themes = helix_themes
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|(name, table)| Theme::from_toml(table).map(|t| (name, t)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Self { themes }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
struct Color(String);
|
||||||
|
|
||||||
|
impl Color {
|
||||||
|
fn from_str(color: &str) -> Option<Self> {
|
||||||
|
if color.starts_with('#') {
|
||||||
|
Some(Self(color.into()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_palette(color: &str, palette: &HashMap<String, Color>) -> Option<Self> {
|
||||||
|
if color.starts_with('#') {
|
||||||
|
Self::from_str(color)
|
||||||
|
} else {
|
||||||
|
palette.get(color).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
struct Style {
|
||||||
|
fg: Option<Color>,
|
||||||
|
bold: bool,
|
||||||
|
italic: bool,
|
||||||
|
underlined: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Style {
|
||||||
|
fn from_toml(style: Value, palette: &HashMap<String, Color>) -> Option<Self> {
|
||||||
|
let resolve = |s: &str| Color::from_palette(s, palette);
|
||||||
|
match style {
|
||||||
|
Value::String(s) => Some(Self {
|
||||||
|
fg: resolve(&s),
|
||||||
|
bold: false,
|
||||||
|
italic: false,
|
||||||
|
underlined: false,
|
||||||
|
}),
|
||||||
|
Value::Table(table) => {
|
||||||
|
let fg = table.get("fg").and_then(Value::as_str).and_then(resolve);
|
||||||
|
let modifiers: HashSet<&str> = table
|
||||||
|
.get("modifiers")
|
||||||
|
.and_then(Value::as_array)
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.flat_map(Value::as_str)
|
||||||
|
.collect();
|
||||||
|
Some(Self {
|
||||||
|
fg,
|
||||||
|
bold: modifiers.contains("bold"),
|
||||||
|
italic: modifiers.contains("italic"),
|
||||||
|
underlined: modifiers.contains("underlined"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Theme {
|
||||||
|
foreground: Option<Color>,
|
||||||
|
background: Option<Color>,
|
||||||
|
styles: HashMap<String, Style>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static IGNORED_SCOPES: &[&str] = &["warning", "error", "info", "hint"];
|
||||||
|
|
||||||
|
impl Theme {
|
||||||
|
fn from_toml(theme: Table) -> Option<Self> {
|
||||||
|
let palette = theme
|
||||||
|
.get("palette")
|
||||||
|
.and_then(Value::as_table)
|
||||||
|
.cloned()
|
||||||
|
.map(|table| {
|
||||||
|
table
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|(k, v)| v.as_str().and_then(Color::from_str).map(|v| (k, v)))
|
||||||
|
.collect::<HashMap<String, Color>>()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let resolve = |c: &str| Color::from_palette(c, &palette);
|
||||||
|
|
||||||
|
let ui_background = theme.get("ui.background").and_then(Value::as_table)?;
|
||||||
|
let foreground = ui_background
|
||||||
|
.get("fg")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.and_then(resolve);
|
||||||
|
let background = ui_background
|
||||||
|
.get("bg")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.and_then(resolve);
|
||||||
|
|
||||||
|
let styles = theme
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(k, _)| {
|
||||||
|
!k.starts_with("ui.")
|
||||||
|
&& !k.starts_with("diagnostic.")
|
||||||
|
&& !IGNORED_SCOPES.contains(&k.as_str())
|
||||||
|
})
|
||||||
|
.flat_map(|(k, v)| Style::from_toml(v, &palette).map(|v| (k, v)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
foreground,
|
||||||
|
background,
|
||||||
|
styles,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(config: &AppConfig) {
|
||||||
|
match &config.themes_root {
|
||||||
|
Some(themes_root) => {
|
||||||
|
info!("Loading themes from {}", themes_root.display());
|
||||||
|
REGISTRY.get_or_init(|| ThemeRegistry::new(themes_root));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
warn!("No themes root has been configured, theme-based colouring will not be possible.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn theme_css_func() -> Func {
|
||||||
|
theme_css::func()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scope_to_selector(scope: &str) -> String {
|
||||||
|
format!(".hl-{}", scope.replace('.', "-"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn style_declarations(style: &Style) -> String {
|
||||||
|
let mut parts = Vec::new();
|
||||||
|
if let Some(Color(hex)) = &style.fg {
|
||||||
|
parts.push(format!("color:{hex}"));
|
||||||
|
}
|
||||||
|
parts.push(format!(
|
||||||
|
"font-weight:{}",
|
||||||
|
if style.bold { "bold" } else { "normal" }
|
||||||
|
));
|
||||||
|
parts.push(format!(
|
||||||
|
"font-style:{}",
|
||||||
|
if style.italic { "italic" } else { "normal" }
|
||||||
|
));
|
||||||
|
parts.push(format!(
|
||||||
|
"text-decoration:{}",
|
||||||
|
if style.underlined {
|
||||||
|
"underline"
|
||||||
|
} else {
|
||||||
|
"none"
|
||||||
|
}
|
||||||
|
));
|
||||||
|
parts.join(";")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_css(scopes: &[&str], theme: &Theme) -> String {
|
||||||
|
let mut groups: HashMap<&Style, Vec<&str>> = HashMap::new();
|
||||||
|
for &scope in scopes {
|
||||||
|
if let Some(style) = theme.styles.get(scope) {
|
||||||
|
groups.entry(style).or_default().push(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
groups
|
||||||
|
.iter()
|
||||||
|
.map(|(style, scopes)| {
|
||||||
|
let selectors = scopes
|
||||||
|
.iter()
|
||||||
|
.map(|s| scope_to_selector(s))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",");
|
||||||
|
format!("{}{{{}}}", selectors, style_declarations(style))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[func]
|
||||||
|
fn theme_css(light: String, dark: String) -> String {
|
||||||
|
let Some(registry) = REGISTRY.get() else {
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
let Some(light_theme) = registry.themes.get(&light) else {
|
||||||
|
warn!("Unknown light theme: {light}");
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
let Some(dark_theme) = registry.themes.get(&dark) else {
|
||||||
|
warn!("Unknown dark theme: {dark}");
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut shared: Vec<&str> = light_theme
|
||||||
|
.styles
|
||||||
|
.keys()
|
||||||
|
.filter(|k| dark_theme.styles.contains_key(*k))
|
||||||
|
.map(String::as_str)
|
||||||
|
.collect();
|
||||||
|
shared.sort();
|
||||||
|
|
||||||
|
let light_css = generate_css(&shared, light_theme);
|
||||||
|
let dark_css = generate_css(&shared, dark_theme);
|
||||||
|
|
||||||
|
format!("{light_css}\n@media (prefers-color-scheme:dark){{{dark_css}}}")
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ use tracing::info;
|
|||||||
use typst::{
|
use typst::{
|
||||||
Library, LibraryExt, World,
|
Library, LibraryExt, World,
|
||||||
diag::{FileError, FileResult, PackageError, PackageResult, SourceDiagnostic},
|
diag::{FileError, FileResult, PackageError, PackageResult, SourceDiagnostic},
|
||||||
foundations::{Bytes, Datetime, Duration},
|
foundations::{Bytes, Datetime, Dict, Duration, Value},
|
||||||
syntax::{FileId, RootedPath, Source, VirtualPath, VirtualRoot, package::PackageSpec},
|
syntax::{FileId, RootedPath, Source, VirtualPath, VirtualRoot, package::PackageSpec},
|
||||||
text::{Font, FontBook},
|
text::{Font, FontBook},
|
||||||
utils::LazyHash,
|
utils::LazyHash,
|
||||||
@@ -23,7 +23,21 @@ pub struct TypstContext {
|
|||||||
|
|
||||||
impl TypstContext {
|
impl TypstContext {
|
||||||
pub fn new(config: AppConfig) -> Self {
|
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()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if config.themes_root.is_some() {
|
||||||
|
inputs.insert(
|
||||||
|
"theme_css".into(),
|
||||||
|
Value::Func(crate::themes::theme_css_func()),
|
||||||
|
);
|
||||||
|
}
|
||||||
let library = Library::builder()
|
let library = Library::builder()
|
||||||
|
.with_inputs(inputs)
|
||||||
.with_features([typst::Feature::Html].into_iter().collect())
|
.with_features([typst::Feature::Html].into_iter().collect())
|
||||||
.build();
|
.build();
|
||||||
let library = LazyHash::new(library);
|
let library = LazyHash::new(library);
|
||||||
Reference in new issue
Block a user