feat: keymap Lua bindings

This commit is contained in:
sxyazi 2026-06-06 12:42:14 +08:00
parent 8e80798984
commit 22a4e384bd
No known key found for this signature in database
76 changed files with 1068 additions and 422 deletions

View file

@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
- Drag and drop ([#4005]) - Drag and drop ([#4005])
- Bulk create ([#3793]) - Bulk create ([#3793])
- Image preview with Überzug++ on Niri ([#3990]) - Image preview with Überzug++ on Niri ([#3990])
- New gait for input `backward` and `forward` actions ([#4012])
### Changed ### Changed

331
Cargo.lock generated
View file

@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe"
dependencies = [ dependencies = [
"crypto-common 0.2.2", "crypto-common 0.2.2",
"inout 0.2.2", "inout",
] ]
[[package]] [[package]]
@ -378,9 +378,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.11.1" version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
dependencies = [ dependencies = [
"serde_core", "serde_core",
] ]
@ -434,15 +434,6 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array 0.14.7",
]
[[package]] [[package]]
name = "block-padding" name = "block-padding"
version = "0.4.2" version = "0.4.2"
@ -594,9 +585,9 @@ dependencies = [
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.44" version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
dependencies = [ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
@ -614,7 +605,7 @@ checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
dependencies = [ dependencies = [
"block-buffer 0.12.0", "block-buffer 0.12.0",
"crypto-common 0.2.2", "crypto-common 0.2.2",
"inout 0.2.2", "inout",
"zeroize", "zeroize",
] ]
@ -804,6 +795,12 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.15" version = "0.5.15"
@ -844,7 +841,7 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"crossterm_winapi", "crossterm_winapi",
"mio", "mio",
"parking_lot", "parking_lot",
@ -861,7 +858,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"crossterm_winapi", "crossterm_winapi",
"derive_more", "derive_more",
"document-features", "document-features",
@ -969,12 +966,12 @@ dependencies = [
[[package]] [[package]]
name = "curve25519-dalek" name = "curve25519-dalek"
version = "5.0.0-pre.6" version = "5.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335f1947f241137a14106b6f5acc5918a5ede29c9d71d3f2cb1678d5075d9fc3" checksum = "4f359e08ca85e7bd759e1fd933ff2bccd81864c60a8fba0e259c7f822b0924bf"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.2.17", "cpufeatures 0.3.0",
"curve25519-dalek-derive", "curve25519-dalek-derive",
"digest 0.11.3", "digest 0.11.3",
"fiat-crypto", "fiat-crypto",
@ -1274,9 +1271,9 @@ dependencies = [
[[package]] [[package]]
name = "ed25519-dalek" name = "ed25519-dalek"
version = "3.0.0-pre.7" version = "3.0.0-rc.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20449acd54b660981ae5caa2bcb56d1fe7f25f2e37a38ec507400fab034d4bb6" checksum = "b011170fe4f04665565b4110afef66774fe9ffff278f3eb5b81cc73d26e27d60"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"ed25519", "ed25519",
@ -1296,22 +1293,22 @@ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
[[package]] [[package]]
name = "elliptic-curve" name = "elliptic-curve"
version = "0.14.0-rc.32" version = "0.14.0-rc.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda94f31325c4275e9706adecbb6f0650dee2f904c915a98e3d81adaaaa757aa" checksum = "102d3643d30dd8b559613c5cced68317199597fffb278cdc88daa2ef7fafc935"
dependencies = [ dependencies = [
"base16ct", "base16ct",
"crypto-bigint", "crypto-bigint",
"crypto-common 0.2.2", "crypto-common 0.2.2",
"digest 0.11.3", "digest 0.11.3",
"ff",
"group",
"hkdf", "hkdf",
"hybrid-array", "hybrid-array",
"once_cell", "once_cell",
"pem-rfc7468", "pem-rfc7468",
"pkcs8", "pkcs8",
"rand_core 0.10.1", "rand_core 0.10.1",
"rustcrypto-ff",
"rustcrypto-group",
"sec1", "sec1",
"subtle", "subtle",
"zeroize", "zeroize",
@ -1469,6 +1466,16 @@ dependencies = [
"thiserror 1.0.69", "thiserror 1.0.69",
] ]
[[package]]
name = "ff"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f"
dependencies = [
"rand_core 0.10.1",
"subtle",
]
[[package]] [[package]]
name = "fiat-crypto" name = "fiat-crypto"
version = "0.3.0" version = "0.3.0"
@ -1663,10 +1670,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"wasi", "wasi",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -1688,11 +1693,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"r-efi 6.0.0", "r-efi 6.0.0",
"rand_core 0.10.1", "rand_core 0.10.1",
"wasip2", "wasip2",
"wasip3", "wasip3",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -1733,6 +1740,17 @@ dependencies = [
"regex-syntax", "regex-syntax",
] ]
[[package]]
name = "group"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4"
dependencies = [
"ff",
"rand_core 0.10.1",
"subtle",
]
[[package]] [[package]]
name = "half" name = "half"
version = "2.7.1" version = "2.7.1"
@ -1946,11 +1964,11 @@ dependencies = [
[[package]] [[package]]
name = "inotify" name = "inotify"
version = "0.11.1" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"inotify-sys", "inotify-sys",
"libc", "libc",
] ]
@ -1964,23 +1982,13 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"block-padding 0.3.3",
"generic-array 0.14.7",
]
[[package]] [[package]]
name = "inout" name = "inout"
version = "0.2.2" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [ dependencies = [
"block-padding 0.4.2", "block-padding",
"hybrid-array", "hybrid-array",
] ]
@ -2020,6 +2028,15 @@ dependencies = [
"syn 2.0.117", "syn 2.0.117",
] ]
[[package]]
name = "inventory"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b"
dependencies = [
"rustversion",
]
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.2" version = "1.70.2"
@ -2070,7 +2087,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899"
dependencies = [ dependencies = [
"hashbrown 0.16.1", "hashbrown 0.16.1",
"portable-atomic",
"thiserror 2.0.18", "thiserror 2.0.18",
] ]
@ -2110,7 +2126,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"libc", "libc",
] ]
@ -2146,9 +2162,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]] [[package]]
name = "libfuzzer-sys" name = "libfuzzer-sys"
version = "0.4.12" version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2"
dependencies = [ dependencies = [
"arbitrary", "arbitrary",
"cc", "cc",
@ -2175,7 +2191,7 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
] ]
[[package]] [[package]]
@ -2207,9 +2223,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.30" version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
[[package]] [[package]]
name = "loop9" name = "loop9"
@ -2220,15 +2236,6 @@ dependencies = [
"imgref", "imgref",
] ]
[[package]]
name = "lru"
version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
dependencies = [
"hashbrown 0.16.1",
]
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.18.0" version = "0.18.0"
@ -2287,9 +2294,9 @@ dependencies = [
[[package]] [[package]]
name = "md5" name = "md5"
version = "0.7.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0"
[[package]] [[package]]
name = "memchr" name = "memchr"
@ -2438,7 +2445,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@ -2451,7 +2458,7 @@ version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"cfg-if", "cfg-if",
"cfg_aliases", "cfg_aliases",
"libc", "libc",
@ -2497,7 +2504,7 @@ version = "8.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"fsevent-sys", "fsevent-sys",
"inotify", "inotify",
"kqueue", "kqueue",
@ -2515,7 +2522,7 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
] ]
[[package]] [[package]]
@ -2630,7 +2637,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"objc2", "objc2",
] ]
@ -2661,7 +2668,7 @@ version = "6.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2" checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"libc", "libc",
"once_cell", "once_cell",
"onig_sys", "onig_sys",
@ -2714,9 +2721,9 @@ dependencies = [
[[package]] [[package]]
name = "p256" name = "p256"
version = "0.14.0-rc.9" version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b97e3bf0465157ae90975ff52dbeb1362ba618924878c9f74c25baa27a65f9a" checksum = "41adc63effe99d48837a8cc0e6d7a77e32ae6a07f6000df466178dbc2193093e"
dependencies = [ dependencies = [
"ecdsa", "ecdsa",
"elliptic-curve", "elliptic-curve",
@ -2727,9 +2734,9 @@ dependencies = [
[[package]] [[package]]
name = "p384" name = "p384"
version = "0.14.0-rc.9" version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "437f30ebcb1e16ff48acead5f08bd69fbcdbc82421687bb48af5c315a0bfab03" checksum = "9bd5333afa5ae0347f39e6a0f2c9c155da431583fd71fe5555bd0521b4ccaf02"
dependencies = [ dependencies = [
"ecdsa", "ecdsa",
"elliptic-curve", "elliptic-curve",
@ -2741,9 +2748,9 @@ dependencies = [
[[package]] [[package]]
name = "p521" name = "p521"
version = "0.14.0-rc.9" version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e9fd792bab86ecf6249561752fb5a413511f999887107dd054bbda5143743d7" checksum = "a3a5297f53dc16d35909060ba3032cff7867e8809f01e273ff325579d5f0ceae"
dependencies = [ dependencies = [
"base16ct", "base16ct",
"ecdsa", "ecdsa",
@ -3046,7 +3053,7 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"crc32fast", "crc32fast",
"fdeflate", "fdeflate",
"flate2", "flate2",
@ -3075,12 +3082,6 @@ dependencies = [
"universal-hash", "universal-hash",
] ]
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]] [[package]]
name = "powerfmt" name = "powerfmt"
version = "0.2.0" version = "0.2.0"
@ -3108,23 +3109,23 @@ dependencies = [
[[package]] [[package]]
name = "primefield" name = "primefield"
version = "0.14.0-rc.9" version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b52e6ee42db392378a95622b463c9740631171d1efce43fa445a569c1600cb6" checksum = "f845ec3240cd5ed5e1e31cf3ff633a5bf47c698dc4092ba9e767415b3d393406"
dependencies = [ dependencies = [
"crypto-bigint", "crypto-bigint",
"crypto-common 0.2.2", "crypto-common 0.2.2",
"ff",
"rand_core 0.10.1", "rand_core 0.10.1",
"rustcrypto-ff",
"subtle", "subtle",
"zeroize", "zeroize",
] ]
[[package]] [[package]]
name = "primeorder" name = "primeorder"
version = "0.14.0-rc.9" version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0556580e42c19833f5d232aca11a7687a503ee41f937b54f5ae1d50fc2a6a36a" checksum = "7d2793f22b9b6fd11ef3ac1d59bf003c2573593e4968702341605c2748fd90bf"
dependencies = [ dependencies = [
"elliptic-curve", "elliptic-curve",
] ]
@ -3319,9 +3320,9 @@ checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]] [[package]]
name = "ratatui" name = "ratatui"
version = "0.30.0" version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" checksum = "1695748e3a735b34968c887ceea5a380b43545903868ae8f5b666593100f6b68"
dependencies = [ dependencies = [
"instability", "instability",
"ratatui-core", "ratatui-core",
@ -3334,19 +3335,20 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-core" name = "ratatui-core"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" checksum = "42d3603f354bba8c595fa47860e60142d7372b7210c27044c6a7d0e1a4336b44"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"compact_str", "compact_str",
"hashbrown 0.16.1", "critical-section",
"hashbrown 0.17.1",
"indoc", "indoc",
"itertools", "itertools",
"kasuari", "kasuari",
"lru 0.16.4", "lru",
"serde", "serde",
"strum 0.27.2", "strum",
"thiserror 2.0.18", "thiserror 2.0.18",
"unicode-segmentation", "unicode-segmentation",
"unicode-truncate", "unicode-truncate",
@ -3355,9 +3357,9 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-crossterm" name = "ratatui-crossterm"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" checksum = "2b2867bedcbd6a690ca4f8672a687b730ec07660c79844517b084311b529980c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossterm 0.28.1", "crossterm 0.28.1",
@ -3368,9 +3370,9 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-termion" name = "ratatui-termion"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cade85a8591fbc911e147951422f0d6fd40f4948b271b6216c7dc01838996f8" checksum = "5c16cc35a9d9114e0b2bb4b22018b96ae7f5fe60e2595dc73e622b4e78624835"
dependencies = [ dependencies = [
"instability", "instability",
"ratatui-core", "ratatui-core",
@ -3379,9 +3381,9 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-termwiz" name = "ratatui-termwiz"
version = "0.1.0" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c" checksum = "386b8ff8f74ed749509391c56d549761a2fcdb408e1f42e467286bcb7dac8967"
dependencies = [ dependencies = [
"ratatui-core", "ratatui-core",
"termwiz", "termwiz",
@ -3389,20 +3391,19 @@ dependencies = [
[[package]] [[package]]
name = "ratatui-widgets" name = "ratatui-widgets"
version = "0.3.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" checksum = "7ef4f17dd7ac3abf5adc2b920a03c61eee4bfe6a88fa5191936895525371d79c"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"hashbrown 0.16.1", "hashbrown 0.17.1",
"indoc", "indoc",
"instability", "instability",
"itertools", "itertools",
"line-clipping", "line-clipping",
"ratatui-core", "ratatui-core",
"serde", "serde",
"strum 0.27.2", "strum",
"time",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
] ]
@ -3482,7 +3483,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
] ]
[[package]] [[package]]
@ -3596,13 +3597,13 @@ dependencies = [
[[package]] [[package]]
name = "russh" name = "russh"
version = "0.61.1" version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67013f080c226e5a34db1c71f2567f44d95a6300005bb6cd4e2c8fe3c326d1b" checksum = "bbf893f64684e58da8a68d56a5e84d1cf0440226274c515770fe267707a7d0b0"
dependencies = [ dependencies = [
"aes", "aes",
"bitflags 2.11.1", "bitflags 2.13.0",
"block-padding 0.4.2", "block-padding",
"byteorder", "byteorder",
"bytes", "bytes",
"cbc", "cbc",
@ -3620,12 +3621,11 @@ dependencies = [
"enum_dispatch", "enum_dispatch",
"futures", "futures",
"generic-array 1.4.3", "generic-array 1.4.3",
"getrandom 0.2.17", "getrandom 0.4.2",
"ghash", "ghash",
"hex-literal", "hex-literal",
"hkdf",
"hmac", "hmac",
"inout 0.1.4", "inout",
"internal-russh-num-bigint", "internal-russh-num-bigint",
"keccak", "keccak",
"log", "log",
@ -3711,34 +3711,13 @@ dependencies = [
"semver", "semver",
] ]
[[package]]
name = "rustcrypto-ff"
version = "0.14.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6"
dependencies = [
"rand_core 0.10.1",
"subtle",
]
[[package]]
name = "rustcrypto-group"
version = "0.14.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "369f9b61aa45933c062c9f6b5c3c50ab710687eca83dd3802653b140b43f85ed"
dependencies = [
"rand_core 0.10.1",
"rustcrypto-ff",
"subtle",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.44" version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.4.15", "linux-raw-sys 0.4.15",
@ -3751,7 +3730,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.12.1", "linux-raw-sys 0.12.1",
@ -3924,9 +3903,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.20.0" version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c"
dependencies = [ dependencies = [
"base64", "base64",
"bs58", "bs58",
@ -3944,9 +3923,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_with_macros" name = "serde_with_macros"
version = "3.20.0" version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
dependencies = [ dependencies = [
"darling 0.23.0", "darling 0.23.0",
"proc-macro2", "proc-macro2",
@ -4223,34 +4202,13 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
"strum_macros 0.27.2",
]
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.28.0" version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd"
dependencies = [ dependencies = [
"strum_macros 0.28.0", "strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.117",
] ]
[[package]] [[package]]
@ -4378,7 +4336,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
"bitflags 2.11.1", "bitflags 2.13.0",
"fancy-regex", "fancy-regex",
"filedescriptor", "filedescriptor",
"finl_unicode", "finl_unicode",
@ -4779,9 +4737,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.13.2" version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]] [[package]]
name = "unicode-truncate" name = "unicode-truncate"
@ -5045,7 +5003,7 @@ version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"indexmap 2.14.0", "indexmap 2.14.0",
"semver", "semver",
@ -5142,9 +5100,9 @@ dependencies = [
[[package]] [[package]]
name = "wide" name = "wide"
version = "1.4.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a7714cd0430a663154667c74da5d09325c2387695bee18b3f7f72825aa3693a" checksum = "dfdfe6a32973f2d1b268b8895845a8a96cac2f0191e72c27cc929036060dbf89"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"safe_arch", "safe_arch",
@ -5579,7 +5537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags 2.11.1", "bitflags 2.13.0",
"indexmap 2.14.0", "indexmap 2.14.0",
"log", "log",
"serde", "serde",
@ -5676,7 +5634,7 @@ dependencies = [
"palette", "palette",
"quantette", "quantette",
"ratatui", "ratatui",
"strum 0.28.0", "strum",
"tokio", "tokio",
"tracing", "tracing",
"yazi-config", "yazi-config",
@ -5790,15 +5748,16 @@ version = "26.5.6"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
"bitflags 2.11.1", "bitflags 2.13.0",
"globset", "globset",
"hashbrown 0.17.1", "hashbrown 0.17.1",
"indexmap 2.14.0", "indexmap 2.14.0",
"mlua",
"ratatui", "ratatui",
"regex", "regex",
"serde", "serde",
"serde_with", "serde_with",
"strum 0.28.0", "strum",
"tokio", "tokio",
"toml", "toml",
"tracing", "tracing",
@ -5824,7 +5783,7 @@ dependencies = [
"ratatui", "ratatui",
"serde", "serde",
"serde_with", "serde_with",
"strum 0.28.0", "strum",
"syntect", "syntect",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
@ -5855,6 +5814,7 @@ dependencies = [
"anyhow", "anyhow",
"hashbrown 0.17.1", "hashbrown 0.17.1",
"indexmap 2.14.0", "indexmap 2.14.0",
"inventory",
"mlua", "mlua",
"ordered-float 5.3.0", "ordered-float 5.3.0",
"parking_lot", "parking_lot",
@ -5951,13 +5911,14 @@ version = "26.5.6"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
"bitflags 2.11.1", "bitflags 2.13.0",
"core-foundation-sys", "core-foundation-sys",
"dirs", "dirs",
"either", "either",
"foldhash 0.2.0", "foldhash 0.2.0",
"hashbrown 0.17.1", "hashbrown 0.17.1",
"libc", "libc",
"mlua",
"objc2", "objc2",
"parking_lot", "parking_lot",
"percent-encoding", "percent-encoding",
@ -5965,7 +5926,7 @@ dependencies = [
"regex", "regex",
"scopeguard", "scopeguard",
"serde", "serde",
"strum 0.28.0", "strum",
"tokio", "tokio",
"tracing", "tracing",
"trash", "trash",
@ -5991,14 +5952,14 @@ name = "yazi-parser"
version = "26.5.6" version = "26.5.6"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags 2.11.1", "bitflags 2.13.0",
"hashbrown 0.17.1", "hashbrown 0.17.1",
"mlua", "mlua",
"paste", "paste",
"ratatui", "ratatui",
"serde", "serde",
"serde_with", "serde_with",
"strum 0.28.0", "strum",
"tokio", "tokio",
"yazi-binding", "yazi-binding",
"yazi-boot", "yazi-boot",
@ -6021,9 +5982,9 @@ dependencies = [
"ansi-to-tui", "ansi-to-tui",
"anyhow", "anyhow",
"futures", "futures",
"inventory",
"libc", "libc",
"mlua", "mlua",
"paste",
"percent-encoding", "percent-encoding",
"ratatui", "ratatui",
"serde_json", "serde_json",
@ -6036,6 +5997,7 @@ dependencies = [
"yazi-adapter", "yazi-adapter",
"yazi-binding", "yazi-binding",
"yazi-boot", "yazi-boot",
"yazi-codegen",
"yazi-config", "yazi-config",
"yazi-core", "yazi-core",
"yazi-dds", "yazi-dds",
@ -6104,12 +6066,12 @@ dependencies = [
"foldhash 0.2.0", "foldhash 0.2.0",
"hashbrown 0.17.1", "hashbrown 0.17.1",
"libc", "libc",
"lru 0.18.0", "lru",
"mlua", "mlua",
"ordered-float 5.3.0", "ordered-float 5.3.0",
"parking_lot", "parking_lot",
"serde", "serde",
"strum 0.28.0", "strum",
"tokio", "tokio",
"tracing", "tracing",
"yazi-binding", "yazi-binding",
@ -6127,7 +6089,7 @@ dependencies = [
name = "yazi-sftp" name = "yazi-sftp"
version = "26.5.6" version = "26.5.6"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"parking_lot", "parking_lot",
"russh", "russh",
"serde", "serde",
@ -6144,15 +6106,17 @@ dependencies = [
"foldhash 0.2.0", "foldhash 0.2.0",
"futures", "futures",
"hashbrown 0.17.1", "hashbrown 0.17.1",
"inventory",
"libc", "libc",
"memchr", "memchr",
"mlua",
"ordered-float 5.3.0", "ordered-float 5.3.0",
"parking_lot", "parking_lot",
"paste", "paste",
"percent-encoding", "percent-encoding",
"serde", "serde",
"serde_with", "serde_with",
"strum 0.28.0", "strum",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"typed-path", "typed-path",
@ -6188,13 +6152,14 @@ version = "26.5.6"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
"bitflags 2.11.1", "bitflags 2.13.0",
"futures", "futures",
"parking_lot", "parking_lot",
"ratatui", "ratatui",
"rustix 1.1.4", "rustix 1.1.4",
"serde",
"signal-hook 0.4.4", "signal-hook 0.4.4",
"strum 0.28.0", "strum",
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio", "tokio",
"windows-sys 0.61.2", "windows-sys 0.61.2",

View file

@ -39,8 +39,8 @@ ansi-to-tui = "8.0.1"
anyhow = "1.0.102" anyhow = "1.0.102"
arc-swap = { version = "1.9.1", features = [ "serde" ] } arc-swap = { version = "1.9.1", features = [ "serde" ] }
base64 = "0.22.1" base64 = "0.22.1"
bitflags = { version = "2.11.1", features = [ "serde" ] } bitflags = { version = "2.13.0", features = [ "serde" ] }
chrono = "0.4.44" chrono = "0.4.45"
clap = { version = "4.6.1", features = [ "derive" ] } clap = { version = "4.6.1", features = [ "derive" ] }
core-foundation-sys = "0.8.7" core-foundation-sys = "0.8.7"
dirs = "6.0.0" dirs = "6.0.0"
@ -51,6 +51,7 @@ futures = "0.3.32"
globset = "0.4.18" globset = "0.4.18"
hashbrown = { version = "0.17.1", features = [ "serde" ] } hashbrown = { version = "0.17.1", features = [ "serde" ] }
indexmap = { version = "2.14.0", features = [ "serde" ] } indexmap = { version = "2.14.0", features = [ "serde" ] }
inventory = "0.3.24"
libc = "0.2.186" libc = "0.2.186"
lru = "0.18.0" lru = "0.18.0"
mlua = { version = "0.11.6", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] } mlua = { version = "0.11.6", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] }
@ -60,13 +61,13 @@ parking_lot = "0.12.5"
paste = "1.0.15" paste = "1.0.15"
percent-encoding = "2.3.2" percent-encoding = "2.3.2"
rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] } rand = { version = "0.10.1", default-features = false, features = [ "std", "sys_rng" ] }
ratatui = { version = "0.30.0", default-features = false, features = [ "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] } ratatui = { version = "0.30.1", default-features = false, features = [ "layout-cache", "serde", "underline-color", "unstable-rendered-line-info", "unstable-widget-ref" ] }
regex = "1.12.3" regex = "1.12.3"
russh = { version = "0.61.1", default-features = false, features = [ "ring", "rsa" ] } russh = { version = "0.61.2", default-features = false, features = [ "ring", "rsa" ] }
scopeguard = "1.2.0" scopeguard = "1.2.0"
serde = { version = "1.0.228", features = [ "derive" ] } serde = { version = "1.0.228", features = [ "derive" ] }
serde_json = "1.0.150" serde_json = "1.0.150"
serde_with = "3.20.0" serde_with = "3.21.0"
strum = { version = "0.28.0", features = [ "derive" ] } strum = { version = "0.28.0", features = [ "derive" ] }
syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
thiserror = "2.0.18" thiserror = "2.0.18"

View file

@ -29,7 +29,7 @@ impl UserData for Which {
cached_field!(fields, tx, |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx))); cached_field!(fields, tx, |_, me| Ok(me.tx.clone().map(yazi_binding::MpscUnboundedTx)));
fields.add_field_method_get("times", |_, me| Ok(me.inner.times)); fields.add_field_method_get("times", |_, me| Ok(me.inner.times));
cached_field!(fields, cands, |lua, me| { cached_field!(fields, cands, |lua, me| {
lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::ChordCow)) lua.create_sequence_from(me.inner.cands.iter().cloned().map(yazi_binding::keymap::ChordCow))
}); });
fields.add_field_method_get("active", |_, me| Ok(me.inner.active)); fields.add_field_method_get("active", |_, me| Ok(me.inner.active));

View file

@ -0,0 +1,16 @@
use mlua::UserData;
use yazi_codegen::FromLuaOwned;
#[derive(FromLuaOwned)]
pub struct DataAny(Box<dyn yazi_shared::data::DataAny>);
impl DataAny {
pub fn new<T>(value: T) -> Self
where
T: yazi_shared::data::DataAny,
{
Self(Box::new(value))
}
}
impl UserData for DataAny {}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(any);

View file

@ -0,0 +1,19 @@
use std::ops::Deref;
use mlua::UserData;
pub struct Action {
inner: yazi_shared::event::Action,
}
impl Deref for Action {
type Target = yazi_shared::event::Action;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Action {
pub fn new(inner: impl Into<yazi_shared::event::Action>) -> Self { Self { inner: inner.into() } }
}
impl UserData for Action {}

View file

@ -0,0 +1,19 @@
use std::ops::Deref;
use mlua::UserData;
pub struct Cmd {
inner: yazi_shared::event::Cmd,
}
impl Deref for Cmd {
type Target = yazi_shared::event::Cmd;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Cmd {
pub fn new(inner: impl Into<yazi_shared::event::Cmd>) -> Self { Self { inner: inner.into() } }
}
impl UserData for Cmd {}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(action cmd);

View file

@ -0,0 +1,80 @@
use std::{ops::Deref, sync::Arc};
use mlua::{ExternalError, FromLua, IntoLua, Lua, LuaSerdeExt, Table, UserData, UserDataFields, Value};
use crate::{Id, Iter, cached_field, event::Action, keymap::Key};
pub struct Chord {
inner: Arc<yazi_config::keymap::Chord>,
v_on: Option<Value>,
v_run: Option<Value>,
}
impl Deref for Chord {
type Target = Arc<yazi_config::keymap::Chord>;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl From<Chord> for Arc<yazi_config::keymap::Chord> {
fn from(value: Chord) -> Self { value.inner }
}
impl Chord {
pub fn new(inner: impl Into<Arc<yazi_config::keymap::Chord>>) -> Self {
Self { inner: inner.into(), v_on: None, v_run: None }
}
}
impl FromLua for Chord {
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
Ok(Self::new(lua.from_value::<yazi_config::keymap::Chord>(value)?))
}
}
impl UserData for Chord {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, me| Ok(Id(me.inner.id)));
cached_field!(fields, on, |lua, me| lua.create_sequence_from(me.on.iter().copied().map(Key)));
cached_field!(fields, run, |lua, me| lua
.create_sequence_from(me.run.iter().cloned().map(Action::new)));
}
}
// --- Matcher
pub struct ChordMatcher(pub(super) yazi_config::keymap::ChordMatcher);
impl TryFrom<Table> for ChordMatcher {
type Error = mlua::Error;
fn try_from(value: Table) -> Result<Self, Self::Error> {
let id: Id = value.raw_get("id").unwrap_or_default();
Ok(Self(yazi_config::keymap::ChordMatcher { id: id.0, ..Default::default() }))
}
}
impl FromLua for ChordMatcher {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::Table(t) => t.try_into(),
_ => Err("expected a table of ChordMatcher".into_lua_err()),
}
}
}
// --- ChordIter
pub struct ChordIter(pub(super) yazi_config::keymap::ChordIter);
impl ChordIter {
pub fn new(inner: impl Into<yazi_config::keymap::ChordIter>) -> Self { Self(inner.into()) }
}
impl IntoLua for ChordIter {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
Iter::new(self.0.map(Chord::new), None).into_lua(lua)
}
}

View file

@ -0,0 +1,53 @@
use std::ops::Deref;
use mlua::{ExternalError, ExternalResult, MetaMethod, UserData, UserDataMethods};
use super::{Chord, ChordMatcher};
use crate::keymap::ChordIter;
pub struct Chords {
inner: &'static yazi_config::keymap::Chords,
}
impl Deref for Chords {
type Target = yazi_config::keymap::Chords;
fn deref(&self) -> &Self::Target { self.inner }
}
impl Chords {
pub fn new(inner: &'static yazi_config::keymap::Chords) -> Self { Self { inner } }
}
impl UserData for Chords {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("match", |_, me, matcher: Option<ChordMatcher>| {
Ok(match matcher {
Some(matcher) => ChordIter::new(yazi_config::keymap::ChordIter {
chords: me.as_erased(),
matcher: matcher.0,
..Default::default()
}),
None => ChordIter::new(&*me.inner),
})
});
methods.add_method("insert", |_, me, (index, chord): (isize, Chord)| {
let index = match index {
1.. => index - 1,
0 => return Err("index must be 1-based or negative".into_lua_err()),
_ => index,
};
me.insert(index, chord.clone()).into_lua_err()?;
Ok(chord)
});
methods.add_method("remove", |_, me, matcher: ChordMatcher| {
me.remove(matcher.0);
Ok(())
});
methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.load().len()));
}
}

View file

@ -0,0 +1,18 @@
use std::ops::Deref;
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
use crate::SER_OPT;
#[derive(Clone, Copy, Default, FromLua)]
pub struct Key(pub yazi_config::keymap::Key);
impl Deref for Key {
type Target = yazi_config::keymap::Key;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl IntoLua for Key {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { lua.to_value_with(&self.0, SER_OPT) }
}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(chord chord_cow chords key section);

View file

@ -0,0 +1,48 @@
use std::ops::Deref;
use anyhow::bail;
use mlua::{UserData, UserDataFields, Value};
use yazi_config::KEYMAP;
use yazi_shared::Layer;
use crate::{cached_field, keymap::Chords};
pub struct KeymapSection {
inner: &'static yazi_config::keymap::KeymapSection,
v_rules: Option<Value>,
}
impl Deref for KeymapSection {
type Target = yazi_config::keymap::KeymapSection;
fn deref(&self) -> &Self::Target { self.inner }
}
impl TryFrom<Layer> for KeymapSection {
type Error = anyhow::Error;
fn try_from(value: Layer) -> Result<Self, Self::Error> {
let inner = match value {
Layer::Null | Layer::App => bail!("invalid layer"),
Layer::Mgr => KEYMAP.mgr.as_erased(),
Layer::Tasks => KEYMAP.tasks.as_erased(),
Layer::Spot => KEYMAP.spot.as_erased(),
Layer::Pick => KEYMAP.pick.as_erased(),
Layer::Input => KEYMAP.input.as_erased(),
Layer::Confirm => KEYMAP.confirm.as_erased(),
Layer::Help => KEYMAP.help.as_erased(),
Layer::Cmp => KEYMAP.cmp.as_erased(),
Layer::Which => bail!("invalid layer"),
Layer::Notify => bail!("invalid layer"),
};
Ok(Self { inner, v_rules: None })
}
}
impl UserData for KeymapSection {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, rules, |_, me| Ok(Chords::new(me.inner)));
}
}

View file

@ -1,5 +1,5 @@
mod macros; mod macros;
yazi_macro::mod_pub!(config elements process theme); yazi_macro::mod_pub!(config data elements event keymap process theme);
yazi_macro::mod_flat!(access calculator cha chan chord_cow composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style tty url utils); yazi_macro::mod_flat!(access calculator cha chan composer dnd error fd file handle icon id image input iter layer mouse path permit range runtime scheme selector stage style tty url utils);

View file

@ -28,6 +28,7 @@ bitflags = { workspace = true }
globset = { workspace = true } globset = { workspace = true }
hashbrown = { workspace = true } hashbrown = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
mlua = { workspace = true }
ratatui = { workspace = true } ratatui = { workspace = true }
regex = { workspace = true } regex = { workspace = true }
serde = { workspace = true } serde = { workspace = true }

View file

@ -1,17 +1,19 @@
use std::{borrow::Cow, hash::{Hash, Hasher}, sync::OnceLock}; use std::{borrow::Cow, hash::{Hash, Hasher}, sync::{Arc, OnceLock}};
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Deserializer, de}; use serde::{Deserialize, Deserializer, de};
use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany}; use serde_with::{DeserializeAs, DisplayFromStr, OneOrMany};
use yazi_shared::{Layer, Source, event::Action}; use yazi_shared::{Id, Layer, Source, event::Action};
use super::Key; use super::{Key, ids::chord_id};
use crate::{Mixable, Platform}; use crate::{Mixable, Platform, keymap::Chords};
static RE: OnceLock<Regex> = OnceLock::new(); static RE: OnceLock<Regex> = OnceLock::new();
#[derive(Clone, Debug, Default, Deserialize)] #[derive(Debug, Default, Deserialize)]
pub struct Chord<const L: u8 = { Layer::App as u8 }> { pub struct Chord<const L: u8 = { Layer::Null as u8 }> {
#[serde(skip, default = "chord_id")]
pub id: Id,
#[serde(deserialize_with = "deserialize_on")] #[serde(deserialize_with = "deserialize_on")]
pub on: Vec<Key>, pub on: Vec<Key>,
#[serde(deserialize_with = "deserialize_run::<L, _>")] #[serde(deserialize_with = "deserialize_run::<L, _>")]
@ -22,6 +24,18 @@ pub struct Chord<const L: u8 = { Layer::App as u8 }> {
pub r#for: Platform, pub r#for: Platform,
} }
impl<const L: u8> Clone for Chord<L> {
fn clone(&self) -> Self {
Self {
id: chord_id(),
on: self.on.clone(),
run: self.run.clone(),
desc: self.desc.clone(),
r#for: self.r#for,
}
}
}
impl<const L: u8> PartialEq for Chord<L> { impl<const L: u8> PartialEq for Chord<L> {
fn eq(&self, other: &Self) -> bool { self.on == other.on } fn eq(&self, other: &Self) -> bool { self.on == other.on }
} }
@ -33,6 +47,10 @@ impl<const L: u8> Hash for Chord<L> {
} }
impl<const L: u8> Chord<L> { impl<const L: u8> Chord<L> {
pub fn as_erased<const M: u8>(self: &Arc<Self>) -> &Arc<Chord<M>> {
unsafe { &*(self as *const Arc<Chord<L>> as *const Arc<Chord<M>>) }
}
pub fn on(&self) -> String { self.on.iter().map(ToString::to_string).collect() } pub fn on(&self) -> String { self.on.iter().map(ToString::to_string).collect() }
pub fn run(&self) -> String { pub fn run(&self) -> String {
@ -81,12 +99,12 @@ fn deserialize_run<'de, const L: u8, D>(deserializer: D) -> Result<Vec<Action>,
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let mut actions: Vec<Action> = OneOrMany::<DisplayFromStr>::deserialize_as(deserializer)?;
let Some(layer) = Layer::from_repr(L) else { let Some(layer) = Layer::from_repr(L) else {
return Err(de::Error::custom(format!("invalid keymap layer const: {L}"))); return Err(de::Error::custom(format!("invalid keymap layer const: {L}")));
}; };
let mut actions: Vec<Action> = OneOrMany::<DisplayFromStr>::deserialize_as(deserializer)?;
for action in &mut actions { for action in &mut actions {
action.source = Source::Key; action.source = Source::Key;
if action.layer == Layer::Null { if action.layer == Layer::Null {
@ -96,3 +114,54 @@ where
Ok(actions) Ok(actions)
} }
// --- Matcher
#[derive(Default)]
pub struct ChordMatcher {
pub id: Id,
pub all: bool,
}
impl ChordMatcher {
pub fn matches(&self, chord: &Chord) -> bool {
if self.all {
true
} else if self.id != Id::ZERO {
chord.id == self.id
} else {
false
}
}
}
// --- Iter
#[derive(Default)]
pub struct ChordIter {
pub chords: Arc<Vec<Arc<Chord>>>,
pub matcher: ChordMatcher,
pub offset: usize,
}
impl From<&Chords> for ChordIter {
fn from(chords: &Chords) -> Self {
Self {
chords: chords.load_full(),
matcher: ChordMatcher { all: true, ..Default::default() },
..Default::default()
}
}
}
impl Iterator for ChordIter {
type Item = Arc<Chord>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(chord) = self.chords.get(self.offset) {
self.offset += 1;
if self.matcher.matches(chord) {
return Some(chord.clone());
}
}
None
}
}

View file

@ -0,0 +1,57 @@
use std::{ops::Deref, sync::Arc};
use arc_swap::ArcSwap;
use serde::Deserialize;
use yazi_shared::Layer;
use yazi_shim::{arc_swap::{ArcSwapExt, IntoPointee}, vec::{IndexAtError, VecExt}};
use super::Chord;
use crate::keymap::ChordMatcher;
#[derive(Debug, Default, Deserialize)]
pub struct Chords<const L: u8 = { Layer::Null as u8 }>(ArcSwap<Vec<Arc<Chord<L>>>>);
impl<const L: u8> Deref for Chords<L> {
type Target = ArcSwap<Vec<Arc<Chord<L>>>>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl<const L: u8> From<Vec<Arc<Chord<L>>>> for Chords<L> {
fn from(inner: Vec<Arc<Chord<L>>>) -> Self { Self(inner.into_pointee()) }
}
impl<const L: u8> Chords<L> {
pub fn as_erased<const M: u8>(&self) -> Arc<Vec<Arc<Chord<M>>>> {
let chords = self.0.load_full();
unsafe { Arc::from_raw(Arc::into_raw(chords) as *const Vec<Arc<Chord<M>>>) }
}
pub fn insert(&self, index: isize, rule: Arc<Chord>) -> Result<(), IndexAtError> {
self.0.try_rcu(|rules| {
let (before, after) = rules.split_at(rules.index_at(index)?);
Ok(
before
.iter()
.cloned()
.chain([rule.as_erased().clone()])
.chain(after.iter().cloned())
.collect::<Vec<_>>(),
)
})?;
Ok(())
}
pub fn remove(&self, matcher: ChordMatcher) {
self.0.rcu(|chords| {
let mut next = Vec::clone(chords);
next.retain(|chord| !matcher.matches(chord.as_erased()));
next
});
}
pub(crate) fn unwrap_unchecked(self) -> Vec<Arc<Chord<L>>> {
Arc::try_unwrap(self.0.into_inner()).expect("unique chords arc")
}
}

View file

@ -1,21 +1,25 @@
use std::ops::Deref; use std::{ops::Deref, sync::Arc};
use yazi_shared::{Layer, event::ActionCow}; use yazi_shared::{Layer, event::ActionCow};
use super::Chord; use super::Chord;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum ChordCow<const L: u8 = { Layer::App as u8 }> { pub enum ChordCow<const L: u8 = { Layer::Null as u8 }> {
Owned(Chord<L>), Owned(Chord<L>),
Borrowed(&'static Chord<L>), Borrowed(Arc<Chord<L>>),
} }
impl<const L: u8> From<Chord<L>> for ChordCow<L> { impl<const L: u8> From<Chord<L>> for ChordCow<L> {
fn from(c: Chord<L>) -> Self { Self::Owned(c) } fn from(c: Chord<L>) -> Self { Self::Owned(c) }
} }
impl<const L: u8> From<&'static Chord<L>> for ChordCow<L> { impl<const L: u8> From<Arc<Chord<L>>> for ChordCow<L> {
fn from(c: &'static Chord<L>) -> Self { Self::Borrowed(c) } fn from(c: Arc<Chord<L>>) -> Self { Self::Borrowed(c) }
}
impl<const L: u8> From<&Arc<Chord<L>>> for ChordCow<L> {
fn from(c: &Arc<Chord<L>>) -> Self { Self::Borrowed(c.clone()) }
} }
impl<const L: u8> Deref for ChordCow<L> { impl<const L: u8> Deref for ChordCow<L> {
@ -37,7 +41,7 @@ impl<const L: u8> ChordCow<L> {
pub fn into_seq(self) -> Vec<ActionCow> { pub fn into_seq(self) -> Vec<ActionCow> {
match self { match self {
Self::Owned(c) => c.run.into_iter().rev().map(Into::into).collect(), Self::Owned(c) => c.run.into_iter().rev().map(Into::into).collect(),
Self::Borrowed(c) => c.run.iter().rev().map(Into::into).collect(), Self::Borrowed(c) => c.run.iter().rev().cloned().map(Into::into).collect(),
} }
} }
} }

View file

@ -0,0 +1,6 @@
use yazi_shared::{Id, Ids};
pub fn chord_id() -> Id {
static IDS: Ids = Ids::new();
IDS.next()
}

View file

@ -1,14 +1,17 @@
use std::{fmt::{Display, Write}, str::FromStr}; use std::{fmt::{Display, Write}, str::FromStr};
use anyhow::bail; use anyhow::bail;
use serde::{Deserialize, Serialize};
use yazi_term::event::{KeyCode, KeyEvent, Modifiers}; use yazi_term::event::{KeyCode, KeyEvent, Modifiers};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Key { pub struct Key {
#[serde(flatten)]
pub code: KeyCode, pub code: KeyCode,
pub shift: bool, pub shift: bool,
pub ctrl: bool, pub ctrl: bool,
pub alt: bool, pub alt: bool,
#[serde(rename = "super")]
pub super_: bool, pub super_: bool,
} }

View file

@ -1,37 +1,39 @@
use std::{ops::Deref, sync::Arc};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use serde::Deserialize; use serde::Deserialize;
use yazi_codegen::{DeserializeOver, DeserializeOver1}; use yazi_codegen::{DeserializeOver, DeserializeOver1};
use yazi_fs::{Xdg, ok_or_not_found}; use yazi_fs::{Xdg, ok_or_not_found};
use yazi_shared::Layer; use yazi_shared::Layer;
use super::{Chord, KeymapRules}; use super::{Chord, KeymapSection};
#[derive(Deserialize, DeserializeOver, DeserializeOver1)] #[derive(Deserialize, DeserializeOver, DeserializeOver1)]
pub struct Keymap { pub struct Keymap {
pub mgr: KeymapRules<{ Layer::Mgr as u8 }>, pub mgr: KeymapSection<{ Layer::Mgr as u8 }>,
pub tasks: KeymapRules<{ Layer::Tasks as u8 }>, pub tasks: KeymapSection<{ Layer::Tasks as u8 }>,
pub spot: KeymapRules<{ Layer::Spot as u8 }>, pub spot: KeymapSection<{ Layer::Spot as u8 }>,
pub pick: KeymapRules<{ Layer::Pick as u8 }>, pub pick: KeymapSection<{ Layer::Pick as u8 }>,
pub input: KeymapRules<{ Layer::Input as u8 }>, pub input: KeymapSection<{ Layer::Input as u8 }>,
pub confirm: KeymapRules<{ Layer::Confirm as u8 }>, pub confirm: KeymapSection<{ Layer::Confirm as u8 }>,
pub help: KeymapRules<{ Layer::Help as u8 }>, pub help: KeymapSection<{ Layer::Help as u8 }>,
pub cmp: KeymapRules<{ Layer::Cmp as u8 }>, pub cmp: KeymapSection<{ Layer::Cmp as u8 }>,
} }
impl Keymap { impl Keymap {
pub fn get(&self, layer: Layer) -> &[Chord] { pub fn get(&self, layer: Layer) -> Arc<Vec<Arc<Chord>>> {
match layer { match layer {
Layer::Null | Layer::App => &[], Layer::Null | Layer::App => Arc::new(Vec::new()),
Layer::Mgr => self.mgr.as_erased_slice(), Layer::Mgr => self.mgr.deref().as_erased(),
Layer::Tasks => self.tasks.as_erased_slice(), Layer::Tasks => self.tasks.deref().as_erased(),
Layer::Spot => self.spot.as_erased_slice(), Layer::Spot => self.spot.deref().as_erased(),
Layer::Pick => self.pick.as_erased_slice(), Layer::Pick => self.pick.deref().as_erased(),
Layer::Input => self.input.as_erased_slice(), Layer::Input => self.input.deref().as_erased(),
Layer::Confirm => self.confirm.as_erased_slice(), Layer::Confirm => self.confirm.deref().as_erased(),
Layer::Help => self.help.as_erased_slice(), Layer::Help => self.help.deref().as_erased(),
Layer::Cmp => self.cmp.as_erased_slice(), Layer::Cmp => self.cmp.deref().as_erased(),
Layer::Which => &[], Layer::Which => Arc::new(Vec::new()),
Layer::Notify => &[], Layer::Notify => Arc::new(Vec::new()),
} }
} }
} }

View file

@ -1 +1 @@
yazi_macro::mod_flat!(chord cow key keymap rules); yazi_macro::mod_flat!(chord chords cow ids key keymap section);

View file

@ -1,53 +0,0 @@
use std::ops::Deref;
use hashbrown::HashSet;
use serde::Deserialize;
use yazi_codegen::DeserializeOver2;
use yazi_shim::toml::DeserializeOverHook;
use super::{Chord, Key};
use crate::mix;
#[derive(Default, Deserialize, DeserializeOver2)]
pub struct KeymapRules<const L: u8> {
keymap: Vec<Chord<L>>,
#[serde(default)]
prepend_keymap: Vec<Chord<L>>,
#[serde(default)]
append_keymap: Vec<Chord<L>>,
}
impl<const L: u8> Deref for KeymapRules<L> {
type Target = [Chord];
fn deref(&self) -> &Self::Target { self.as_erased_slice() }
}
impl<const L: u8> KeymapRules<L> {
pub(super) fn as_erased_slice(&self) -> &[Chord] {
// Safety: `Chord<L>` only changes deserialization behavior; the const parameter
// does not participate in layout, so a shared slice can be reinterpreted as
// the default `Chord` view.
unsafe { &*(self.keymap.as_slice() as *const [Chord<L>] as *const [Chord]) }
}
}
impl<const L: u8> DeserializeOverHook for KeymapRules<L> {
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
#[inline]
fn on<const L: u8>(Chord { on, .. }: &Chord<L>) -> [Key; 2] {
[on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()]
}
let a_seen: HashSet<_> = self.prepend_keymap.iter().map(on).collect();
let b_seen: HashSet<_> = self.keymap.iter().map(on).collect();
let keymap = mix(
self.prepend_keymap,
self.keymap.into_iter().filter(|v| !a_seen.contains(&on(v))),
self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))),
);
Ok(Self { keymap, ..Default::default() })
}
}

View file

@ -0,0 +1,52 @@
use std::{ops::Deref, sync::Arc};
use hashbrown::HashSet;
use serde::Deserialize;
use yazi_codegen::DeserializeOver2;
use yazi_shared::Layer;
use yazi_shim::toml::DeserializeOverHook;
use super::{Chord, Chords, Key};
use crate::mix;
#[derive(Default, Deserialize, DeserializeOver2)]
pub struct KeymapSection<const L: u8 = { Layer::Null as u8 }> {
keymap: Chords<L>,
#[serde(default)]
prepend_keymap: Vec<Chord<L>>,
#[serde(default)]
append_keymap: Vec<Chord<L>>,
}
impl<const L: u8> Deref for KeymapSection<L> {
type Target = Chords<L>;
fn deref(&self) -> &Self::Target { &self.keymap }
}
impl<const L: u8> KeymapSection<L> {
pub fn as_erased<const M: u8>(&self) -> &KeymapSection<M> {
unsafe { &*(self as *const Self as *const KeymapSection<M>) }
}
}
impl<const L: u8> DeserializeOverHook for KeymapSection<L> {
fn deserialize_over_hook(self) -> Result<Self, toml::de::Error> {
#[inline]
fn on<const L: u8>(Chord { on, .. }: &Chord<L>) -> [Key; 2] {
[on.first().copied().unwrap_or_default(), on.get(1).copied().unwrap_or_default()]
}
let keymap = self.keymap.unwrap_unchecked();
let a_seen: HashSet<_> = self.prepend_keymap.iter().map(on).collect();
let b_seen: HashSet<_> = keymap.iter().map(|c| on(c)).collect();
let keymap: Vec<Arc<Chord<L>>> = mix(
self.prepend_keymap,
keymap.into_iter().filter(|v| !a_seen.contains(&on(v))),
self.append_keymap.into_iter().filter(|v| !b_seen.contains(&on(v))),
);
Ok(Self { keymap: keymap.into(), ..Default::default() })
}
}

View file

@ -1,4 +1,4 @@
yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme which vfs); yazi_macro::mod_pub!(keymap mgr open opener plugin popup preview tasks theme vfs which);
yazi_macro::mod_flat!(icon layout mixing pattern platform preset priority selectable selector style utils yazi); yazi_macro::mod_flat!(icon layout mixing pattern platform preset priority selectable selector style utils yazi);

View file

@ -1,9 +1,11 @@
use ratatui::{text::{Line, Text}, widgets::{Paragraph, Wrap}}; use ratatui::{text::{Line, Text}, widgets::{Paragraph, Wrap}};
use yazi_macro::impl_data_any;
use yazi_shared::{scheme::Encode as EncodeScheme, strand::ToStrand, url::{Url, UrlBuf}}; use yazi_shared::{scheme::Encode as EncodeScheme, strand::ToStrand, url::{Url, UrlBuf}};
use super::{Offset, Position}; use super::{Offset, Position};
use crate::{YAZI, popup::Origin}; use crate::{YAZI, popup::Origin};
// --- InputCfg
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct InputCfg { pub struct InputCfg {
pub title: String, pub title: String,
@ -15,20 +17,7 @@ pub struct InputCfg {
pub completion: bool, pub completion: bool,
} }
#[derive(Clone, Debug, Default)] impl_data_any!(InputCfg);
pub struct PickCfg {
pub title: String,
pub items: Vec<String>,
pub position: Position,
}
#[derive(Clone, Debug, Default)]
pub struct ConfirmCfg {
pub position: Position,
pub title: Line<'static>,
pub body: Paragraph<'static>,
pub list: Paragraph<'static>,
}
impl InputCfg { impl InputCfg {
pub fn cd(cwd: Url) -> Self { pub fn cd(cwd: Url) -> Self {
@ -117,6 +106,45 @@ impl InputCfg {
} }
} }
// --- PickCfg
#[derive(Clone, Debug, Default)]
pub struct PickCfg {
pub title: String,
pub items: Vec<String>,
pub position: Position,
}
impl_data_any!(PickCfg);
impl PickCfg {
fn max_height(len: usize) -> u16 {
YAZI.pick.open_offset.height.min(YAZI.pick.border().saturating_add(len as u16))
}
pub fn open(items: Vec<String>) -> Self {
let max_height = Self::max_height(items.len());
Self {
title: YAZI.pick.open_title.clone(),
items,
position: Position::new(YAZI.pick.open_origin, Offset {
height: max_height,
..YAZI.pick.open_offset
}),
}
}
}
// --- ConfirmCfg
#[derive(Clone, Debug, Default)]
pub struct ConfirmCfg {
pub position: Position,
pub title: Line<'static>,
pub body: Paragraph<'static>,
pub list: Paragraph<'static>,
}
impl_data_any!(ConfirmCfg);
impl ConfirmCfg { impl ConfirmCfg {
fn new( fn new(
title: String, title: String,
@ -188,21 +216,3 @@ impl ConfirmCfg {
Some(Text::from_iter(lines)) Some(Text::from_iter(lines))
} }
} }
impl PickCfg {
fn max_height(len: usize) -> u16 {
YAZI.pick.open_offset.height.min(YAZI.pick.border().saturating_add(len as u16))
}
pub fn open(items: Vec<String>) -> Self {
let max_height = Self::max_height(items.len());
Self {
title: YAZI.pick.open_title.clone(),
items,
position: Position::new(YAZI.pick.open_origin, Offset {
height: max_height,
..YAZI.pick.open_offset
}),
}
}
}

View file

@ -6,9 +6,9 @@ use hashbrown::HashMap;
use mlua::{Lua, Table}; use mlua::{Lua, Table};
use serde::Deserialize; use serde::Deserialize;
use strum::EnumString; use strum::EnumString;
use yazi_macro::impl_data_any;
use yazi_scheduler::plugin::PluginInEntry; use yazi_scheduler::plugin::PluginInEntry;
use yazi_shared::{SStr, data::{Data, DataKey}, event::{ActionCow, Cmd}}; use yazi_shared::{SStr, data::{Data, DataKey}, event::{ActionCow, Cmd}};
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct PluginOpt { pub struct PluginOpt {
pub id: SStr, pub id: SStr,
@ -17,6 +17,8 @@ pub struct PluginOpt {
pub callback: Option<Box<dyn PluginCallback>>, pub callback: Option<Box<dyn PluginCallback>>,
} }
impl_data_any!(PluginOpt);
impl TryFrom<ActionCow> for PluginOpt { impl TryFrom<ActionCow> for PluginOpt {
type Error = anyhow::Error; type Error = anyhow::Error;

View file

@ -1,6 +1,7 @@
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use yazi_binding::SER_OPT; use yazi_binding::SER_OPT;
use yazi_macro::impl_data_any;
use yazi_shared::{event::ActionCow, strand::StrandBuf}; use yazi_shared::{event::ActionCow, strand::StrandBuf};
#[derive(Clone, Debug, Default, Deserialize, Serialize)] #[derive(Clone, Debug, Default, Deserialize, Serialize)]
@ -13,6 +14,8 @@ pub struct QuitOpt {
pub no_cwd_file: bool, pub no_cwd_file: bool,
} }
impl_data_any!(QuitOpt);
impl TryFrom<ActionCow> for QuitOpt { impl TryFrom<ActionCow> for QuitOpt {
type Error = anyhow::Error; type Error = anyhow::Error;

View file

@ -1,3 +1,4 @@
use yazi_macro::impl_data_any;
use yazi_shared::{Id, path::PathBufDyn, url::UrlBuf}; use yazi_shared::{Id, path::PathBufDyn, url::UrlBuf};
use crate::cmp::CmpItem; use crate::cmp::CmpItem;
@ -9,3 +10,5 @@ pub struct CmpOpt {
pub word: PathBufDyn, pub word: PathBufDyn,
pub ticket: Id, pub ticket: Id,
} }
impl_data_any!(CmpOpt);

View file

@ -1,3 +1,5 @@
use std::sync::Arc;
use anyhow::Result; use anyhow::Result;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use yazi_config::{KEYMAP, YAZI, keymap::{Chord, Key}}; use yazi_config::{KEYMAP, YAZI, keymap::{Chord, Key}};
@ -12,7 +14,7 @@ use crate::help::HELP_MARGIN;
pub struct Help { pub struct Help {
pub visible: bool, pub visible: bool,
pub layer: Layer, pub layer: Layer,
pub(super) bindings: Vec<&'static Chord>, pub(super) bindings: Vec<Arc<Chord>>,
// Filter // Filter
pub keyword: String, pub keyword: String,
@ -51,10 +53,10 @@ impl Help {
if kw.is_empty() { if kw.is_empty() {
self.keyword = String::new(); self.keyword = String::new();
self.bindings = KEYMAP.get(self.layer).iter().collect(); self.bindings = KEYMAP.get(self.layer).iter().cloned().collect();
} else if self.keyword != kw { } else if self.keyword != kw {
self.keyword = kw.to_owned(); self.keyword = kw.to_owned();
self.bindings = KEYMAP.get(self.layer).iter().filter(|&c| c.contains(kw)).collect(); self.bindings = KEYMAP.get(self.layer).iter().filter(|&c| c.contains(kw)).cloned().collect();
} }
render!(self.scroll(0)); render!(self.scroll(0));
@ -73,7 +75,7 @@ impl Help {
} }
// --- Bindings // --- Bindings
pub fn window(&self) -> &[&Chord] { pub fn window(&self) -> &[Arc<Chord>] {
let end = (self.offset + self.limit()).min(self.bindings.len()); let end = (self.offset + self.limit()).min(self.bindings.len());
&self.bindings[self.offset..end] &self.bindings[self.offset..end]
} }

View file

@ -1,3 +1,4 @@
use yazi_macro::impl_data_any;
use yazi_shared::url::UrlBuf; use yazi_shared::url::UrlBuf;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -5,3 +6,5 @@ pub struct DisplaceOpt {
pub to: Result<UrlBuf, yazi_fs::error::Error>, pub to: Result<UrlBuf, yazi_fs::error::Error>,
pub from: UrlBuf, pub from: UrlBuf,
} }
impl_data_any!(DisplaceOpt);

View file

@ -1,4 +1,5 @@
use yazi_fs::FilterCase; use yazi_fs::FilterCase;
use yazi_macro::impl_data_any;
use yazi_shared::{SStr, event::ActionCow}; use yazi_shared::{SStr, event::ActionCow};
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
@ -8,6 +9,8 @@ pub struct FilterOpt {
pub done: bool, pub done: bool,
} }
impl_data_any!(FilterOpt);
impl TryFrom<ActionCow> for FilterOpt { impl TryFrom<ActionCow> for FilterOpt {
type Error = anyhow::Error; type Error = anyhow::Error;

View file

@ -1,5 +1,6 @@
use anyhow::bail; use anyhow::bail;
use yazi_fs::FilterCase; use yazi_fs::FilterCase;
use yazi_macro::impl_data_any;
use yazi_shared::{SStr, event::ActionCow}; use yazi_shared::{SStr, event::ActionCow};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -9,6 +10,8 @@ pub struct FindDoOpt {
pub case: FilterCase, pub case: FilterCase,
} }
impl_data_any!(FindDoOpt);
impl TryFrom<ActionCow> for FindDoOpt { impl TryFrom<ActionCow> for FindDoOpt {
type Error = anyhow::Error; type Error = anyhow::Error;

View file

@ -1,6 +1,8 @@
use yazi_fs::File; use yazi_fs::File;
use yazi_macro::impl_data_any;
use yazi_shared::{event::ActionCow, url::UrlBuf}; use yazi_shared::{event::ActionCow, url::UrlBuf};
// --- OpenOpt
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct OpenOpt { pub struct OpenOpt {
pub cwd: Option<UrlBuf>, pub cwd: Option<UrlBuf>,
@ -9,6 +11,8 @@ pub struct OpenOpt {
pub hovered: bool, pub hovered: bool,
} }
impl_data_any!(OpenOpt);
impl TryFrom<ActionCow> for OpenOpt { impl TryFrom<ActionCow> for OpenOpt {
type Error = anyhow::Error; type Error = anyhow::Error;
@ -22,10 +26,12 @@ impl TryFrom<ActionCow> for OpenOpt {
} }
} }
// OpenDoOpt // --- OpenDoOpt
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct OpenDoOpt { pub struct OpenDoOpt {
pub cwd: UrlBuf, pub cwd: UrlBuf,
pub targets: Vec<File>, pub targets: Vec<File>,
pub interactive: bool, pub interactive: bool,
} }
impl_data_any!(OpenDoOpt);

View file

@ -1,8 +1,10 @@
use anyhow::bail; use anyhow::bail;
use serde::Deserialize; use serde::Deserialize;
use strum::{EnumString, IntoStaticStr}; use strum::{EnumString, IntoStaticStr};
use yazi_macro::impl_data_any;
use yazi_shared::{SStr, event::ActionCow, url::{UrlBuf, UrlLike}}; use yazi_shared::{SStr, event::ActionCow, url::{UrlBuf, UrlLike}};
// --- SearchOpt
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SearchOpt { pub struct SearchOpt {
pub via: SearchVia, pub via: SearchVia,
@ -12,6 +14,8 @@ pub struct SearchOpt {
pub r#in: Option<UrlBuf>, pub r#in: Option<UrlBuf>,
} }
impl_data_any!(SearchOpt);
impl TryFrom<ActionCow> for SearchOpt { impl TryFrom<ActionCow> for SearchOpt {
type Error = anyhow::Error; type Error = anyhow::Error;
@ -37,7 +41,7 @@ impl TryFrom<ActionCow> for SearchOpt {
} }
} }
// Via // --- Via
#[derive(Clone, Copy, Debug, Deserialize, EnumString, Eq, IntoStaticStr, PartialEq)] #[derive(Clone, Copy, Debug, Deserialize, EnumString, Eq, IntoStaticStr, PartialEq)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
#[strum(serialize_all = "kebab-case")] #[strum(serialize_all = "kebab-case")]

View file

@ -4,6 +4,7 @@ use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{DurationSecondsWithFrac, serde_as}; use serde_with::{DurationSecondsWithFrac, serde_as};
use yazi_binding::SER_OPT; use yazi_binding::SER_OPT;
use yazi_macro::impl_data_any;
use yazi_shared::event::ActionCow; use yazi_shared::event::ActionCow;
use crate::notify::MessageLevel; use crate::notify::MessageLevel;
@ -21,6 +22,8 @@ pub struct MessageOpt {
pub timeout: Duration, pub timeout: Duration,
} }
impl_data_any!(MessageOpt);
impl TryFrom<ActionCow> for MessageOpt { impl TryFrom<ActionCow> for MessageOpt {
type Error = anyhow::Error; type Error = anyhow::Error;

View file

@ -1,5 +1,6 @@
use mlua::Table; use mlua::Table;
use yazi_binding::{FileRef, elements::Renderable}; use yazi_binding::{FileRef, elements::Renderable};
use yazi_macro::impl_data_any;
use yazi_shared::Id; use yazi_shared::Id;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -13,6 +14,8 @@ pub struct SpotLock {
pub data: Vec<Renderable>, pub data: Vec<Renderable>,
} }
impl_data_any!(SpotLock);
impl TryFrom<Table> for SpotLock { impl TryFrom<Table> for SpotLock {
type Error = mlua::Error; type Error = mlua::Error;

View file

@ -1,5 +1,6 @@
use mlua::Table; use mlua::Table;
use yazi_binding::{FileRef, elements::{Rect, Renderable}}; use yazi_binding::{FileRef, elements::{Rect, Renderable}};
use yazi_macro::impl_data_any;
use yazi_shared::pool::{InternStr, Symbol}; use yazi_shared::pool::{InternStr, Symbol};
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
@ -13,6 +14,8 @@ pub struct PreviewLock {
pub data: Vec<Renderable>, pub data: Vec<Renderable>,
} }
impl_data_any!(PreviewLock);
impl TryFrom<Table> for PreviewLock { impl TryFrom<Table> for PreviewLock {
type Error = mlua::Error; type Error = mlua::Error;

View file

@ -1,5 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use yazi_macro::impl_data_any;
use yazi_scheduler::{TaskIn, file::FileInCut, plugin::PluginInEntry}; use yazi_scheduler::{TaskIn, file::FileInCut, plugin::PluginInEntry};
use yazi_shared::{Id, SStr}; use yazi_shared::{Id, SStr};
@ -10,6 +11,8 @@ pub enum TaskOpt {
Plugin(PluginInEntry), Plugin(PluginInEntry),
} }
impl_data_any!(TaskOpt);
impl TaskIn for TaskOpt { impl TaskIn for TaskOpt {
type Prog = (); type Prog = ();

View file

@ -1,16 +1,19 @@
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use yazi_config::{KEYMAP, keymap::{ChordCow, Key}}; use yazi_config::{KEYMAP, keymap::{ChordCow, Key}};
use yazi_macro::impl_data_any;
use yazi_shared::{Layer, event::ActionCow}; use yazi_shared::{Layer, event::ActionCow};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct WhichOpt { pub struct WhichOpt {
pub tx: Option<mpsc::UnboundedSender<Option<yazi_binding::ChordCow>>>, pub tx: Option<mpsc::UnboundedSender<Option<yazi_binding::keymap::ChordCow>>>,
pub cands: Vec<ChordCow>, pub cands: Vec<ChordCow>,
pub silent: bool, pub silent: bool,
pub times: usize, pub times: usize,
} }
impl_data_any!(WhichOpt);
impl TryFrom<ActionCow> for WhichOpt { impl TryFrom<ActionCow> for WhichOpt {
type Error = anyhow::Error; type Error = anyhow::Error;
@ -21,7 +24,7 @@ impl TryFrom<ActionCow> for WhichOpt {
Ok(Self { Ok(Self {
tx: a.take_any2("tx").transpose()?, tx: a.take_any2("tx").transpose()?,
cands: a.take_any_iter::<yazi_binding::ChordCow>().map(Into::into).collect(), cands: a.take_any_iter::<yazi_binding::keymap::ChordCow>().map(Into::into).collect(),
silent: a.bool("silent"), silent: a.bool("silent"),
times: a.get("times").unwrap_or(0), times: a.get("times").unwrap_or(0),
}) })
@ -35,7 +38,7 @@ impl From<(Layer, Key)> for WhichOpt {
cands: KEYMAP cands: KEYMAP
.get(layer) .get(layer)
.iter() .iter()
.filter(|c| c.on.len() > 1 && c.on[0] == key) .filter(|&c| c.on.len() > 1 && c.on[0] == key)
.map(Into::into) .map(Into::into)
.collect(), .collect(),
times: 1, times: 1,
@ -54,7 +57,7 @@ impl FromLua for WhichOpt {
tx: t.raw_get::<yazi_binding::MpscUnboundedTx<_>>("tx").ok().map(|t| t.0), tx: t.raw_get::<yazi_binding::MpscUnboundedTx<_>>("tx").ok().map(|t| t.0),
cands: t cands: t
.raw_get::<Table>("cands")? .raw_get::<Table>("cands")?
.sequence_values::<yazi_binding::ChordCow>() .sequence_values::<yazi_binding::keymap::ChordCow>()
.map(|c| c.map(Into::into)) .map(|c| c.map(Into::into))
.collect::<mlua::Result<Vec<_>>>()?, .collect::<mlua::Result<Vec<_>>>()?,
times: t.raw_get("times").unwrap_or_default(), times: t.raw_get("times").unwrap_or_default(),
@ -69,7 +72,7 @@ impl IntoLua for WhichOpt {
lua lua
.create_table_from([ .create_table_from([
("tx", self.tx.map(yazi_binding::MpscUnboundedTx).into_lua(lua)?), ("tx", self.tx.map(yazi_binding::MpscUnboundedTx).into_lua(lua)?),
("cands", lua.create_sequence_from(self.cands.into_iter().map(yazi_binding::ChordCow))?.into_lua(lua)?), ("cands", lua.create_sequence_from(self.cands.into_iter().map(yazi_binding::keymap::ChordCow))?.into_lua(lua)?),
("times", self.times.into_lua(lua)?), ("times", self.times.into_lua(lua)?),
("silent", self.silent.into_lua(lua)?), ("silent", self.silent.into_lua(lua)?),
])? ])?

View file

@ -4,7 +4,7 @@ use yazi_macro::{emit, render_and};
#[derive(Default)] #[derive(Default)]
pub struct Which { pub struct Which {
pub tx: Option<mpsc::UnboundedSender<Option<yazi_binding::ChordCow>>>, pub tx: Option<mpsc::UnboundedSender<Option<yazi_binding::keymap::ChordCow>>>,
pub times: usize, pub times: usize,
pub cands: Vec<ChordCow>, pub cands: Vec<ChordCow>,

View file

@ -29,6 +29,7 @@ yazi-version = { path = "../yazi-version", version = "26.5.6" }
anyhow = { workspace = true } anyhow = { workspace = true }
hashbrown = { workspace = true } hashbrown = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
inventory = { workspace = true }
mlua = { workspace = true } mlua = { workspace = true }
ordered-float = { workspace = true } ordered-float = { workspace = true }
parking_lot = { workspace = true } parking_lot = { workspace = true }

View file

@ -1,9 +1,10 @@
use std::borrow::Cow; use std::borrow::Cow;
use indexmap::IndexSet; use indexmap::IndexSet;
use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods, Value}; use mlua::{AnyUserData, FromLua, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods, Value};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use yazi_binding::get_metatable; use yazi_binding::get_metatable;
use yazi_macro::impl_data_any;
use yazi_shared::url::UrlBufCov; use yazi_shared::url::UrlBufCov;
use super::Ember; use super::Ember;
@ -19,6 +20,8 @@ pub struct EmberYank<'a> {
pub urls: Cow<'a, IndexSet<UrlBufCov>>, pub urls: Cow<'a, IndexSet<UrlBufCov>>,
} }
impl_data_any!(EmberYank<'static>, from_into_lua = inherit);
impl<'a> EmberYank<'a> { impl<'a> EmberYank<'a> {
pub fn borrowed(cut: bool, urls: &'a IndexSet<UrlBufCov>) -> Ember<'a> { pub fn borrowed(cut: bool, urls: &'a IndexSet<UrlBufCov>) -> Ember<'a> {
Self { cut, urls: Cow::Borrowed(urls) }.into() Self { cut, urls: Cow::Borrowed(urls) }.into()
@ -35,6 +38,19 @@ impl<'a> From<EmberYank<'a>> for Ember<'a> {
fn from(value: EmberYank<'a>) -> Self { Self::Yank(value) } fn from(value: EmberYank<'a>) -> Self { Self::Yank(value) }
} }
impl FromLua for EmberYank<'static> {
fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
match value {
Value::UserData(ud) => ud.take::<EmberYankIter>()?.collect(lua),
_ => Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "EmberYank".to_owned(),
message: Some("expected EmberYankIter userdata".to_owned()),
}),
}
}
}
impl IntoLua for EmberYank<'_> { impl IntoLua for EmberYank<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> { fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
let len = self.urls.len(); let len = self.urls.len();

View file

@ -3,7 +3,7 @@ use std::{fmt::Display, io::Write, str::FromStr};
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
use mlua::{IntoLua, Lua, Value}; use mlua::{IntoLua, Lua, Value};
use yazi_boot::BOOT; use yazi_boot::BOOT;
use yazi_macro::{emit, relay}; use yazi_macro::{emit, impl_data_any, relay};
use yazi_shared::{Id, event::ActionCow}; use yazi_shared::{Id, event::ActionCow};
use crate::{ID, ember::Ember}; use crate::{ID, ember::Ember};
@ -15,6 +15,8 @@ pub struct Payload<'a> {
pub body: Ember<'a>, pub body: Ember<'a>,
} }
impl_data_any!(Payload<'static>);
impl<'a> Payload<'a> { impl<'a> Payload<'a> {
pub fn new(body: Ember<'a>) -> Self { Self { receiver: Id::ZERO, sender: *ID, body } } pub fn new(body: Ember<'a>) -> Self { Self { receiver: Id::ZERO, sender: *ID, body } }

View file

@ -3,26 +3,23 @@ use std::{any::TypeId, borrow::Cow};
use hashbrown::HashMap; use hashbrown::HashMap;
use mlua::{ExternalError, IntoLua, Lua, MultiValue, Table, Value}; use mlua::{ExternalError, IntoLua, Lua, MultiValue, Table, Value};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use yazi_shared::{data::{Data, DataKey}, replace_cow}; use yazi_shared::{any_data::AnyData, data::{Data, DataInventory, DataKey}, replace_cow};
use crate::ember;
pub struct Sendable; pub struct Sendable;
impl Sendable { impl Sendable {
pub fn value_to_data(lua: &Lua, value: Value) -> mlua::Result<Data> { pub fn value_to_data(lua: &Lua, value: Value) -> mlua::Result<Data> {
Ok(match value { match &value {
Value::Nil => Data::Nil, Value::Nil => return Ok(Data::Nil),
Value::Boolean(b) => Data::Boolean(b), Value::Boolean(b) => return Ok(Data::Boolean(*b)),
Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?, Value::Integer(i) => return Ok(Data::Integer(*i)),
Value::Integer(i) => Data::Integer(i), Value::Number(n) => return Ok(Data::Number(*n)),
Value::Number(n) => Data::Number(n),
Value::String(b) => { Value::String(b) => {
if let Ok(s) = b.to_str() { return Ok(if let Ok(s) = b.to_str() {
Data::String(s.to_owned().into()) Data::String(s.to_owned().into())
} else { } else {
Data::Bytes(b.as_bytes().to_owned()) Data::Bytes(b.as_bytes().to_owned())
} });
} }
Value::Table(t) => { Value::Table(t) => {
let (mut i, mut map) = (1, HashMap::with_capacity(t.raw_len())); let (mut i, mut map) = (1, HashMap::with_capacity(t.raw_len()));
@ -36,38 +33,40 @@ impl Sendable {
map.insert(k, Self::value_to_data(lua, v)?); map.insert(k, Self::value_to_data(lua, v)?);
} }
if map.len() == i as usize - 1 { return Ok(if map.len() == i as usize - 1 {
Data::List((1..i).map(|i| map.remove(&DataKey::Integer(i)).unwrap()).collect()) Data::List((1..i).map(|i| map.remove(&DataKey::Integer(i)).unwrap()).collect())
} else { } else {
Data::Dict(map) Data::Dict(map)
} });
} }
Value::Function(_) => Err("function is not supported".into_lua_err())?,
Value::Thread(_) => Err("thread is not supported".into_lua_err())?,
Value::UserData(ud) => match ud.type_id() { Value::UserData(ud) => match ud.type_id() {
Some(t) if t == TypeId::of::<yazi_binding::Url>() => { Some(t) if t == TypeId::of::<yazi_binding::Url>() => {
Data::Url(ud.take::<yazi_binding::Url>()?.into()) return Ok(Data::Url(ud.take::<yazi_binding::Url>()?.into()));
} }
Some(t) if t == TypeId::of::<yazi_binding::Path>() => { Some(t) if t == TypeId::of::<yazi_binding::Path>() => {
Data::Path(ud.take::<yazi_binding::Path>()?.into()) return Ok(Data::Path(ud.take::<yazi_binding::Path>()?.into()));
} }
Some(t) if t == TypeId::of::<yazi_binding::Id>() => { Some(t) if t == TypeId::of::<yazi_binding::Id>() => {
Data::Id(**ud.borrow::<yazi_binding::Id>()?) return Ok(Data::Id(**ud.borrow::<yazi_binding::Id>()?));
} }
Some(t) if t == TypeId::of::<yazi_fs::FilesOp>() => { Some(t) if t == TypeId::of::<AnyData>() => return Ok(Data::Any(ud.take::<AnyData>()?.0)),
Data::Any(Box::new(ud.take::<yazi_fs::FilesOp>()?)) _ => {}
}
Some(t) if t == TypeId::of::<ember::EmberYankIter>() => {
Data::Any(Box::new(ud.take::<ember::EmberYankIter>()?.collect(lua)?))
}
Some(t) if t == TypeId::of::<yazi_binding::ChordCow>() => {
Data::Any(Box::new(ud.take::<yazi_binding::ChordCow>()?))
}
_ => Err(format!("unsupported userdata included: {ud:?}").into_lua_err())?,
}, },
Value::Error(_) => Err("error is not supported".into_lua_err())?, Value::LightUserData(_) => {}
Value::Other(..) => Err("unknown data is not supported".into_lua_err())?, Value::Function(_) => {}
}) Value::Thread(_) => {}
Value::Error(_) => {}
Value::Other(_) => {}
}
for inv in inventory::iter::<DataInventory> {
match (inv.from_lua)(value.clone(), lua) {
Ok(data) => return Ok(Data::Any(data)),
Err(mlua::Error::UserDataTypeMismatch) => continue,
Err(e) => return Err(e),
}
}
Err(format!("unsupported value included: {value:?}").into_lua_err())?
} }
pub fn data_to_value(lua: &Lua, data: Data) -> mlua::Result<Value> { pub fn data_to_value(lua: &Lua, data: Data) -> mlua::Result<Value> {
@ -91,23 +90,7 @@ impl Sendable {
Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?,
Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?,
Data::Bytes(b) => Value::String(lua.create_external_string(b)?), Data::Bytes(b) => Value::String(lua.create_external_string(b)?),
Data::Any(b) => { Data::Any(a) => a.into_lua(lua)?,
let mut b = b.into_any();
macro_rules! try_cast {
($f:expr) => {
match b.downcast() {
Ok(v) => return $f(*v)?.into_lua(lua),
#[allow(unused_assignments)]
Err(e) => b = e,
}
};
}
try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v));
try_cast!(|v: ember::EmberYank| v.into_lua(lua));
try_cast!(|v: yazi_binding::ChordCow| v.into_lua(lua));
Err("unsupported DataAny included".into_lua_err())?
}
_ => Self::data_to_value_ref(lua, &data)?, _ => Self::data_to_value_ref(lua, &data)?,
}) })
} }
@ -138,20 +121,7 @@ impl Sendable {
Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?,
Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?, Data::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?,
Data::Bytes(b) => Value::String(lua.create_string(b)?), Data::Bytes(b) => Value::String(lua.create_string(b)?),
Data::Any(b) => { Data::Any(a) => a.to_lua(lua)?,
macro_rules! try_cast {
($f:expr) => {
if let Some(v) = b.as_any().downcast_ref() {
return $f(Clone::clone(v))?.into_lua(lua);
}
};
}
try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v));
try_cast!(|v: ember::EmberYank| v.into_lua(lua));
try_cast!(|v: yazi_binding::ChordCow| v.into_lua(lua));
Err("unsupported DataAny included".into_lua_err())?
}
}) })
} }

View file

@ -12,7 +12,7 @@ impl<'a> Help<'a> {
pub fn new(core: &'a Core) -> Self { Self { core } } pub fn new(core: &'a Core) -> Self { Self { core } }
fn tips() -> String { fn tips() -> String {
match KEYMAP.help.iter().find(|&c| c.run.iter().any(|c| c.name == "filter")) { match KEYMAP.help.load().iter().find(|&c| c.run.iter().any(|a| a.name == "filter")) {
Some(c) => format!(" (Press `{}` to filter)", c.on()), Some(c) => format!(" (Press `{}` to filter)", c.on()),
None => String::new(), None => String::new(),
} }

View file

@ -36,7 +36,8 @@ impl<'a> Router<'a> {
} }
fn matches(&mut self, layer: Layer, key: Key) -> bool { fn matches(&mut self, layer: Layer, key: Key) -> bool {
for chord @ Chord { on, .. } in KEYMAP.get(layer) { for chord in &*KEYMAP.get(layer) {
let Chord { on, .. } = &**chord;
if on.is_empty() || on[0] != key { if on.is_empty() || on[0] != key {
continue; continue;
} }

View file

@ -27,6 +27,7 @@ either = { workspace = true }
foldhash = { workspace = true } foldhash = { workspace = true }
hashbrown = { workspace = true } hashbrown = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
mlua = { workspace = true }
parking_lot = { workspace = true } parking_lot = { workspace = true }
percent-encoding = { workspace = true } percent-encoding = { workspace = true }
rand = { workspace = true } rand = { workspace = true }

View file

@ -1,7 +1,7 @@
use std::path::Path; use std::path::Path;
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use yazi_macro::relay; use yazi_macro::{impl_data_any, relay};
use yazi_shared::{Id, Ids, path::PathBufDyn, url::{UrlBuf, UrlLike, UrlMapExt}}; use yazi_shared::{Id, Ids, path::PathBufDyn, url::{UrlBuf, UrlLike, UrlMapExt}};
use super::File; use super::File;
@ -23,6 +23,8 @@ pub enum FilesOp {
Upserting(UrlBuf, HashMap<PathBufDyn, File>), Upserting(UrlBuf, HashMap<PathBufDyn, File>),
} }
impl_data_any!(FilesOp);
impl FilesOp { impl FilesOp {
#[inline] #[inline]
pub fn cwd(&self) -> &UrlBuf { pub fn cwd(&self) -> &UrlBuf {

116
yazi-macro/src/data.rs Normal file
View file

@ -0,0 +1,116 @@
#[macro_export]
macro_rules! impl_data_any {
($ty:ty) => {
impl ::yazi_shared::data::DataAny for $ty {
$crate::impl_data_any!(@core);
$crate::impl_data_any!(@into_lua_default);
}
};
($ty:ty => $($target:ty),+) => {
impl ::yazi_shared::data::DataAny for $ty {
$crate::impl_data_any!(@core $($target),+);
$crate::impl_data_any!(@into_lua_default);
}
};
// --- from_into_lua
($ty:ty, from_into_lua = inherit) => {
impl ::yazi_shared::data::DataAny for $ty {
$crate::impl_data_any!(@core);
$crate::impl_data_any!(@from_lua_inherit);
$crate::impl_data_any!(@into_lua_inherit);
}
$crate::impl_data_any!(@from_lua_register $ty);
};
($ty:ty => $($target:ty),+ ; from_into_lua = inherit) => {
impl ::yazi_shared::data::DataAny for $ty {
$crate::impl_data_any!(@core $($target),+);
$crate::impl_data_any!(@from_lua_inherit);
$crate::impl_data_any!(@into_lua_inherit);
}
$crate::impl_data_any!(@from_lua_register $ty);
};
// --- to_lua
($ty:ty, to_lua = $body:expr) => {
impl ::yazi_shared::data::DataAny for $ty {
$crate::impl_data_any!(@core);
$crate::impl_data_any!(@from_lua_inherit);
$crate::impl_data_any!(@into_lua_default);
$crate::impl_data_any!(@to_lua $body);
}
$crate::impl_data_any!(@from_lua_register $ty);
};
($ty:ty => $($target:ty),+ ; to_lua = $body:expr) => {
impl ::yazi_shared::data::DataAny for $ty {
$crate::impl_data_any!(@core $($target),+);
$crate::impl_data_any!(@from_lua_inherit);
$crate::impl_data_any!(@into_lua_default);
$crate::impl_data_any!(@to_lua $body);
}
$crate::impl_data_any!(@from_lua_register $ty);
};
(@core $($target:ty),*) => {
fn as_any(&self, id: std::any::TypeId) -> Option<&dyn std::any::Any> {
use std::any::{TypeId, Any};
if id == TypeId::of::<Self>() {
return Some(self as &dyn Any);
}
$(
if id == TypeId::of::<$target>() {
return Some(<Self as AsRef<$target>>::as_ref(self) as &dyn Any);
}
)*
None
}
fn into_any(self: Box<Self>, id: std::any::TypeId) -> Result<Box<dyn std::any::Any>, Box<dyn ::yazi_shared::data::DataAny>> {
use std::any::TypeId;
if id == TypeId::of::<Self>() { return Ok(self); }
$(
if id == TypeId::of::<$target>() {
return Ok(Box::new(<Self as Into<$target>>::into(*self)));
}
)*
Err(self)
}
};
(@into_lua_default) => {
fn into_lua(self: Box<Self>, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
use ::yazi_shared::any_data::AnyData;
use mlua::IntoLua;
AnyData(self).into_lua(lua)
}
};
(@into_lua_inherit) => {
fn into_lua(self: Box<Self>, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
use mlua::IntoLua;
IntoLua::into_lua(*self, lua)
}
};
(@to_lua $body:expr) => {
fn to_lua(&self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
use mlua::{self, Lua, Value};
($body as fn(&Self, &Lua) -> mlua::Result<Value>)(self, lua)
}
};
(@from_lua_inherit) => {
fn from_lua(value: mlua::Value, lua: &mlua::Lua) -> mlua::Result<Box<dyn ::yazi_shared::data::DataAny>> {
use mlua::FromLua;
<Self as FromLua>::from_lua(value, lua)
.map(|v| Box::new(v) as Box<dyn ::yazi_shared::data::DataAny>)
}
};
(@from_lua_register $ty:ty) => {
::inventory::submit! {
::yazi_shared::data::DataInventory {
from_lua: <$ty as ::yazi_shared::data::DataAny>::from_lua,
}
}
};
}

View file

@ -1,4 +1,5 @@
mod actor; mod actor;
mod data;
mod asset; mod asset;
mod confirm; mod confirm;
mod context; mod context;

View file

@ -20,6 +20,7 @@ vendored-lua = [ "mlua/vendored" ]
yazi-adapter = { path = "../yazi-adapter", version = "26.5.6" } yazi-adapter = { path = "../yazi-adapter", version = "26.5.6" }
yazi-binding = { path = "../yazi-binding", version = "26.5.6" } yazi-binding = { path = "../yazi-binding", version = "26.5.6" }
yazi-boot = { path = "../yazi-boot", version = "26.5.6" } yazi-boot = { path = "../yazi-boot", version = "26.5.6" }
yazi-codegen = { path = "../yazi-codegen", version = "26.5.6" }
yazi-config = { path = "../yazi-config", version = "26.5.6" } yazi-config = { path = "../yazi-config", version = "26.5.6" }
yazi-core = { path = "../yazi-core", version = "26.5.6" } yazi-core = { path = "../yazi-core", version = "26.5.6" }
yazi-dds = { path = "../yazi-dds", version = "26.5.6" } yazi-dds = { path = "../yazi-dds", version = "26.5.6" }
@ -39,8 +40,8 @@ yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" }
ansi-to-tui = { workspace = true } ansi-to-tui = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
inventory = { workspace = true }
mlua = { workspace = true } mlua = { workspace = true }
paste = { workspace = true }
percent-encoding = { workspace = true } percent-encoding = { workspace = true }
ratatui = { workspace = true } ratatui = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }

View file

@ -1,8 +1,21 @@
use mlua::{IntoLua, Lua, Table, Value}; use mlua::{Lua, Table, UserData};
use yazi_binding::{Cha, File, Id, Path, Url}; use yazi_binding::{Cha, File, Id, Path, Url};
use yazi_codegen::FromLuaOwned;
use yazi_macro::impl_data_any;
#[derive(Clone, FromLuaOwned)]
pub(super) struct FilesOp(yazi_fs::FilesOp); pub(super) struct FilesOp(yazi_fs::FilesOp);
impl_data_any!(FilesOp => yazi_fs::FilesOp; from_into_lua = inherit);
impl From<FilesOp> for yazi_fs::FilesOp {
fn from(op: FilesOp) -> Self { op.0 }
}
impl AsRef<yazi_fs::FilesOp> for FilesOp {
fn as_ref(&self) -> &yazi_fs::FilesOp { &self.0 }
}
impl FilesOp { impl FilesOp {
pub(super) fn part(_: &Lua, t: Table) -> mlua::Result<Self> { pub(super) fn part(_: &Lua, t: Table) -> mlua::Result<Self> {
let id: Id = t.raw_get("id")?; let id: Id = t.raw_get("id")?;
@ -41,8 +54,4 @@ impl FilesOp {
} }
} }
impl IntoLua for FilesOp { impl UserData for FilesOp {}
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua.create_any_userdata(self.0)?.into_lua(lua)
}
}

View file

@ -0,0 +1,23 @@
use mlua::{IntoLua, Lua, Value};
use yazi_binding::{Composer, ComposerGet, ComposerSet, keymap::KeymapSection};
use yazi_shared::Layer;
pub fn compose() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
match key {
b"mgr" => KeymapSection::try_from(Layer::Mgr)?.into_lua(lua),
b"tasks" => KeymapSection::try_from(Layer::Tasks)?.into_lua(lua),
b"spot" => KeymapSection::try_from(Layer::Spot)?.into_lua(lua),
b"pick" => KeymapSection::try_from(Layer::Pick)?.into_lua(lua),
b"input" => KeymapSection::try_from(Layer::Input)?.into_lua(lua),
b"confirm" => KeymapSection::try_from(Layer::Confirm)?.into_lua(lua),
b"cmp" => KeymapSection::try_from(Layer::Cmp)?.into_lua(lua),
b"help" => KeymapSection::try_from(Layer::Help)?.into_lua(lua),
_ => Ok(Value::Nil),
}
}
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::new(get, set)
}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(keymap);

View file

@ -1,4 +1,4 @@
yazi_macro::mod_pub!(elements external fs pubsub runtime tasks theme utils); yazi_macro::mod_pub!(elements external fs keymap pubsub runtime tasks theme utils);
yazi_macro::mod_flat!(slim standard); yazi_macro::mod_flat!(slim standard);

View file

@ -8,6 +8,7 @@ pub fn slim_lua(lua: &Lua) -> mlua::Result<()> {
globals.raw_set("ya", crate::utils::compose(true))?; globals.raw_set("ya", crate::utils::compose(true))?;
globals.raw_set("fs", crate::fs::compose())?; globals.raw_set("fs", crate::fs::compose())?;
globals.raw_set("rt", crate::runtime::compose())?; globals.raw_set("rt", crate::runtime::compose())?;
globals.raw_set("km", crate::keymap::compose())?;
globals.raw_set("th", crate::theme::compose().into_lua(lua)?)?; globals.raw_set("th", crate::theme::compose().into_lua(lua)?)?;
yazi_binding::Cha::install(lua)?; yazi_binding::Cha::install(lua)?;

View file

@ -27,6 +27,7 @@ fn stage_1(lua: &Lua) -> Result<()> {
globals.raw_set("fs", crate::fs::compose())?; globals.raw_set("fs", crate::fs::compose())?;
globals.raw_set("ps", crate::pubsub::compose())?; globals.raw_set("ps", crate::pubsub::compose())?;
globals.raw_set("rt", crate::runtime::compose())?; globals.raw_set("rt", crate::runtime::compose())?;
globals.raw_set("km", crate::keymap::compose())?;
globals.raw_set("th", crate::theme::compose())?; globals.raw_set("th", crate::theme::compose())?;
yazi_binding::Error::install(lua)?; yazi_binding::Error::install(lua)?;

View file

@ -25,6 +25,7 @@ impl Utils {
.map(|(i, cand)| { .map(|(i, cand)| {
let cand = cand?; let cand = cand?;
Ok(ChordCow::Owned(Chord { Ok(ChordCow::Owned(Chord {
id: yazi_config::keymap::chord_id(),
on: Self::parse_keys(cand.raw_get("on")?)?, on: Self::parse_keys(cand.raw_get("on")?)?,
run: vec![relay!(which:callback, [i + 1])], run: vec![relay!(which:callback, [i + 1])],
desc: cand.raw_get("desc").unwrap_or_default(), desc: cand.raw_get("desc").unwrap_or_default(),

View file

@ -1,5 +1,6 @@
use std::ffi::OsString; use std::ffi::OsString;
use yazi_macro::impl_data_any;
use yazi_shared::url::{UrlBuf, UrlCow}; use yazi_shared::url::{UrlBuf, UrlCow};
// TODO: remove in favor of ShellForm // TODO: remove in favor of ShellForm
@ -13,3 +14,5 @@ pub struct ProcessOpt {
pub spread: bool, // TODO: remove pub spread: bool, // TODO: remove
} }
impl_data_any!(ProcessOpt);

View file

@ -1,5 +1,6 @@
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use serde::Serialize; use serde::Serialize;
use yazi_macro::impl_data_any;
use crate::{Ongoing, Progress}; use crate::{Ongoing, Progress};
@ -11,6 +12,8 @@ pub struct TaskSummary {
pub percent: Option<OrderedFloat<f32>>, pub percent: Option<OrderedFloat<f32>>,
} }
impl_data_any!(TaskSummary);
impl From<&Ongoing> for TaskSummary { impl From<&Ongoing> for TaskSummary {
fn from(value: &Ongoing) -> Self { fn from(value: &Ongoing) -> Self {
let mut summary = Self::default(); let mut summary = Self::default();

View file

@ -23,7 +23,9 @@ dyn-clone = { workspace = true }
foldhash = { workspace = true } foldhash = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
hashbrown = { workspace = true } hashbrown = { workspace = true }
inventory = { workspace = true }
memchr = "2.8.1" memchr = "2.8.1"
mlua = { workspace = true }
ordered-float = { workspace = true } ordered-float = { workspace = true }
parking_lot = { workspace = true } parking_lot = { workspace = true }
paste = { workspace = true } paste = { workspace = true }

View file

@ -0,0 +1,8 @@
use mlua::UserData;
use crate::data::DataAny;
#[derive(Debug)]
pub struct AnyData(pub Box<dyn DataAny>);
impl UserData for AnyData {}

View file

@ -1,12 +1,15 @@
use std::sync::{Arc, atomic::{AtomicU8, Ordering}}; use std::sync::{Arc, atomic::{AtomicU8, Ordering}};
use tokio::sync::Notify; use tokio::sync::Notify;
use yazi_macro::impl_data_any;
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct CompletionToken { pub struct CompletionToken {
inner: Arc<(AtomicU8, Notify)>, inner: Arc<(AtomicU8, Notify)>,
} }
impl_data_any!(CompletionToken);
impl CompletionToken { impl CompletionToken {
pub fn complete(&self, success: bool) { pub fn complete(&self, success: bool) {
let new = if success { 1 } else { 2 }; let new = if success { 1 } else { 2 };

View file

@ -1,20 +1,39 @@
use std::{any::Any, fmt::{self, Debug}}; use std::{any::{self, Any, TypeId}, fmt::{self, Debug}};
use dyn_clone::DynClone; use dyn_clone::DynClone;
use mlua::{IntoLua, Lua, Value};
use crate::any_data::AnyData;
pub trait DataAny: Any + Send + Sync + DynClone { pub trait DataAny: Any + Send + Sync + DynClone {
fn as_any(&self) -> &dyn Any; fn as_any(&self, id: TypeId) -> Option<&dyn Any>;
fn into_any(self: Box<Self>) -> Box<dyn Any>; fn into_any(self: Box<Self>, id: TypeId) -> Result<Box<dyn Any>, Box<dyn DataAny>>;
fn into_lua(self: Box<Self>, lua: &Lua) -> mlua::Result<Value>;
fn to_lua(&self, lua: &Lua) -> mlua::Result<Value> { dyn_clone::clone_box(self).into_lua(lua) }
fn from_lua(_value: Value, _lua: &Lua) -> mlua::Result<Box<dyn DataAny>>
where
Self: Sized,
{
Err(mlua::Error::runtime(format!(
"DataAny::from_lua is not implemented for `{}`",
any::type_name::<Self>(),
)))
}
} }
impl<T> DataAny for T impl dyn DataAny {
where pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
T: Any + Send + Sync + DynClone, self.as_any(TypeId::of::<T>()).and_then(|a| a.downcast_ref::<T>())
{ }
fn as_any(&self) -> &dyn Any { self }
fn into_any(self: Box<Self>) -> Box<dyn Any> { self } pub fn downcast<T: 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Any>> {
let id = TypeId::of::<T>();
self.into_any(id).map_or_else(|me| Err(me as Box<dyn Any>), |a| a.downcast::<T>())
}
} }
impl Clone for Box<dyn DataAny> { impl Clone for Box<dyn DataAny> {
@ -26,3 +45,27 @@ impl Debug for dyn DataAny {
f.debug_struct("DataAny").finish_non_exhaustive() f.debug_struct("DataAny").finish_non_exhaustive()
} }
} }
impl<T: Clone + Send + Sync + 'static> DataAny for Vec<T> {
fn as_any(&self, id: TypeId) -> Option<&dyn Any> {
(id == TypeId::of::<Self>()).then(|| self as &dyn Any)
}
fn into_any(self: Box<Self>, id: TypeId) -> Result<Box<dyn Any>, Box<dyn DataAny>> {
if id == TypeId::of::<Self>() { Ok(self) } else { Err(self) }
}
fn into_lua(self: Box<Self>, lua: &Lua) -> mlua::Result<Value> { AnyData(self).into_lua(lua) }
}
impl<T: Send + 'static> DataAny for tokio::sync::mpsc::UnboundedSender<T> {
fn as_any(&self, id: TypeId) -> Option<&dyn Any> {
(id == TypeId::of::<Self>()).then(|| self as &dyn Any)
}
fn into_any(self: Box<Self>, id: TypeId) -> Result<Box<dyn Any>, Box<dyn DataAny>> {
if id == TypeId::of::<Self>() { Ok(self) } else { Err(self) }
}
fn into_lua(self: Box<Self>, lua: &Lua) -> mlua::Result<Value> { AnyData(self).into_lua(lua) }
}

View file

@ -257,7 +257,7 @@ impl Data {
pub fn as_any<T: 'static>(&self) -> Option<&T> { pub fn as_any<T: 'static>(&self) -> Option<&T> {
match self { match self {
Self::Any(b) => (**b).as_any().downcast_ref::<T>(), Self::Any(a) => a.downcast_ref::<T>(),
_ => None, _ => None,
} }
} }
@ -271,15 +271,15 @@ impl Data {
pub fn into_any<T: 'static>(self) -> Option<T> { pub fn into_any<T: 'static>(self) -> Option<T> {
match self { match self {
Self::Any(b) => b.into_any().downcast::<T>().ok().map(|b| *b), Self::Any(a) => a.downcast::<T>().ok().map(|b| *b),
_ => None, _ => None,
} }
} }
// FIXME: find a better name // FIXME: find a better name
pub fn into_any2<T: 'static>(self) -> Result<T> { pub fn into_any2<T: 'static>(self) -> Result<T> {
if let Self::Any(b) = self if let Self::Any(a) = self
&& let Ok(t) = b.into_any().downcast::<T>() && let Ok(t) = a.downcast::<T>()
{ {
Ok(*t) Ok(*t)
} else { } else {

View file

@ -0,0 +1,9 @@
use mlua::{Lua, Value};
use crate::data::DataAny;
pub struct DataInventory {
pub from_lua: fn(Value, &Lua) -> mlua::Result<Box<dyn DataAny>>,
}
inventory::collect!(DataInventory);

View file

@ -1,4 +1,4 @@
#[macro_use] #[macro_use]
mod macros; mod macros;
yazi_macro::mod_flat!(any data de de_bytes de_key de_owned key); yazi_macro::mod_flat!(any data de de_bytes de_key de_owned inventory key);

View file

@ -1,4 +1,6 @@
yazi_macro::mod_pub!(data event loc path pool scheme shell strand translit url wtf8); extern crate self as yazi_shared;
yazi_macro::mod_pub!(any_data data event loc path pool scheme shell strand translit url wtf8);
yazi_macro::mod_flat!(alias bytes chars completion_token condition debounce env id kebab_cased_string last_value layer localset natsort non_empty_string os predictor snake_cased_string source tests throttle time); yazi_macro::mod_flat!(alias bytes chars completion_token condition debounce env id kebab_cased_string last_value layer localset natsort non_empty_string os predictor snake_cased_string source tests throttle time);

View file

@ -2,6 +2,7 @@ use std::{borrow::Cow, fmt::{Debug, Formatter}, hash::{Hash, Hasher}, path::{Pat
use anyhow::Result; use anyhow::Result;
use serde::{Deserialize, Serialize, de::{self, IntoDeserializer}}; use serde::{Deserialize, Serialize, de::{self, IntoDeserializer}};
use yazi_macro::impl_data_any;
use crate::{loc::LocBuf, path::{PathBufDyn, PathDynError, SetNameError}, pool::{InternStr, Pool, Symbol}, scheme::{Scheme, SchemeLike}, strand::AsStrand, url::{AsUrl, Url, UrlCow, UrlDeserializer, UrlLike}}; use crate::{loc::LocBuf, path::{PathBufDyn, PathDynError, SetNameError}, pool::{InternStr, Pool, Symbol}, scheme::{Scheme, SchemeLike}, strand::AsStrand, url::{AsUrl, Url, UrlCow, UrlDeserializer, UrlLike}};
@ -13,6 +14,8 @@ pub enum UrlBuf {
Sftp { loc: LocBuf<typed_path::UnixPathBuf>, domain: Symbol<str> }, Sftp { loc: LocBuf<typed_path::UnixPathBuf>, domain: Symbol<str> },
} }
impl_data_any!(UrlBuf);
// FIXME: remove // FIXME: remove
impl Default for UrlBuf { impl Default for UrlBuf {
fn default() -> Self { Self::Regular(Default::default()) } fn default() -> Self { Self::Regular(Default::default()) }

View file

@ -33,4 +33,4 @@ twox-hash = { workspace = true }
unicode-width = { workspace = true } unicode-width = { workspace = true }
[dependencies.unicode-segmentation] [dependencies.unicode-segmentation]
version = "1.13.2" version = "1.13.3"

View file

@ -14,7 +14,7 @@ pub trait DeserializeOverLua: DeserializeOverWith {
impl<T: DeserializeOverWith> DeserializeOverLua for T {} impl<T: DeserializeOverWith> DeserializeOverLua for T {}
// --- IntoLua // --- LuaTableExt
pub trait LuaTableExt { pub trait LuaTableExt {
fn sequence_iter<V: FromLua>(&self, lua: &Lua) -> SequenceIter<V>; fn sequence_iter<V: FromLua>(&self, lua: &Lua) -> SequenceIter<V>;
} }

View file

@ -24,6 +24,7 @@ bitflags = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
parking_lot = { workspace = true } parking_lot = { workspace = true }
ratatui = { workspace = true } ratatui = { workspace = true }
serde = { workspace = true }
strum = { workspace = true } strum = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }

View file

@ -1,4 +1,5 @@
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Serialize};
use crate::{ParseError, Result, bail, event::Modifiers}; use crate::{ParseError, Result, bail, event::Modifiers};
@ -71,7 +72,8 @@ impl KeyEventState {
} }
// --- Code // --- Code
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(tag = "type", content = "value")]
pub enum KeyCode { pub enum KeyCode {
Char(char), Char(char),
Enter, Enter,
@ -217,7 +219,8 @@ impl KeyCode {
} }
// --- Modifier key // --- Modifier key
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(untagged)]
pub enum ModifierKeyCode { pub enum ModifierKeyCode {
LeftShift, LeftShift,
LeftControl, LeftControl,
@ -250,7 +253,8 @@ impl ModifierKeyCode {
} }
// --- Media key // --- Media key
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(untagged)]
pub enum MediaKeyCode { pub enum MediaKeyCode {
Play, Play,
Pause, Pause,