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.
-->
## 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])
- Bulk create ([#3793])
- Image preview with Überzug++ on Niri ([#3990])
- New gait for input `backward` and `forward` actions ([#4012])
### Changed

283
Cargo.lock generated
View file

@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe"
dependencies = [
"crypto-common 0.2.2",
"inout 0.2.2",
"inout",
]
[[package]]
@ -165,6 +165,15 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "arbitrary"
version = "1.4.2"
@ -378,9 +387,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.11.1"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
dependencies = [
"serde_core",
]
@ -434,15 +443,6 @@ dependencies = [
"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]]
name = "block-padding"
version = "0.4.2"
@ -594,9 +594,9 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.44"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
dependencies = [
"iana-time-zone",
"js-sys",
@ -614,7 +614,7 @@ checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
dependencies = [
"block-buffer 0.12.0",
"crypto-common 0.2.2",
"inout 0.2.2",
"inout",
"zeroize",
]
@ -804,6 +804,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
@ -844,7 +850,7 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"crossterm_winapi",
"mio",
"parking_lot",
@ -861,7 +867,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"crossterm_winapi",
"derive_more",
"document-features",
@ -969,12 +975,12 @@ dependencies = [
[[package]]
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"
checksum = "335f1947f241137a14106b6f5acc5918a5ede29c9d71d3f2cb1678d5075d9fc3"
checksum = "4f359e08ca85e7bd759e1fd933ff2bccd81864c60a8fba0e259c7f822b0924bf"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"cpufeatures 0.3.0",
"curve25519-dalek-derive",
"digest 0.11.3",
"fiat-crypto",
@ -1274,9 +1280,9 @@ dependencies = [
[[package]]
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"
checksum = "20449acd54b660981ae5caa2bcb56d1fe7f25f2e37a38ec507400fab034d4bb6"
checksum = "b011170fe4f04665565b4110afef66774fe9ffff278f3eb5b81cc73d26e27d60"
dependencies = [
"curve25519-dalek",
"ed25519",
@ -1296,22 +1302,22 @@ checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
[[package]]
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"
checksum = "cda94f31325c4275e9706adecbb6f0650dee2f904c915a98e3d81adaaaa757aa"
checksum = "102d3643d30dd8b559613c5cced68317199597fffb278cdc88daa2ef7fafc935"
dependencies = [
"base16ct",
"crypto-bigint",
"crypto-common 0.2.2",
"digest 0.11.3",
"ff",
"group",
"hkdf",
"hybrid-array",
"once_cell",
"pem-rfc7468",
"pkcs8",
"rand_core 0.10.1",
"rustcrypto-ff",
"rustcrypto-group",
"sec1",
"subtle",
"zeroize",
@ -1469,6 +1475,16 @@ dependencies = [
"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]]
name = "fiat-crypto"
version = "0.3.0"
@ -1663,10 +1679,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
@ -1688,11 +1702,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 6.0.0",
"rand_core 0.10.1",
"wasip2",
"wasip3",
"wasm-bindgen",
]
[[package]]
@ -1733,6 +1749,17 @@ dependencies = [
"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]]
name = "half"
version = "2.7.1"
@ -1946,11 +1973,11 @@ dependencies = [
[[package]]
name = "inotify"
version = "0.11.1"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199"
checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"inotify-sys",
"libc",
]
@ -1964,23 +1991,13 @@ dependencies = [
"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]]
name = "inout"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [
"block-padding 0.4.2",
"block-padding",
"hybrid-array",
]
@ -2020,6 +2037,15 @@ dependencies = [
"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]]
name = "is_terminal_polyfill"
version = "1.70.2"
@ -2110,7 +2136,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"libc",
]
@ -2146,9 +2172,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libfuzzer-sys"
version = "0.4.12"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d"
checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2"
dependencies = [
"arbitrary",
"cc",
@ -2175,7 +2201,7 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
]
[[package]]
@ -2207,9 +2233,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.30"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
[[package]]
name = "loop9"
@ -2220,15 +2246,6 @@ dependencies = [
"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]]
name = "lru"
version = "0.18.0"
@ -2287,9 +2304,9 @@ dependencies = [
[[package]]
name = "md5"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0"
[[package]]
name = "memchr"
@ -2438,7 +2455,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"cfg-if",
"cfg_aliases",
"libc",
@ -2451,7 +2468,7 @@ version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"cfg-if",
"cfg_aliases",
"libc",
@ -2497,7 +2514,7 @@ version = "8.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"fsevent-sys",
"inotify",
"kqueue",
@ -2515,7 +2532,7 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
]
[[package]]
@ -2630,7 +2647,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"objc2",
]
@ -2661,7 +2678,7 @@ version = "6.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"libc",
"once_cell",
"onig_sys",
@ -2714,9 +2731,9 @@ dependencies = [
[[package]]
name = "p256"
version = "0.14.0-rc.9"
version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b97e3bf0465157ae90975ff52dbeb1362ba618924878c9f74c25baa27a65f9a"
checksum = "41adc63effe99d48837a8cc0e6d7a77e32ae6a07f6000df466178dbc2193093e"
dependencies = [
"ecdsa",
"elliptic-curve",
@ -2727,9 +2744,9 @@ dependencies = [
[[package]]
name = "p384"
version = "0.14.0-rc.9"
version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "437f30ebcb1e16ff48acead5f08bd69fbcdbc82421687bb48af5c315a0bfab03"
checksum = "9bd5333afa5ae0347f39e6a0f2c9c155da431583fd71fe5555bd0521b4ccaf02"
dependencies = [
"ecdsa",
"elliptic-curve",
@ -2741,9 +2758,9 @@ dependencies = [
[[package]]
name = "p521"
version = "0.14.0-rc.9"
version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e9fd792bab86ecf6249561752fb5a413511f999887107dd054bbda5143743d7"
checksum = "a3a5297f53dc16d35909060ba3032cff7867e8809f01e273ff325579d5f0ceae"
dependencies = [
"base16ct",
"ecdsa",
@ -2779,6 +2796,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6"
dependencies = [
"approx",
"bytemuck",
"fast-srgb8",
"libm",
@ -3046,7 +3064,7 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"crc32fast",
"fdeflate",
"flate2",
@ -3108,23 +3126,23 @@ dependencies = [
[[package]]
name = "primefield"
version = "0.14.0-rc.9"
version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b52e6ee42db392378a95622b463c9740631171d1efce43fa445a569c1600cb6"
checksum = "f845ec3240cd5ed5e1e31cf3ff633a5bf47c698dc4092ba9e767415b3d393406"
dependencies = [
"crypto-bigint",
"crypto-common 0.2.2",
"ff",
"rand_core 0.10.1",
"rustcrypto-ff",
"subtle",
"zeroize",
]
[[package]]
name = "primeorder"
version = "0.14.0-rc.9"
version = "0.14.0-rc.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0556580e42c19833f5d232aca11a7687a503ee41f937b54f5ae1d50fc2a6a36a"
checksum = "7d2793f22b9b6fd11ef3ac1d59bf003c2573593e4968702341605c2748fd90bf"
dependencies = [
"elliptic-curve",
]
@ -3334,19 +3352,21 @@ dependencies = [
[[package]]
name = "ratatui-core"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293"
checksum = "42d3603f354bba8c595fa47860e60142d7372b7210c27044c6a7d0e1a4336b44"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"compact_str",
"hashbrown 0.16.1",
"critical-section",
"hashbrown 0.17.1",
"indoc",
"itertools",
"kasuari",
"lru 0.16.4",
"lru",
"palette",
"serde",
"strum 0.27.2",
"strum 0.28.0",
"thiserror 2.0.18",
"unicode-segmentation",
"unicode-truncate",
@ -3355,9 +3375,9 @@ dependencies = [
[[package]]
name = "ratatui-crossterm"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3"
checksum = "2b2867bedcbd6a690ca4f8672a687b730ec07660c79844517b084311b529980c"
dependencies = [
"cfg-if",
"crossterm 0.28.1",
@ -3368,9 +3388,9 @@ dependencies = [
[[package]]
name = "ratatui-termion"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cade85a8591fbc911e147951422f0d6fd40f4948b271b6216c7dc01838996f8"
checksum = "5c16cc35a9d9114e0b2bb4b22018b96ae7f5fe60e2595dc73e622b4e78624835"
dependencies = [
"instability",
"ratatui-core",
@ -3379,9 +3399,9 @@ dependencies = [
[[package]]
name = "ratatui-termwiz"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c"
checksum = "386b8ff8f74ed749509391c56d549761a2fcdb408e1f42e467286bcb7dac8967"
dependencies = [
"ratatui-core",
"termwiz",
@ -3393,7 +3413,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"hashbrown 0.16.1",
"indoc",
"instability",
@ -3482,7 +3502,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
]
[[package]]
@ -3596,13 +3616,13 @@ dependencies = [
[[package]]
name = "russh"
version = "0.61.1"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67013f080c226e5a34db1c71f2567f44d95a6300005bb6cd4e2c8fe3c326d1b"
checksum = "bbf893f64684e58da8a68d56a5e84d1cf0440226274c515770fe267707a7d0b0"
dependencies = [
"aes",
"bitflags 2.11.1",
"block-padding 0.4.2",
"bitflags 2.13.0",
"block-padding",
"byteorder",
"bytes",
"cbc",
@ -3620,12 +3640,11 @@ dependencies = [
"enum_dispatch",
"futures",
"generic-array 1.4.3",
"getrandom 0.2.17",
"getrandom 0.4.2",
"ghash",
"hex-literal",
"hkdf",
"hmac",
"inout 0.1.4",
"inout",
"internal-russh-num-bigint",
"keccak",
"log",
@ -3711,34 +3730,13 @@ dependencies = [
"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]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"errno",
"libc",
"linux-raw-sys 0.4.15",
@ -3751,7 +3749,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"errno",
"libc",
"linux-raw-sys 0.12.1",
@ -3924,9 +3922,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.20.0"
version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2"
checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c"
dependencies = [
"base64",
"bs58",
@ -3944,9 +3942,9 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.20.0"
version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac"
checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
dependencies = [
"darling 0.23.0",
"proc-macro2",
@ -4378,7 +4376,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7"
dependencies = [
"anyhow",
"base64",
"bitflags 2.11.1",
"bitflags 2.13.0",
"fancy-regex",
"filedescriptor",
"finl_unicode",
@ -4779,9 +4777,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-segmentation"
version = "1.13.2"
version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]]
name = "unicode-truncate"
@ -5045,7 +5043,7 @@ version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"hashbrown 0.15.5",
"indexmap 2.14.0",
"semver",
@ -5142,9 +5140,9 @@ dependencies = [
[[package]]
name = "wide"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a7714cd0430a663154667c74da5d09325c2387695bee18b3f7f72825aa3693a"
checksum = "dfdfe6a32973f2d1b268b8895845a8a96cac2f0191e72c27cc929036060dbf89"
dependencies = [
"bytemuck",
"safe_arch",
@ -5579,7 +5577,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags 2.11.1",
"bitflags 2.13.0",
"indexmap 2.14.0",
"log",
"serde",
@ -5790,10 +5788,11 @@ version = "26.5.6"
dependencies = [
"anyhow",
"arc-swap",
"bitflags 2.11.1",
"bitflags 2.13.0",
"globset",
"hashbrown 0.17.1",
"indexmap 2.14.0",
"mlua",
"ratatui",
"regex",
"serde",
@ -5855,6 +5854,7 @@ dependencies = [
"anyhow",
"hashbrown 0.17.1",
"indexmap 2.14.0",
"inventory",
"mlua",
"ordered-float 5.3.0",
"parking_lot",
@ -5951,13 +5951,14 @@ version = "26.5.6"
dependencies = [
"anyhow",
"arc-swap",
"bitflags 2.11.1",
"bitflags 2.13.0",
"core-foundation-sys",
"dirs",
"either",
"foldhash 0.2.0",
"hashbrown 0.17.1",
"libc",
"mlua",
"objc2",
"parking_lot",
"percent-encoding",
@ -5991,7 +5992,7 @@ name = "yazi-parser"
version = "26.5.6"
dependencies = [
"anyhow",
"bitflags 2.11.1",
"bitflags 2.13.0",
"hashbrown 0.17.1",
"mlua",
"paste",
@ -6021,9 +6022,9 @@ dependencies = [
"ansi-to-tui",
"anyhow",
"futures",
"inventory",
"libc",
"mlua",
"paste",
"percent-encoding",
"ratatui",
"serde_json",
@ -6036,6 +6037,7 @@ dependencies = [
"yazi-adapter",
"yazi-binding",
"yazi-boot",
"yazi-codegen",
"yazi-config",
"yazi-core",
"yazi-dds",
@ -6104,7 +6106,7 @@ dependencies = [
"foldhash 0.2.0",
"hashbrown 0.17.1",
"libc",
"lru 0.18.0",
"lru",
"mlua",
"ordered-float 5.3.0",
"parking_lot",
@ -6127,7 +6129,7 @@ dependencies = [
name = "yazi-sftp"
version = "26.5.6"
dependencies = [
"bitflags 2.11.1",
"bitflags 2.13.0",
"parking_lot",
"russh",
"serde",
@ -6144,8 +6146,10 @@ dependencies = [
"foldhash 0.2.0",
"futures",
"hashbrown 0.17.1",
"inventory",
"libc",
"memchr",
"mlua",
"ordered-float 5.3.0",
"parking_lot",
"paste",
@ -6188,11 +6192,12 @@ version = "26.5.6"
dependencies = [
"anyhow",
"base64",
"bitflags 2.11.1",
"bitflags 2.13.0",
"futures",
"parking_lot",
"ratatui",
"rustix 1.1.4",
"serde",
"signal-hook 0.4.4",
"strum 0.28.0",
"thiserror 2.0.18",

View file

@ -39,8 +39,8 @@ ansi-to-tui = "8.0.1"
anyhow = "1.0.102"
arc-swap = { version = "1.9.1", features = [ "serde" ] }
base64 = "0.22.1"
bitflags = { version = "2.11.1", features = [ "serde" ] }
chrono = "0.4.44"
bitflags = { version = "2.13.0", features = [ "serde" ] }
chrono = "0.4.45"
clap = { version = "4.6.1", features = [ "derive" ] }
core-foundation-sys = "0.8.7"
dirs = "6.0.0"
@ -51,6 +51,7 @@ futures = "0.3.32"
globset = "0.4.18"
hashbrown = { version = "0.17.1", features = [ "serde" ] }
indexmap = { version = "2.14.0", features = [ "serde" ] }
inventory = "0.3.24"
libc = "0.2.186"
lru = "0.18.0"
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"
percent-encoding = "2.3.2"
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"
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"
serde = { version = "1.0.228", features = [ "derive" ] }
serde_json = "1.0.150"
serde_with = "3.20.0"
serde_with = "3.21.0"
strum = { version = "0.28.0", features = [ "derive" ] }
syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
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)));
fields.add_field_method_get("times", |_, me| Ok(me.inner.times));
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));

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;
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 }
hashbrown = { workspace = true }
indexmap = { workspace = true }
mlua = { workspace = true }
ratatui = { workspace = true }
regex = { 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 serde::{Deserialize, Deserializer, de};
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 crate::{Mixable, Platform};
use super::{Key, ids::chord_id};
use crate::{Mixable, Platform, keymap::Chords};
static RE: OnceLock<Regex> = OnceLock::new();
#[derive(Clone, Debug, Default, Deserialize)]
pub struct Chord<const L: u8 = { Layer::App as u8 }> {
#[derive(Debug, Default, Deserialize)]
pub struct Chord<const L: u8 = { Layer::Null as u8 }> {
#[serde(skip, default = "chord_id")]
pub id: Id,
#[serde(deserialize_with = "deserialize_on")]
pub on: Vec<Key>,
#[serde(deserialize_with = "deserialize_run::<L, _>")]
@ -22,6 +24,18 @@ pub struct Chord<const L: u8 = { Layer::App as u8 }> {
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> {
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> {
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 run(&self) -> String {
@ -81,12 +99,12 @@ fn deserialize_run<'de, const L: u8, D>(deserializer: D) -> Result<Vec<Action>,
where
D: Deserializer<'de>,
{
let mut actions: Vec<Action> = OneOrMany::<DisplayFromStr>::deserialize_as(deserializer)?;
let Some(layer) = Layer::from_repr(L) else {
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 {
action.source = Source::Key;
if action.layer == Layer::Null {
@ -96,3 +114,54 @@ where
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 super::Chord;
#[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>),
Borrowed(&'static Chord<L>),
Borrowed(Arc<Chord<L>>),
}
impl<const L: u8> From<Chord<L>> for ChordCow<L> {
fn from(c: Chord<L>) -> Self { Self::Owned(c) }
}
impl<const L: u8> From<&'static Chord<L>> for ChordCow<L> {
fn from(c: &'static 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) }
}
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> {
@ -37,7 +41,7 @@ impl<const L: u8> ChordCow<L> {
pub fn into_seq(self) -> Vec<ActionCow> {
match self {
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 anyhow::bail;
use serde::{Deserialize, Serialize};
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 {
#[serde(flatten)]
pub code: KeyCode,
pub shift: bool,
pub ctrl: bool,
pub alt: bool,
#[serde(rename = "super")]
pub super_: bool,
}

View file

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

View file

@ -1,9 +1,11 @@
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 super::{Offset, Position};
use crate::{YAZI, popup::Origin};
// --- InputCfg
#[derive(Clone, Debug, Default)]
pub struct InputCfg {
pub title: String,
@ -15,20 +17,7 @@ pub struct InputCfg {
pub completion: bool,
}
#[derive(Clone, Debug, Default)]
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_data_any!(InputCfg);
impl InputCfg {
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 {
fn new(
title: String,
@ -188,21 +216,3 @@ impl ConfirmCfg {
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 serde::Deserialize;
use strum::EnumString;
use yazi_macro::impl_data_any;
use yazi_scheduler::plugin::PluginInEntry;
use yazi_shared::{SStr, data::{Data, DataKey}, event::{ActionCow, Cmd}};
#[derive(Clone, Debug, Default)]
pub struct PluginOpt {
pub id: SStr,
@ -17,6 +17,8 @@ pub struct PluginOpt {
pub callback: Option<Box<dyn PluginCallback>>,
}
impl_data_any!(PluginOpt);
impl TryFrom<ActionCow> for PluginOpt {
type Error = anyhow::Error;

View file

@ -1,6 +1,7 @@
use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value};
use serde::{Deserialize, Serialize};
use yazi_binding::SER_OPT;
use yazi_macro::impl_data_any;
use yazi_shared::{event::ActionCow, strand::StrandBuf};
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
@ -13,6 +14,8 @@ pub struct QuitOpt {
pub no_cwd_file: bool,
}
impl_data_any!(QuitOpt);
impl TryFrom<ActionCow> for QuitOpt {
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 crate::cmp::CmpItem;
@ -9,3 +10,5 @@ pub struct CmpOpt {
pub word: PathBufDyn,
pub ticket: Id,
}
impl_data_any!(CmpOpt);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,10 @@
use anyhow::bail;
use serde::Deserialize;
use strum::{EnumString, IntoStaticStr};
use yazi_macro::impl_data_any;
use yazi_shared::{SStr, event::ActionCow, url::{UrlBuf, UrlLike}};
// --- SearchOpt
#[derive(Clone, Debug)]
pub struct SearchOpt {
pub via: SearchVia,
@ -12,6 +14,8 @@ pub struct SearchOpt {
pub r#in: Option<UrlBuf>,
}
impl_data_any!(SearchOpt);
impl TryFrom<ActionCow> for SearchOpt {
type Error = anyhow::Error;
@ -37,7 +41,7 @@ impl TryFrom<ActionCow> for SearchOpt {
}
}
// Via
// --- Via
#[derive(Clone, Copy, Debug, Deserialize, EnumString, Eq, IntoStaticStr, PartialEq)]
#[serde(rename_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_with::{DurationSecondsWithFrac, serde_as};
use yazi_binding::SER_OPT;
use yazi_macro::impl_data_any;
use yazi_shared::event::ActionCow;
use crate::notify::MessageLevel;
@ -21,6 +22,8 @@ pub struct MessageOpt {
pub timeout: Duration,
}
impl_data_any!(MessageOpt);
impl TryFrom<ActionCow> for MessageOpt {
type Error = anyhow::Error;

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,7 @@ use yazi_macro::{emit, render_and};
#[derive(Default)]
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 cands: Vec<ChordCow>,

View file

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

View file

@ -1,9 +1,10 @@
use std::borrow::Cow;
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 yazi_binding::get_metatable;
use yazi_macro::impl_data_any;
use yazi_shared::url::UrlBufCov;
use super::Ember;
@ -19,6 +20,8 @@ pub struct EmberYank<'a> {
pub urls: Cow<'a, IndexSet<UrlBufCov>>,
}
impl_data_any!(EmberYank<'static>, from_into_lua = inherit);
impl<'a> EmberYank<'a> {
pub fn borrowed(cut: bool, urls: &'a IndexSet<UrlBufCov>) -> Ember<'a> {
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) }
}
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<'_> {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
let len = self.urls.len();

View file

@ -3,7 +3,7 @@ use std::{fmt::Display, io::Write, str::FromStr};
use anyhow::{Result, anyhow};
use mlua::{IntoLua, Lua, Value};
use yazi_boot::BOOT;
use yazi_macro::{emit, relay};
use yazi_macro::{emit, impl_data_any, relay};
use yazi_shared::{Id, event::ActionCow};
use crate::{ID, ember::Ember};
@ -15,6 +15,8 @@ pub struct Payload<'a> {
pub body: Ember<'a>,
}
impl_data_any!(Payload<'static>);
impl<'a> Payload<'a> {
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 mlua::{ExternalError, IntoLua, Lua, MultiValue, Table, Value};
use ordered_float::OrderedFloat;
use yazi_shared::{data::{Data, DataKey}, replace_cow};
use crate::ember;
use yazi_shared::{any_data::AnyData, data::{Data, DataInventory, DataKey}, replace_cow};
pub struct Sendable;
impl Sendable {
pub fn value_to_data(lua: &Lua, value: Value) -> mlua::Result<Data> {
Ok(match value {
Value::Nil => Data::Nil,
Value::Boolean(b) => Data::Boolean(b),
Value::LightUserData(_) => Err("light userdata is not supported".into_lua_err())?,
Value::Integer(i) => Data::Integer(i),
Value::Number(n) => Data::Number(n),
match &value {
Value::Nil => return Ok(Data::Nil),
Value::Boolean(b) => return Ok(Data::Boolean(*b)),
Value::Integer(i) => return Ok(Data::Integer(*i)),
Value::Number(n) => return Ok(Data::Number(*n)),
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())
} else {
Data::Bytes(b.as_bytes().to_owned())
}
});
}
Value::Table(t) => {
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)?);
}
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())
} else {
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() {
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>() => {
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>() => {
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>() => {
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())?,
Some(t) if t == TypeId::of::<AnyData>() => return Ok(Data::Any(ud.take::<AnyData>()?.0)),
_ => {}
},
Value::Error(_) => Err("error is not supported".into_lua_err())?,
Value::Other(..) => Err("unknown data is not supported".into_lua_err())?,
})
Value::LightUserData(_) => {}
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> {
@ -91,23 +90,7 @@ impl Sendable {
Data::Url(u) => yazi_binding::Url::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::Any(b) => {
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())?
}
Data::Any(a) => a.into_lua(lua)?,
_ => 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::Path(u) => yazi_binding::Path::new(u).into_lua(lua)?,
Data::Bytes(b) => Value::String(lua.create_string(b)?),
Data::Any(b) => {
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())?
}
Data::Any(a) => a.to_lua(lua)?,
})
}

View file

@ -12,7 +12,7 @@ impl<'a> Help<'a> {
pub fn new(core: &'a Core) -> Self { Self { core } }
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()),
None => String::new(),
}

View file

@ -36,7 +36,8 @@ impl<'a> Router<'a> {
}
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 {
continue;
}

View file

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

View file

@ -1,7 +1,7 @@
use std::path::Path;
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 super::File;
@ -23,6 +23,8 @@ pub enum FilesOp {
Upserting(UrlBuf, HashMap<PathBufDyn, File>),
}
impl_data_any!(FilesOp);
impl FilesOp {
#[inline]
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 confirm;
mod context;
mod data;
mod event;
mod fmt;
mod fs;

View file

@ -20,6 +20,7 @@ vendored-lua = [ "mlua/vendored" ]
yazi-adapter = { path = "../yazi-adapter", version = "26.5.6" }
yazi-binding = { path = "../yazi-binding", 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-core = { path = "../yazi-core", 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 }
anyhow = { workspace = true }
futures = { workspace = true }
inventory = { workspace = true }
mlua = { workspace = true }
paste = { workspace = true }
percent-encoding = { workspace = true }
ratatui = { 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_codegen::FromLuaOwned;
use yazi_macro::impl_data_any;
#[derive(Clone, FromLuaOwned)]
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 {
pub(super) fn part(_: &Lua, t: Table) -> mlua::Result<Self> {
let id: Id = t.raw_get("id")?;
@ -41,8 +54,4 @@ impl FilesOp {
}
}
impl IntoLua for FilesOp {
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
lua.create_any_userdata(self.0)?.into_lua(lua)
}
}
impl UserData for FilesOp {}

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);

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("fs", crate::fs::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)?)?;
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("ps", crate::pubsub::compose())?;
globals.raw_set("rt", crate::runtime::compose())?;
globals.raw_set("km", crate::keymap::compose())?;
globals.raw_set("th", crate::theme::compose())?;
yazi_binding::Error::install(lua)?;

View file

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

View file

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

View file

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

View file

@ -23,7 +23,9 @@ dyn-clone = { workspace = true }
foldhash = { workspace = true }
futures = { workspace = true }
hashbrown = { workspace = true }
inventory = { workspace = true }
memchr = "2.8.1"
mlua = { workspace = true }
ordered-float = { workspace = true }
parking_lot = { 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 tokio::sync::Notify;
use yazi_macro::impl_data_any;
#[derive(Clone, Debug, Default)]
pub struct CompletionToken {
inner: Arc<(AtomicU8, Notify)>,
}
impl_data_any!(CompletionToken);
impl CompletionToken {
pub fn complete(&self, success: bool) {
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 mlua::{IntoLua, Lua, Value};
use crate::any_data::AnyData;
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
where
T: Any + Send + Sync + DynClone,
{
fn as_any(&self) -> &dyn Any { self }
impl dyn DataAny {
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.as_any(TypeId::of::<T>()).and_then(|a| a.downcast_ref::<T>())
}
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> {
@ -26,3 +45,27 @@ impl Debug for dyn DataAny {
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> {
match self {
Self::Any(b) => (**b).as_any().downcast_ref::<T>(),
Self::Any(a) => a.downcast_ref::<T>(),
_ => None,
}
}
@ -271,15 +271,15 @@ impl Data {
pub fn into_any<T: 'static>(self) -> Option<T> {
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,
}
}
// FIXME: find a better name
pub fn into_any2<T: 'static>(self) -> Result<T> {
if let Self::Any(b) = self
&& let Ok(t) = b.into_any().downcast::<T>()
if let Self::Any(a) = self
&& let Ok(t) = a.downcast::<T>()
{
Ok(*t)
} 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]
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);

View file

@ -2,6 +2,7 @@ use std::{borrow::Cow, fmt::{Debug, Formatter}, hash::{Hash, Hasher}, path::{Pat
use anyhow::Result;
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}};
@ -13,6 +14,8 @@ pub enum UrlBuf {
Sftp { loc: LocBuf<typed_path::UnixPathBuf>, domain: Symbol<str> },
}
impl_data_any!(UrlBuf);
// FIXME: remove
impl Default for UrlBuf {
fn default() -> Self { Self::Regular(Default::default()) }

View file

@ -33,4 +33,4 @@ twox-hash = { workspace = true }
unicode-width = { workspace = true }
[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 {}
// --- IntoLua
// --- LuaTableExt
pub trait LuaTableExt {
fn sequence_iter<V: FromLua>(&self, lua: &Lua) -> SequenceIter<V>;
}

View file

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

View file

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