From dbb0cc0d556d73935ebdc1e3d61a2a8aa27059d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Wed, 8 Jul 2026 17:04:12 +0800 Subject: [PATCH] feat: experimental `%y`, `%Y`, `%t`, `%T`, `%yN`, `%YN`, `%tN`, `%TN` shell formatting parameters (#4108) --- CHANGELOG.md | 2 + Cargo.lock | 171 +++++++++++--------- Cargo.toml | 5 +- flake.lock | 12 +- yazi-actor/src/app/reflow.rs | 4 +- yazi-actor/src/context.rs | 3 + yazi-actor/src/lives/core.rs | 4 +- yazi-actor/src/mgr/bulk_create.rs | 17 +- yazi-actor/src/mgr/bulk_rename.rs | 15 +- yazi-actor/src/mgr/copy.rs | 2 +- yazi-actor/src/mgr/hover.rs | 2 +- yazi-actor/src/mgr/open.rs | 2 +- yazi-actor/src/mgr/open_do.rs | 50 +++--- yazi-actor/src/mgr/shell.rs | 12 +- yazi-actor/src/mgr/update_peeked.rs | 2 +- yazi-actor/src/mgr/update_spotted.rs | 2 +- yazi-actor/src/tasks/mod.rs | 2 +- yazi-actor/src/tasks/open_shell_compat.rs | 19 --- yazi-binding/src/chan.rs | 20 +-- yazi-binding/src/composer.rs | 6 +- yazi-binding/src/elements/color.rs | 4 +- yazi-binding/src/elements/constraint.rs | 4 +- yazi-binding/src/elements/line.rs | 4 +- yazi-binding/src/elements/text.rs | 4 +- yazi-binding/src/error.rs | 6 +- yazi-binding/src/permit.rs | 2 +- yazi-binding/src/position/lua.rs | 4 +- yazi-binding/src/process/child.rs | 8 +- yazi-binding/src/process/command.rs | 21 ++- yazi-codegen/src/lib.rs | 12 +- yazi-config/src/keymap/key.rs | 13 +- yazi-config/src/opener/opener.rs | 31 ++-- yazi-config/src/opener/opener_rule.rs | 12 +- yazi-config/src/theme/custom_section_arc.rs | 4 +- yazi-core/src/help/help.rs | 2 +- yazi-core/src/mgr/mgr.rs | 34 +--- yazi-core/src/mgr/mod.rs | 2 +- yazi-core/src/mgr/snap.rs | 54 +++++++ yazi-core/src/spot/lock.rs | 8 +- yazi-core/src/tab/folder.rs | 3 + yazi-core/src/tab/mod.rs | 2 +- yazi-core/src/tab/preview_lock.rs | 12 +- yazi-core/src/tab/snap.rs | 17 ++ yazi-core/src/tab/tab.rs | 5 +- yazi-core/src/tasks/mod.rs | 2 +- yazi-core/src/tasks/process.rs | 37 ----- yazi-fm/Cargo.toml | 2 +- yazi-fm/src/executor.rs | 1 - yazi-fm/src/router.rs | 4 +- yazi-fs/src/splatter.rs | 120 ++++++-------- yazi-parser/src/tasks/process_open.rs | 4 +- yazi-plugin/src/fs/fs.rs | 14 +- yazi-plugin/src/fs/op.rs | 4 +- yazi-plugin/src/pubsub/pubsub.rs | 14 +- yazi-plugin/src/tasks/option.rs | 4 +- yazi-plugin/src/ui/utils.rs | 12 +- yazi-plugin/src/utils/app.rs | 4 +- yazi-plugin/src/utils/json.rs | 4 +- yazi-plugin/src/utils/layer.rs | 4 +- yazi-plugin/src/utils/log.rs | 26 +-- yazi-plugin/src/utils/sync.rs | 6 +- yazi-plugin/src/utils/tasks.rs | 4 +- yazi-plugin/src/utils/text.rs | 10 +- yazi-proxy/src/tasks.rs | 25 +-- yazi-runner/src/loader/chunk.rs | 2 +- yazi-runner/src/loader/loader.rs | 2 +- yazi-runner/src/loader/require.rs | 8 +- yazi-scheduler/src/process/in.rs | 23 ++- yazi-scheduler/src/process/mod.rs | 2 +- yazi-scheduler/src/process/option.rs | 18 --- yazi-scheduler/src/process/process.rs | 10 +- yazi-scheduler/src/process/shell.rs | 34 ++-- yazi-scheduler/src/scheduler.rs | 18 +-- yazi-shared/Cargo.toml | 2 +- yazi-shared/src/any_data.rs | 4 +- yazi-shared/src/id/lua.rs | 12 +- yazi-shared/src/layer.rs | 4 +- yazi-shared/src/path/lua.rs | 14 +- yazi-shared/src/url/lua.rs | 16 +- yazi-shim/src/mlua/ser.rs | 2 +- yazi-shim/src/mlua/string.rs | 8 +- yazi-term/Cargo.toml | 1 + yazi-term/src/event/keyboard.rs | 27 ++-- yazi-term/src/parser/csi.rs | 27 +++- yazi-tty/src/sequence/query.rs | 7 - yazi-tui/src/raterm.rs | 22 +-- yazi-tui/src/state.rs | 13 +- yazi-vfs/src/provider/lua.rs | 4 +- yazi-widgets/src/input/actor/type.rs | 9 +- 89 files changed, 561 insertions(+), 649 deletions(-) delete mode 100644 yazi-actor/src/tasks/open_shell_compat.rs create mode 100644 yazi-core/src/mgr/snap.rs create mode 100644 yazi-core/src/tab/snap.rs delete mode 100644 yazi-core/src/tasks/process.rs delete mode 100644 yazi-scheduler/src/process/option.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a64090e..15e56815 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Bulk create ([#3793]) - Make help menu a command palette ([#4074]) - Input history ([#4104]) +- Experimental `%y`, `%Y`, `%t`, `%T`, `%yN`, `%YN`, `%tN`, `%TN` shell formatting parameters ([#4108]) - Make visual mode support wraparound scrolling ([#4101]) - H/M/L Vim-like motion for moving cursor relative to viewport ([#3970]) - Context-aware icons for inputs ([#4080]) @@ -1773,3 +1774,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#4096]: https://github.com/sxyazi/yazi/pull/4096 [#4101]: https://github.com/sxyazi/yazi/pull/4101 [#4104]: https://github.com/sxyazi/yazi/pull/4104 +[#4108]: https://github.com/sxyazi/yazi/pull/4108 diff --git a/Cargo.lock b/Cargo.lock index cec37e11..dba80645 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -543,9 +543,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" dependencies = [ "find-msvc-tools", "jobserver", @@ -780,18 +780,18 @@ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -799,18 +799,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crunchy" @@ -1194,14 +1194,16 @@ dependencies = [ [[package]] name = "exr" -version = "1.74.0" +version = "1.74.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +checksum = "6be87932f10230a4339ab394edd8e4611fcb72553d8295b4d52ea55249b3daa5" dependencies = [ "bit_field", "half", "lebe", "miniz_oxide", + "num-complex", + "pulp", "smallvec", "zune-inflate", ] @@ -1695,9 +1697,9 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1" +checksum = "dd854a95a4ac672fed8c054136039fd32c22cf039ff09ead7280afe920486483" dependencies = [ "bitflags", "inotify-sys", @@ -1706,9 +1708,9 @@ dependencies = [ [[package]] name = "inotify-sys" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea94e891b3606826e9c998be69ddca42247dad8ad50b1649a5cb7e1c9ae06fd" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" dependencies = [ "libc", ] @@ -1791,11 +1793,11 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] @@ -1954,18 +1956,18 @@ dependencies = [ [[package]] name = "lua-src" -version = "550.0.0" +version = "550.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e836dc8ae16806c9bdcf42003a88da27d163433e3f9684c52f0301258004a4fb" +checksum = "75c110c2fa33f34e0de05448e1f3eb2e0631e7a69e2d8ae1586cffc9fc9f9949" dependencies = [ "cc", ] [[package]] name = "luajit-src" -version = "210.6.6+707c12b" +version = "210.7.2+b925b3e" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86cc925d4053d0526ae7f5bc765dbd0d7a5d1a63d43974f4966cb349ca63295" +checksum = "920cf654b23d217c550ceea57c32cd2a413ea27b6d47ed77b5ee0cf655adefa6" dependencies = [ "cc", "which", @@ -1997,9 +1999,9 @@ checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memoffset" @@ -2048,31 +2050,31 @@ dependencies = [ [[package]] name = "mlua" -version = "0.11.6" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd36acfa49ce6ee56d1307a061dd302c564eee757e6e4cd67eb4f7204846fab" +checksum = "ad72ffa037cf5970c9860674f32f703fda25d86cf217475fe7a79c5f9961bcaa" dependencies = [ "anyhow", "bstr", "either", "erased-serde", "futures-util", + "inventory", "libc", "mlua-sys", "mlua_derive", "num-traits", "parking_lot", "rustc-hash", - "rustversion", "serde", "serde-value", ] [[package]] name = "mlua-sys" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1c3a7fc7580227ece249fd90aa2fa3b39eb2b49d3aec5e103b3e85f2c3dfc8" +checksum = "92136787b906d4e55cfe96cd6c62e010bb1a56889d0d6cf83eb016dbad07576b" dependencies = [ "cc", "cfg-if", @@ -2084,16 +2086,12 @@ dependencies = [ [[package]] name = "mlua_derive" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465bddde514c4eb3b50b543250e97c1d4b284fa3ef7dc0ba2992c77545dbceb2" +checksum = "0232231813b214d44b57c7678e7dbbcfaac35bd1a01acef0dad7c5dfdc1b1973" dependencies = [ - "itertools", - "once_cell", - "proc-macro-error2", "proc-macro2", "quote", - "regex", "syn", ] @@ -2206,6 +2204,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "bytemuck", + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.2" @@ -2694,28 +2702,6 @@ dependencies = [ "wnaf", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -2745,10 +2731,33 @@ dependencies = [ ] [[package]] -name = "pxfm" -version = "0.1.29" +name = "pulp" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" +checksum = "046aa45b989642ec2e4717c8e72d677b13edd831a4d3b6cf37d9a3e54912496a" +dependencies = [ + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "paste", + "pulp-wasm-simd-flag", + "raw-cpuid", + "reborrow", + "version_check", +] + +[[package]] +name = "pulp-wasm-simd-flag" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8f70e07b9c3962945a74e59ca1c511bba65b6419468acc217c457d93f3c740" + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" [[package]] name = "qoi" @@ -2973,6 +2982,15 @@ dependencies = [ "rgb", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags", +] + [[package]] name = "rayon" version = "1.12.0" @@ -2993,6 +3011,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "reborrow" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -3113,9 +3137,9 @@ dependencies = [ [[package]] name = "russh" -version = "0.62.1" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20d2039c3e50abb1ec3bdadea27098679da12a33befa9ab4e62adad9a13ebcb2" +checksum = "ca6c3c1dd0a9ad3a9915a2f6e907d158f9a7e9ac32ddc772b003faafc027d9a8" dependencies = [ "aes", "bitflags", @@ -3242,9 +3266,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" @@ -3813,9 +3837,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" +version = "0.7.1+5.3.1-0-g81034ce1f1373e37dc865038e1bc8eeecf559ce8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" +checksum = "1a2825c78386b4ae0314074867860ba9577875de945f05992c38815cbec327f0" dependencies = [ "cc", "libc", @@ -3823,9 +3847,9 @@ dependencies = [ [[package]] name = "tikv-jemallocator" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" +checksum = "249f09e49ab1609436f34c776e84231bead18d6a955f119f939bdc1d847561bd" dependencies = [ "libc", "tikv-jemalloc-sys", @@ -5287,6 +5311,7 @@ dependencies = [ "anyhow", "base64", "bitflags", + "compact_str", "futures", "mlua", "parking_lot", @@ -5421,18 +5446,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 2778deef..a7a57091 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ base64 = "0.22.1" bitflags = { version = "2.13.0", features = [ "serde" ] } chrono = "0.4.45" clap = { version = "4.6.1", features = [ "derive" ] } +compact_str = { version = "0.9.1", features = [ "serde" ] } core-foundation-sys = "0.8.7" dirs = "6.0.0" dyn-clone = "1.0.20" @@ -55,7 +56,7 @@ 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" ] } +mlua = { version = "0.12.0", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] } objc2 = "0.6.4" ordered-float = { version = "5.3.0", features = [ "serde" ] } parking_lot = "0.12.5" @@ -65,7 +66,7 @@ rand = { version = "0.10.2", default-features = false, features = ratatui-core = { version = "0.1.2", default-features = false, features = [ "std", "layout-cache", "serde", "underline-color" ] } ratatui-widgets = { version = "0.3.2", default-features = false, features = [ "std", "unstable-rendered-line-info" ] } regex = "1.12.4" -russh = { version = "0.62.1", default-features = false, features = [ "ring", "rsa" ] } +russh = { version = "0.62.2", default-features = false, features = [ "ring", "rsa" ] } scopeguard = "1.2.0" serde = { version = "1.0.228", features = [ "derive" ] } serde_json = "1.0.150" diff --git a/flake.lock b/flake.lock index 052f6d36..f1c51f5c 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1779877693, - "narHash": "sha256-NOF9NAREhxr50bbBfVcVOq+ArCMSoe8dP79Pk2uyARk=", + "lastModified": 1783279667, + "narHash": "sha256-/NAkDSsve+GNM0Bt6tleJdCGfsTlK89nPjkVOzZMo0s=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4100e830e085863741bc69b156ec4ccd53ab5be0", + "rev": "f205b5574fd0cb7da5b702a2da51507b7f4fdd1b", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1779851998, - "narHash": "sha256-UkkMh3bX9QW4Luqkm98nUaOqKWrU6i65mUnph3WeSSw=", + "lastModified": 1783320166, + "narHash": "sha256-l7C/OsjcnWDOk2K3ssj+SBduwL67LashjBqis9+t468=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "6cddd512fa2bf7231f098d3a2f92f6e4cff71e0a", + "rev": "20ee15370c9256669d66968b89ee20a4b0a4e673", "type": "github" }, "original": { diff --git a/yazi-actor/src/app/reflow.rs b/yazi-actor/src/app/reflow.rs index cfb55e3b..9b911c0d 100644 --- a/yazi-actor/src/app/reflow.rs +++ b/yazi-actor/src/app/reflow.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use mlua::Value; +use mlua::{LuaString, Value}; use ratatui_core::layout::Position; use tracing::error; use yazi_actor::lives::Lives; @@ -30,7 +30,7 @@ impl Actor for Reflow { continue; }; - let id: mlua::String = t.get("_id")?; + let id: LuaString = t.get("_id")?; match &*id.as_bytes() { b"current" => layout.current = *t.raw_get::("_area")?, b"preview" => layout.preview = *t.raw_get::("_area")?, diff --git a/yazi-actor/src/context.rs b/yazi-actor/src/context.rs index fbdc2863..16d92db7 100644 --- a/yazi-actor/src/context.rs +++ b/yazi-actor/src/context.rs @@ -108,6 +108,9 @@ impl<'a> Ctx<'a> { #[inline] pub fn hovered(&self) -> Option<&File> { self.tab().hovered() } + #[inline] + pub fn hovered_url(&self) -> Option<&UrlBuf> { self.tab().hovered_url() } + #[inline] pub fn hovered_folder(&self) -> Option<&Folder> { self.tab().hovered_folder() } diff --git a/yazi-actor/src/lives/core.rs b/yazi-actor/src/lives/core.rs index 666a219b..92e07589 100644 --- a/yazi-actor/src/lives/core.rs +++ b/yazi-actor/src/lives/core.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -use mlua::{AnyUserData, IntoLua, MetaMethod, UserData, UserDataMethods, UserDataRef, Value}; +use mlua::{AnyUserData, IntoLua, LuaString, MetaMethod, UserData, UserDataMethods, UserDataRef, Value}; use paste::paste; use super::{Lives, PtrCell}; @@ -43,7 +43,7 @@ impl Core { impl UserData for Core { fn add_methods>(methods: &mut M) { - methods.add_meta_method_mut(MetaMethod::Index, |lua, me, key: mlua::String| { + methods.add_meta_method_mut(MetaMethod::Index, |lua, me, key: LuaString| { macro_rules! reuse { ($key:ident, $value:expr) => { match paste! { &me.[] } { diff --git a/yazi-actor/src/mgr/bulk_create.rs b/yazi-actor/src/mgr/bulk_create.rs index e96f59c3..ba294eb3 100644 --- a/yazi-actor/src/mgr/bulk_create.rs +++ b/yazi-actor/src/mgr/bulk_create.rs @@ -8,8 +8,8 @@ use yazi_fs::{FilesOp, Splatter, file::File, provider::{Provider, local::Local}} use yazi_macro::{succ, writef}; use yazi_parser::VoidForm; use yazi_proxy::TasksProxy; -use yazi_scheduler::{AppProxy, NotifyProxy}; -use yazi_shared::{data::Data, strand::Strand, url::{AsUrl, UrlBuf, UrlCow, UrlLike}}; +use yazi_scheduler::{AppProxy, NotifyProxy, process::ShellOpt}; +use yazi_shared::{data::Data, strand::Strand, url::{AsUrl, UrlBuf, UrlLike}}; use yazi_shim::path::CROSS_SEPARATOR; use yazi_term::YIELD_TO_SUBPROCESS; use yazi_tty::{TTY, sequence::EraseScreen}; @@ -40,13 +40,12 @@ impl Actor for BulkCreate { }); } - TasksProxy::process_exec( - cwd.clone(), - Splatter::new(&[UrlCow::default(), tmp.as_url().into()]).splat(&opener.run), - vec![UrlCow::default(), UrlBuf::from(&tmp).into()], - opener.block, - opener.orphan, - ) + TasksProxy::process_exec(ShellOpt { + cwd: cwd.clone(), + cmd: Splatter::new(&[tmp.as_url()]).splat(&opener.run), + block: opener.block, + orphan: opener.orphan, + }) .await; let _permit = Permit::new(YIELD_TO_SUBPROCESS.acquire().await.unwrap(), AppProxy::resume()); diff --git a/yazi-actor/src/mgr/bulk_rename.rs b/yazi-actor/src/mgr/bulk_rename.rs index ba001880..4f42978a 100644 --- a/yazi-actor/src/mgr/bulk_rename.rs +++ b/yazi-actor/src/mgr/bulk_rename.rs @@ -11,8 +11,8 @@ use yazi_fs::{FilesOp, Splatter, file::File, max_common_root, path::skip_url, pr use yazi_macro::{err, succ, writef}; use yazi_parser::VoidForm; use yazi_proxy::TasksProxy; -use yazi_scheduler::{AppProxy, NotifyProxy}; -use yazi_shared::{data::Data, path::PathDyn, strand::{AsStrand, AsStrandJoin, Strand, StrandBuf, StrandLike}, url::{AsUrl, UrlBuf, UrlCow, UrlLike}}; +use yazi_scheduler::{AppProxy, NotifyProxy, process::ShellOpt}; +use yazi_shared::{data::Data, path::PathDyn, strand::{AsStrand, AsStrandJoin, Strand, StrandBuf, StrandLike}, url::{AsUrl, UrlBuf, UrlLike}}; use yazi_term::YIELD_TO_SUBPROCESS; use yazi_tty::{TTY, sequence::EraseScreen}; use yazi_vfs::{VfsFile, maybe_exists, provider}; @@ -63,13 +63,12 @@ impl Actor for BulkRename { } batcher.prime(&tmp); - TasksProxy::process_exec( + TasksProxy::process_exec(ShellOpt { cwd, - Splatter::new(&[UrlCow::default(), tmp.as_url().into()]).splat(&opener.run), - vec![UrlCow::default(), UrlBuf::from(&tmp).into()], - opener.block, - opener.orphan, - ) + cmd: Splatter::new(&[tmp.as_url()]).splat(&opener.run), + block: opener.block, + orphan: opener.orphan, + }) .await; let _permit = Permit::new(YIELD_TO_SUBPROCESS.acquire().await.unwrap(), AppProxy::resume()); diff --git a/yazi-actor/src/mgr/copy.rs b/yazi-actor/src/mgr/copy.rs index 8361da5a..90e5beb2 100644 --- a/yazi-actor/src/mgr/copy.rs +++ b/yazi-actor/src/mgr/copy.rs @@ -18,7 +18,7 @@ impl Actor for Copy { let mut s = Vec::::new(); let mut it = if form.hovered { - Box::new(cx.hovered().map(|h| &h.url).into_iter()) + Box::new(cx.hovered_url().into_iter()) } else { cx.tab().selected_or_hovered_urls() } diff --git a/yazi-actor/src/mgr/hover.rs b/yazi-actor/src/mgr/hover.rs index ecfab58f..b702d837 100644 --- a/yazi-actor/src/mgr/hover.rs +++ b/yazi-actor/src/mgr/hover.rs @@ -36,7 +36,7 @@ impl Actor for Hover { // Publish through DDS let tab = tab!(cx); - err!(Pubsub::pub_after_hover(tab.id, tab.hovered().map(|h| &h.url))); + err!(Pubsub::pub_after_hover(tab.id, tab.hovered_url())); succ!(); } } diff --git a/yazi-actor/src/mgr/open.rs b/yazi-actor/src/mgr/open.rs index 03daa337..492abb61 100644 --- a/yazi-actor/src/mgr/open.rs +++ b/yazi-actor/src/mgr/open.rs @@ -24,7 +24,7 @@ impl Actor for Open { succ!(if !opt.targets.is_empty() { Quit::with_selected(opt.targets) } else if opt.hovered { - Quit::with_selected(cx.hovered().map(|h| &h.url)) + Quit::with_selected(cx.hovered_url()) } else { act!(mgr:escape_visual, cx)?; Quit::with_selected(cx.tab().selected_or_hovered_urls()) diff --git a/yazi-actor/src/mgr/open_do.rs b/yazi-actor/src/mgr/open_do.rs index 253151fb..db6232bd 100644 --- a/yazi-actor/src/mgr/open_do.rs +++ b/yazi-actor/src/mgr/open_do.rs @@ -1,13 +1,13 @@ use anyhow::Result; use hashbrown::HashMap; use indexmap::IndexSet; -use yazi_config::YAZI; -use yazi_fs::file::File; +use yazi_config::{YAZI, opener::OpenerRule}; +use yazi_fs::{Splatter, file::File}; use yazi_macro::succ; use yazi_parser::mgr::OpenDoForm; use yazi_proxy::{PickProxy, TasksProxy}; -use yazi_scheduler::process::ProcessOpt; -use yazi_shared::{data::Data, url::{UrlBuf, UrlCow}}; +use yazi_scheduler::process::ShellOpt; +use yazi_shared::{data::Data, url::UrlBuf}; use crate::{Actor, Ctx}; @@ -32,7 +32,7 @@ impl Actor for OpenDo { if targets.is_empty() { succ!(); } else if !opt.interactive { - succ!(Self::match_and_open(cx, opt.cwd, targets)); + succ!(Self::match_and_open(opt.cwd, targets)); } let openers: IndexSet<_> = @@ -42,20 +42,10 @@ impl Actor for OpenDo { } let pick = PickProxy::show(YAZI.pick.open(openers.iter().map(|o| o.desc()).collect())); - let urls: Vec<_> = [UrlCow::default()] - .into_iter() - .chain(targets.into_iter().map(|(file, _)| file.url.into())) - .collect(); + let urls: Vec<_> = targets.into_iter().map(|(file, _)| file.url).collect(); tokio::spawn(async move { if let Some(choice) = pick.await { - TasksProxy::open_shell_compat(ProcessOpt { - cwd: opt.cwd, - cmd: openers[choice].run.clone().into(), - args: urls, - block: openers[choice].block, - orphan: openers[choice].orphan, - spread: openers[choice].spread, - }); + Self::open_with(&openers[choice], &opt.cwd, &urls); } }); succ!(); @@ -63,24 +53,28 @@ impl Actor for OpenDo { } impl OpenDo { - // TODO: remove - fn match_and_open(cx: &Ctx, cwd: UrlBuf, targets: Vec<(File, &str)>) { - let mut openers = HashMap::new(); + fn match_and_open(cwd: UrlBuf, targets: Vec<(File, &str)>) { + let mut openers: HashMap<_, Vec<_>> = Default::default(); for (file, mime) in targets { if let Some(open) = YAZI.open.matches(&file, mime) && let Some(opener) = YAZI.opener.first(&open) { - openers.entry(opener).or_insert_with(|| vec![UrlCow::default()]).push(file.url.into()); + openers.entry(opener).or_default().push(file.url); } } - for (opener, args) in openers { - cx.tasks.open_shell_compat(ProcessOpt { - cwd: cwd.clone(), - cmd: opener.run.clone().into(), - args, - block: opener.block, + for (opener, urls) in openers { + Self::open_with(&opener, &cwd, &urls); + } + } + + fn open_with(opener: &OpenerRule, cwd: &UrlBuf, urls: &[UrlBuf]) { + let size = if opener.spread { urls.len().max(1) } else { 1 }; + for urls in urls.chunks(size) { + TasksProxy::process_open(ShellOpt { + cwd: cwd.clone(), + cmd: Splatter::new(urls).splat(&opener.run), + block: opener.block, orphan: opener.orphan, - spread: opener.spread, }); } } diff --git a/yazi-actor/src/mgr/shell.rs b/yazi-actor/src/mgr/shell.rs index 33fb79c3..96431385 100644 --- a/yazi-actor/src/mgr/shell.rs +++ b/yazi-actor/src/mgr/shell.rs @@ -2,10 +2,12 @@ use std::borrow::Cow; use anyhow::Result; use yazi_config::YAZI; +use yazi_core::mgr::MgrSnap; +use yazi_fs::Splatter; use yazi_macro::{act, input, succ}; use yazi_parser::mgr::ShellForm; use yazi_proxy::TasksProxy; -use yazi_scheduler::process::ProcessOpt; +use yazi_scheduler::process::ShellOpt; use yazi_shared::data::Data; use yazi_widgets::input::InputEvent; @@ -22,7 +24,7 @@ impl Actor for Shell { act!(mgr:escape_visual, cx)?; let cwd = form.cwd.take().unwrap_or_else(|| cx.cwd().clone()); - let selected: Vec<_> = cx.tab().hovered_and_selected().cloned().map(Into::into).collect(); + let snap = MgrSnap::from(&cx.mgr); let input = if form.interactive { Some(input!( @@ -44,13 +46,11 @@ impl Actor for Shell { return; } - TasksProxy::open_shell_compat(ProcessOpt { + TasksProxy::process_open(ShellOpt { cwd, - cmd: form.run.to_string().into(), - args: selected, + cmd: Splatter::new(snap).splat(&*form.run), block: form.block, orphan: form.orphan, - spread: true, }); }); diff --git a/yazi-actor/src/mgr/update_peeked.rs b/yazi-actor/src/mgr/update_peeked.rs index 150ae25e..c93a8964 100644 --- a/yazi-actor/src/mgr/update_peeked.rs +++ b/yazi-actor/src/mgr/update_peeked.rs @@ -13,7 +13,7 @@ impl Actor for UpdatePeeked { const NAME: &str = "update_peeked"; fn act(cx: &mut Ctx, form: Self::Form) -> Result { - let Some(hovered) = cx.hovered().map(|h| &h.url) else { + let Some(hovered) = cx.hovered_url() else { succ!(cx.tab_mut().preview.reset()); }; diff --git a/yazi-actor/src/mgr/update_spotted.rs b/yazi-actor/src/mgr/update_spotted.rs index 4f0f23ad..0bf5f955 100644 --- a/yazi-actor/src/mgr/update_spotted.rs +++ b/yazi-actor/src/mgr/update_spotted.rs @@ -14,7 +14,7 @@ impl Actor for UpdateSpotted { fn act(cx: &mut Ctx, mut form: Self::Form) -> Result { let tab = cx.tab_mut(); - let Some(hovered) = tab.hovered().map(|h| &h.url) else { + let Some(hovered) = tab.hovered_url() else { succ!(tab.spot.reset()); }; diff --git a/yazi-actor/src/tasks/mod.rs b/yazi-actor/src/tasks/mod.rs index 542c41d2..980f8dbe 100644 --- a/yazi-actor/src/tasks/mod.rs +++ b/yazi-actor/src/tasks/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(arrow cancel close inspect open_shell_compat process_open show spawn update_succeed); +yazi_macro::mod_flat!(arrow cancel close inspect process_open show spawn update_succeed); diff --git a/yazi-actor/src/tasks/open_shell_compat.rs b/yazi-actor/src/tasks/open_shell_compat.rs deleted file mode 100644 index ff1fb3c1..00000000 --- a/yazi-actor/src/tasks/open_shell_compat.rs +++ /dev/null @@ -1,19 +0,0 @@ -use anyhow::Result; -use yazi_macro::succ; -use yazi_parser::tasks::ProcessOpenForm; -use yazi_shared::data::Data; - -use crate::{Actor, Ctx}; - -pub struct OpenShellCompat; - -// TODO: remove -impl Actor for OpenShellCompat { - type Form = ProcessOpenForm; - - const NAME: &str = "open_shell_compat"; - - fn act(cx: &mut Ctx, Self::Form { opt, .. }: Self::Form) -> Result { - succ!(cx.tasks.open_shell_compat(opt)); - } -} diff --git a/yazi-binding/src/chan.rs b/yazi-binding/src/chan.rs index afea9dc8..651f0bc2 100644 --- a/yazi-binding/src/chan.rs +++ b/yazi-binding/src/chan.rs @@ -1,4 +1,4 @@ -use mlua::{ExternalError, FromLua, IntoLua, IntoLuaMulti, UserData, UserDataMethods, Value}; +use mlua::{FromLua, IntoLua, IntoLuaMulti, UserData, UserDataMethods, Value}; use yazi_codegen::FromLuaOwned; use crate::Error; @@ -54,16 +54,13 @@ impl UserData for MpscUnboundedRx { } #[derive(FromLuaOwned)] -pub struct OneshotTx(pub Option>); -pub struct OneshotRx(pub Option>); +pub struct OneshotTx(pub tokio::sync::oneshot::Sender); +pub struct OneshotRx(pub tokio::sync::oneshot::Receiver); impl UserData for OneshotTx { fn add_methods>(methods: &mut M) { - methods.add_method_mut("send", |lua, me, value: Value| { - let Some(tx) = me.0.take() else { - return Err("Oneshot sender already used".into_lua_err()); - }; - match tx.send(T::from_lua(value, lua)?) { + methods.add_method_once("send", |lua, me, value: Value| { + match me.0.send(T::from_lua(value, lua)?) { Ok(()) => true.into_lua_multi(lua), Err(_) => (false, Error::custom("Oneshot receiver closed")).into_lua_multi(lua), } @@ -73,11 +70,8 @@ impl UserData for OneshotTx { impl UserData for OneshotRx { fn add_methods>(methods: &mut M) { - methods.add_async_method_mut("recv", |lua, mut me, ()| async move { - let Some(rx) = me.0.take() else { - return Err("Oneshot receiver already used".into_lua_err()); - }; - match rx.await { + methods.add_async_method_once("recv", |lua, me, ()| async move { + match me.0.await { Ok(value) => value.into_lua_multi(&lua), Err(e) => (Value::Nil, Error::custom(e.to_string())).into_lua_multi(&lua), } diff --git a/yazi-binding/src/composer.rs b/yazi-binding/src/composer.rs index e12d237f..4a2e5cc8 100644 --- a/yazi-binding/src/composer.rs +++ b/yazi-binding/src/composer.rs @@ -1,5 +1,5 @@ use hashbrown::HashMap; -use mlua::{Lua, MetaMethod, UserData, UserDataMethods, Value}; +use mlua::{Lua, LuaString, MetaMethod, UserData, UserDataMethods, Value}; pub type ComposerGet = fn(&Lua, &[u8]) -> mlua::Result; pub type ComposerSet = fn(&Lua, &[u8], Value) -> mlua::Result; @@ -24,7 +24,7 @@ where S: Fn(&Lua, &[u8], Value) -> mlua::Result + 'static, { fn add_methods>(methods: &mut M) { - methods.add_meta_method_mut(MetaMethod::Index, |lua, me, key: mlua::String| { + methods.add_meta_method_mut(MetaMethod::Index, |lua, me, key: LuaString| { let key = key.as_bytes(); if let Some(v) = me.cache.get(key.as_ref()) { return Ok(v.clone()); @@ -38,7 +38,7 @@ where methods.add_meta_method_mut( MetaMethod::NewIndex, - |lua, me, (key, value): (mlua::String, Value)| { + |lua, me, (key, value): (LuaString, Value)| { let key = key.as_bytes(); let value = (me.set)(lua, key.as_ref(), value)?; diff --git a/yazi-binding/src/elements/color.rs b/yazi-binding/src/elements/color.rs index a7bf7926..f4900eb5 100644 --- a/yazi-binding/src/elements/color.rs +++ b/yazi-binding/src/elements/color.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, Value}; -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy, Default, UserData)] pub struct Color(pub ratatui_core::style::Color); impl Color { @@ -32,5 +32,3 @@ impl FromLua for Color { })) } } - -impl UserData for Color {} diff --git a/yazi-binding/src/elements/constraint.rs b/yazi-binding/src/elements/constraint.rs index 82fe300c..93d5681f 100644 --- a/yazi-binding/src/elements/constraint.rs +++ b/yazi-binding/src/elements/constraint.rs @@ -1,6 +1,6 @@ use mlua::{FromLua, IntoLua, Lua, UserData, Value}; -#[derive(Clone, Copy, Default, FromLua)] +#[derive(Clone, Copy, Default, FromLua, UserData)] pub struct Constraint(pub(super) ratatui_core::layout::Constraint); impl Constraint { @@ -23,5 +23,3 @@ impl Constraint { impl From for ratatui_core::layout::Constraint { fn from(value: Constraint) -> Self { value.0 } } - -impl UserData for Constraint {} diff --git a/yazi-binding/src/elements/line.rs b/yazi-binding/src/elements/line.rs index 4bcc6048..9a19fbea 100644 --- a/yazi-binding/src/elements/line.rs +++ b/yazi-binding/src/elements/line.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, mem, ops::{Deref, DerefMut}}; use ansi_to_tui::IntoText; -use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, Function, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, Function, IntoLua, Lua, LuaString, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui_core::widgets::Widget; use unicode_width::UnicodeWidthChar; @@ -31,7 +31,7 @@ impl Line { pub fn compose(lua: &Lua) -> mlua::Result { let new = lua.create_function(|_, (_, line): (Table, Self)| Ok(line))?; - let parse = lua.create_function(|_, code: mlua::String| { + let parse = lua.create_function(|_, code: LuaString| { let code = code.as_bytes(); let Some(line) = code.split_inclusive(|&b| b == b'\n').next() else { return Ok(Self::default()); diff --git a/yazi-binding/src/elements/text.rs b/yazi-binding/src/elements/text.rs index fa511d2d..a647d032 100644 --- a/yazi-binding/src/elements/text.rs +++ b/yazi-binding/src/elements/text.rs @@ -1,7 +1,7 @@ use std::{any::TypeId, mem}; use ansi_to_tui::IntoText; -use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, LuaString, MetaMethod, Table, UserData, UserDataMethods, Value}; use ratatui_core::widgets::Widget; use yazi_shim::SStr; @@ -24,7 +24,7 @@ impl Text { pub fn compose(lua: &Lua) -> mlua::Result { let new = lua.create_function(|_, (_, text): (Table, Self)| Ok(text))?; - let parse = lua.create_function(|_, code: mlua::String| { + let parse = lua.create_function(|_, code: LuaString| { Ok(Self { inner: code.as_bytes().into_text().into_lua_err()?, ..Default::default() }) })?; diff --git a/yazi-binding/src/error.rs b/yazi-binding/src/error.rs index ddf5e52b..b13103f6 100644 --- a/yazi-binding/src/error.rs +++ b/yazi-binding/src/error.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, fmt::Display}; -use mlua::{ExternalError, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, Value}; +use mlua::{ExternalError, Lua, LuaString, MetaMethod, UserData, UserDataFields, UserDataMethods, Value}; use yazi_codegen::FromLuaOwned; use yazi_shim::SStr; @@ -19,9 +19,9 @@ impl Error { let fs = lua.create_function(|_, value: Value| { Ok(Self::Fs(match value { Value::Table(t) => yazi_shim::fs::Error::custom( - &t.raw_get::("kind")?.to_str()?, + &t.raw_get::("kind")?.to_str()?, t.raw_get("code")?, - &t.raw_get::("message")?.to_str()?, + &t.raw_get::("message")?.to_str()?, )?, _ => Err("expected a table".into_lua_err())?, })) diff --git a/yazi-binding/src/permit.rs b/yazi-binding/src/permit.rs index 1224108b..55d5ea5f 100644 --- a/yazi-binding/src/permit.rs +++ b/yazi-binding/src/permit.rs @@ -46,6 +46,6 @@ impl Drop for Permit { impl UserData for Permit { fn add_methods>(methods: &mut M) { - methods.add_async_method_mut("drop", |_, mut me, ()| async move { Ok(me.dropping().await) }); + methods.add_async_method_once("drop", |_, mut me, ()| async move { Ok(me.dropping().await) }); } } diff --git a/yazi-binding/src/position/lua.rs b/yazi-binding/src/position/lua.rs index c76ef309..d2ab5400 100644 --- a/yazi-binding/src/position/lua.rs +++ b/yazi-binding/src/position/lua.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, MetaMethod, Table, UserData, UserDataFields, UserDataMethods, Value}; +use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, LuaString, MetaMethod, Table, UserData, UserDataFields, UserDataMethods, Value}; use yazi_shim::strum::IntoStr; use crate::{elements::Pad, position::{Offset, Origin, Position}}; @@ -20,7 +20,7 @@ impl TryFrom for Position { fn try_from(t: Table) -> Result { Ok(Self { - origin: Origin::from_str(&t.raw_get::(1)?.to_str()?).into_lua_err()?, + origin: Origin::from_str(&t.raw_get::(1)?.to_str()?).into_lua_err()?, offset: Offset { x: t.raw_get("x").unwrap_or_default(), y: t.raw_get("y").unwrap_or_default(), diff --git a/yazi-binding/src/process/child.rs b/yazi-binding/src/process/child.rs index b0550167..e4226408 100644 --- a/yazi-binding/src/process/child.rs +++ b/yazi-binding/src/process/child.rs @@ -1,7 +1,7 @@ use std::{ops::DerefMut, process::ExitStatus, time::Duration}; use futures::future::try_join3; -use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Table, UserData, UserDataMethods, Value}; +use mlua::{ExternalError, IntoLua, IntoLuaMulti, LuaString, Table, UserData, UserDataMethods, Value}; use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, process::{ChildStderr, ChildStdin, ChildStdout}, select}; use super::Status; @@ -133,7 +133,7 @@ impl UserData for Child { } }); - methods.add_async_method_mut("write_all", |lua, mut me, src: mlua::String| async move { + methods.add_async_method_mut("write_all", |lua, mut me, src: LuaString| async move { let Some(stdin) = &mut me.stdin else { return Err("stdin is not piped".into_lua_err()); }; @@ -158,8 +158,8 @@ impl UserData for Child { Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } }); - methods.add_async_function("wait_with_output", |lua, ud: AnyUserData| async move { - match ud.take::()?.wait_with_output().await { + methods.add_async_method_once("wait_with_output", |lua, me, ()| async move { + match me.wait_with_output().await { Ok(output) => Output::new(output).into_lua_multi(&lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } diff --git a/yazi-binding/src/process/command.rs b/yazi-binding/src/process/command.rs index 4949b266..21f2dd75 100644 --- a/yazi-binding/src/process/command.rs +++ b/yazi-binding/src/process/command.rs @@ -1,6 +1,6 @@ use std::{any::TypeId, ffi::OsStr, io, process::Stdio}; -use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; +use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Lua, LuaString, MetaMethod, Table, UserData, UserDataMethods, Value}; use tokio::process::{ChildStderr, ChildStdin, ChildStdout}; use yazi_shim::wtf8::FromWtf8; @@ -152,7 +152,7 @@ impl UserData for Command { me.inner.arg(OsStr::from_wtf8(&s.as_bytes())?); } Value::Table(t) => { - for s in t.sequence_values::() { + for s in t.sequence_values::() { me.inner.arg(OsStr::from_wtf8(&s?.as_bytes())?); } } @@ -160,19 +160,16 @@ impl UserData for Command { } ud.into_lua(lua) }); - methods.add_function("cwd", |_, (ud, dir): (AnyUserData, mlua::String)| { + methods.add_function("cwd", |_, (ud, dir): (AnyUserData, LuaString)| { ud.borrow_mut::()?.inner.current_dir(dir.to_str()?.as_ref()); Ok(ud) }); - methods.add_function( - "env", - |_, (ud, key, value): (AnyUserData, mlua::String, mlua::String)| { - ud.borrow_mut::()? - .inner - .env(OsStr::from_wtf8(&key.as_bytes())?, OsStr::from_wtf8(&value.as_bytes())?); - Ok(ud) - }, - ); + methods.add_function("env", |_, (ud, key, value): (AnyUserData, LuaString, LuaString)| { + ud.borrow_mut::()? + .inner + .env(OsStr::from_wtf8(&key.as_bytes())?, OsStr::from_wtf8(&value.as_bytes())?); + Ok(ud) + }); methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, Value)| { ud.borrow_mut::()?.inner.stdin(make_stdio(stdio)?); Ok(ud) diff --git a/yazi-codegen/src/lib.rs b/yazi-codegen/src/lib.rs index e50e64ff..d3b247b3 100644 --- a/yazi-codegen/src/lib.rs +++ b/yazi-codegen/src/lib.rs @@ -196,21 +196,13 @@ pub fn overlay(input: TokenStream) -> TokenStream { pub fn from_lua(input: TokenStream) -> TokenStream { let DeriveInput { ident, generics, .. } = parse_macro_input!(input as DeriveInput); - let ident_str = ident.to_string(); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { impl #impl_generics ::mlua::FromLua for #ident #ty_generics #where_clause { #[inline] - fn from_lua(value: ::mlua::Value, _: &::mlua::Lua) -> ::mlua::Result { - match value { - ::mlua::Value::UserData(ud) => ud.take::(), - _ => Err(::mlua::Error::FromLuaConversionError { - from: value.type_name(), - to: #ident_str.to_owned(), - message: None, - }), - } + fn from_lua(value: ::mlua::Value, lua: &::mlua::Lua) -> ::mlua::Result { + <::mlua::UserDataOwned as ::mlua::FromLua>::from_lua(value, lua).map(|ud| ud.0) } } } diff --git a/yazi-config/src/keymap/key.rs b/yazi-config/src/keymap/key.rs index a93f318c..9b9b60d6 100644 --- a/yazi-config/src/keymap/key.rs +++ b/yazi-config/src/keymap/key.rs @@ -19,9 +19,14 @@ pub struct Key { impl Key { pub fn plain(&self) -> Option { - match self.code { - KeyCode::Char(c) if !self.ctrl && !self.alt && !self.super_ => Some(c), - _ => None, + if self.ctrl || self.alt || self.super_ { + None + } else if self.shift && !self.code.implies_shift() { + None + } else if let KeyCode::Char(c) = self.code { + Some(c) + } else { + None } } } @@ -130,7 +135,7 @@ impl Display for Key { if self.alt { write!(f, "A-")?; } - if self.shift && !matches!(self.code, KeyCode::Char(_)) { + if self.shift && !self.code.implies_shift() { write!(f, "S-")?; } diff --git a/yazi-config/src/opener/opener.rs b/yazi-config/src/opener/opener.rs index b175ea5d..5a28c481 100644 --- a/yazi-config/src/opener/opener.rs +++ b/yazi-config/src/opener/opener.rs @@ -2,7 +2,7 @@ use std::{mem, ops::Deref, sync::Arc}; use arc_swap::ArcSwap; use hashbrown::HashMap; -use mlua::{ExternalError, FromLua, IntoLua, IntoLuaMulti, MetaMethod, UserData, UserDataMethods, Value}; +use mlua::{ExternalError, FromLua, IntoLua, IntoLuaMulti, LuaString, MetaMethod, UserData, UserDataMethods, Value}; use serde::{Deserialize, Deserializer}; use yazi_shim::{arc_swap::IntoPointee, toml::{DeserializeOverHook, DeserializeOverWith}}; @@ -86,7 +86,7 @@ impl DeserializeOverWith for Opener { impl UserData for &'static Opener { fn add_methods>(methods: &mut M) { - methods.add_meta_method(MetaMethod::Index, |lua, &me, key: mlua::String| { + methods.add_meta_method(MetaMethod::Index, |lua, &me, key: LuaString| { let key = key.to_str()?; match me.load().get(&*key) { Some(rules) => rules.clone().into_lua(lua), @@ -94,22 +94,19 @@ impl UserData for &'static Opener { } }); - methods.add_meta_method( - MetaMethod::NewIndex, - |lua, &me, (key, value): (mlua::String, Value)| { - let key = key.to_str()?; - match value { - t @ Value::Table(_) => { - me.insert(&key, &OpenerRulesArc::from_lua(t, lua)?); - } - Value::Nil => { - me.remove(&key); - } - _ => return Err("expected a table or nil".into_lua_err()), + methods.add_meta_method(MetaMethod::NewIndex, |lua, &me, (key, value): (LuaString, Value)| { + let key = key.to_str()?; + match value { + t @ Value::Table(_) => { + me.insert(&key, &OpenerRulesArc::from_lua(t, lua)?); } - Ok(()) - }, - ); + Value::Nil => { + me.remove(&key); + } + _ => return Err("expected a table or nil".into_lua_err()), + } + Ok(()) + }); methods.add_meta_method(MetaMethod::Pairs, |lua, &me, ()| { let mut matcher = OpenerRulesMatcher::from(me); diff --git a/yazi-config/src/opener/opener_rule.rs b/yazi-config/src/opener/opener_rule.rs index 71347026..130b10bf 100644 --- a/yazi-config/src/opener/opener_rule.rs +++ b/yazi-config/src/opener/opener_rule.rs @@ -32,15 +32,5 @@ impl OpenerRule { } } - pub fn fill(&mut self) { - #[cfg(unix)] - { - self.spread = - Splatter::<()>::spread(&self.run) || self.run.contains("$@") || self.run.contains("$*"); - } - #[cfg(windows)] - { - self.spread = Splatter::<()>::spread(&self.run) || self.run.contains("%*"); - } - } + pub fn fill(&mut self) { self.spread = Splatter::<()>::spread(&self.run); } } diff --git a/yazi-config/src/theme/custom_section_arc.rs b/yazi-config/src/theme/custom_section_arc.rs index fa26dadc..a63a0c52 100644 --- a/yazi-config/src/theme/custom_section_arc.rs +++ b/yazi-config/src/theme/custom_section_arc.rs @@ -1,7 +1,7 @@ use std::{ops::Deref, sync::Arc}; use hashbrown::HashMap; -use mlua::{MetaMethod, UserData, UserDataMethods}; +use mlua::{LuaString, MetaMethod, UserData, UserDataMethods}; use yazi_shared::SnakeCasedString; use crate::theme::{CustomField, CustomSection}; @@ -20,7 +20,7 @@ impl From<&CustomSection> for CustomSectionArc { impl UserData for CustomSectionArc { fn add_methods>(methods: &mut M) { - methods.add_meta_method(MetaMethod::Index, |_, me, key: mlua::String| { + methods.add_meta_method(MetaMethod::Index, |_, me, key: LuaString| { Ok(me.get(&*key.to_str()?).cloned()) }); } diff --git a/yazi-core/src/help/help.rs b/yazi-core/src/help/help.rs index fbe8fbfb..f7eea0fc 100644 --- a/yazi-core/src/help/help.rs +++ b/yazi-core/src/help/help.rs @@ -25,7 +25,7 @@ pub struct Help { } impl Help { - pub fn r#type(&mut self, key: KeyEvent) -> Result { + pub fn r#type(&mut self, key: &KeyEvent) -> Result { if !self.input.r#type(key)? { return Ok(false); } diff --git a/yazi-core/src/mgr/mgr.rs b/yazi-core/src/mgr/mgr.rs index 99f60a3c..28c1cf32 100644 --- a/yazi-core/src/mgr/mgr.rs +++ b/yazi-core/src/mgr/mgr.rs @@ -1,9 +1,6 @@ -use std::iter; - use ratatui_core::layout::Rect; use yazi_binding::position::{Origin, Position}; -use yazi_fs::Splatable; -use yazi_shared::url::{AsUrl, Url, UrlBuf}; +use yazi_shared::url::UrlBuf; use yazi_term::TERM; use yazi_watcher::Watcher; @@ -52,35 +49,12 @@ impl Mgr { #[inline] pub fn active_mut(&mut self) -> &mut Tab { self.tabs.active_mut() } + #[inline] + pub fn current(&self) -> &Folder { &self.active().current } + #[inline] pub fn current_mut(&mut self) -> &mut Folder { &mut self.active_mut().current } #[inline] pub fn parent_mut(&mut self) -> Option<&mut Folder> { self.active_mut().parent.as_mut() } } - -impl Splatable for Mgr { - fn tab(&self) -> usize { self.tabs.cursor } - - fn selected(&self, tab: usize, mut idx: Option) -> impl Iterator> { - idx = idx.and_then(|i| i.checked_sub(1)); - tab - .checked_sub(1) - .and_then(|tab| self.tabs.get(tab)) - .map(|tab| tab.selected_or_hovered_urls()) - .unwrap_or_else(|| Box::new(iter::empty())) - .skip(idx.unwrap_or(0)) - .take(if idx.is_some() { 1 } else { usize::MAX }) - .map(|u| u.as_url()) - } - - fn hovered(&self, tab: usize) -> Option> { - tab - .checked_sub(1) - .and_then(|tab| self.tabs.get(tab)) - .and_then(|tab| tab.hovered()) - .map(|h| h.url.as_url()) - } - - fn yanked(&self) -> impl Iterator> { self.yanked.iter().map(|f| f.url.as_url()) } -} diff --git a/yazi-core/src/mgr/mod.rs b/yazi-core/src/mgr/mod.rs index b1816933..9076722d 100644 --- a/yazi-core/src/mgr/mod.rs +++ b/yazi-core/src/mgr/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(batcher cd displace filter find mgr mimetype open search tabs yanked); +yazi_macro::mod_flat!(batcher cd displace filter find mgr mimetype open search snap tabs yanked); diff --git a/yazi-core/src/mgr/snap.rs b/yazi-core/src/mgr/snap.rs new file mode 100644 index 00000000..f726bd56 --- /dev/null +++ b/yazi-core/src/mgr/snap.rs @@ -0,0 +1,54 @@ +use yazi_fs::Splatable; +use yazi_shared::url::{AsUrl, Url, UrlBuf}; + +use crate::{mgr::Mgr, tab::TabSnap}; + +pub struct MgrSnap { + tab: usize, + tabs: Vec, + yanked: Vec, +} + +impl From<&Mgr> for MgrSnap { + fn from(value: &Mgr) -> Self { + Self { + tab: value.tabs.cursor, + tabs: value.tabs.iter().map(Into::into).collect(), + yanked: value.yanked.urls().cloned().collect(), + } + } +} + +impl Splatable for MgrSnap { + fn tab(&self) -> usize { self.tab + 1 } + + fn selected(&self, tab: usize, mut idx: Option) -> impl Iterator> { + idx = idx.and_then(|i| i.checked_sub(1)); + tab + .checked_sub(1) + .and_then(|tab| self.tabs.get(tab)) + .map_or_else(|| &[][..], |s| &s.selected) + .into_iter() + .skip(idx.unwrap_or(0)) + .take(if idx.is_some() { 1 } else { usize::MAX }) + .map(AsUrl::as_url) + } + + fn hovered(&self, tab: usize) -> Option> { + tab + .checked_sub(1) + .and_then(|tab| self.tabs.get(tab)) + .and_then(|tab| tab.hovered.as_ref()) + .map(AsUrl::as_url) + } + + fn yanked(&self, mut idx: Option) -> impl Iterator> { + idx = idx.and_then(|i| i.checked_sub(1)); + self + .yanked + .iter() + .skip(idx.unwrap_or(0)) + .take(if idx.is_some() { 1 } else { usize::MAX }) + .map(AsUrl::as_url) + } +} diff --git a/yazi-core/src/spot/lock.rs b/yazi-core/src/spot/lock.rs index 30f11ae5..1793f095 100644 --- a/yazi-core/src/spot/lock.rs +++ b/yazi-core/src/spot/lock.rs @@ -1,13 +1,13 @@ use mlua::Table; -use yazi_fs::file::FileRef; +use yazi_fs::{cha::Cha, file::FileRef}; use yazi_macro::impl_data_any; -use yazi_shared::id::Id; +use yazi_shared::{id::Id, url::UrlBuf}; use yazi_widgets::Renderable; #[derive(Clone, Debug)] pub struct SpotLock { - pub url: yazi_shared::url::UrlBuf, - pub cha: yazi_fs::cha::Cha, + pub url: UrlBuf, + pub cha: Cha, pub mime: String, pub id: Id, diff --git a/yazi-core/src/tab/folder.rs b/yazi-core/src/tab/folder.rs index 49d8d63a..60f68565 100644 --- a/yazi-core/src/tab/folder.rs +++ b/yazi-core/src/tab/folder.rs @@ -169,6 +169,9 @@ impl Folder { #[inline] pub fn hovered_mut(&mut self) -> Option<&mut File> { self.entries.get_mut(self.cursor) } + #[inline] + pub fn hovered_url(&self) -> Option<&UrlBuf> { self.hovered().map(|f| &f.url) } + pub fn paginate(&self, page: usize) -> &[File] { let len = self.entries.len(); let limit = LAYOUT.get().folder_limit(); diff --git a/yazi-core/src/tab/mod.rs b/yazi-core/src/tab/mod.rs index 8f7666e1..508b8072 100644 --- a/yazi-core/src/tab/mod.rs +++ b/yazi-core/src/tab/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(backstack finder folder history visual mode preference preview preview_lock selected tab); +yazi_macro::mod_flat!(backstack finder folder history mode preference preview preview_lock selected snap tab visual); diff --git a/yazi-core/src/tab/preview_lock.rs b/yazi-core/src/tab/preview_lock.rs index 243bec31..a7a264f6 100644 --- a/yazi-core/src/tab/preview_lock.rs +++ b/yazi-core/src/tab/preview_lock.rs @@ -1,14 +1,14 @@ -use mlua::Table; +use mlua::{LuaString, Table}; use yazi_binding::elements::Rect; -use yazi_fs::file::FileRef; +use yazi_fs::{cha::Cha, file::FileRef}; use yazi_macro::impl_data_any; -use yazi_shared::pool::{InternStr, Symbol}; +use yazi_shared::{pool::{InternStr, Symbol}, url::UrlBuf}; use yazi_widgets::Renderable; #[derive(Clone, Debug, Default)] pub struct PreviewLock { - pub url: yazi_shared::url::UrlBuf, - pub cha: yazi_fs::cha::Cha, + pub url: UrlBuf, + pub cha: Cha, pub mime: Symbol, pub skip: usize, @@ -27,7 +27,7 @@ impl TryFrom
for PreviewLock { Ok(Self { url: f.url_owned(), cha: f.cha, - mime: t.raw_get::("mime")?.to_str()?.intern(), + mime: t.raw_get::("mime")?.to_str()?.intern(), skip: t.raw_get("skip")?, area: t.raw_get("area")?, diff --git a/yazi-core/src/tab/snap.rs b/yazi-core/src/tab/snap.rs new file mode 100644 index 00000000..5d59bfd2 --- /dev/null +++ b/yazi-core/src/tab/snap.rs @@ -0,0 +1,17 @@ +use yazi_shared::url::UrlBuf; + +use crate::tab::Tab; + +pub struct TabSnap { + pub hovered: Option, + pub selected: Vec, +} + +impl From<&Tab> for TabSnap { + fn from(value: &Tab) -> Self { + Self { + hovered: value.hovered_url().cloned(), + selected: value.selected.urls().cloned().collect(), + } + } +} diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index 6fd8660d..958ba927 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -78,6 +78,9 @@ impl Tab { #[inline] pub fn hovered(&self) -> Option<&File> { self.current.hovered() } + #[inline] + pub fn hovered_url(&self) -> Option<&UrlBuf> { self.current.hovered_url() } + #[inline] pub fn hovered_mut(&mut self) -> Option<&mut File> { self.current.hovered_mut() } @@ -109,7 +112,7 @@ impl Tab { pub fn selected_or_hovered_urls(&self) -> Box + '_> { if self.selected.is_empty() { - Box::new(self.hovered().map(|h| &h.url).into_iter()) + Box::new(self.hovered_url().into_iter()) } else { Box::new(self.selected.urls()) } diff --git a/yazi-core/src/tasks/mod.rs b/yazi-core/src/tasks/mod.rs index 888cc397..f43e59d6 100644 --- a/yazi-core/src/tasks/mod.rs +++ b/yazi-core/src/tasks/mod.rs @@ -1,4 +1,4 @@ -yazi_macro::mod_flat!(file option prework process tasks); +yazi_macro::mod_flat!(file option prework tasks); pub const TASKS_BORDER: u16 = 2; pub const TASKS_PADDING: u16 = 2; diff --git a/yazi-core/src/tasks/process.rs b/yazi-core/src/tasks/process.rs deleted file mode 100644 index c403d2a7..00000000 --- a/yazi-core/src/tasks/process.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::mem; - -use yazi_fs::Splatter; -use yazi_scheduler::process::ProcessOpt; - -use super::Tasks; - -impl Tasks { - // TODO: remove - pub fn open_shell_compat(&self, mut opt: ProcessOpt) { - if opt.spread { - opt.cmd = Splatter::new(&opt.args).splat(opt.cmd); - self.scheduler.process_open(opt); - return; - } - if opt.args.is_empty() { - return; - } - if opt.args.len() == 2 { - opt.cmd = Splatter::new(&opt.args).splat(opt.cmd); - self.scheduler.process_open(opt); - return; - } - let hovered = mem::take(&mut opt.args[0]); - for target in opt.args.into_iter().skip(1) { - let args = vec![hovered.clone(), target]; - self.scheduler.process_open(ProcessOpt { - cwd: opt.cwd.clone(), - cmd: Splatter::new(&args).splat(&opt.cmd), - args, - block: opt.block, - orphan: opt.orphan, - spread: opt.spread, - }); - } - } -} diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index f8f04d3d..ea383ee5 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -59,7 +59,7 @@ libc = { workspace = true } signal-hook-tokio = { version = "0.4.0", features = [ "futures-v0_3" ] } [target.'cfg(all(not(target_os = "macos"), not(target_os = "windows")))'.dependencies] -tikv-jemallocator = "0.6.1" +tikv-jemallocator = "0.7.0" [[bin]] name = "yazi" diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 61e06097..f2efbbca 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -182,7 +182,6 @@ impl<'a> Executor<'a> { on!(inspect); on!(cancel); on!(process_open); - on!(open_shell_compat); match action.name.as_ref() { // Help diff --git a/yazi-fm/src/router.rs b/yazi-fm/src/router.rs index d28812a6..96ca463b 100644 --- a/yazi-fm/src/router.rs +++ b/yazi-fm/src/router.rs @@ -19,12 +19,12 @@ impl<'a> Router<'a> { use Layer as L; let core = &mut self.app.core; - if core.help.visible && core.help.r#type(key)? { + if core.help.visible && core.help.r#type(&key)? { return Ok(true); } if let Some(mut guard) = core.input.lock_mut() - && guard.r#type(key)? + && guard.r#type(&key)? { return Ok(true); } diff --git a/yazi-fs/src/splatter.rs b/yazi-fs/src/splatter.rs index 9db2b6cf..42cd4af5 100644 --- a/yazi-fs/src/splatter.rs +++ b/yazi-fs/src/splatter.rs @@ -4,7 +4,7 @@ use std::os::unix::ffi::{OsStrExt, OsStringExt}; use std::os::windows::ffi::{OsStrExt, OsStringExt}; use std::{cell::Cell, ffi::{OsStr, OsString}, iter::{self, Peekable}, mem}; -use yazi_shared::url::{AsUrl, Url, UrlCow}; +use yazi_shared::url::{AsUrl, Url}; use crate::FsUrl; @@ -31,7 +31,7 @@ pub trait Splatable { fn hovered(&self, tab: usize) -> Option>; - fn yanked(&self) -> impl Iterator>; + fn yanked(&self, idx: Option) -> impl Iterator>; } #[cfg(unix)] @@ -50,7 +50,7 @@ impl Splatter where T: Splatable, { - pub fn new(src: T) -> Self { Self { tab: src.tab() + 1, src } } + pub fn new(src: T) -> Self { Self { tab: src.tab(), src } } pub fn splat(mut self, cmd: impl AsRef) -> OsString { #[cfg(unix)] @@ -82,8 +82,6 @@ where Some('t') | Some('T') => self.visit_tab(it, buf), Some('y') | Some('Y') => self.visit_yanked(it, buf), Some('%') => self.visit_escape(it, buf), - Some('*') => self.visit_selected(it, buf), // TODO: remove this - Some(c) if c.is_ascii_digit() => self.visit_digit(it, buf), _ => self.visit_unknown(it, buf), } } @@ -154,32 +152,12 @@ where self.tab = old; } - fn visit_digit(&mut self, it: &mut Iter, buf: &mut Buf) { - // TODO: remove - match self.consume_digit(it) { - Some(0) => { - cue(buf, self.src.hovered(self.tab).map(|u| u.unified_path_str()).unwrap_or_default()); - } - Some(n) => { - cue( - buf, - self - .src - .selected(self.tab, Some(n)) - .next() - .map(|u| u.unified_path_str()) - .unwrap_or_default(), - ); - } - None => unreachable!(), - } - } - fn visit_yanked(&mut self, it: &mut Iter, buf: &mut Buf) { let c = it.next().and_then(b2c); + let idx = self.consume_digit(it); let mut first = true; - for url in self.src.yanked() { + for url in self.src.yanked(idx) { if !mem::replace(&mut first, false) { buf.push(b' ' as _); } @@ -190,6 +168,9 @@ where cue(buf, url.unified_path_str()); } } + if first && idx.is_some() { + cue(buf, ""); + } } fn visit_escape(&mut self, it: &mut Iter, buf: &mut Buf) { buf.push(it.next().unwrap()); } @@ -232,10 +213,7 @@ impl Splatter { fn hovered(&self, _tab: usize) -> Option> { None } - fn yanked(&self) -> impl Iterator> { - self.0.set(true); - iter::empty() - } + fn yanked(&self, _idx: Option) -> impl Iterator> { iter::empty() } } let src = Source(Cell::new(false)); @@ -244,28 +222,27 @@ impl Splatter { } } -// TODO: remove -impl<'a, T> Splatable for &'a T +impl<'a, I, T> Splatable for &'a I where - T: AsRef<[UrlCow<'a>]>, + I: ?Sized, + &'a I: IntoIterator, + T: AsUrl + 'a, { - fn tab(&self) -> usize { 0 } + fn tab(&self) -> usize { 1 } - fn selected(&self, tab: usize, idx: Option) -> impl Iterator> { - self - .as_ref() - .iter() + fn selected(&self, tab: usize, mut idx: Option) -> impl Iterator> { + idx = idx.and_then(|i| i.checked_sub(1)); + (*self) + .into_iter() .filter(move |_| tab == 1) .map(|u| u.as_url()) - .skip(idx.unwrap_or(1)) + .skip(idx.unwrap_or(0)) .take(if idx.is_some() { 1 } else { usize::MAX }) } - fn hovered(&self, tab: usize) -> Option> { - self.as_ref().first().filter(|_| tab == 1).map(|u| u.as_url()) - } + fn hovered(&self, _tab: usize) -> Option> { None } - fn yanked(&self) -> impl Iterator> { iter::empty() } + fn yanked(&self, _idx: Option) -> impl Iterator> { iter::empty() } } #[cfg(test)] @@ -300,8 +277,12 @@ mod tests { } } - fn yanked(&self) -> impl Iterator> { - [Url::regular("y1"), Url::regular("y 2"), Url::regular("y3")].into_iter() + fn yanked(&self, mut idx: Option) -> impl Iterator> { + idx = idx.and_then(|i| i.checked_sub(1)); + [Url::regular("y1"), Url::regular("y 2"), Url::regular("y3")] + .into_iter() + .skip(idx.unwrap_or(0)) + .take(if idx.is_some() { 1 } else { usize::MAX }) } } @@ -310,41 +291,40 @@ mod tests { fn test_unix() { let cases = [ // Selected - (Source(0), r#"ls %s"#, r#"ls t1/s1 t1/s2"#), - (Source(0), r#"ls %s1 %s2 %s3"#, r#"ls t1/s1 t1/s2 ''"#), - (Source(0), r#"ls %s %s2 %s"#, r#"ls t1/s1 t1/s2 t1/s2 t1/s1 t1/s2"#), - (Source(1), r#"ls %s"#, r#"ls 't 2/s 1' 't 2/s 2'"#), - (Source(1), r#"ls %s1 %s3 %s2"#, r#"ls 't 2/s 1' '' 't 2/s 2'"#), - (Source(2), r#"ls %s"#, r#"ls "#), - (Source(2), r#"ls %s1 %s %s2"#, r#"ls '' ''"#), + (Source(1), r#"ls %s"#, r#"ls t1/s1 t1/s2"#), + (Source(1), r#"ls %s1 %s2 %s3"#, r#"ls t1/s1 t1/s2 ''"#), + (Source(1), r#"ls %s %s2 %s"#, r#"ls t1/s1 t1/s2 t1/s2 t1/s1 t1/s2"#), + (Source(2), r#"ls %s"#, r#"ls 't 2/s 1' 't 2/s 2'"#), + (Source(2), r#"ls %s1 %s3 %s2"#, r#"ls 't 2/s 1' '' 't 2/s 2'"#), + (Source(3), r#"ls %s"#, r#"ls "#), + (Source(3), r#"ls %s1 %s %s2"#, r#"ls '' ''"#), // Hovered - (Source(0), r#"ls %h"#, r#"ls hovered"#), - (Source(1), r#"ls %h"#, r#"ls 'hover ed'"#), - (Source(2), r#"ls %h"#, r#"ls ''"#), + (Source(1), r#"ls %h"#, r#"ls hovered"#), + (Source(2), r#"ls %h"#, r#"ls 'hover ed'"#), + (Source(3), r#"ls %h"#, r#"ls ''"#), // Dirname - (Source(0), r#"cd %d"#, r#"cd t1 t1"#), - (Source(1), r#"cd %d"#, r#"cd 't 2' 't 2'"#), - (Source(1), r#"cd %d1 %d3 %d2"#, r#"cd 't 2' '' 't 2'"#), - (Source(2), r#"cd %d %d1"#, r#"cd ''"#), + (Source(1), r#"cd %d"#, r#"cd t1 t1"#), + (Source(2), r#"cd %d"#, r#"cd 't 2' 't 2'"#), + (Source(2), r#"cd %d1 %d3 %d2"#, r#"cd 't 2' '' 't 2'"#), + (Source(3), r#"cd %d %d1"#, r#"cd ''"#), // Yanked - (Source(0), r#"cd %y"#, r#"cd y1 'y 2' y3"#), (Source(1), r#"cd %y"#, r#"cd y1 'y 2' y3"#), (Source(2), r#"cd %y"#, r#"cd y1 'y 2' y3"#), + (Source(3), r#"cd %y"#, r#"cd y1 'y 2' y3"#), + (Source(1), r#"cd %y1 %y3 %y2 %y4"#, r#"cd y1 y3 'y 2' ''"#), // Tab - (Source(0), r#"ls %s %ts %s"#, r#"ls t1/s1 t1/s2 't 2/s 1' 't 2/s 2' t1/s1 t1/s2"#), - (Source(1), r#"ls %s1 %ts %s2"#, r#"ls 't 2/s 1' 't 2/s 2'"#), - (Source(1), r#"ls %s1 %Ts1 %s2 %Ts2"#, r#"ls 't 2/s 1' t1/s1 't 2/s 2' t1/s2"#), - (Source(0), r#"ls %s1 %Ts1 %s2 %Ts2"#, r#"ls t1/s1 '' t1/s2 ''"#), - (Source(0), r#"ls %ty"#, r#"ls y1 'y 2' y3"#), - (Source(0), r#"ls %Ty"#, r#"ls y1 'y 2' y3"#), + (Source(1), r#"ls %s %ts %s"#, r#"ls t1/s1 t1/s2 't 2/s 1' 't 2/s 2' t1/s1 t1/s2"#), + (Source(2), r#"ls %s1 %ts %s2"#, r#"ls 't 2/s 1' 't 2/s 2'"#), + (Source(2), r#"ls %s1 %Ts1 %s2 %Ts2"#, r#"ls 't 2/s 1' t1/s1 't 2/s 2' t1/s2"#), + (Source(1), r#"ls %s1 %Ts1 %s2 %Ts2"#, r#"ls t1/s1 '' t1/s2 ''"#), + (Source(1), r#"ls %ty"#, r#"ls y1 'y 2' y3"#), + (Source(1), r#"ls %Ty"#, r#"ls y1 'y 2' y3"#), // Escape ( - Source(0), + Source(1), r#"echo % %% %s2 %%h %d %%%y %%%%ts %%%%%ts1"#, r#"echo % % t1/s2 %h t1 t1 %y1 'y 2' y3 %%ts %%'t 2/s 1'"#, ), - // TODO: remove - (Source(0), r#"ls %1 %* %2 %0 %3"#, r#"ls t1/s1 t1/s1 t1/s2 t1/s2 hovered ''"#), ]; for (src, cmd, expected) in cases { diff --git a/yazi-parser/src/tasks/process_open.rs b/yazi-parser/src/tasks/process_open.rs index 5d2c6e2a..a1717884 100644 --- a/yazi-parser/src/tasks/process_open.rs +++ b/yazi-parser/src/tasks/process_open.rs @@ -1,11 +1,11 @@ use anyhow::anyhow; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use yazi_scheduler::process::ProcessOpt; +use yazi_scheduler::process::ShellOpt; use yazi_shared::event::{ActionCow, Replier}; #[derive(Clone, Debug)] pub struct ProcessOpenForm { - pub opt: ProcessOpt, + pub opt: ShellOpt, pub replier: Option, } diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index 7d7d8311..404b6b2b 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use mlua::{ExternalError, Function, IntoLua, IntoLuaMulti, Lua, Table, Value}; +use mlua::{ExternalError, Function, IntoLua, IntoLuaMulti, Lua, LuaString, Table, Value}; use yazi_binding::{Composer, ComposerGet, ComposerSet, Error}; use yazi_config::Pattern; use yazi_fs::{file::File, mounts::PARTITIONS, provider::{Attrs, DirReader, FileHolder}}; @@ -81,7 +81,7 @@ fn copy(lua: &Lua) -> mlua::Result { } fn create(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, (r#type, url): (mlua::String, UrlRef)| async move { + lua.create_async_function(|lua, (r#type, url): (LuaString, UrlRef)| async move { let result = match &*r#type.as_bytes() { b"dir" => provider::create_dir(&*url).await, b"dir_all" => provider::create_dir_all(&*url).await, @@ -132,7 +132,7 @@ fn file(lua: &Lua) -> mlua::Result { } fn op(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (name, t): (mlua::String, Table)| match &*name.as_bytes() { + lua.create_function(|lua, (name, t): (LuaString, Table)| match &*name.as_bytes() { b"part" => super::FilesOp::part(lua, t), b"done" => super::FilesOp::done(lua, t), b"size" => super::FilesOp::size(lua, t), @@ -162,7 +162,7 @@ fn partitions(lua: &Lua) -> mlua::Result { fn read_dir(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (dir, options): (UrlRef, Table)| async move { - let pat = if let Ok(s) = options.raw_get::("glob") { + let pat = if let Ok(s) = options.raw_get::("glob") { Some(Pattern::from_str(&s.to_str()?)?) } else { None @@ -202,7 +202,7 @@ fn read_dir(lua: &Lua) -> mlua::Result { } fn remove(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, (r#type, url): (mlua::String, UrlRef)| async move { + lua.create_async_function(|lua, (r#type, url): (LuaString, UrlRef)| async move { let result = match &*r#type.as_bytes() { b"file" => provider::remove_file(&*url).await, b"dir" => provider::remove_dir(&*url).await, @@ -228,7 +228,7 @@ fn rename(lua: &Lua) -> mlua::Result { } fn unique(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, (r#type, url): (mlua::String, UrlRef)| async move { + lua.create_async_function(|lua, (r#type, url): (LuaString, UrlRef)| async move { let result = match &*r#type.as_bytes() { b"dir" => yazi_vfs::unique_file(url.clone(), true).await, b"file" => yazi_vfs::unique_file(url.clone(), false).await, @@ -243,7 +243,7 @@ fn unique(lua: &Lua) -> mlua::Result { } fn write(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move { + lua.create_async_function(|lua, (url, data): (UrlRef, LuaString)| async move { match provider::write(&*url, data.as_bytes()).await { Ok(()) => true.into_lua_multi(&lua), Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), diff --git a/yazi-plugin/src/fs/op.rs b/yazi-plugin/src/fs/op.rs index d894985a..b613eb9e 100644 --- a/yazi-plugin/src/fs/op.rs +++ b/yazi-plugin/src/fs/op.rs @@ -3,7 +3,7 @@ use yazi_codegen::FromLuaOwned; use yazi_macro::impl_data_any; use yazi_shared::url::UrlBuf; -#[derive(Clone, FromLuaOwned)] +#[derive(Clone, FromLuaOwned, UserData)] pub(super) struct FilesOp(yazi_fs::FilesOp); impl_data_any!(FilesOp => yazi_fs::FilesOp; from_into_lua = inherit); @@ -44,5 +44,3 @@ impl FilesOp { Ok(Self(yazi_fs::FilesOp::Size(url, sizes.pairs().collect::>()?))) } } - -impl UserData for FilesOp {} diff --git a/yazi-plugin/src/pubsub/pubsub.rs b/yazi-plugin/src/pubsub/pubsub.rs index 21c1388a..2ab83063 100644 --- a/yazi-plugin/src/pubsub/pubsub.rs +++ b/yazi-plugin/src/pubsub/pubsub.rs @@ -1,4 +1,4 @@ -use mlua::{ExternalResult, Function, Lua, Value}; +use mlua::{ExternalResult, Function, Lua, LuaString, Value}; use yazi_binding::runtime; use yazi_dds::ember::Ember; use yazi_shared::id::Id; @@ -7,20 +7,20 @@ pub struct Pubsub; impl Pubsub { pub(super) fn r#pub(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (kind, value): (mlua::String, Value)| { + lua.create_function(|lua, (kind, value): (LuaString, Value)| { yazi_dds::Pubsub::r#pub(Ember::from_lua(lua, &kind.to_str()?, value)?).into_lua_err() }) } pub(super) fn pub_to(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (receiver, kind, value): (Id, mlua::String, Value)| { + lua.create_function(|lua, (receiver, kind, value): (Id, LuaString, Value)| { yazi_dds::Pubsub::pub_to(receiver, Ember::from_lua(lua, &kind.to_str()?, value)?) .into_lua_err() }) } pub(super) fn sub(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (kind, f): (mlua::String, Function)| { + lua.create_function(|lua, (kind, f): (LuaString, Function)| { let rt = runtime!(lua)?; if !yazi_dds::Pubsub::sub(rt.current()?, &kind.to_str()?, f) { return Err("`sub()` called twice").into_lua_err(); @@ -30,7 +30,7 @@ impl Pubsub { } pub(super) fn sub_remote(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (kind, f): (mlua::String, Function)| { + lua.create_function(|lua, (kind, f): (LuaString, Function)| { let rt = runtime!(lua)?; if !yazi_dds::Pubsub::sub_remote(rt.current()?, &kind.to_str()?, f) { return Err("`sub_remote()` called twice").into_lua_err(); @@ -40,14 +40,14 @@ impl Pubsub { } pub(super) fn unsub(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, kind: mlua::String| { + lua.create_function(|lua, kind: LuaString| { let rt = runtime!(lua)?; Ok(yazi_dds::Pubsub::unsub(rt.current()?, &kind.to_str()?)) }) } pub(super) fn unsub_remote(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, kind: mlua::String| { + lua.create_function(|lua, kind: LuaString| { let rt = runtime!(lua)?; Ok(yazi_dds::Pubsub::unsub_remote(rt.current()?, &kind.to_str()?)) }) diff --git a/yazi-plugin/src/tasks/option.rs b/yazi-plugin/src/tasks/option.rs index fd15e878..4252d18d 100644 --- a/yazi-plugin/src/tasks/option.rs +++ b/yazi-plugin/src/tasks/option.rs @@ -1,4 +1,4 @@ -use mlua::{AnyUserData, UserData, UserDataMethods}; +use mlua::{AnyUserData, LuaString, UserData, UserDataMethods}; use yazi_proxy::TasksProxy; use yazi_scheduler::TaskIn; @@ -9,7 +9,7 @@ pub(crate) struct TaskOpt(pub(crate) yazi_core::tasks::TaskOpt); impl UserData for TaskOpt { fn add_methods>(methods: &mut M) { - methods.add_function("name", |_, (ud, name): (AnyUserData, mlua::String)| { + methods.add_function("name", |_, (ud, name): (AnyUserData, LuaString)| { ud.borrow_mut::()?.0.set_title(name.to_string_lossy()); Ok(ud) }); diff --git a/yazi-plugin/src/ui/utils.rs b/yazi-plugin/src/ui/utils.rs index b5a61827..6252cf01 100644 --- a/yazi-plugin/src/ui/utils.rs +++ b/yazi-plugin/src/ui/utils.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, iter}; use ansi_to_tui::IntoText; -use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, ObjectLike, Table, Value}; +use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, LuaString, ObjectLike, Table, Value}; use tracing::error; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use yazi_binding::{Permit, PermitRef, elements::{Line, Rect, Span, Wrap}, runtime}; @@ -12,7 +12,7 @@ use yazi_shim::ratatui::LineIter; use yazi_term::YIELD_TO_SUBPROCESS; pub(super) fn area(lua: &Lua) -> mlua::Result { - let f = lua.create_function(|_, s: mlua::String| { + let f = lua.create_function(|_, s: LuaString| { let layout = LAYOUT.get(); Ok(match &*s.as_bytes() { b"current" => Rect(layout.current), @@ -46,7 +46,7 @@ pub(super) fn hide(lua: &Lua) -> mlua::Result { } pub(super) fn lines(lua: &Lua) -> mlua::Result { - let f = lua.create_function(|lua, (s, opts): (mlua::String, Table)| { + let f = lua.create_function(|lua, (s, opts): (LuaString, Table)| { let b = s.as_bytes(); let s = &*String::from_utf8_lossy(&b); @@ -74,7 +74,7 @@ pub(super) fn lines(lua: &Lua) -> mlua::Result { } pub(super) fn printable(lua: &Lua) -> mlua::Result { - let f = lua.create_function(|lua, s: mlua::String| { + let f = lua.create_function(|lua, s: LuaString| { Ok(match replace_to_printable(&s.as_bytes(), false, 1, true) { Cow::Borrowed(_) => s, Cow::Owned(new) => lua.create_external_string(new)?, @@ -86,7 +86,7 @@ pub(super) fn printable(lua: &Lua) -> mlua::Result { pub(super) fn redraw(lua: &Lua) -> mlua::Result { let f = lua.create_function(|lua, c: Table| { - let id: mlua::String = c.get("_id")?; + let id: LuaString = c.get("_id")?; let mut layout = LAYOUT.get(); match &*id.as_bytes() { @@ -135,7 +135,7 @@ pub(super) fn truncate(lua: &Lua) -> mlua::Result { (idx, last, adv > max) } - let f = lua.create_function(|lua, (s, t): (mlua::String, Table)| { + let f = lua.create_function(|lua, (s, t): (LuaString, Table)| { let b = s.as_bytes(); if b.is_empty() { return Ok(s); diff --git a/yazi-plugin/src/utils/app.rs b/yazi-plugin/src/utils/app.rs index ca760651..d3d0a431 100644 --- a/yazi-plugin/src/utils/app.rs +++ b/yazi-plugin/src/utils/app.rs @@ -1,6 +1,6 @@ use std::any::TypeId; -use mlua::{AnyUserData, ExternalError, Function, Lua}; +use mlua::{AnyUserData, ExternalError, Function, Lua, LuaString}; use tokio::process::{ChildStderr, ChildStdin, ChildStdout}; use yazi_vfs::provider::RwFile; @@ -8,7 +8,7 @@ use super::Utils; impl Utils { pub(super) fn id(lua: &Lua) -> mlua::Result { - lua.create_function(|_, r#type: mlua::String| { + lua.create_function(|_, r#type: LuaString| { Ok(match &*r#type.as_bytes() { b"app" => *yazi_dds::ID, b"ft" => yazi_fs::FILES_TICKET.next(), diff --git a/yazi-plugin/src/utils/json.rs b/yazi-plugin/src/utils/json.rs index 447e5e82..b2211bec 100644 --- a/yazi-plugin/src/utils/json.rs +++ b/yazi-plugin/src/utils/json.rs @@ -1,4 +1,4 @@ -use mlua::{Function, IntoLuaMulti, Lua, LuaSerdeExt, Value}; +use mlua::{Function, IntoLuaMulti, Lua, LuaSerdeExt, LuaString, Value}; use yazi_binding::Error; use yazi_shim::mlua::SER_OPT; @@ -15,7 +15,7 @@ impl Utils { } pub(super) fn json_decode(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, s: mlua::String| async move { + lua.create_async_function(|lua, s: LuaString| async move { match serde_json::from_slice::(&s.as_bytes()) { Ok(v) => lua.to_value_with(&v, SER_OPT)?.into_lua_multi(&lua), Err(e) => (Value::Nil, Error::Serde(e)).into_lua_multi(&lua), diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 1057189b..acb65c5b 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -1,6 +1,6 @@ use std::{str::FromStr, time::Duration}; -use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, Table, Value}; +use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, LuaString, Table, Value}; use tokio_stream::wrappers::UnboundedReceiverStream; use yazi_binding::{elements::{Line, Text}, runtime}; use yazi_config::{Platform, keymap::{Chord, ChordArc, Key}, popup::ConfirmCfg}; @@ -99,7 +99,7 @@ impl Utils { } Value::Table(t) => { let mut v = Vec::with_capacity(10); - for s in t.sequence_values::() { + for s in t.sequence_values::() { v.push(Key::from_str(&s?.to_str()?).into_lua_err()?); } v diff --git a/yazi-plugin/src/utils/log.rs b/yazi-plugin/src/utils/log.rs index e1dc93c2..b34d6275 100644 --- a/yazi-plugin/src/utils/log.rs +++ b/yazi-plugin/src/utils/log.rs @@ -1,8 +1,7 @@ -use std::{any::TypeId, fmt::Write}; +use std::fmt::Write; -use mlua::{Function, Lua, MultiValue, Value}; +use mlua::{Function, Lua, MultiValue}; use tracing::{debug, error}; -use yazi_shared::{id::Id, path::PathBufDyn, url::UrlBuf}; use super::Utils; @@ -21,27 +20,8 @@ impl Utils { if !s.is_empty() { s.push(' '); } - Self::format_one(&mut s, value)?; + write!(s, "{value:#?}")?; } Ok(s) } - - fn format_one(buf: &mut String, value: Value) -> anyhow::Result<()> { - let Value::UserData(ud) = &value else { - return Ok(write!(buf, "{value:#?}")?); - }; - - let id = ud.type_id(); - let ptr = ud.to_pointer(); - Ok(match id { - Some(t) if t == TypeId::of::() => { - write!(buf, "Url({ptr:?}): {:?}", *ud.borrow::()?)? - } - Some(t) if t == TypeId::of::() => { - write!(buf, "Path({ptr:?}): {:?}", *ud.borrow::()?)? - } - Some(t) if t == TypeId::of::() => write!(buf, "Id({ptr:?}): {}", *ud.borrow::()?)?, - _ => write!(buf, "{value:#?}")?, - }) - } } diff --git a/yazi-plugin/src/utils/sync.rs b/yazi-plugin/src/utils/sync.rs index e8ebf7da..0de4dfa1 100644 --- a/yazi-plugin/src/utils/sync.rs +++ b/yazi-plugin/src/utils/sync.rs @@ -1,6 +1,6 @@ use anyhow::Context; use futures::future::join_all; -use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Table, Value, Variadic}; +use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, LuaString, MultiValue, Table, Value, Variadic}; use tokio::sync::mpsc; use yazi_binding::{Handle, MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx, runtime, runtime_mut}; use yazi_core::{AppProxy, app::PluginOpt}; @@ -88,7 +88,7 @@ impl Utils { } pub(super) fn chan(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (r#type, buffer): (mlua::String, Option)| { + lua.create_function(|lua, (r#type, buffer): (LuaString, Option)| { match (&*r#type.as_bytes(), buffer) { (b"mpsc", Some(buffer)) if buffer < 1 => { Err("Buffer size must be greater than 0".into_lua_err()) @@ -103,7 +103,7 @@ impl Utils { } (b"oneshot", _) => { let (tx, rx) = tokio::sync::oneshot::channel::(); - (OneshotTx(Some(tx)), OneshotRx(Some(rx))).into_lua_multi(lua) + (OneshotTx(tx), OneshotRx(rx)).into_lua_multi(lua) } _ => Err("Channel type must be `mpsc` or `oneshot`".into_lua_err()), } diff --git a/yazi-plugin/src/utils/tasks.rs b/yazi-plugin/src/utils/tasks.rs index 4c5c63e7..fcf0d108 100644 --- a/yazi-plugin/src/utils/tasks.rs +++ b/yazi-plugin/src/utils/tasks.rs @@ -1,4 +1,4 @@ -use mlua::{ExternalError, FromLua, Function, Lua, Value}; +use mlua::{ExternalError, FromLua, Function, Lua, LuaString, Value}; use yazi_core::tasks; use super::Utils; @@ -6,7 +6,7 @@ use crate::tasks::TaskOpt; impl Utils { pub(super) fn task(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (kind, value): (mlua::String, Value)| { + lua.create_function(|lua, (kind, value): (LuaString, Value)| { Ok(TaskOpt(match &*kind.as_bytes() { b"cut" => tasks::TaskOpt::Cut(<_>::from_lua(value, lua)?), diff --git a/yazi-plugin/src/utils/text.rs b/yazi-plugin/src/utils/text.rs index f5522195..f9523424 100644 --- a/yazi-plugin/src/utils/text.rs +++ b/yazi-plugin/src/utils/text.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use mlua::{Function, Lua}; +use mlua::{Function, Lua, LuaString}; use twox_hash::XxHash3_128; use yazi_shim::RFC_3986; use yazi_widgets::CLIPBOARD; @@ -9,13 +9,13 @@ use super::Utils; impl Utils { pub(super) fn hash(lua: &Lua) -> mlua::Result { - lua.create_async_function(move |_, s: mlua::String| async move { + lua.create_async_function(move |_, s: LuaString| async move { Ok(format!("{:x}", XxHash3_128::oneshot(&s.as_bytes()))) }) } pub(super) fn quote(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (s, unix): (mlua::String, Option)| { + lua.create_function(|lua, (s, unix): (LuaString, Option)| { let b = s.as_bytes(); let s = match unix { Some(true) => yazi_shared::shell::unix::escape_os_bytes(&b), @@ -38,7 +38,7 @@ impl Utils { } pub(super) fn percent_decode(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, s: mlua::String| { + lua.create_function(|lua, s: LuaString| { let b = s.as_bytes(); match percent_encoding::percent_decode(&b).into() { Cow::Borrowed(_) => Ok(s), @@ -48,7 +48,7 @@ impl Utils { } pub(super) fn percent_encode(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, s: mlua::String| { + lua.create_function(|lua, s: LuaString| { let b = s.as_bytes(); match percent_encoding::percent_encode(&b, RFC_3986).into() { Cow::Borrowed(_) => Ok(s), diff --git a/yazi-proxy/src/tasks.rs b/yazi-proxy/src/tasks.rs index bc64a3df..e89200b1 100644 --- a/yazi-proxy/src/tasks.rs +++ b/yazi-proxy/src/tasks.rs @@ -1,11 +1,9 @@ -use std::ffi::OsString; - use anyhow::{Result, anyhow}; use tokio::sync::mpsc; use yazi_core::tasks::TaskOpt; use yazi_macro::{emit, relay}; -use yazi_scheduler::process::ProcessOpt; -use yazi_shared::{id::Id, url::{UrlBuf, UrlCow}}; +use yazi_scheduler::process::ShellOpt; +use yazi_shared::id::Id; pub struct TasksProxy; @@ -17,24 +15,13 @@ impl TasksProxy { rx.recv().await.ok_or_else(|| anyhow!("channel closed"))??.try_into() } - // TODO: remove - pub fn open_shell_compat(opt: ProcessOpt) { - emit!(Call(relay!(tasks:open_shell_compat).with_any("opt", opt))); + pub fn process_open(opt: ShellOpt) { + emit!(Call(relay!(tasks:process_open).with_any("opt", opt))); } - pub async fn process_exec( - cwd: UrlBuf, - cmd: OsString, - args: Vec>, - block: bool, - orphan: bool, - ) { + pub async fn process_exec(opt: ShellOpt) { let (tx, mut rx) = mpsc::unbounded_channel(); - emit!(Call( - relay!(tasks:process_open) - .with_any("opt", ProcessOpt { cwd, cmd, args, block, orphan, spread: false }) - .with_replier(tx) - )); + emit!(Call(relay!(tasks:process_open).with_any("opt", opt).with_replier(tx))); rx.recv().await; } } diff --git a/yazi-runner/src/loader/chunk.rs b/yazi-runner/src/loader/chunk.rs index 33cff2b9..9ee38aed 100644 --- a/yazi-runner/src/loader/chunk.rs +++ b/yazi-runner/src/loader/chunk.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use mlua::{AsChunk, ChunkMode}; +use mlua::chunk::{AsChunk, ChunkMode}; use yazi_shared::natsort; pub struct Chunk { diff --git a/yazi-runner/src/loader/loader.rs b/yazi-runner/src/loader/loader.rs index 6f69da1a..6d118786 100644 --- a/yazi-runner/src/loader/loader.rs +++ b/yazi-runner/src/loader/loader.rs @@ -2,7 +2,7 @@ use std::{borrow::Cow, ops::Deref}; use anyhow::{Context, Result, bail, ensure}; use hashbrown::HashMap; -use mlua::{ChunkMode, ExternalError, Lua, Table}; +use mlua::{ExternalError, Lua, Table, chunk::ChunkMode}; use parking_lot::RwLock; use yazi_fs::{Xdg, provider::local::Local}; use yazi_macro::plugin_preset as preset; diff --git a/yazi-runner/src/loader/require.rs b/yazi-runner/src/loader/require.rs index 507588fc..10ad47f2 100644 --- a/yazi-runner/src/loader/require.rs +++ b/yazi-runner/src/loader/require.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, sync::Arc}; -use mlua::{ExternalResult, Function, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, Table, Value}; +use mlua::{ExternalResult, Function, IntoLua, Lua, LuaString, MetaMethod, MultiValue, ObjectLike, Table, Value}; use yazi_binding::{runtime, runtime_mut}; use super::LOADER; @@ -11,7 +11,7 @@ impl Require { pub(super) fn install(lua: &Lua) -> mlua::Result<()> { lua.globals().raw_set( "require", - lua.create_async_function(|lua, id: mlua::String| async move { + lua.create_async_function(|lua, id: LuaString| async move { let id = id.to_str()?; let id = Self::absolute_id(&lua, &id)?; LOADER.ensure(&id, |_| ()).await.into_lua_err()?; @@ -30,7 +30,7 @@ impl Require { let mt = lua.create_table_from([ ( MetaMethod::Index.name(), - lua.create_function(move |lua, (ts, key): (Table, mlua::String)| { + lua.create_function(move |lua, (ts, key): (Table, LuaString)| { match ts.raw_get::
("__mod")?.raw_get::(&key)? { Value::Function(_) => { Self::create_wrapper(lua, id.clone(), &key.to_str()?)?.into_lua(lua) @@ -41,7 +41,7 @@ impl Require { ), ( MetaMethod::NewIndex.name(), - lua.create_function(move |_, (ts, key, value): (Table, mlua::String, Value)| { + lua.create_function(move |_, (ts, key, value): (Table, LuaString, Value)| { ts.raw_get::
("__mod")?.raw_set(key, value) })?, ), diff --git a/yazi-scheduler/src/process/in.rs b/yazi-scheduler/src/process/in.rs index 082742f1..1d1fd23d 100644 --- a/yazi-scheduler/src/process/in.rs +++ b/yazi-scheduler/src/process/in.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, ffi::OsString}; -use yazi_shared::{CompletionToken, id::Id, url::{UrlBuf, UrlCow}}; +use yazi_shared::{CompletionToken, id::Id, url::UrlBuf}; use super::ShellOpt; use crate::{TaskIn, process::{ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}}; @@ -46,10 +46,9 @@ impl TaskIn for ProcessIn { // --- Block #[derive(Debug)] pub(crate) struct ProcessInBlock { - pub(crate) id: Id, - pub(crate) cwd: UrlBuf, - pub(crate) cmd: OsString, - pub(crate) args: Vec>, + pub(crate) id: Id, + pub(crate) cwd: UrlBuf, + pub(crate) cmd: OsString, } impl TaskIn for ProcessInBlock { @@ -67,17 +66,16 @@ impl TaskIn for ProcessInBlock { impl From for ShellOpt { fn from(r#in: ProcessInBlock) -> Self { - Self { cwd: r#in.cwd, cmd: r#in.cmd, args: r#in.args, piped: false, orphan: false } + Self { cwd: r#in.cwd, cmd: r#in.cmd, block: true, orphan: false } } } // --- Orphan #[derive(Debug)] pub(crate) struct ProcessInOrphan { - pub(crate) id: Id, - pub(crate) cwd: UrlBuf, - pub(crate) cmd: OsString, - pub(crate) args: Vec>, + pub(crate) id: Id, + pub(crate) cwd: UrlBuf, + pub(crate) cmd: OsString, } impl TaskIn for ProcessInOrphan { @@ -95,7 +93,7 @@ impl TaskIn for ProcessInOrphan { impl From for ShellOpt { fn from(r#in: ProcessInOrphan) -> Self { - Self { cwd: r#in.cwd, cmd: r#in.cmd, args: r#in.args, piped: false, orphan: true } + Self { cwd: r#in.cwd, cmd: r#in.cmd, block: false, orphan: true } } } @@ -105,7 +103,6 @@ pub(crate) struct ProcessInBg { pub(crate) id: Id, pub(crate) cwd: UrlBuf, pub(crate) cmd: OsString, - pub(crate) args: Vec>, pub(crate) done: CompletionToken, } @@ -124,6 +121,6 @@ impl TaskIn for ProcessInBg { impl From for ShellOpt { fn from(r#in: ProcessInBg) -> Self { - Self { cwd: r#in.cwd, cmd: r#in.cmd, args: r#in.args, piped: true, orphan: false } + Self { cwd: r#in.cwd, cmd: r#in.cmd, block: false, orphan: false } } } diff --git a/yazi-scheduler/src/process/mod.rs b/yazi-scheduler/src/process/mod.rs index 6bbfc5f7..7db19e8f 100644 --- a/yazi-scheduler/src/process/mod.rs +++ b/yazi-scheduler/src/process/mod.rs @@ -1,4 +1,4 @@ #[macro_use] mod macros; -yazi_macro::mod_flat!(option out process progress r#in shell); +yazi_macro::mod_flat!(out process progress r#in shell); diff --git a/yazi-scheduler/src/process/option.rs b/yazi-scheduler/src/process/option.rs deleted file mode 100644 index 6b95af14..00000000 --- a/yazi-scheduler/src/process/option.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::ffi::OsString; - -use yazi_macro::impl_data_any; -use yazi_shared::url::{UrlBuf, UrlCow}; - -// TODO: remove in favor of ShellForm -#[derive(Clone, Debug)] -pub struct ProcessOpt { - pub cwd: UrlBuf, - pub cmd: OsString, - pub args: Vec>, - pub block: bool, - pub orphan: bool, - - pub spread: bool, // TODO: remove -} - -impl_data_any!(ProcessOpt); diff --git a/yazi-scheduler/src/process/process.rs b/yazi-scheduler/src/process/process.rs index b85594bd..770c1832 100644 --- a/yazi-scheduler/src/process/process.rs +++ b/yazi-scheduler/src/process/process.rs @@ -54,14 +54,8 @@ impl Process { } pub(crate) async fn bg(&self, task: ProcessInBg) -> Result<(), ProcessOutBg> { - let mut child = super::shell(ShellOpt { - cwd: task.cwd, - cmd: task.cmd, - args: task.args, - piped: true, - orphan: false, - }) - .await?; + let mut child = + super::shell(ShellOpt { cwd: task.cwd, cmd: task.cmd, block: false, orphan: false }).await?; let done = task.done; let mut stdout = BufReader::new(child.stdout.take().unwrap()).lines(); diff --git a/yazi-scheduler/src/process/shell.rs b/yazi-scheduler/src/process/shell.rs index 59c580ae..2f6f95ef 100644 --- a/yazi-scheduler/src/process/shell.rs +++ b/yazi-scheduler/src/process/shell.rs @@ -3,25 +3,28 @@ use std::{ffi::OsString, process::Stdio}; use anyhow::Result; use tokio::process::{Child, Command}; use yazi_fs::Cwd; -use yazi_shared::url::{AsUrl, UrlBuf, UrlCow}; +use yazi_macro::impl_data_any; +use yazi_shared::url::{AsUrl, UrlBuf}; -pub(crate) struct ShellOpt { - pub(crate) cwd: UrlBuf, - pub(crate) cmd: OsString, - pub(crate) args: Vec>, - pub(crate) piped: bool, - pub(crate) orphan: bool, +#[derive(Clone, Debug)] +pub struct ShellOpt { + pub cwd: UrlBuf, + pub cmd: OsString, + pub block: bool, + pub orphan: bool, } +impl_data_any!(ShellOpt); + impl ShellOpt { #[inline] fn stdio(&self) -> Stdio { - if self.orphan { - Stdio::null() - } else if self.piped { - Stdio::piped() - } else { + if self.block { Stdio::inherit() + } else if self.orphan { + Stdio::null() + } else { + Stdio::piped() } } } @@ -32,21 +35,16 @@ pub(crate) async fn shell(opt: ShellOpt) -> Result { #[cfg(unix)] return Ok(unsafe { - use yazi_fs::FsUrl; - use yazi_shared::url::AsUrl; - Command::new("sh") .stdin(opt.stdio()) .stdout(opt.stdio()) .stderr(opt.stdio()) .arg("-c") .arg(opt.cmd) - // TODO: remove - .args(opt.args.iter().map(|u| u.as_url().unified_path_str())) .current_dir(cwd) .kill_on_drop(!opt.orphan) .pre_exec(move || { - if (opt.piped || opt.orphan) && libc::setsid() < 0 { + if !opt.block && libc::setsid() < 0 { return Err(std::io::Error::last_os_error()); } Ok(()) diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index e52376e9..b8dae4c3 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -5,7 +5,7 @@ use yazi_config::{YAZI, plugin::{FetcherArc, PreloaderArc}}; use yazi_fs::FsHash64; use yazi_shared::{CompletionToken, Throttle, id::Id, url::{UrlBuf, UrlLike}}; -use crate::{Behavior, HIGH, LOW, NORMAL, Task, TaskIn, TaskProg, Worker, fetch::FetchIn, file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload}, hook::{HookIn, HookInDelete, HookInDownload, HookInPreload, HookInTrash, HookInUpload}, plugin::PluginInEntry, preload::PreloadIn, process::{ProcessIn, ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessOpt}, size::SizeIn}; +use crate::{Behavior, HIGH, LOW, NORMAL, Task, TaskIn, TaskProg, Worker, fetch::FetchIn, file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload}, hook::{HookIn, HookInDelete, HookInDownload, HookInPreload, HookInTrash, HookInUpload}, plugin::PluginInEntry, preload::PreloadIn, process::{ProcessIn, ProcessInBg, ProcessInBlock, ProcessInOrphan, ShellOpt}, size::SizeIn}; pub struct Scheduler { pub worker: Worker, @@ -227,20 +227,14 @@ impl Scheduler { } } - pub fn process_open(&self, opt: ProcessOpt) -> CompletionToken { + pub fn process_open(&self, opt: ShellOpt) -> CompletionToken { let mut r#in: ProcessIn = if opt.block { - ProcessInBlock { id: Id::ZERO, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }.into() + ProcessInBlock { id: Id::ZERO, cwd: opt.cwd, cmd: opt.cmd }.into() } else if opt.orphan { - ProcessInOrphan { id: Id::ZERO, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }.into() + ProcessInOrphan { id: Id::ZERO, cwd: opt.cwd, cmd: opt.cmd }.into() } else { - ProcessInBg { - id: Id::ZERO, - cwd: opt.cwd, - cmd: opt.cmd, - args: opt.args, - done: CompletionToken::default(), - } - .into() + ProcessInBg { id: Id::ZERO, cwd: opt.cwd, cmd: opt.cmd, done: CompletionToken::default() } + .into() }; let done = match &mut r#in { diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 56cc7e1a..eff058bd 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -26,7 +26,7 @@ futures = { workspace = true } hashbrown = { workspace = true } indexmap = { workspace = true } inventory = { workspace = true } -memchr = "2.8.2" +memchr = "2.8.3" mlua = { workspace = true } ordered-float = { workspace = true } parking_lot = { workspace = true } diff --git a/yazi-shared/src/any_data.rs b/yazi-shared/src/any_data.rs index ecd4208e..a5204bf8 100644 --- a/yazi-shared/src/any_data.rs +++ b/yazi-shared/src/any_data.rs @@ -2,7 +2,5 @@ use mlua::UserData; use crate::data::DataAny; -#[derive(Debug)] +#[derive(Debug, UserData)] pub struct AnyData(pub Box); - -impl UserData for AnyData {} diff --git a/yazi-shared/src/id/lua.rs b/yazi-shared/src/id/lua.rs index 15b68077..30a7e9c3 100644 --- a/yazi-shared/src/id/lua.rs +++ b/yazi-shared/src/id/lua.rs @@ -1,6 +1,6 @@ -use mlua::{ExternalError, ExternalResult, FromLua, Lua, UserData, UserDataFields, Value}; +use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, Value}; -use crate::id::Id; +use crate::{LOG_LEVEL, id::Id}; impl FromLua for Id { fn from_lua(value: Value, _: &Lua) -> mlua::Result { @@ -16,4 +16,12 @@ impl UserData for Id { fn add_fields>(fields: &mut F) { fields.add_field_method_get("value", |_, me| Ok(me.get())); } + + fn add_methods>(methods: &mut M) { + if !LOG_LEVEL.get().is_none() { + methods.add_meta_function(MetaMethod::ToDebugString, |_, ud: AnyUserData| { + Ok(format!("Id({:?}): {}", ud.to_pointer(), *ud.borrow::()?)) + }); + } + } } diff --git a/yazi-shared/src/layer.rs b/yazi-shared/src/layer.rs index 53ef3f91..bb6c1227 100644 --- a/yazi-shared/src/layer.rs +++ b/yazi-shared/src/layer.rs @@ -1,4 +1,4 @@ -use mlua::{ExternalError, ExternalResult, FromLua, MetaMethod, UserData, UserDataMethods, Value}; +use mlua::{ExternalError, ExternalResult, FromLua, Lua, MetaMethod, UserData, UserDataMethods, Value}; use serde::Deserialize; use strum::{Display, EnumString, FromRepr, IntoStaticStr}; use yazi_shim::strum::IntoStr; @@ -41,7 +41,7 @@ impl Layer { } impl FromLua for Layer { - fn from_lua(value: Value, _: &mlua::Lua) -> mlua::Result { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { Ok(match value { Value::String(s) => s.to_str()?.parse().into_lua_err()?, Value::UserData(ud) => *ud.borrow::()?, diff --git a/yazi-shared/src/path/lua.rs b/yazi-shared/src/path/lua.rs index a087470e..c53d9ffe 100644 --- a/yazi-shared/src/path/lua.rs +++ b/yazi-shared/src/path/lua.rs @@ -1,7 +1,7 @@ -use mlua::{ExternalError, ExternalResult, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value}; +use mlua::{AnyUserData, ExternalError, ExternalResult, Lua, LuaString, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value}; use yazi_shim::mlua::UserDataFieldsExt; -use crate::{path::{PathBufDyn, PathLike, StripPrefixError}, strand::{AsStrand, StrandCow}}; +use crate::{LOG_LEVEL, path::{PathBufDyn, PathLike, StripPrefixError}, strand::{AsStrand, StrandCow}}; pub type PathRef = UserDataRef; @@ -11,7 +11,7 @@ impl PathBufDyn { "Path", lua.create_table_from([( "os", - lua.create_function(|_, s: mlua::String| { + lua.create_function(|_, s: LuaString| { Ok(Self::from(s.as_bytes().as_strand().as_os_path().into_lua_err()?)) })?, )])?, @@ -89,11 +89,17 @@ impl UserData for PathBufDyn { methods.add_method("starts_with", |_, me, base: Value| me.starts_with(base)); methods.add_method("strip_prefix", |_, me, base: Value| me.strip_prefix(base)); - methods.add_meta_method(MetaMethod::Concat, |lua, lhs, rhs: mlua::String| { + methods.add_meta_method(MetaMethod::Concat, |lua, lhs, rhs: LuaString| { lua.create_external_string([lhs.encoded_bytes(), &rhs.as_bytes()].concat()) }); methods.add_meta_method(MetaMethod::Eq, |_, me, other: PathRef| Ok(*me == *other)); methods .add_meta_method(MetaMethod::ToString, |lua, me, ()| lua.create_string(me.encoded_bytes())); + + if !LOG_LEVEL.get().is_none() { + methods.add_meta_function(MetaMethod::ToDebugString, |_, ud: AnyUserData| { + Ok(format!("Path({:?}): {:?}", ud.to_pointer(), *ud.borrow::()?)) + }); + } } } diff --git a/yazi-shared/src/url/lua.rs b/yazi-shared/src/url/lua.rs index bd8e787a..559a5fff 100644 --- a/yazi-shared/src/url/lua.rs +++ b/yazi-shared/src/url/lua.rs @@ -1,7 +1,7 @@ -use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, UserDataRegistry, Value}; +use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, LuaString, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, UserDataRegistry, Value}; use yazi_shim::mlua::UserDataFieldsExt; -use crate::{path::{PathBufDyn, PathLike, StripPrefixError}, scheme::{SchemeCow, SchemeLike}, strand::{StrandLike, ToStrand}, url::{UrlBuf, UrlBufInventory, UrlCow, UrlLike}}; +use crate::{LOG_LEVEL, path::{PathBufDyn, PathLike, StripPrefixError}, scheme::{SchemeCow, SchemeLike}, strand::{StrandLike, ToStrand}, url::{UrlBuf, UrlBufInventory, UrlCow, UrlLike}}; pub type UrlRef = UserDataRef; @@ -124,17 +124,23 @@ impl UserData for UrlBuf { methods.add_method("starts_with", |_, me, base: Value| me.starts_with(base)); methods.add_method("strip_prefix", |_, me, base: Value| me.strip_prefix(base)); - methods.add_function("into_search", |_, (ud, domain): (AnyUserData, mlua::String)| { - ud.take::()?.into_search(domain.to_str()?).into_lua_err() + methods.add_method_once("into_search", |_, me, domain: LuaString| { + me.into_search(domain.to_str()?).into_lua_err() }); methods.add_meta_method(MetaMethod::Eq, |_, me, other: UrlRef| Ok(*me == *other)); methods.add_meta_method(MetaMethod::ToString, |lua, me, ()| { lua.create_string(me.to_strand().encoded_bytes()) }); - methods.add_meta_method(MetaMethod::Concat, |lua, lhs, rhs: mlua::String| { + methods.add_meta_method(MetaMethod::Concat, |lua, lhs, rhs: LuaString| { lua.create_external_string([lhs.to_strand().encoded_bytes(), &rhs.as_bytes()].concat()) }); + + if !LOG_LEVEL.get().is_none() { + methods.add_meta_function(MetaMethod::ToDebugString, |_, ud: AnyUserData| { + Ok(format!("Url({:?}): {:?}", ud.to_pointer(), *ud.borrow::()?)) + }); + } } fn register(registry: &mut UserDataRegistry) { diff --git a/yazi-shim/src/mlua/ser.rs b/yazi-shim/src/mlua/ser.rs index f7c55eb2..ebddd5db 100644 --- a/yazi-shim/src/mlua/ser.rs +++ b/yazi-shim/src/mlua/ser.rs @@ -1,4 +1,4 @@ -use mlua::SerializeOptions; +use mlua::serde::SerializeOptions; pub const SER_OPT: SerializeOptions = SerializeOptions::new().serialize_none_to_null(false).serialize_unit_to_null(false); diff --git a/yazi-shim/src/mlua/string.rs b/yazi-shim/src/mlua/string.rs index 176ecb79..866bc44a 100644 --- a/yazi-shim/src/mlua/string.rs +++ b/yazi-shim/src/mlua/string.rs @@ -1,12 +1,12 @@ use std::{fmt, ops::Deref}; -use mlua::{FromLua, IntoLua, Lua, Value}; +use mlua::{FromLua, IntoLua, Lua, LuaString, Value}; #[derive(Clone)] -pub struct ByteString(mlua::String); +pub struct ByteString(LuaString); impl Deref for ByteString { - type Target = mlua::String; + type Target = LuaString; fn deref(&self) -> &Self::Target { &self.0 } } @@ -17,7 +17,7 @@ impl fmt::Display for ByteString { impl FromLua for ByteString { fn from_lua(value: Value, lua: &Lua) -> mlua::Result { - Ok(Self(mlua::String::from_lua(value, lua)?)) + Ok(Self(LuaString::from_lua(value, lua)?)) } } diff --git a/yazi-term/Cargo.toml b/yazi-term/Cargo.toml index a41e5553..42a01089 100644 --- a/yazi-term/Cargo.toml +++ b/yazi-term/Cargo.toml @@ -21,6 +21,7 @@ yazi-tty = { path = "../yazi-tty", version = "26.5.9" } anyhow = { workspace = true } base64 = { workspace = true } bitflags = { workspace = true } +compact_str = { workspace = true } futures = { workspace = true } mlua = { workspace = true } parking_lot = { workspace = true } diff --git a/yazi-term/src/event/keyboard.rs b/yazi-term/src/event/keyboard.rs index b94130da..6952eeb8 100644 --- a/yazi-term/src/event/keyboard.rs +++ b/yazi-term/src/event/keyboard.rs @@ -1,41 +1,39 @@ use bitflags::bitflags; +use compact_str::CompactString; use serde::{Deserialize, Serialize}; use crate::{ParseError, Result, bail, event::Modifiers}; -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize)] pub struct KeyEvent { #[serde(flatten)] pub code: KeyCode, pub kind: KeyEventKind, pub modifiers: Modifiers, pub state: KeyEventState, + #[serde(skip_serializing_if = "CompactString::is_empty")] + pub text: CompactString, } impl KeyEvent { - pub const fn new(code: KeyCode, modifiers: Modifiers) -> Self { - Self { code, kind: KeyEventKind::Press, modifiers, state: KeyEventState::empty() } + pub fn new(code: KeyCode, modifiers: Modifiers) -> Self { + Self { code, modifiers, ..Default::default() } } - pub fn plain(&self) -> Option { + pub fn text<'a>(&'a self, buf: &'a mut [u8; 4]) -> Option<&'a str> { use Modifiers as M; match self.code { - KeyCode::Char(c) if !self.modifiers.intersects(M::CONTROL | M::ALT | M::SUPER) => Some(c), + _ if self.modifiers.intersects(M::CONTROL | M::ALT | M::SUPER) => None, + KeyCode::Char(c) if self.text.is_empty() => Some(c.encode_utf8(buf)), + KeyCode::Char(_) | KeyCode::Null if !self.text.is_empty() => Some(&self.text), _ => None, } } } impl From for KeyEvent { - fn from(code: KeyCode) -> Self { - Self { - code, - kind: KeyEventKind::Press, - modifiers: Modifiers::empty(), - state: KeyEventState::empty(), - } - } + fn from(code: KeyCode) -> Self { Self { code, ..Default::default() } } } // --- Kind @@ -116,6 +114,8 @@ pub enum KeyCode { } impl KeyCode { + pub fn implies_shift(self) -> bool { matches!(self, Self::Char(c) if c.is_uppercase()) } + pub(crate) fn from_xterm_modifier(r#final: u8) -> Result { Ok(match r#final { b'A' => Self::Up, @@ -138,6 +138,7 @@ impl KeyCode { } let code = match char::from_u32(codepoint).ok_or(ParseError::Invalid)? { + '\0' => Self::Null, '\x1B' => Self::Escape, '\r' => Self::Enter, '\t' => Self::Tab, diff --git a/yazi-term/src/parser/csi.rs b/yazi-term/src/parser/csi.rs index 82a40b1a..b5b733fa 100644 --- a/yazi-term/src/parser/csi.rs +++ b/yazi-term/src/parser/csi.rs @@ -1,5 +1,7 @@ use std::str::{self, FromStr}; +use compact_str::CompactString; + use super::parser::Parser; use crate::{ParseError, Result, bail, event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, Modifiers, MouseEvent, MouseEventKind}}; @@ -84,8 +86,10 @@ impl Parser { let (mut code, state_from_keycode) = KeyCode::from_codepoint(parse_next(&mut codepoints)?)?; let (mut modifiers, kind, state_from_modifiers) = parse_mks(&mut it).unwrap_or_default(); + let text = parse_text(it.next().unwrap_or_default())?; if let KeyCode::Modifier(c) = code + && kind != KeyEventKind::Release && let Some(m) = c.to_modifier() { modifiers |= m; @@ -97,9 +101,10 @@ impl Parser { // which contains the shifted character according to the keyboard layout. if modifiers.contains(Modifiers::SHIFT) && let Ok(Some(shifted)) = parse_next(&mut codepoints).map(char::from_u32) + && code != KeyCode::Char(shifted) { code = KeyCode::Char(shifted); - modifiers.remove(Modifiers::SHIFT); + modifiers.set(Modifiers::SHIFT, code.implies_shift()); } Ok(Event::Key(KeyEvent { @@ -107,6 +112,7 @@ impl Parser { modifiers, kind, state: state_from_keycode | state_from_modifiers, + text, })) } @@ -123,7 +129,7 @@ impl Parser { let (modifiers, kind, _) = parse_mks(&mut it).unwrap_or_default(); let code = KeyCode::from_xterm_modifier(seq[seq.len() - 1])?; - Ok(Event::Key(KeyEvent { code, modifiers, kind, state: KeyEventState::empty() })) + Ok(Event::Key(KeyEvent { code, modifiers, kind, ..Default::default() })) } /// Parses legacy `CSI modifier final` - no semicolon, modifier digit @@ -138,10 +144,9 @@ impl Parser { } Ok(Event::Key(KeyEvent { - code: KeyCode::from_xterm_modifier(seq[seq.len() - 1])?, + code: KeyCode::from_xterm_modifier(seq[seq.len() - 1])?, modifiers: Modifiers::from_vt_mask(modifier - b'0'), - kind: KeyEventKind::Press, - state: KeyEventState::empty(), + ..Default::default() })) } @@ -173,7 +178,7 @@ impl Parser { _ => bail!(), }; - let event = Event::Key(KeyEvent { code, modifiers, kind, state }); + let event = Event::Key(KeyEvent { code, modifiers, kind, state, ..Default::default() }); Ok(event) } @@ -277,3 +282,13 @@ where KeyEventState::from_vt_mask(mask), )) } + +fn parse_text(s: &str) -> Result { + if s.is_empty() { + return Ok(CompactString::default()); + } + + s.split(':') + .map(|codepoint| char::from_u32(codepoint.parse()?).ok_or(ParseError::Invalid)) + .collect() +} diff --git a/yazi-tty/src/sequence/query.rs b/yazi-tty/src/sequence/query.rs index f43ba3f7..34785a20 100644 --- a/yazi-tty/src/sequence/query.rs +++ b/yazi-tty/src/sequence/query.rs @@ -51,13 +51,6 @@ impl Display for RequestCursorBlink { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("\x1b[?12$p") } } -/// Request keyboard enhancement flags (CSI u query) -pub struct RequestKeyboardFlags; - -impl Display for RequestKeyboardFlags { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("\x1b[?u") } -} - /// Device Status Report (DSR) pub struct RequestDeviceStatus; diff --git a/yazi-tui/src/raterm.rs b/yazi-tui/src/raterm.rs index 24b8a259..4908d59c 100644 --- a/yazi-tui/src/raterm.rs +++ b/yazi-tui/src/raterm.rs @@ -1,4 +1,4 @@ -use std::{io::{self, Write}, ops::Deref}; +use std::{io, ops::Deref}; use anyhow::Result; use ratatui_core::{buffer::Buffer, layout::Rect, terminal::{CompletedFrame, Frame, Terminal}}; @@ -8,7 +8,7 @@ use yazi_macro::writef; use yazi_proxy::AppProxy; use yazi_shim::cell::SyncCell; use yazi_term::{TERM, event::{Event, KeyEventKind}, stream::EventStream}; -use yazi_tty::{TTY, TtyWriter, sequence::{DisableBracketedPaste, DisableDrag, DisableDrop, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, EnableDrag, EnableDrop, EnableFocusChange, EnableMouseCapture, EnterAlternateScreen, If, LeaveAlternateScreen, PopKeyboardFlags, PushKeyboardFlags, RequestCursorBlink, RequestCursorStyle, RequestDA1, RequestKeyboardFlags, RestoreCursorStyle, SetTitle, ShowCursor}}; +use yazi_tty::{TTY, TtyWriter, sequence::{DisableBracketedPaste, DisableDrag, DisableDrop, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, EnableDrag, EnableDrop, EnableFocusChange, EnableMouseCapture, EnterAlternateScreen, If, LeaveAlternateScreen, PopKeyboardFlags, PushKeyboardFlags, RequestCursorBlink, RequestCursorStyle, RequestDA1, RestoreCursorStyle, SetTitle, ShowCursor}}; use crate::{RatermBackend, RatermOption, RatermState}; @@ -43,9 +43,12 @@ impl Raterm { let opt = RatermOption::default(); writef!( TTY.writer(), - "{}{RequestCursorStyle}{RequestCursorBlink}{RequestKeyboardFlags}{RequestDA1}{}{EnableBracketedPaste}{EnableFocusChange}{}{}{}", + "{}{RequestCursorStyle}{RequestCursorBlink}{RequestDA1}{}{EnableBracketedPaste}{EnableFocusChange}{}{}{}{}", If(!TMUX.get(), EnterAlternateScreen), If(TMUX.get(), EnterAlternateScreen), + PushKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES + | PushKeyboardFlags::REPORT_ALTERNATE_KEYS + | PushKeyboardFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES, EnableDrag(""), EnableDrop(&["text/uri-list"]), If(opt.mouse, EnableMouseCapture), @@ -55,14 +58,6 @@ impl Raterm { Mux::tmux_drain()?; STATE.set(RatermState::new(&resp, &opt)); - if STATE.get().csi_u { - write!( - TTY.writer(), - "{}", - PushKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES | PushKeyboardFlags::REPORT_ALTERNATE_KEYS, - )?; - } - let mut term = Self { inner: Terminal::new(RatermBackend::new(TTY.writer()))?, stream: EventStream::from(&*TERM), @@ -82,9 +77,8 @@ impl Raterm { _ = writef!( TTY.writer(), - "{}{DisableDrop}{DisableDrag}{}{}{}{DisableFocusChange}{DisableBracketedPaste}{LeaveAlternateScreen}{ShowCursor}", + "{}{PopKeyboardFlags}{DisableDrop}{DisableDrag}{}{}{DisableFocusChange}{DisableBracketedPaste}{LeaveAlternateScreen}{ShowCursor}", If(state.mouse, DisableMouseCapture), - If(state.csi_u, PopKeyboardFlags), RestoreCursorStyle { shape: state.cursor_shape, blink: state.cursor_blink }, If(state.title, SetTitle("")), ); @@ -100,7 +94,7 @@ impl Raterm { loop { match rx.recv().await { Some(Ok(event)) => match event { - Event::Key(key) if key.kind != KeyEventKind::Press => continue, + Event::Key(key) if key.kind == KeyEventKind::Release => continue, Event::Mouse(mouse) if !YAZI.mgr.mouse_events.get().contains(mouse.kind.into()) => { continue; } diff --git a/yazi-tui/src/state.rs b/yazi-tui/src/state.rs index 645e3c43..445ab719 100644 --- a/yazi-tui/src/state.rs +++ b/yazi-tui/src/state.rs @@ -2,7 +2,6 @@ use crate::RatermOption; #[derive(Clone, Copy)] pub struct RatermState { - pub csi_u: bool, pub mouse: bool, pub title: bool, pub cursor_shape: u8, @@ -11,18 +10,10 @@ pub struct RatermState { impl RatermState { pub(super) const fn default() -> Self { - Self { - csi_u: false, - mouse: false, - title: false, - cursor_shape: 0, - cursor_blink: false, - } + Self { mouse: false, title: false, cursor_shape: 0, cursor_blink: false } } pub(super) fn new(resp: &str, opt: &RatermOption) -> Self { - let csi_u = resp.contains("\x1b[?0u"); - let cursor_shape = resp .split_once("\x1bP1$r") .and_then(|(_, s)| s.bytes().next()) @@ -31,6 +22,6 @@ impl RatermState { let cursor_blink = resp.contains("\x1b[?12;1$y"); - Self { csi_u, mouse: opt.mouse, title: false, cursor_shape, cursor_blink } + Self { mouse: opt.mouse, title: false, cursor_shape, cursor_blink } } } diff --git a/yazi-vfs/src/provider/lua.rs b/yazi-vfs/src/provider/lua.rs index 5e86b21e..e61ba9bd 100644 --- a/yazi-vfs/src/provider/lua.rs +++ b/yazi-vfs/src/provider/lua.rs @@ -1,4 +1,4 @@ -use mlua::{IntoLuaMulti, UserData, UserDataMethods, Value}; +use mlua::{IntoLuaMulti, LuaString, UserData, UserDataMethods, Value}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use yazi_binding::Error; @@ -22,7 +22,7 @@ impl UserData for RwFile { Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } }); - methods.add_async_method_mut("write_all", |lua, mut me, src: mlua::String| async move { + methods.add_async_method_mut("write_all", |lua, mut me, src: LuaString| async move { match me.write_all(&src.as_bytes()).await { Ok(()) => true.into_lua_multi(&lua), Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), diff --git a/yazi-widgets/src/input/actor/type.rs b/yazi-widgets/src/input/actor/type.rs index 2de0332c..950b492a 100644 --- a/yazi-widgets/src/input/actor/type.rs +++ b/yazi-widgets/src/input/actor/type.rs @@ -6,14 +6,15 @@ use yazi_term::event::KeyEvent; use crate::input::{Input, InputMode}; impl Input { - pub fn r#type(&mut self, key: KeyEvent) -> Result { - let Some(c) = key.plain() else { return Ok(false) }; + pub fn r#type(&mut self, key: &KeyEvent) -> Result { + let mut buf = [0; 4]; + let Some(text) = key.text(&mut buf) else { return Ok(false) }; if self.mode() == InputMode::Insert { - self.type_str(c.encode_utf8(&mut [0; 4]))?; + self.type_str(text)?; return Ok(true); } else if self.mode() == InputMode::Replace { - self.replace_str(c.encode_utf8(&mut [0; 4]))?; + self.replace_str(text)?; return Ok(true); }