feat: keymap Lua bindings (#4021)

This commit is contained in:
三咲雅 misaki masa 2026-06-06 13:45:52 +08:00 committed by GitHub
parent 8e80798984
commit 9bdd6777e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
77 changed files with 1068 additions and 378 deletions

View file

@ -15,3 +15,7 @@ A clear and concise description of the rationale of the changes, to help our rev
If it has already been detailed in the associated issue, please skip this section. If it has already been detailed in the associated issue, please skip this section.
--> -->
## Checklist
- [ ] I have read [CONTRIBUTING.md](https://github.com/sxyazi/yazi/blob/main/CONTRIBUTING.md)

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

283
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]]
@ -165,6 +165,15 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "arbitrary" name = "arbitrary"
version = "1.4.2" version = "1.4.2"
@ -378,9 +387,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 +443,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 +594,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 +614,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 +804,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 +850,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 +867,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 +975,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 +1280,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 +1302,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 +1475,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 +1679,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 +1702,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 +1749,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 +1973,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 +1991,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 +2037,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"
@ -2110,7 +2136,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 +2172,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 +2201,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 +2233,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 +2246,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 +2304,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 +2455,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 +2468,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 +2514,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 +2532,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 +2647,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 +2678,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 +2731,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 +2744,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 +2758,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",
@ -2779,6 +2796,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6"
dependencies = [ dependencies = [
"approx",
"bytemuck", "bytemuck",
"fast-srgb8", "fast-srgb8",
"libm", "libm",
@ -3046,7 +3064,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",
@ -3108,23 +3126,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",
] ]
@ -3334,19 +3352,21 @@ 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",
"palette",
"serde", "serde",
"strum 0.27.2", "strum 0.28.0",
"thiserror 2.0.18", "thiserror 2.0.18",
"unicode-segmentation", "unicode-segmentation",
"unicode-truncate", "unicode-truncate",
@ -3355,9 +3375,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 +3388,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 +3399,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",
@ -3393,7 +3413,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.13.0",
"hashbrown 0.16.1", "hashbrown 0.16.1",
"indoc", "indoc",
"instability", "instability",
@ -3482,7 +3502,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 +3616,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 +3640,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 +3730,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 +3749,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 +3922,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 +3942,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",
@ -4378,7 +4376,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 +4777,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 +5043,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 +5140,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 +5577,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",
@ -5790,10 +5788,11 @@ 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",
@ -5855,6 +5854,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 +5951,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",
@ -5991,7 +5992,7 @@ 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",
@ -6021,9 +6022,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 +6037,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,7 +6106,7 @@ 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",
@ -6127,7 +6129,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,8 +6146,10 @@ 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",
@ -6188,11 +6192,12 @@ 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 0.28.0",
"thiserror 2.0.18", "thiserror 2.0.18",

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.0", 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

@ -2,6 +2,7 @@ mod actor;
mod asset; mod asset;
mod confirm; mod confirm;
mod context; mod context;
mod data;
mod event; mod event;
mod fmt; mod fmt;
mod fs; mod fs;

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,