Compare commits

...

7 commits

Author SHA1 Message Date
56fc99146f
chore: Update tree-sitter-rust 2023-04-21 21:49:26 +02:00
fe69041113
feat: Use custom version of tree-sitter-rust
Not much development is happening in that plugin.
2023-04-21 21:35:22 +02:00
c97e22fedb
refactor: Use common function to initialize highlighter
This avoids differences between code, tests and benchmarks.
2023-04-21 20:56:29 +02:00
718a7fde41
feat: Improve performances
- create a map of byte offsets to Kakoune ranges once, to avoid having to
iterate the content for every highlight event
- create a direct lookup for faces from the token index passed by the
highlighter event, rather than performing a string map lookup each time
- minimize allocations by pushing only a single string with sufficient
capacity, rather than building small strings
2023-04-18 10:35:14 +02:00
f0ab5e46ad
fix: Fix removing hooks on disable 2023-04-17 10:37:52 +02:00
659d93fc8f
fix: Fix some errors not being logged properly 2023-04-17 10:37:05 +02:00
efa642a14b
feat: Rework daemon startup and configuration 2023-04-14 13:36:00 +02:00
11 changed files with 715 additions and 132 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "tree-sitter-rust"]
path = tree-sitter-rust
url = git@github.com:sclu1034/tree-sitter-rust.git

384
Cargo.lock generated
View file

@ -26,6 +26,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.2.6" version = "0.2.6"
@ -66,6 +72,17 @@ dependencies = [
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -93,6 +110,18 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.79" version = "1.0.79"
@ -105,6 +134,45 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
[[package]]
name = "ciborium-ll"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
"bitflags",
"clap_lex 0.2.4",
"indexmap",
"textwrap",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.2.1" version = "4.2.1"
@ -125,7 +193,7 @@ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
"bitflags", "bitflags",
"clap_lex", "clap_lex 0.4.1",
"once_cell", "once_cell",
"strsim", "strsim",
"unicase", "unicase",
@ -144,6 +212,15 @@ dependencies = [
"syn 2.0.14", "syn 2.0.14",
] ]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.4.1" version = "0.4.1"
@ -192,6 +269,42 @@ dependencies = [
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
[[package]]
name = "criterion"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [
"anes",
"atty",
"cast",
"ciborium",
"clap 3.2.23",
"criterion-plot",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]] [[package]]
name = "crossbeam" name = "crossbeam"
version = "0.8.2" version = "0.8.2"
@ -259,6 +372,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.1" version = "0.3.1"
@ -296,6 +415,12 @@ version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@ -308,6 +433,24 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.1" version = "0.3.1"
@ -336,7 +479,7 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi 0.3.1",
"libc", "libc",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
@ -347,18 +490,43 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi 0.3.1",
"io-lifetimes", "io-lifetimes",
"rustix", "rustix",
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "js-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "kak-highlight" name = "kak-highlight"
version = "1.0.0" version = "1.0.0"
dependencies = [ dependencies = [
"clap", "clap 4.2.1",
"color-eyre", "color-eyre",
"criterion",
"crossbeam", "crossbeam",
"serde", "serde",
"signal-hook", "signal-hook",
@ -441,6 +609,25 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi 0.2.6",
"libc",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.30.3" version = "0.30.3"
@ -456,6 +643,18 @@ version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "os_str_bytes"
version = "6.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -474,6 +673,34 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "plotters"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
[[package]]
name = "plotters-svg"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
dependencies = [
"plotters-backend",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.56" version = "1.0.56"
@ -492,6 +719,28 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rayon"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.7.3" version = "1.7.3"
@ -538,6 +787,21 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "ryu"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@ -564,6 +828,17 @@ dependencies = [
"syn 2.0.14", "syn 2.0.14",
] ]
[[package]]
name = "serde_json"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.1" version = "0.6.1"
@ -635,6 +910,12 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.40" version = "1.0.40"
@ -665,6 +946,16 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.7.3" version = "0.7.3"
@ -795,8 +1086,6 @@ dependencies = [
[[package]] [[package]]
name = "tree-sitter-rust" name = "tree-sitter-rust"
version = "0.20.3" version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "797842733e252dc11ae5d403a18060bf337b822fc2ae5ddfaa6ff4d9cc20bda6"
dependencies = [ dependencies = [
"cc", "cc",
"tree-sitter", "tree-sitter",
@ -841,6 +1130,80 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 1.0.109",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "web-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -857,6 +1220,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

View file

@ -1,6 +1,5 @@
[package] [package]
name = "kak-highlight" name = "kak-highlight"
author = "Lucas Schwiderski"
description = "Tree-sitter-based highlighting for Kakoune" description = "Tree-sitter-based highlighting for Kakoune"
version = "1.0.0" version = "1.0.0"
edition = "2021" edition = "2021"
@ -19,4 +18,19 @@ tracing-error = "0.2.0"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
tree-sitter = "0.20.10" tree-sitter = "0.20.10"
tree-sitter-highlight = "0.20.1" tree-sitter-highlight = "0.20.1"
tree-sitter-rust = "0.20.3" tree-sitter-rust = { path = "./tree-sitter-rust", version = "*" }
[dev-dependencies]
criterion = "0.4.0"
[lib]
bench = false
[[bin]]
name = "kak-highlight"
path = "src/main.rs"
bench = false
[[bench]]
name = "worker"
harness = false

66
benches/worker.rs Normal file
View file

@ -0,0 +1,66 @@
use std::collections::HashMap;
use criterion::{criterion_group, criterion_main, Criterion};
use tree_sitter_highlight::{HighlightConfiguration, Highlighter};
use kak_highlight::daemon::worker::{highlight_content, make_highlighter_config};
fn make_tokens() -> HashMap<String, String> {
[
"constructor",
"function",
"function.macro",
"function.method",
"keyword",
"module",
"operator",
"punctuation",
"string",
"type",
"variable",
"variable.parameter",
]
.into_iter()
.fold(HashMap::new(), |mut map, val| {
map.insert(String::from(val), String::from(val));
map
})
}
fn run_benchmark(c: &mut Criterion, name: &str, content: &str) {
let timestamp = 0;
let tokens = make_tokens();
let names: Vec<_> = tokens.keys().collect();
let mut highlighter = Highlighter::new();
let highlight_config = make_highlighter_config(&names).unwrap();
c.bench_function(name, |b| {
b.iter(|| {
let _ = highlight_content(
&mut highlighter,
&highlight_config,
&tokens,
content.as_bytes(),
timestamp,
);
})
});
}
fn bench_main_rs(c: &mut Criterion) {
let content = r#"fn main() {
println!("Hello, world!");
}
"#;
run_benchmark(c, "main.rs", content);
}
fn bench_ast_rs(c: &mut Criterion) {
let content = include_str!("../tree-sitter-rust/examples/ast.rs");
run_benchmark(c, "ast.rs", content);
}
criterion_group!(benches, bench_main_rs, bench_ast_rs);
criterion_main!(benches);

View file

@ -1,5 +1,4 @@
declare-option str kak_highlight_cmd "kak-highlight" declare-option str kak_highlight_cmd "kak-highlight"
set-option global kak_highlight_cmd "kak-highlight -vv"
declare-option str kak_highlight_log "/tmp/kak-highlight.log" declare-option str kak_highlight_log "/tmp/kak-highlight.log"
declare-option -hidden range-specs kak_highlight_ranges declare-option -hidden range-specs kak_highlight_ranges
@ -26,29 +25,44 @@ content = """
} }
} }
define-command kak-highlight-enable -docstring %{ define-command kak-highlight-enable-window -docstring %{
kak-highlight-enable kak-highlight-enable-window
Start a daemon for the current session Enable the highlighter for this window
} %{ } %{
nop %sh{
(eval "${kak_opt_kak_highlight_cmd} --log '${kak_opt_kak_highlight_log}' daemon '${kak_session}'") >/dev/null 2>&1 </dev/null &
}
hook global KakEnd .* %{
nop %sh{
pkill -f "${kak_opt_kak_highlight_cmd} .* daemon ${kak_session}"
}
}
}
hook global WinSetOption filetype=rust %{
add-highlighter window/kak_highlight_ranges ranges kak_highlight_ranges add-highlighter window/kak_highlight_ranges ranges kak_highlight_ranges
hook window -group kak-highlight NormalIdle .* kak-highlight hook window -group kak-highlight NormalIdle .* kak-highlight
hook window -group kak-highlight InsertIdle .* kak-highlight hook window -group kak-highlight InsertIdle .* kak-highlight
hook window -group kak-highlight BufReload .* kak-highlight hook window -group kak-highlight BufReload .* kak-highlight
hook -once -always window WinSetOption filetype=.* %{ kak-highlight
}
define-command kak-highlight-disable-window -docstring %{
kak-highlight-disable-window
Disable the highlighter for this window
} %{
remove-hooks window kak-highlight
remove-highlighter window/kak_highlight_ranges remove-highlighter window/kak_highlight_ranges
}
define-command kak-highlight-start -docstring %{
kak-highlight-start
Start the daemon
} %{
nop %sh{
(eval "${kak_opt_kak_highlight_cmd} --log '${kak_opt_kak_highlight_log}' daemon '${kak_session}'") >/dev/null 2>&1 </dev/null &
} }
} }
define-command kak-highlight-stop -docstring %{
kak-highlight-stop
Stop the daemon
} %{
nop %sh{
pkill -f "${kak_opt_kak_highlight_cmd} .* daemon ${kak_session}"
}
}
hook global KakBegin .* kak-highlight-start
hook global KakEnd .* kak-highlight-stop

View file

@ -17,7 +17,7 @@ use crate::daemon::worker::TaskScheduler;
use self::listener::Listener; use self::listener::Listener;
mod listener; mod listener;
mod worker; pub mod worker;
#[tracing::instrument] #[tracing::instrument]
pub fn handle(runtime_dir: PathBuf, config_path: Option<PathBuf>, session: String) -> Result<()> { pub fn handle(runtime_dir: PathBuf, config_path: Option<PathBuf>, session: String) -> Result<()> {

View file

@ -13,7 +13,7 @@ use color_eyre::Result;
use crossbeam::deque::{Injector, Stealer, Worker}; use crossbeam::deque::{Injector, Stealer, Worker};
use tree_sitter_highlight::{HighlightConfiguration, HighlightEvent, Highlighter}; use tree_sitter_highlight::{HighlightConfiguration, HighlightEvent, Highlighter};
use crate::kakoune::{self, editor_quote}; use crate::kakoune::editor_quote;
use crate::Request; use crate::Request;
type Task = Result<UnixStream>; type Task = Result<UnixStream>;
@ -34,6 +34,22 @@ pub struct TaskContext {
tokens: Arc<HashMap<String, String>>, tokens: Arc<HashMap<String, String>>,
} }
pub fn make_highlighter_config<S: AsRef<str>>(
names: impl AsRef<[S]>,
) -> Result<HighlightConfiguration> {
let mut config = HighlightConfiguration::new(
tree_sitter_rust::language(),
tree_sitter_rust::HIGHLIGHT_QUERY,
tree_sitter_rust::INJECTIONS_QUERY,
tree_sitter_rust::LOCALS_QUERY,
)
.wrap_err("Invalid highlighter config")?;
config.configure(names.as_ref());
Ok(config)
}
impl TaskScheduler { impl TaskScheduler {
pub fn new(workers: u8, tokens: HashMap<String, String>) -> Result<Self> { pub fn new(workers: u8, tokens: HashMap<String, String>) -> Result<Self> {
let terminate = Arc::new(AtomicBool::new(false)); let terminate = Arc::new(AtomicBool::new(false));
@ -43,18 +59,10 @@ impl TaskScheduler {
let stealers: Vec<_> = workers.iter().map(|w| w.stealer()).collect(); let stealers: Vec<_> = workers.iter().map(|w| w.stealer()).collect();
let stealers = Arc::new(stealers); let stealers = Arc::new(stealers);
let mut highlight_config = HighlightConfiguration::new(
tree_sitter_rust::language(),
tree_sitter_rust::HIGHLIGHT_QUERY,
"",
"",
)
.wrap_err("Invalid highlighter config")?;
let names: Vec<_> = tokens.keys().collect(); let names: Vec<_> = tokens.keys().collect();
tracing::debug!("Highlighter tokens: {:?}", names); tracing::debug!("Highlighter tokens: {:?}", names);
highlight_config.configure(&names);
let highlight_config = make_highlighter_config(names)?;
let highlight_config = Arc::new(highlight_config); let highlight_config = Arc::new(highlight_config);
let tokens = Arc::new(tokens); let tokens = Arc::new(tokens);
@ -151,7 +159,13 @@ fn handle_connection(ctx: &mut TaskContext, task: Task) -> Result<()> {
tracing::info!("Received request"); tracing::info!("Received request");
tracing::debug!(?req); tracing::debug!(?req);
let response = process_request(ctx, &req) let response = highlight_content(
&mut ctx.highlighter,
&ctx.highlight_config,
&ctx.tokens,
req.content.as_bytes(),
req.timestamp,
)
.unwrap_or_else(|err| format!("fail {}", editor_quote(format!("{}", err)))); .unwrap_or_else(|err| format!("fail {}", editor_quote(format!("{}", err))));
let mut child = Command::new("kak") let mut child = Command::new("kak")
@ -181,24 +195,69 @@ fn handle_connection(ctx: &mut TaskContext, task: Task) -> Result<()> {
Ok(()) Ok(())
} }
#[tracing::instrument(skip(ctx, req), fields( // Tree-sitter operates on byte offsets, while Kakoune's range specs use
session = req.session, // (row, column) tuples. Calculating those on the fly is very expensive, as it requires
client = req.client, // iterating the whole content to find line breaks.
content_len = req.content.len(), // So instead, generate a map once where any byte offset can be looked up later.
timestamp = req.timestamp, fn build_range_map(content: &[u8]) -> Vec<(usize, usize)> {
))] let mut map = Vec::with_capacity(content.len());
fn process_request(ctx: &mut TaskContext, req: &Request) -> Result<String> {
let names: Vec<_> = ctx.tokens.keys().collect();
let highlights = ctx // Kakoune's line indices are 1-based
.highlighter let mut row = 1;
.highlight(&ctx.highlight_config, req.content.as_bytes(), None, |_| { let mut column = 1;
for byte in content.iter() {
map.push((row, column));
if *byte == b'\n' {
row += 1;
column = 1;
} else {
column += 1;
}
}
map
}
#[tracing::instrument(skip(highlighter, highlight_config, tokens), fields(
content_len = content.len(),
))]
pub fn highlight_content(
highlighter: &mut Highlighter,
highlight_config: &HighlightConfiguration,
tokens: &HashMap<String, String>,
content: &[u8],
timestamp: u64,
) -> Result<String> {
// By building vectors for both keys and values with the same order, we can
// use the highlighter's returned index to retrieve the face directly, rather
// than having to perform a second lookup in the hash map.
let mut names = Vec::with_capacity(tokens.len());
let mut faces = Vec::with_capacity(tokens.len());
for (name, face) in tokens.iter() {
names.push(name);
faces.push(face);
}
let highlights = highlighter
.highlight(highlight_config, content, None, |lang| {
if lang == "rust" {
Some(highlight_config)
} else {
None None
}
}) })
.wrap_err("Failed to highlight content")?; .wrap_err("Failed to highlight content")?;
let mut stack = VecDeque::new(); let mut stack = VecDeque::with_capacity(64);
let mut range_spec = String::new(); let range_map = build_range_map(content);
// We're going to do lots of small pushes to this String, which would trigger
// lots of incremental re-allocations. To avoid that, start with a capacity that should
// hoefully be somewhat close to, but bigger than the final size.
let mut response = String::with_capacity(content.len() * 2);
response.push_str("set-option buffer kak_highlight_ranges ");
response.push_str(&timestamp.to_string());
for res in highlights { for res in highlights {
match res? { match res? {
@ -208,22 +267,23 @@ fn process_request(ctx: &mut TaskContext, req: &Request) -> Result<String> {
// as `end` here. // as `end` here.
let end = end.saturating_sub(1); let end = end.saturating_sub(1);
let range = let (start_row, start_column) = range_map[start];
kakoune::range_from_byte_offsets(req.content.as_bytes(), start, end); let (end_row, end_column) = range_map[end];
let face: &String = faces[*index];
tracing::trace!(start, end, ?range, index); response.push(' ');
let spec = format!( tracing::trace!(index, start_offset = start, end_offset = end, face);
"{}.{},{}.{}|{}",
range.start_point.row,
range.start_point.column,
range.end_point.row,
range.end_point.column,
ctx.tokens[names[*index]]
);
range_spec.push(' '); response.push_str(&start_row.to_string());
range_spec.push_str(&spec); response.push('.');
response.push_str(&start_column.to_string());
response.push(',');
response.push_str(&end_row.to_string());
response.push('.');
response.push_str(&end_column.to_string());
response.push('|');
response.push_str(face);
} }
} }
HighlightEvent::HighlightStart(index) => { HighlightEvent::HighlightStart(index) => {
@ -237,10 +297,97 @@ fn process_request(ctx: &mut TaskContext, req: &Request) -> Result<String> {
} }
} }
let response = format!(
"set-option buffer kak_highlight_ranges {}{range_spec}",
req.timestamp
);
Ok(response) Ok(response)
} }
#[cfg(test)]
mod test {
use std::collections::HashMap;
use tree_sitter_highlight::Highlighter;
use crate::daemon::worker::make_highlighter_config;
use super::highlight_content;
fn make_tokens() -> HashMap<String, String> {
[
"constructor",
"function",
"function.macro",
"function.method",
"keyword",
"module",
"operator",
"punctuation",
"string",
"type",
"variable",
"variable.parameter",
]
.into_iter()
.fold(HashMap::new(), |mut map, val| {
map.insert(String::from(val), String::from(val));
map
})
}
fn run_test(content: impl AsRef<str>, specs: &[&str]) {
let timestamp = 0;
let tokens = make_tokens();
let names: Vec<_> = tokens.keys().collect();
let mut highlighter = Highlighter::new();
let highlight_config = make_highlighter_config(&names).unwrap();
let response = highlight_content(
&mut highlighter,
&highlight_config,
&tokens,
content.as_ref().as_bytes(),
timestamp,
)
.unwrap();
assert_eq!(
response,
format!(
"set-option buffer kak_highlight_ranges {timestamp} {}",
specs.join(" ")
)
.trim()
);
}
#[test]
fn empty_content() {
let content = String::new();
let specs = [];
run_test(content, &specs);
}
#[test]
fn main_rs() {
let content = r#"fn main() {
println!("Hello, world!");
}
"#;
let specs = vec![
"1.1,1.2|keyword",
"1.4,1.7|function",
"1.8,1.8|punctuation",
"1.9,1.9|punctuation",
"1.11,1.11|punctuation",
"2.5,2.11|function.macro",
"2.12,2.12|function.macro",
"2.13,2.13|punctuation",
"2.14,2.28|string",
"2.29,2.29|punctuation",
"2.30,2.30|punctuation",
"3.1,3.1|punctuation",
];
run_test(content, &specs);
}
}

View file

@ -1,47 +1,4 @@
use tree_sitter::{Point, Range};
pub fn editor_quote(s: impl AsRef<str>) -> String { pub fn editor_quote(s: impl AsRef<str>) -> String {
// TODO // TODO
format!("'{}'", s.as_ref()) format!("'{}'", s.as_ref())
} }
pub fn range_from_byte_offsets(content: impl AsRef<[u8]>, start: usize, end: usize) -> Range {
// Kakoune's line indices are 1-based
let mut start_row = 1;
let mut start_column = 1;
let mut end_row = 1;
let mut end_column = 1;
for (i, byte) in content.as_ref().iter().enumerate() {
if i < start {
if *byte == b'\n' {
start_row += 1;
start_column = 1;
} else {
start_column += 1;
}
}
if i < end {
if *byte == b'\n' {
end_row += 1;
end_column = 1;
} else {
end_column += 1;
}
}
}
Range {
start_byte: start,
end_byte: end,
start_point: Point {
row: start_row,
column: start_column,
},
end_point: Point {
row: end_row,
column: end_column,
},
}
}

19
src/lib.rs Normal file
View file

@ -0,0 +1,19 @@
use serde::Deserialize;
mod config;
mod kakoune;
pub mod client;
pub mod daemon;
#[derive(Clone, Debug, Deserialize)]
pub struct Request {
/// The buffer content
content: String,
/// The Kakoune timestamp
timestamp: u64,
/// The Kakoune session
session: String,
/// The Kakoune client
client: String,
}

View file

@ -5,16 +5,12 @@ use std::path::PathBuf;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use color_eyre::eyre::Context; use color_eyre::eyre::Context;
use color_eyre::Result; use color_eyre::Result;
use serde::Deserialize;
use tracing::metadata::LevelFilter; use tracing::metadata::LevelFilter;
use tracing_error::ErrorLayer; use tracing_error::ErrorLayer;
use tracing_subscriber::fmt; use tracing_subscriber::fmt;
use tracing_subscriber::prelude::*; use tracing_subscriber::prelude::*;
mod client; use kak_highlight::{client, daemon};
mod config;
mod daemon;
mod kakoune;
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -44,18 +40,6 @@ enum Command {
}, },
} }
#[derive(Clone, Debug, Deserialize)]
struct Request {
/// The buffer content
content: String,
/// The Kakoune timestamp
timestamp: u64,
/// The Kakoune session
session: String,
/// The Kakoune client
client: String,
}
#[tracing::instrument] #[tracing::instrument]
fn main() -> Result<()> { fn main() -> Result<()> {
let cli = Cli::parse(); let cli = Cli::parse();
@ -90,8 +74,14 @@ fn main() -> Result<()> {
.unwrap_or_else(|_| "/var".into()) .unwrap_or_else(|_| "/var".into())
.join("kak-highlight"); .join("kak-highlight");
match cli.command { let res = match cli.command {
Command::Daemon { session } => daemon::handle(runtime_dir, cli.config, session), Command::Daemon { session } => daemon::handle(runtime_dir, cli.config, session),
Command::Request => client::handle(runtime_dir), Command::Request => client::handle(runtime_dir),
};
if let Err(err) = &res {
tracing::error!("{:?}", err)
} }
res
} }

1
tree-sitter-rust Submodule

@ -0,0 +1 @@
Subproject commit 15452ac6becd16a622ec6ea70de283063926a0bd