diff --git a/.cargo/config.toml b/.cargo/config.toml index 4e52ead7..29a55dea 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -11,3 +11,6 @@ JEMALLOC_SYS_WITH_LG_PAGE = "16" # # Improve memory efficiency by reducing fragmentation and ensuring all threads allocate from the same pool JEMALLOC_SYS_WITH_MALLOC_CONF = "narenas:1" + +[target.aarch64-apple-darwin] +rustflags = [ "-Ctarget-cpu=apple-m1" ] diff --git a/Cargo.lock b/Cargo.lock index 2ec600e0..81e9122c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,9 +273,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "serde", @@ -328,9 +328,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.17" +version = "1.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" dependencies = [ "jobserver", "libc", @@ -367,9 +367,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.34" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -377,9 +377,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.34" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -507,9 +507,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -552,7 +552,7 @@ dependencies = [ "libc", "mio", "parking_lot", - "rustix", + "rustix 0.38.44", "signal-hook", "signal-hook-mio", "winapi", @@ -585,9 +585,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -595,9 +595,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", @@ -609,9 +609,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", @@ -620,9 +620,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -725,9 +725,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -809,9 +809,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -945,7 +945,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -979,9 +991,9 @@ dependencies = [ [[package]] name = "half" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -1006,9 +1018,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "iana-time-zone" -version = "0.1.62" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1016,7 +1028,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core 0.61.0", ] [[package]] @@ -1073,9 +1085,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", @@ -1164,10 +1176,11 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] @@ -1246,6 +1259,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "lock_api" version = "0.4.12" @@ -1359,9 +1378,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -1375,7 +1394,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1603,9 +1622,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "onig" @@ -1833,6 +1852,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -1860,7 +1885,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -1945,9 +1970,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags 2.9.0", ] @@ -1958,7 +1983,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 2.0.12", ] @@ -2043,7 +2068,20 @@ dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -2220,9 +2258,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smawk" @@ -2232,9 +2270,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2441,9 +2479,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.1" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", @@ -2720,9 +2758,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vergen" -version = "9.0.4" +version = "9.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d2f179f8075b805a43a2a21728a46f0cc2921b3c58695b28fa8817e103cd9a" +checksum = "6b2bf58be11fc9414104c6d3a2e464163db5ef74b12296bda593cac37b6e4777" dependencies = [ "anyhow", "derive_builder", @@ -2734,9 +2772,9 @@ dependencies = [ [[package]] name = "vergen-gitcl" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f89d70a58a4506a6079cedf575c64cf51649ccbb4e02a63dac539b264b7711" +checksum = "92ee058141ff0fab661d1e2837d1cf0fe1ac6173cf46224e25cec21e780eb5cc" dependencies = [ "anyhow", "derive_builder", @@ -2785,6 +2823,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -2851,13 +2898,13 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "which" -version = "7.0.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2774c861e1f072b3aadc02f8ba886c26ad6321567ecc294c935434cad06f1283" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix", + "rustix 1.0.5", "winsafe", ] @@ -2904,23 +2951,27 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.52.0" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" dependencies = [ + "windows-implement 0.56.0", + "windows-interface 0.56.0", + "windows-result 0.1.2", "windows-targets", ] [[package]] name = "windows-core" -version = "0.56.0" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-targets", + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.2", + "windows-strings", ] [[package]] @@ -2934,6 +2985,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-interface" version = "0.56.0" @@ -2945,6 +3007,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.1.1" @@ -2960,6 +3033,24 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -3044,9 +3135,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] @@ -3057,9 +3148,18 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "yazi-adapter" -version = "25.3.7" +version = "25.4.8" dependencies = [ "ansi-to-tui", "anyhow", @@ -3076,9 +3176,21 @@ dependencies = [ "yazi-shared", ] +[[package]] +name = "yazi-binding" +version = "25.4.8" +dependencies = [ + "mlua", + "paste", + "serde_json", + "yazi-fs", + "yazi-macro", + "yazi-shared", +] + [[package]] name = "yazi-boot" -version = "25.3.7" +version = "25.4.8" dependencies = [ "clap", "clap_complete", @@ -3096,7 +3208,7 @@ dependencies = [ [[package]] name = "yazi-cli" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "clap", @@ -3119,7 +3231,7 @@ dependencies = [ [[package]] name = "yazi-codegen" -version = "25.3.7" +version = "25.4.8" dependencies = [ "quote", "syn", @@ -3127,7 +3239,7 @@ dependencies = [ [[package]] name = "yazi-config" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "bitflags 2.9.0", @@ -3147,7 +3259,7 @@ dependencies = [ [[package]] name = "yazi-core" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "bitflags 2.9.0", @@ -3182,7 +3294,7 @@ dependencies = [ [[package]] name = "yazi-dds" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "mlua", @@ -3195,6 +3307,7 @@ dependencies = [ "tracing", "uzers", "vergen-gitcl", + "yazi-binding", "yazi-boot", "yazi-fs", "yazi-macro", @@ -3203,7 +3316,7 @@ dependencies = [ [[package]] name = "yazi-ffi" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "core-foundation-sys", @@ -3214,16 +3327,18 @@ dependencies = [ [[package]] name = "yazi-fm" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "better-panic", "crossterm", "fdlimit", + "foldhash", "futures", "indexmap", "libc", "mlua", + "paste", "ratatui", "scopeguard", "signal-hook-tokio", @@ -3235,6 +3350,7 @@ dependencies = [ "tracing-appender", "tracing-subscriber", "yazi-adapter", + "yazi-binding", "yazi-boot", "yazi-codegen", "yazi-config", @@ -3250,7 +3366,7 @@ dependencies = [ [[package]] name = "yazi-fs" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "arc-swap", @@ -3276,11 +3392,11 @@ dependencies = [ [[package]] name = "yazi-macro" -version = "25.3.7" +version = "25.4.8" [[package]] name = "yazi-plugin" -version = "25.3.7" +version = "25.4.8" dependencies = [ "ansi-to-tui", "anyhow", @@ -3289,9 +3405,11 @@ dependencies = [ "crossterm", "futures", "globset", + "libc", "md-5", "mlua", "parking_lot", + "paste", "ratatui", "serde_json", "syntect", @@ -3302,7 +3420,9 @@ dependencies = [ "twox-hash", "unicode-width 0.2.0", "uzers", + "windows-sys 0.59.0", "yazi-adapter", + "yazi-binding", "yazi-boot", "yazi-config", "yazi-dds", @@ -3321,7 +3441,7 @@ checksum = "33232d9116df6415ddfcdf72701b1b7439ce87f240a14723e8dd5d17e7ed5f98" [[package]] name = "yazi-proxy" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "mlua", @@ -3334,7 +3454,7 @@ dependencies = [ [[package]] name = "yazi-scheduler" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "async-priority-channel", @@ -3357,7 +3477,7 @@ dependencies = [ [[package]] name = "yazi-shared" -version = "25.3.7" +version = "25.4.8" dependencies = [ "anyhow", "crossterm", @@ -3377,7 +3497,7 @@ dependencies = [ [[package]] name = "yazi-widgets" -version = "25.3.7" +version = "25.4.8" dependencies = [ "futures", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index 1a8e6d1a..3115d4af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,27 +18,28 @@ ansi-to-tui = "7.0.0" anyhow = "1.0.97" base64 = "0.22.1" bitflags = "2.9.0" -clap = { version = "4.5.34", features = [ "derive" ] } +clap = { version = "4.5.35", features = [ "derive" ] } core-foundation-sys = "0.8.7" crossterm = { version = "0.28.1", features = [ "event-stream" ] } dirs = "6.0.0" foldhash = "0.1.5" futures = "0.3.31" globset = "0.4.16" -indexmap = { version = "2.8.0", features = [ "serde" ] } +indexmap = { version = "2.9.0", features = [ "serde" ] } libc = "0.2.171" lru = "0.13.0" md-5 = "0.10.6" mlua = { version = "0.10.3", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serialize" ] } objc = "0.2.7" parking_lot = "0.12.3" +paste = "1.0.15" ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] } regex = "1.11.1" scopeguard = "1.2.0" serde = { version = "1.0.219", features = [ "derive" ] } serde_json = "1.0.140" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } -tokio = { version = "1.44.1", features = [ "full" ] } +tokio = { version = "1.44.2", features = [ "full" ] } tokio-stream = "0.1.17" tokio-util = "0.7.14" toml = { version = "0.8.20" } diff --git a/README.md b/README.md index 683bf737..e9b11502 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ Yazi is currently in heavy development, expect breaking changes. | [Ghostty](https://github.com/ghostty-org/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in | | [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.10352.0) | [Sixel graphics format][sixel] | ✅ Built-in | | [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in | +| [Warp](https://www.warp.dev) | [Inline images protocol][iip] | ✅ Built-in | | [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in | | [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in | | [Rio](https://github.com/raphamorim/rio) | [Inline images protocol][iip] | ❌ Rio doesn't correctly clear images [#709][rio-bug] | diff --git a/scripts/build.sh b/scripts/build.sh index bc6a7ea0..86c33b76 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -euo pipefail export ARTIFACT_NAME="yazi-$1" diff --git a/scripts/bump.sh b/scripts/bump.sh index 9db4b58b..efbfd1c8 100755 --- a/scripts/bump.sh +++ b/scripts/bump.sh @@ -1,8 +1,8 @@ -#!/bin/bash +#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd $SCRIPT_DIR/.. +cd "$SCRIPT_DIR/.." echo "Bumping version: $1" diff --git a/scripts/publish.sh b/scripts/publish.sh index dcaacbda..72cd7ea1 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + cargo publish -p yazi-macro && sleep 30 cargo publish -p yazi-codegen && sleep 30 cargo publish -p yazi-shared && sleep 30 @@ -7,9 +9,11 @@ cargo publish -p yazi-config && sleep 30 cargo publish -p yazi-proxy && sleep 30 cargo publish -p yazi-adapter && sleep 30 cargo publish -p yazi-boot && sleep 30 +cargo publish -p yazi-binding && sleep 30 cargo publish -p yazi-dds && sleep 30 cargo publish -p yazi-scheduler && sleep 30 cargo publish -p yazi-plugin && sleep 30 +cargo publish -p yazi-widgets && sleep 30 cargo publish -p yazi-core && sleep 30 cargo publish -p yazi-fm && sleep 30 cargo publish -p yazi-cli diff --git a/scripts/validate-form/main.js b/scripts/validate-form/main.js index d7cc3c82..fe10c66b 100644 --- a/scripts/validate-form/main.js +++ b/scripts/validate-form/main.js @@ -4,7 +4,7 @@ const RE_DEPENDENCIES = /Dependencies\s+[/a-z]+\s*:\s/gm const RE_CHECKLIST = /#{3}\s+Checklist\s+(?:^-\s+\[x]\s+.+?(?:\n|\r\n|$)){2}/gm function bugReportBody(creator, content, hash) { - if (RE_DEPENDENCIES.test(content) && RE_CHECKLIST.test(content) && new RegExp(` \\(${hash}[a-z]? `).test(content)) { + if (RE_DEPENDENCIES.test(content) && RE_CHECKLIST.test(content) && new RegExp(` \\(${hash}[a-f0-9]? `).test(content)) { return null } diff --git a/yazi-adapter/Cargo.toml b/yazi-adapter/Cargo.toml index 74038a20..90095dff 100644 --- a/yazi-adapter/Cargo.toml +++ b/yazi-adapter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-adapter" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -9,9 +9,9 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-config = { path = "../yazi-config", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-config = { path = "../yazi-config", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies ansi-to-tui = { workspace = true } diff --git a/yazi-adapter/src/adapter.rs b/yazi-adapter/src/adapter.rs index 3bce7cff..b8fdb5a2 100644 --- a/yazi-adapter/src/adapter.rs +++ b/yazi-adapter/src/adapter.rs @@ -109,8 +109,9 @@ impl Adapter { if env_exists("WAYLAND_DISPLAY") { return if supported_compositor { Self::Wayland } else { Self::Chafa }; } - if env_exists("DISPLAY") { - return Self::X11; + match env::var("DISPLAY").unwrap_or_default().as_str() { + s if !s.is_empty() && !s.contains("/org.xquartz") => return Self::X11, + _ => {} } warn!("[Adapter] Falling back to chafa"); diff --git a/yazi-adapter/src/brand.rs b/yazi-adapter/src/brand.rs index 100a11a1..cbf82cbd 100644 --- a/yazi-adapter/src/brand.rs +++ b/yazi-adapter/src/brand.rs @@ -101,7 +101,7 @@ impl Brand { B::Foot => &[A::Sixel], B::Ghostty => &[A::Kgp], B::Microsoft => &[A::Sixel], - B::Warp => &[], + B::Warp => &[A::Iip, A::KgpOld], B::Rio => &[A::Iip, A::Sixel], B::BlackBox => &[A::Sixel], B::VSCode => &[A::Iip, A::Sixel], diff --git a/yazi-adapter/src/unknown.rs b/yazi-adapter/src/unknown.rs index 1c21f301..6d6a578f 100644 --- a/yazi-adapter/src/unknown.rs +++ b/yazi-adapter/src/unknown.rs @@ -13,8 +13,8 @@ impl Unknown { use Adapter as A; match (self.kgp, self.sixel) { - (true, true) => &[A::Kgp, A::Sixel], - (true, false) => &[A::Kgp], + (true, true) => &[A::Sixel, A::KgpOld], + (true, false) => &[A::KgpOld], (false, true) => &[A::Sixel], (false, false) => &[], } diff --git a/yazi-binding/Cargo.toml b/yazi-binding/Cargo.toml new file mode 100644 index 00000000..60ebb0ce --- /dev/null +++ b/yazi-binding/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "yazi-binding" +version = "25.4.8" +edition = "2021" +license = "MIT" +authors = [ "sxyazi " ] +description = "Yazi Lua bindings" +homepage = "https://yazi-rs.github.io" +repository = "https://github.com/sxyazi/yazi" + +[dependencies] +yazi-fs = { path = "../yazi-fs", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } + +# External dependencies +mlua = { workspace = true } +paste = { workspace = true } +serde_json = { workspace = true } diff --git a/yazi-plugin/src/error.rs b/yazi-binding/src/error.rs similarity index 100% rename from yazi-plugin/src/error.rs rename to yazi-binding/src/error.rs diff --git a/yazi-binding/src/lib.rs b/yazi-binding/src/lib.rs new file mode 100644 index 00000000..c9534e84 --- /dev/null +++ b/yazi-binding/src/lib.rs @@ -0,0 +1,3 @@ +mod macros; + +yazi_macro::mod_flat!(error stage url); diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs new file mode 100644 index 00000000..90c65098 --- /dev/null +++ b/yazi-binding/src/macros.rs @@ -0,0 +1,19 @@ +#[macro_export] +macro_rules! cached_field { + ($fields:ident, $key:ident, $value:expr) => { + $fields.add_field_function_get(stringify!($key), |lua, ud| { + use mlua::IntoLua; + ud.borrow_mut_scoped::>(|me| { + match paste::paste! { &me.[] } { + Some(v) => Ok(v.clone()), + None => { + let v: mlua::Result<_> = $value(lua, me); + let v = v?.into_lua(lua)?; + paste::paste! { me.[] = Some(v.clone()) }; + Ok(v) + } + } + })? + }); + }; +} diff --git a/yazi-binding/src/stage.rs b/yazi-binding/src/stage.rs new file mode 100644 index 00000000..35de10ff --- /dev/null +++ b/yazi-binding/src/stage.rs @@ -0,0 +1,21 @@ +use mlua::{IntoLuaMulti, MetaMethod, UserData, UserDataMethods}; + +pub struct FolderStage(yazi_fs::FolderStage); + +impl FolderStage { + pub fn new(inner: yazi_fs::FolderStage) -> Self { Self(inner) } +} + +impl UserData for FolderStage { + fn add_methods>(methods: &mut M) { + methods.add_meta_method(MetaMethod::Call, |lua, me, ()| { + use yazi_fs::FolderStage::*; + + match me.0 { + Loading => false.into_lua_multi(lua), + Loaded => true.into_lua_multi(lua), + Failed(kind) => (true, crate::Error::IoKind(kind)).into_lua_multi(lua), + } + }); + } +} diff --git a/yazi-binding/src/url.rs b/yazi-binding/src/url.rs new file mode 100644 index 00000000..176f29c1 --- /dev/null +++ b/yazi-binding/src/url.rs @@ -0,0 +1,141 @@ +use std::{ops::Deref, path::Path}; + +use mlua::{AnyUserData, ExternalError, FromLua, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, Value}; + +use crate::cached_field; + +pub type UrlRef = UserDataRef; + +pub struct Url { + inner: yazi_shared::url::Url, + + v_name: Option, + v_stem: Option, + v_ext: Option, + v_base: Option, + v_parent: Option, + v_frag: Option, +} + +impl Deref for Url { + type Target = yazi_shared::url::Url; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl AsRef for Url { + fn as_ref(&self) -> &Path { self.inner.as_path() } +} + +impl From for yazi_shared::url::Url { + fn from(value: Url) -> Self { value.inner } +} + +impl Url { + pub fn new(url: impl Into) -> Self { + Self { + inner: url.into(), + + v_name: None, + v_stem: None, + v_ext: None, + v_base: None, + v_parent: None, + v_frag: None, + } + } + + pub fn install(lua: &Lua) -> mlua::Result<()> { + lua.globals().raw_set( + "Url", + lua.create_function(|_, value: Value| { + Ok(match value { + Value::String(s) => Self::new(s.to_str()?.as_ref()), + Value::UserData(ud) => Self::new(ud.borrow::()?.inner.clone()), + _ => Err("Expected a string or a Url".into_lua_err())?, + }) + })?, + ) + } +} + +impl FromLua for Url { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + Ok(match value { + Value::UserData(ud) => Self::new(ud.take::()?.inner), + _ => Err("Expected a Url".into_lua_err())?, + }) + } +} + +impl UserData for Url { + fn add_fields>(fields: &mut F) { + cached_field!(fields, name, |lua: &Lua, me: &Self| { + Some(me.name()) + .filter(|&s| !s.is_empty()) + .map(|s| lua.create_string(s.as_encoded_bytes())) + .transpose() + }); + cached_field!(fields, stem, |lua: &Lua, me: &Self| { + me.file_stem().map(|s| lua.create_string(s.as_encoded_bytes())).transpose() + }); + cached_field!(fields, ext, |lua: &Lua, me: &Self| { + me.extension().map(|s| lua.create_string(s.as_encoded_bytes())).transpose() + }); + cached_field!(fields, parent, |_, me: &Self| Ok(me.parent_url().map(Self::new))); + cached_field!(fields, base, |_, me: &Self| { + Ok(if me.base().as_os_str().is_empty() { None } else { Some(Self::new(me.base())) }) + }); + cached_field!(fields, frag, |lua: &Lua, me: &Self| lua.create_string(me.frag())); + + fields.add_field_method_get("is_regular", |_, me| Ok(me.is_regular())); + fields.add_field_method_get("is_search", |_, me| Ok(me.is_search())); + fields.add_field_method_get("is_archive", |_, me| Ok(me.is_archive())); + fields.add_field_method_get("is_absolute", |_, me| Ok(me.is_absolute())); + fields.add_field_method_get("has_root", |_, me| Ok(me.has_root())); + } + + fn add_methods>(methods: &mut M) { + methods.add_method("join", |_, me, other: Value| { + Ok(Self::new(match other { + Value::String(s) => me.join(s.to_str()?.as_ref()), + Value::UserData(ud) => me.join(&ud.borrow::()?.inner), + _ => Err("must be a string or a Url".into_lua_err())?, + })) + }); + methods.add_method("starts_with", |_, me, base: Value| { + Ok(match base { + Value::String(s) => me.starts_with(s.to_str()?.as_ref()), + Value::UserData(ud) => me.starts_with(&ud.borrow::()?.inner), + _ => Err("must be a string or a Url".into_lua_err())?, + }) + }); + methods.add_method("ends_with", |_, me, child: Value| { + Ok(match child { + Value::String(s) => me.ends_with(s.to_str()?.as_ref()), + Value::UserData(ud) => me.ends_with(&ud.borrow::()?.inner), + _ => Err("must be a string or a Url".into_lua_err())?, + }) + }); + methods.add_method("strip_prefix", |_, me, base: Value| { + let path = match base { + Value::String(s) => me.strip_prefix(s.to_str()?.as_ref()), + Value::UserData(ud) => me.strip_prefix(&ud.borrow::()?.inner), + _ => Err("must be a string or a Url".into_lua_err())?, + }; + Ok(path.ok().map(Self::new)) + }); + + methods.add_function_mut("into_search", |_, (ud, frag): (AnyUserData, mlua::String)| { + Ok(Self::new(ud.take::()?.inner.into_search(&frag.to_str()?))) + }); + + methods.add_meta_method(MetaMethod::Eq, |_, me, other: UrlRef| Ok(me.inner == other.inner)); + methods.add_meta_method(MetaMethod::ToString, |lua, me, ()| { + lua.create_string(me.as_os_str().as_encoded_bytes()) + }); + methods.add_meta_method(MetaMethod::Concat, |lua, lhs, rhs: mlua::String| { + lua.create_string([lhs.as_os_str().as_encoded_bytes(), rhs.as_bytes().as_ref()].concat()) + }); + } +} diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 25c44ac7..1577fd22 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-boot" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -9,11 +9,11 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-adapter = { path = "../yazi-adapter", version = "25.3.7" } -yazi-config = { path = "../yazi-config", version = "25.3.7" } -yazi-fs = { path = "../yazi-fs", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-adapter = { path = "../yazi-adapter", version = "25.4.8" } +yazi-config = { path = "../yazi-config", version = "25.4.8" } +yazi-fs = { path = "../yazi-fs", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies clap = { workspace = true } @@ -21,11 +21,11 @@ regex = { workspace = true } serde = { workspace = true } [build-dependencies] -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies clap = { workspace = true } clap_complete = "4.5.47" clap_complete_fig = "4.5.2" clap_complete_nushell = "4.5.5" -vergen-gitcl = { version = "1.0.5", features = [ "build", "rustc" ] } +vergen-gitcl = { version = "1.0.7", features = [ "build", "rustc" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index a3690a53..5f9bf38b 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-cli" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -9,11 +9,11 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-boot = { path = "../yazi-boot", version = "25.3.7" } -yazi-dds = { path = "../yazi-dds", version = "25.3.7" } -yazi-fs = { path = "../yazi-fs", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-boot = { path = "../yazi-boot", version = "25.4.8" } +yazi-dds = { path = "../yazi-dds", version = "25.4.8" } +yazi-fs = { path = "../yazi-fs", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies anyhow = { workspace = true } @@ -26,7 +26,7 @@ toml = { workspace = true } twox-hash = { workspace = true } [build-dependencies] -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External build dependencies anyhow = { workspace = true } @@ -35,7 +35,7 @@ clap_complete = "4.5.47" clap_complete_fig = "4.5.2" clap_complete_nushell = "4.5.5" serde_json = { workspace = true } -vergen-gitcl = { version = "1.0.5", features = [ "build" ] } +vergen-gitcl = { version = "1.0.7", features = [ "build" ] } [target.'cfg(target_os = "macos")'.dependencies] crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } diff --git a/yazi-cli/src/package/add.rs b/yazi-cli/src/package/add.rs index 803e8543..a2ec1941 100644 --- a/yazi-cli/src/package/add.rs +++ b/yazi-cli/src/package/add.rs @@ -8,13 +8,13 @@ impl Dependency { self.header("Upgrading package `{name}`")?; let path = self.local(); - if !must_exists(&path).await { - Git::clone(&self.remote(), &path).await?; - } else { + if must_exists(&path).await { Git::pull(&path).await?; + } else { + Git::clone(&self.remote(), &path).await?; }; - self.rev = Git::hash(&path).await?; + self.rev = Git::revision(&path).await?; self.deploy().await } } diff --git a/yazi-cli/src/package/delete.rs b/yazi-cli/src/package/delete.rs index 76bbe2cf..17ac7588 100644 --- a/yazi-cli/src/package/delete.rs +++ b/yazi-cli/src/package/delete.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result, bail}; +use anyhow::{Context, Result}; use tokio::fs; use yazi_fs::{maybe_exists, ok_or_not_found, remove_dir_clean, remove_sealed}; use yazi_macro::outln; @@ -14,36 +14,9 @@ impl Dependency { return Ok(outln!("Not found, skipping")?); } - if self.hash != self.hash().await? { - bail!( - "You have modified the contents of the `{}` {}. For safety, the operation has been aborted. -Please manually delete it from: {}", - self.name, - if self.is_flavor { "flavor" } else { "plugin" }, - dir.display() - ); - } - - let files = if self.is_flavor { - &["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..] - } else { - &["main.lua", "README.md", "LICENSE"][..] - }; - for p in files.iter().map(|&f| dir.join(f)) { - ok_or_not_found(remove_sealed(&p).await) - .with_context(|| format!("failed to delete `{}`", p.display()))?; - } - + self.hash_check().await?; self.delete_assets().await?; - if ok_or_not_found(fs::remove_dir(&dir).await).is_ok() { - outln!("Done!")?; - } else { - outln!( - "Done! -For safety, user data has been preserved, please manually delete them within: {}", - dir.display() - )?; - } + self.delete_sources().await?; Ok(()) } @@ -65,4 +38,29 @@ For safety, user data has been preserved, please manually delete them within: {} remove_dir_clean(&assets).await; Ok(()) } + + pub(super) async fn delete_sources(&self) -> Result<()> { + let dir = self.target(); + let files = if self.is_flavor { + &["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..] + } else { + &["main.lua", "README.md", "LICENSE"][..] + }; + + for p in files.iter().map(|&f| dir.join(f)) { + ok_or_not_found(remove_sealed(&p).await) + .with_context(|| format!("failed to delete `{}`", p.display()))?; + } + + if ok_or_not_found(fs::remove_dir(&dir).await).is_ok() { + outln!("Done!")?; + } else { + outln!( + "Done! +For safety, user data has been preserved, please manually delete them within: {}", + dir.display() + )?; + } + Ok(()) + } } diff --git a/yazi-cli/src/package/deploy.rs b/yazi-cli/src/package/deploy.rs index 663477dd..a5b75bd2 100644 --- a/yazi-cli/src/package/deploy.rs +++ b/yazi-cli/src/package/deploy.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use anyhow::{Context, Result, bail}; +use anyhow::{Context, Result}; use tokio::fs; use yazi_fs::{copy_and_seal, maybe_exists, remove_dir_clean}; use yazi_macro::outln; @@ -15,44 +15,27 @@ impl Dependency { self.is_flavor = maybe_exists(&from.join("flavor.toml")).await; let to = self.target(); - if maybe_exists(&to).await && self.hash != self.hash().await? { - bail!( - "You have modified the contents of the `{}` {}. For safety, the operation has been aborted. -Please manually delete it from `{}` and re-run the command.", - self.name, - if self.is_flavor { "flavor" } else { "plugin" }, - to.display() - ); + let exists = maybe_exists(&to).await; + if exists { + self.hash_check().await?; } fs::create_dir_all(&to).await?; - if let Err(e) = Self::deploy_sources(&from, &to, self.is_flavor).await { - remove_dir_clean(&to).await; - return Err(e); - } - self.delete_assets().await?; - Self::deploy_assets(from.join("assets"), to.join("assets")).await?; - self.hash = self.hash().await?; - outln!("Done!")?; - - Ok(()) - } - - async fn deploy_sources(from: &Path, to: &Path, is_flavor: bool) -> Result<()> { - let files = if is_flavor { - &["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..] - } else { - &["main.lua", "README.md", "LICENSE"][..] - }; - - for file in files { - let (from, to) = (from.join(file), to.join(file)); - copy_and_seal(&from, &to) - .await - .with_context(|| format!("failed to copy `{}` to `{}`", from.display(), to.display()))?; + let res1 = Self::deploy_assets(from.join("assets"), to.join("assets")).await; + let res2 = Self::deploy_sources(&from, &to, self.is_flavor).await; + if !exists && (res2.is_err() || res1.is_err()) { + self.delete_assets().await?; + self.delete_sources().await?; } + + remove_dir_clean(&to).await; + self.hash = self.hash().await?; + res2?; + res1?; + + outln!("Done!")?; Ok(()) } @@ -72,4 +55,20 @@ Please manually delete it from `{}` and re-run the command.", } Ok(()) } + + async fn deploy_sources(from: &Path, to: &Path, is_flavor: bool) -> Result<()> { + let files = if is_flavor { + &["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..] + } else { + &["main.lua", "README.md", "LICENSE"][..] + }; + + for file in files { + let (from, to) = (from.join(file), to.join(file)); + copy_and_seal(&from, &to) + .await + .with_context(|| format!("failed to copy `{}` to `{}`", from.display(), to.display()))?; + } + Ok(()) + } } diff --git a/yazi-cli/src/package/git.rs b/yazi-cli/src/package/git.rs index 5da090bf..58004792 100644 --- a/yazi-cli/src/package/git.rs +++ b/yazi-cli/src/package/git.rs @@ -25,7 +25,7 @@ impl Git { Ok(()) } - pub(super) async fn hash(path: &Path) -> Result { + pub(super) async fn revision(path: &Path) -> Result { let output = Command::new("git") .args(["rev-parse", "--short", "HEAD"]) .current_dir(path) diff --git a/yazi-cli/src/package/hash.rs b/yazi-cli/src/package/hash.rs index 42e83ba6..841f112a 100644 --- a/yazi-cli/src/package/hash.rs +++ b/yazi-cli/src/package/hash.rs @@ -52,4 +52,17 @@ impl Dependency { Ok(format!("{:x}", h.finish_128())) } + + pub(super) async fn hash_check(&self) -> Result<()> { + if self.hash != self.hash().await? { + bail!( + "You have modified the contents of the `{}` {}. For safety, the operation has been aborted. +Please manually delete it from `{}` and re-run the command.", + self.name, + if self.is_flavor { "flavor" } else { "plugin" }, + self.target().display() + ); + } + Ok(()) + } } diff --git a/yazi-cli/src/package/install.rs b/yazi-cli/src/package/install.rs index c1860c95..ae25cbf6 100644 --- a/yazi-cli/src/package/install.rs +++ b/yazi-cli/src/package/install.rs @@ -15,7 +15,7 @@ impl Dependency { }; if self.rev.is_empty() { - self.rev = Git::hash(&path).await?; + self.rev = Git::revision(&path).await?; } else { Git::checkout(&path, self.rev.trim_start_matches('=')).await?; } diff --git a/yazi-cli/src/package/package.rs b/yazi-cli/src/package/package.rs index 05ab7812..6e9aae07 100644 --- a/yazi-cli/src/package/package.rs +++ b/yazi-cli/src/package/package.rs @@ -25,41 +25,36 @@ impl Package { pub(crate) async fn add_many(&mut self, uses: &[String]) -> Result<()> { for u in uses { - if let Err(e) = self.add(u).await { - self.save().await?; - return Err(e); - } + let r = self.add(u).await; + self.save().await?; + r?; } - self.save().await + Ok(()) } pub(crate) async fn delete_many(&mut self, uses: &[String]) -> Result<()> { for u in uses { - if let Err(e) = self.delete(u).await { - self.save().await?; - return Err(e); - } + let r = self.delete(u).await; + self.save().await?; + r?; } - self.save().await + Ok(()) } pub(crate) async fn install(&mut self, upgrade: bool) -> Result<()> { - for d in &mut self.plugins { - if upgrade { - d.upgrade().await?; - } else { - d.install().await?; - } + for i in 0..self.plugins.len() { + let r = + if upgrade { self.plugins[i].upgrade().await } else { self.plugins[i].install().await }; + self.save().await?; + r?; } - for d in &mut self.flavors { - if upgrade { - d.upgrade().await?; - } else { - d.install().await?; - } + for i in 0..self.flavors.len() { + let r = + if upgrade { self.flavors[i].upgrade().await } else { self.flavors[i].install().await }; + self.save().await?; + r?; } - - self.save().await + Ok(()) } pub(crate) fn print(&self) -> Result<()> { diff --git a/yazi-codegen/Cargo.toml b/yazi-codegen/Cargo.toml index 22e9f85f..8eb5f0c4 100644 --- a/yazi-codegen/Cargo.toml +++ b/yazi-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-codegen" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 30a91e7e..80d5ee20 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-config" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -9,10 +9,10 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-codegen = { path = "../yazi-codegen", version = "25.3.7" } -yazi-fs = { path = "../yazi-fs", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-codegen = { path = "../yazi-codegen", version = "25.4.8" } +yazi-fs = { path = "../yazi-fs", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-config/preset/keymap-default.toml b/yazi-config/preset/keymap-default.toml index f4cda809..14fc54e7 100644 --- a/yazi-config/preset/keymap-default.toml +++ b/yazi-config/preset/keymap-default.toml @@ -82,8 +82,8 @@ keymap = [ { on = "s", run = "search --via=fd", desc = "Search files by name via fd" }, { on = "S", run = "search --via=rg", desc = "Search files by content via ripgrep" }, { on = "", run = "escape --search", desc = "Cancel the ongoing search" }, - { on = "z", run = "plugin zoxide", desc = "Jump to a directory via zoxide" }, - { on = "Z", run = "plugin fzf", desc = "Jump to a file/directory via fzf" }, + { on = "z", run = "plugin fzf", desc = "Jump to a file/directory via fzf" }, + { on = "Z", run = "plugin zoxide", desc = "Jump to a directory via zoxide" }, # Linemode { on = [ "m", "s" ], run = "linemode size", desc = "Linemode: size" }, diff --git a/yazi-config/preset/yazi-default.toml b/yazi-config/preset/yazi-default.toml index 3324928a..294e8e1f 100644 --- a/yazi-config/preset/yazi-default.toml +++ b/yazi-config/preset/yazi-default.toml @@ -84,7 +84,7 @@ micro_workers = 10 macro_workers = 10 bizarre_retry = 3 image_alloc = 536870912 # 512MB -image_bound = [ 0, 0 ] +image_bound = [ 5000, 5000 ] suppress_preload = false [plugin] diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index 6493f9ee..2649a490 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -57,8 +57,8 @@ fn try_init_flavor(light: bool, merge: bool) -> anyhow::Result<()> { ))?; let flavor = shadow.flavor.as_ref().map(theme::Flavor::from).unwrap_or_default().read(light)?; - theme = theme.deserialize_over_with::(shadow)?; theme = theme.deserialize_over(toml::Deserializer::new(&flavor))?; + theme = theme.deserialize_over_with::(shadow)?; } THEME.init(theme.reshape(light)?); diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 2ad16e76..b2b27548 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-core" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -9,18 +9,18 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-adapter = { path = "../yazi-adapter", version = "25.3.7" } -yazi-boot = { path = "../yazi-boot", version = "25.3.7" } -yazi-codegen = { path = "../yazi-codegen", version = "25.3.7" } -yazi-config = { path = "../yazi-config", version = "25.3.7" } -yazi-dds = { path = "../yazi-dds", version = "25.3.7" } -yazi-fs = { path = "../yazi-fs", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" } -yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" } -yazi-scheduler = { path = "../yazi-scheduler", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } -yazi-widgets = { path = "../yazi-widgets", version = "25.3.7" } +yazi-adapter = { path = "../yazi-adapter", version = "25.4.8" } +yazi-boot = { path = "../yazi-boot", version = "25.4.8" } +yazi-codegen = { path = "../yazi-codegen", version = "25.4.8" } +yazi-config = { path = "../yazi-config", version = "25.4.8" } +yazi-dds = { path = "../yazi-dds", version = "25.4.8" } +yazi-fs = { path = "../yazi-fs", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-plugin = { path = "../yazi-plugin", version = "25.4.8" } +yazi-proxy = { path = "../yazi-proxy", version = "25.4.8" } +yazi-scheduler = { path = "../yazi-scheduler", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } +yazi-widgets = { path = "../yazi-widgets", version = "25.4.8" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-core/src/mgr/commands/close.rs b/yazi-core/src/mgr/commands/close.rs index a71f9d9f..becfd17a 100644 --- a/yazi-core/src/mgr/commands/close.rs +++ b/yazi-core/src/mgr/commands/close.rs @@ -3,14 +3,10 @@ use yazi_shared::event::CmdCow; use crate::{mgr::{Mgr, commands::quit}, tasks::Tasks}; #[derive(Default)] -struct Opt { - no_cwd_file: bool, -} +struct Opt(quit::Opt); + impl From for Opt { - fn from(c: CmdCow) -> Self { Self { no_cwd_file: c.bool("no-cwd-file") } } -} -impl From for quit::Opt { - fn from(value: Opt) -> Self { Self { no_cwd_file: value.no_cwd_file } } + fn from(c: CmdCow) -> Self { Self(c.into()) } } impl Mgr { @@ -19,6 +15,6 @@ impl Mgr { if self.tabs.len() > 1 { return self.tabs.close(self.tabs.cursor); } - self.quit(opt, tasks); + self.quit(opt.0, tasks); } } diff --git a/yazi-core/src/mgr/commands/create.rs b/yazi-core/src/mgr/commands/create.rs index 7058892c..38541db3 100644 --- a/yazi-core/src/mgr/commands/create.rs +++ b/yazi-core/src/mgr/commands/create.rs @@ -22,13 +22,12 @@ impl Mgr { #[yazi_codegen::command] pub fn create(&self, opt: Opt) { let cwd = self.cwd().to_owned(); + let mut input = InputProxy::show(InputCfg::create(opt.dir)); + tokio::spawn(async move { - let mut result = InputProxy::show(InputCfg::create(opt.dir)); - let Some(Ok(name)) = result.recv().await else { - return Ok(()); - }; + let Some(Ok(name)) = input.recv().await else { return }; if name.is_empty() { - return Ok(()); + return; } let new = cwd.join(&name); @@ -36,10 +35,10 @@ impl Mgr { && maybe_exists(&new).await && !ConfirmProxy::show(ConfirmCfg::overwrite(&new)).await { - return Ok(()); + return; } - Self::create_do(new, opt.dir || name.ends_with('/') || name.ends_with('\\')).await + _ = Self::create_do(new, opt.dir || name.ends_with('/') || name.ends_with('\\')).await; }); } diff --git a/yazi-core/src/mgr/commands/open.rs b/yazi-core/src/mgr/commands/open.rs index 71bcbbd1..716a0948 100644 --- a/yazi-core/src/mgr/commands/open.rs +++ b/yazi-core/src/mgr/commands/open.rs @@ -1,20 +1,18 @@ -use std::{borrow::Cow, ffi::OsString}; +use std::borrow::Cow; use tracing::error; -use yazi_boot::ARGS; use yazi_config::{YAZI, popup::PickCfg}; use yazi_fs::File; -use yazi_macro::emit; use yazi_plugin::isolate; -use yazi_proxy::{MgrProxy, TasksProxy, options::OpenDoOpt}; -use yazi_shared::{MIME_DIR, event::{CmdCow, EventQuit}, url::Url}; +use yazi_proxy::{MgrProxy, PickProxy, TasksProxy, options::OpenDoOpt}; +use yazi_shared::{MIME_DIR, event::CmdCow, url::Url}; use crate::{mgr::Mgr, tab::Folder, tasks::Tasks}; #[derive(Clone, Copy)] -struct Opt { - interactive: bool, - hovered: bool, +pub(super) struct Opt { + pub(super) interactive: bool, + pub(super) hovered: bool, } impl From for Opt { @@ -95,12 +93,10 @@ impl Mgr { return; } + let pick = PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect())); let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect(); tokio::spawn(async move { - let result = - yazi_proxy::PickProxy::show(PickCfg::open(openers.iter().map(|o| o.desc()).collect())); - - if let Ok(choice) = result.await { + if let Ok(choice) = pick.await { TasksProxy::open_with(Cow::Borrowed(openers[choice]), opt.cwd, urls); } }); @@ -122,19 +118,4 @@ impl Mgr { || find(self.hovered_folder()) || find(self.active().history.get(&p)) } - - fn quit_with_selected(opt: Opt, selected: &[&Url]) -> bool { - if opt.interactive || ARGS.chooser_file.is_none() { - return false; - } - - let paths = selected.iter().fold(OsString::new(), |mut s, &u| { - s.push(u.as_os_str()); - s.push("\n"); - s - }); - - emit!(Quit(EventQuit { selected: Some(paths), ..Default::default() })); - true - } } diff --git a/yazi-core/src/mgr/commands/paste.rs b/yazi-core/src/mgr/commands/paste.rs index fcc814cb..39c05838 100644 --- a/yazi-core/src/mgr/commands/paste.rs +++ b/yazi-core/src/mgr/commands/paste.rs @@ -19,7 +19,7 @@ impl Mgr { if self.yanked.cut { tasks.file_cut(&src, dest, opt.force); - self.tabs.iter_mut().for_each(|t| _ = t.selected.remove_many(&src, false)); + self.tabs.iter_mut().for_each(|t| _ = t.selected.remove_many(&src)); self.unyank(()); } else { tasks.file_copy(&src, dest, opt.force, opt.follow); diff --git a/yazi-core/src/mgr/commands/quit.rs b/yazi-core/src/mgr/commands/quit.rs index 343ed732..52457c78 100644 --- a/yazi-core/src/mgr/commands/quit.rs +++ b/yazi-core/src/mgr/commands/quit.rs @@ -1,25 +1,39 @@ -use std::time::Duration; +use std::{ffi::OsString, time::Duration}; use tokio::{select, time}; +use yazi_boot::ARGS; use yazi_config::popup::ConfirmCfg; use yazi_macro::emit; use yazi_proxy::ConfirmProxy; -use yazi_shared::event::{CmdCow, EventQuit}; +use yazi_shared::{event::{CmdCow, Data, EventQuit}, url::Url}; use crate::{mgr::Mgr, tasks::Tasks}; #[derive(Default)] pub(super) struct Opt { + pub(super) code: i32, pub(super) no_cwd_file: bool, } + impl From for Opt { - fn from(c: CmdCow) -> Self { Self { no_cwd_file: c.bool("no-cwd-file") } } + fn from(c: CmdCow) -> Self { + Self { + code: c.get("code").and_then(Data::as_i32).unwrap_or_default(), + no_cwd_file: c.bool("no-cwd-file"), + } + } +} + +impl From for EventQuit { + fn from(value: Opt) -> Self { + EventQuit { code: value.code, no_cwd_file: value.no_cwd_file, ..Default::default() } + } } impl Mgr { #[yazi_codegen::command] pub fn quit(&self, opt: Opt, tasks: &Tasks) { - let opt = EventQuit { no_cwd_file: opt.no_cwd_file, ..Default::default() }; + let event = opt.into(); let ongoing = tasks.ongoing().clone(); let (left, left_names) = { @@ -28,7 +42,7 @@ impl Mgr { }; if left == 0 { - emit!(Quit(opt)); + emit!(Quit(event)); return; } @@ -41,13 +55,13 @@ impl Mgr { i += 1; if i > 40 { break } else if ongoing.lock().is_empty() { - emit!(Quit(opt)); + emit!(Quit(event)); return; } } b = &mut rx => { if b.unwrap_or(false) { - emit!(Quit(opt)); + emit!(Quit(event)); } return; } @@ -55,8 +69,23 @@ impl Mgr { } if rx.await.unwrap_or(false) { - emit!(Quit(opt)); + emit!(Quit(event)); } }); } + + pub(super) fn quit_with_selected(opt: super::open::Opt, selected: &[&Url]) -> bool { + if opt.interactive || ARGS.chooser_file.is_none() { + return false; + } + + let paths = selected.iter().fold(OsString::new(), |mut s, &u| { + s.push(u.as_os_str()); + s.push("\n"); + s + }); + + emit!(Quit(EventQuit { selected: Some(paths), ..Default::default() })); + true + } } diff --git a/yazi-core/src/mgr/commands/remove.rs b/yazi-core/src/mgr/commands/remove.rs index 35f373c9..19a27289 100644 --- a/yazi-core/src/mgr/commands/remove.rs +++ b/yazi-core/src/mgr/commands/remove.rs @@ -41,14 +41,14 @@ impl Mgr { return self.remove_do(opt, tasks); } - tokio::spawn(async move { - let result = ConfirmProxy::show(if opt.permanently { - ConfirmCfg::delete(&opt.targets) - } else { - ConfirmCfg::trash(&opt.targets) - }); + let confirm = ConfirmProxy::show(if opt.permanently { + ConfirmCfg::delete(&opt.targets) + } else { + ConfirmCfg::trash(&opt.targets) + }); - if result.await { + tokio::spawn(async move { + if confirm.await { MgrProxy::remove_do(opt.targets, opt.permanently); } }); @@ -57,7 +57,7 @@ impl Mgr { #[yazi_codegen::command] pub fn remove_do(&mut self, opt: Opt, tasks: &Tasks) { self.tabs.iter_mut().for_each(|t| { - t.selected.remove_many(&opt.targets, false); + t.selected.remove_many(&opt.targets); }); for u in &opt.targets { diff --git a/yazi-core/src/mgr/commands/rename.rs b/yazi-core/src/mgr/commands/rename.rs index 4a9bb4ba..bf6e809d 100644 --- a/yazi-core/src/mgr/commands/rename.rs +++ b/yazi-core/src/mgr/commands/rename.rs @@ -53,12 +53,10 @@ impl Mgr { let old = hovered.url_owned(); let tab = self.tabs.active().id; - tokio::spawn(async move { - let mut result = InputProxy::show(InputCfg::rename().with_value(name).with_cursor(cursor)); - let Some(Ok(name)) = result.recv().await else { - return; - }; + let mut input = InputProxy::show(InputCfg::rename().with_value(name).with_cursor(cursor)); + tokio::spawn(async move { + let Some(Ok(name)) = input.recv().await else { return }; if name.is_empty() { return; } diff --git a/yazi-core/src/tab/commands/cd.rs b/yazi-core/src/tab/commands/cd.rs index ffb36330..6ee5b27d 100644 --- a/yazi-core/src/tab/commands/cd.rs +++ b/yazi-core/src/tab/commands/cd.rs @@ -77,10 +77,10 @@ impl Tab { } fn cd_interactive(&mut self) { - tokio::spawn(async move { - let rx = InputProxy::show(InputCfg::cd()); + let input = InputProxy::show(InputCfg::cd()); - let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); + tokio::spawn(async move { + let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50)); pin!(rx); while let Some(result) = rx.next().await { diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index e8b30569..bd1234d2 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -109,10 +109,9 @@ impl Tab { let urls: Vec<_> = indices.into_iter().filter_map(|i| self.current.files.get(i)).map(|f| &f.url).collect(); - let same = !self.cwd().is_search(); if !select { - self.selected.remove_many(&urls, same); - } else if self.selected.add_many(&urls, same) != urls.len() { + self.selected.remove_many(&urls); + } else if self.selected.add_many(&urls) != urls.len() { AppProxy::notify_warn( "Escape visual mode", "Some files cannot be selected, due to path nesting conflict.", diff --git a/yazi-core/src/tab/commands/filter.rs b/yazi-core/src/tab/commands/filter.rs index e0b93078..8682b14a 100644 --- a/yazi-core/src/tab/commands/filter.rs +++ b/yazi-core/src/tab/commands/filter.rs @@ -30,10 +30,10 @@ impl From for Opt { impl Tab { #[yazi_codegen::command] pub fn filter(&mut self, opt: Opt) { - tokio::spawn(async move { - let rx = InputProxy::show(InputCfg::filter()); + let input = InputProxy::show(InputCfg::filter()); - let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); + tokio::spawn(async move { + let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50)); pin!(rx); while let Some(result) = rx.next().await { diff --git a/yazi-core/src/tab/commands/find.rs b/yazi-core/src/tab/commands/find.rs index d301d554..5fc235bb 100644 --- a/yazi-core/src/tab/commands/find.rs +++ b/yazi-core/src/tab/commands/find.rs @@ -25,10 +25,10 @@ impl From for Opt { impl Tab { #[yazi_codegen::command] pub fn find(&mut self, opt: Opt) { - tokio::spawn(async move { - let rx = InputProxy::show(InputCfg::find(opt.prev)); + let input = InputProxy::show(InputCfg::find(opt.prev)); - let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); + tokio::spawn(async move { + let rx = Debounce::new(UnboundedReceiverStream::new(input), Duration::from_millis(50)); pin!(rx); while let Some(Ok(s)) | Some(Err(InputError::Typed(s))) = rx.next().await { diff --git a/yazi-core/src/tab/commands/search.rs b/yazi-core/src/tab/commands/search.rs index 09ac84fd..a30e9216 100644 --- a/yazi-core/src/tab/commands/search.rs +++ b/yazi-core/src/tab/commands/search.rs @@ -1,6 +1,5 @@ use std::{borrow::Cow, mem, time::Duration}; -use anyhow::bail; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tracing::error; @@ -13,22 +12,18 @@ use crate::tab::Tab; impl Tab { pub fn search(&mut self, opt: impl TryInto) { - let Ok(mut opt) = opt.try_into() else { + let Ok(mut opt): Result = opt.try_into() else { return AppProxy::notify_error("Invalid `search` option", "Failed to parse search option"); }; - if opt.via == SearchOptVia::None { - return self.search_stop(); - } - if let Some(handle) = self.search.take() { handle.abort(); } - tokio::spawn(async move { - let mut input = - InputProxy::show(InputCfg::search(&opt.via.to_string()).with_value(opt.subject)); + let mut input = + InputProxy::show(InputCfg::search(opt.via.as_ref()).with_value(opt.subject.to_owned())); + tokio::spawn(async move { if let Some(Ok(subject)) = input.recv().await { opt.subject = Cow::Owned(subject); TabProxy::search_do(opt); @@ -68,7 +63,6 @@ impl Tab { subject: opt.subject.into_owned(), args: opt.args, }), - SearchOptVia::None => bail!("Invalid `via` option for `search` command"), }?; let rx = UnboundedReceiverStream::new(rx).chunks_timeout(5000, Duration::from_millis(500)); diff --git a/yazi-core/src/tab/commands/shell.rs b/yazi-core/src/tab/commands/shell.rs index 87d083da..ee0d3dc3 100644 --- a/yazi-core/src/tab/commands/shell.rs +++ b/yazi-core/src/tab/commands/shell.rs @@ -54,11 +54,16 @@ impl Tab { let cwd = opt.cwd.take().unwrap_or_else(|| self.cwd().clone()); let selected = self.hovered_and_selected().cloned().collect(); + + let input = opt.interactive.then(|| { + InputProxy::show( + InputCfg::shell(opt.block).with_value(opt.run.to_owned()).with_cursor(opt.cursor), + ) + }); + tokio::spawn(async move { - if opt.interactive { - let mut result = - InputProxy::show(InputCfg::shell(opt.block).with_value(opt.run).with_cursor(opt.cursor)); - match result.recv().await { + if let Some(mut rx) = input { + match rx.recv().await { Some(Ok(e)) => opt.run = Cow::Owned(e), _ => return, } diff --git a/yazi-core/src/tab/commands/toggle_all.rs b/yazi-core/src/tab/commands/toggle_all.rs index 3bf004d3..4228bf6b 100644 --- a/yazi-core/src/tab/commands/toggle_all.rs +++ b/yazi-core/src/tab/commands/toggle_all.rs @@ -1,16 +1,26 @@ use yazi_macro::render; use yazi_proxy::AppProxy; -use yazi_shared::event::CmdCow; +use yazi_shared::{event::CmdCow, url::Url}; use crate::tab::Tab; struct Opt { + urls: Vec, state: Option, } impl From for Opt { - fn from(c: CmdCow) -> Self { + fn from(mut c: CmdCow) -> Self { + let mut urls = Vec::with_capacity(c.len()); + for i in 0..c.len() { + match c.take_url(i) { + Some(url) => urls.push(url), + None => break, + } + } + Self { + urls, state: match c.str("state") { Some("on") => Some(true), Some("off") => Some(false), @@ -19,27 +29,38 @@ impl From for Opt { } } } + impl From> for Opt { - fn from(state: Option) -> Self { Self { state } } + fn from(state: Option) -> Self { Self { urls: vec![], state } } } impl Tab { #[yazi_codegen::command] pub fn toggle_all(&mut self, opt: Opt) { - let iter = self.current.files.iter().map(|f| &f.url); - let (removal, addition): (Vec<_>, Vec<_>) = match opt.state { - Some(true) => (vec![], iter.collect()), - Some(false) => (iter.collect(), vec![]), - None => iter.partition(|&u| self.selected.contains_key(u)), + use yazi_shared::Either::*; + + let it = self.current.files.iter().map(|f| &f.url); + let either = match opt.state { + Some(true) if opt.urls.is_empty() => Left((vec![], it.collect())), + Some(true) => Right((vec![], opt.urls)), + Some(false) if opt.urls.is_empty() => Left((it.collect(), vec![])), + Some(false) => Right((opt.urls, vec![])), + None if opt.urls.is_empty() => Left(it.partition(|&u| self.selected.contains_key(u))), + None => Right(opt.urls.into_iter().partition(|u| self.selected.contains_key(u))), }; - let same = !self.cwd().is_search(); - render!(self.selected.remove_many(&removal, same) > 0); + let warn = match either { + Left((removal, addition)) => { + render!(self.selected.remove_many(&removal) > 0); + render!(self.selected.add_many(&addition), > 0) != addition.len() + } + Right((removal, addition)) => { + render!(self.selected.remove_many(&removal) > 0); + render!(self.selected.add_many(&addition), > 0) != addition.len() + } + }; - let added = self.selected.add_many(&addition, same); - render!(added > 0); - - if added != addition.len() { + if warn { AppProxy::notify_warn( "Toggle all", "Some files cannot be selected, due to path nesting conflict.", diff --git a/yazi-core/src/tab/selected.rs b/yazi-core/src/tab/selected.rs index 9586c1c2..364f5c10 100644 --- a/yazi-core/src/tab/selected.rs +++ b/yazi-core/src/tab/selected.rs @@ -20,11 +20,7 @@ impl Selected { #[inline] pub fn add(&mut self, url: &Url) -> bool { self.add_same(&[url]) == 1 } - pub fn add_many(&mut self, urls: &[impl AsRef], same: bool) -> usize { - if same { - return self.add_same(urls); - } - + pub fn add_many(&mut self, urls: &[impl AsRef]) -> usize { let mut grouped: HashMap<_, Vec<_>> = Default::default(); for u in urls { if let Some(p) = u.as_ref().parent_url() { @@ -66,22 +62,19 @@ impl Selected { #[inline] pub fn remove(&mut self, url: &Url) -> bool { self.remove_same(&[url]) == 1 } - pub fn remove_many(&mut self, urls: &[impl AsRef], same: bool) -> usize { - let affected = if same { - self.remove_same(urls) - } else { - let mut grouped: HashMap<_, Vec<_>> = Default::default(); - for u in urls { - if let Some(p) = u.as_ref().parent_url() { - grouped.entry(p).or_default().push(u); - } + pub fn remove_many(&mut self, urls: &[impl AsRef]) -> usize { + let mut grouped: HashMap<_, Vec<_>> = Default::default(); + for u in urls { + if let Some(p) = u.as_ref().parent_url() { + grouped.entry(p).or_default().push(u); } - grouped.into_values().map(|v| self.remove_same(&v)).sum() - }; + } + let affected = grouped.into_values().map(|v| self.remove_same(&v)).sum(); if affected > 0 { self.inner.sort_unstable_by(|_, a, _, b| a.cmp(b)); } + affected } @@ -113,10 +106,10 @@ impl Selected { pub fn apply_op(&mut self, op: &FilesOp) { let (removal, addition) = op.diff_recoverable(|u| self.contains_key(u)); if !removal.is_empty() { - self.remove_many(&removal, !op.cwd().is_search()); + self.remove_many(&removal); } if !addition.is_empty() { - self.add_many(&addition, !op.cwd().is_search()); + self.add_many(&addition); } } } diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index 191c4453..286830be 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-dds" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -13,10 +13,11 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-boot = { path = "../yazi-boot", version = "25.3.7" } -yazi-fs = { path = "../yazi-fs", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-binding = { path = "../yazi-binding", version = "25.4.8" } +yazi-boot = { path = "../yazi-boot", version = "25.4.8" } +yazi-fs = { path = "../yazi-fs", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies anyhow = { workspace = true } @@ -30,7 +31,7 @@ tokio-util = { workspace = true } tracing = { workspace = true } [build-dependencies] -vergen-gitcl = { version = "1.0.5", features = [ "build" ] } +vergen-gitcl = { version = "1.0.7", features = [ "build" ] } [target."cfg(unix)".dependencies] uzers = { workspace = true } diff --git a/yazi-dds/src/body/bulk.rs b/yazi-dds/src/body/bulk.rs index 96bf4922..11f9828f 100644 --- a/yazi-dds/src/body/bulk.rs +++ b/yazi-dds/src/body/bulk.rs @@ -54,7 +54,7 @@ impl UserData for BodyBulkIter { methods.add_meta_function(MetaMethod::Pairs, |lua, me: AnyUserData| { let iter = lua.create_function(|lua, mut me: UserDataRefMut| { if let Some((Cow::Owned(from), Cow::Owned(to))) = me.inner.next() { - (lua.create_any_userdata(from)?, lua.create_any_userdata(to)?).into_lua_multi(lua) + (yazi_binding::Url::new(from), yazi_binding::Url::new(to)).into_lua_multi(lua) } else { ().into_lua_multi(lua) } diff --git a/yazi-dds/src/body/cd.rs b/yazi-dds/src/body/cd.rs index 05d117cb..e14f2cba 100644 --- a/yazi-dds/src/body/cd.rs +++ b/yazi-dds/src/body/cd.rs @@ -37,7 +37,7 @@ impl IntoLua for BodyCd<'static> { if let Some(Cow::Owned(url)) = Some(self.url).filter(|_| !self.dummy) { lua.create_table_from([ ("tab", self.tab.get().into_lua(lua)?), - ("url", lua.create_any_userdata(url)?.into_lua(lua)?), + ("url", yazi_binding::Url::new(url).into_lua(lua)?), ])? } else { lua.create_table_from([("tab", self.tab.get())])? diff --git a/yazi-dds/src/body/delete.rs b/yazi-dds/src/body/delete.rs index 51888438..4259b083 100644 --- a/yazi-dds/src/body/delete.rs +++ b/yazi-dds/src/body/delete.rs @@ -27,10 +27,8 @@ impl<'a> From> for Body<'a> { impl IntoLua for BodyDelete<'static> { fn into_lua(self, lua: &Lua) -> mlua::Result { - let urls = lua.create_table_with_capacity(self.urls.len(), 0)?; - for (i, url) in self.urls.into_owned().into_iter().enumerate() { - urls.raw_set(i + 1, lua.create_any_userdata(url)?)?; - } + let urls = + lua.create_sequence_from(self.urls.into_owned().into_iter().map(yazi_binding::Url::new))?; lua.create_table_from([("urls", urls)])?.into_lua(lua) } diff --git a/yazi-dds/src/body/hover.rs b/yazi-dds/src/body/hover.rs index 395d601c..c2541775 100644 --- a/yazi-dds/src/body/hover.rs +++ b/yazi-dds/src/body/hover.rs @@ -33,7 +33,7 @@ impl IntoLua for BodyHover<'static> { if let Some(Cow::Owned(url)) = self.url { lua.create_table_from([ ("tab", self.tab.get().into_lua(lua)?), - ("url", lua.create_any_userdata(url)?.into_lua(lua)?), + ("url", yazi_binding::Url::new(url).into_lua(lua)?), ])? } else { lua.create_table_from([("tab", self.tab.get())])? diff --git a/yazi-dds/src/body/load.rs b/yazi-dds/src/body/load.rs index ab31a33f..203cc1c1 100644 --- a/yazi-dds/src/body/load.rs +++ b/yazi-dds/src/body/load.rs @@ -37,8 +37,8 @@ impl IntoLua for BodyLoad<'static> { lua .create_table_from([ ("tab", self.tab.get().into_lua(lua)?), - ("url", lua.create_any_userdata(self.url.into_owned())?.into_lua(lua)?), - ("stage", lua.create_any_userdata(self.stage)?.into_lua(lua)?), + ("url", yazi_binding::Url::new(self.url.into_owned()).into_lua(lua)?), + ("stage", yazi_binding::FolderStage::new(self.stage).into_lua(lua)?), ])? .into_lua(lua) } diff --git a/yazi-dds/src/body/move_.rs b/yazi-dds/src/body/move_.rs index b9025bd5..9323c40b 100644 --- a/yazi-dds/src/body/move_.rs +++ b/yazi-dds/src/body/move_.rs @@ -46,8 +46,8 @@ impl IntoLua for BodyMoveItem { fn into_lua(self, lua: &Lua) -> mlua::Result { lua .create_table_from([ - ("from", lua.create_any_userdata(self.from)?), - ("to", lua.create_any_userdata(self.to)?), + ("from", yazi_binding::Url::new(self.from)), + ("to", yazi_binding::Url::new(self.to)), ])? .into_lua(lua) } diff --git a/yazi-dds/src/body/rename.rs b/yazi-dds/src/body/rename.rs index 84207807..d793b56d 100644 --- a/yazi-dds/src/body/rename.rs +++ b/yazi-dds/src/body/rename.rs @@ -36,8 +36,8 @@ impl IntoLua for BodyRename<'static> { lua .create_table_from([ ("tab", self.tab.get().into_lua(lua)?), - ("from", lua.create_any_userdata(self.from.into_owned())?.into_lua(lua)?), - ("to", lua.create_any_userdata(self.to.into_owned())?.into_lua(lua)?), + ("from", yazi_binding::Url::new(self.from.into_owned()).into_lua(lua)?), + ("to", yazi_binding::Url::new(self.to.into_owned()).into_lua(lua)?), ])? .into_lua(lua) } diff --git a/yazi-dds/src/body/trash.rs b/yazi-dds/src/body/trash.rs index 2c4e354a..5e429c26 100644 --- a/yazi-dds/src/body/trash.rs +++ b/yazi-dds/src/body/trash.rs @@ -27,10 +27,8 @@ impl<'a> From> for Body<'a> { impl IntoLua for BodyTrash<'static> { fn into_lua(self, lua: &Lua) -> mlua::Result { - let urls = lua.create_table_with_capacity(self.urls.len(), 0)?; - for (i, url) in self.urls.into_owned().into_iter().enumerate() { - urls.raw_set(i + 1, lua.create_any_userdata(url)?)?; - } + let urls = + lua.create_sequence_from(self.urls.into_owned().into_iter().map(yazi_binding::Url::new))?; lua.create_table_from([("urls", urls)])?.into_lua(lua) } diff --git a/yazi-dds/src/body/yank.rs b/yazi-dds/src/body/yank.rs index 31c71756..111d311f 100644 --- a/yazi-dds/src/body/yank.rs +++ b/yazi-dds/src/body/yank.rs @@ -57,12 +57,12 @@ impl UserData for BodyYankIter { fn add_methods>(methods: &mut M) { methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.urls.len())); - methods.add_meta_method(MetaMethod::Index, |lua, me, idx: usize| { - if idx > me.urls.len() || idx == 0 { - Ok(None) + methods.add_meta_method(MetaMethod::Index, |_, me, idx: usize| { + Ok(if idx > me.urls.len() || idx == 0 { + None } else { - Some(lua.create_any_userdata(me.urls[idx - 1].clone())).transpose() - } + Some(yazi_binding::Url::new(me.urls[idx - 1].clone())) + }) }); } } diff --git a/yazi-dds/src/sendable.rs b/yazi-dds/src/sendable.rs index 6463ed18..0d226d99 100644 --- a/yazi-dds/src/sendable.rs +++ b/yazi-dds/src/sendable.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, collections::HashMap}; -use mlua::{ExternalError, Lua, MultiValue, Table, Value}; +use mlua::{ExternalError, IntoLua, Lua, MultiValue, Table, Value}; use yazi_shared::{OrderedFloat, event::{Data, DataKey}, replace_cow}; pub struct Sendable; @@ -41,8 +41,8 @@ impl Sendable { Value::Function(_) => Err("function is not supported".into_lua_err())?, Value::Thread(_) => Err("thread is not supported".into_lua_err())?, Value::UserData(ud) => { - if let Ok(t) = ud.take::() { - Data::Url(t) + if let Ok(t) = ud.take::() { + Data::Url(t.into()) } else if let Ok(t) = ud.take::() { Data::Any(Box::new(t)) } else if let Ok(t) = ud.take::() { @@ -73,7 +73,7 @@ impl Sendable { } Value::Table(tbl) } - Data::Url(u) => Value::UserData(lua.create_any_userdata(u)?), + Data::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, Data::Any(a) => Value::UserData(if a.is::() { lua.create_any_userdata(*a.downcast::().unwrap())? } else if a.is::() { @@ -107,7 +107,7 @@ impl Sendable { } Value::Table(tbl) } - Data::Url(u) => Value::UserData(lua.create_any_userdata(u.clone())?), + Data::Url(u) => yazi_binding::Url::new(u.clone()).into_lua(lua)?, Data::Bytes(b) => Value::String(lua.create_string(b)?), Data::Any(a) => Value::UserData(if let Some(t) = a.downcast_ref::() { lua.create_any_userdata(t.clone())? @@ -189,8 +189,8 @@ impl Sendable { Value::Function(_) => Err("function is not supported".into_lua_err())?, Value::Thread(_) => Err("thread is not supported".into_lua_err())?, Value::UserData(ud) => { - if let Ok(t) = ud.take::() { - DataKey::Url(t) + if let Ok(t) = ud.take::() { + DataKey::Url(t.into()) } else { Err("unsupported userdata included".into_lua_err())? } @@ -202,7 +202,7 @@ impl Sendable { fn key_to_value(lua: &Lua, key: DataKey) -> mlua::Result { Ok(match key { - DataKey::Url(u) => Value::UserData(lua.create_any_userdata(u)?), + DataKey::Url(u) => yazi_binding::Url::new(u).into_lua(lua)?, key => Self::key_to_value_ref(lua, &key)?, }) } @@ -214,7 +214,7 @@ impl Sendable { DataKey::Integer(i) => Value::Integer(*i), DataKey::Number(n) => Value::Number(n.get()), DataKey::String(s) => Value::String(lua.create_string(s.as_ref())?), - DataKey::Url(u) => Value::UserData(lua.create_any_userdata(u.clone())?), + DataKey::Url(u) => yazi_binding::Url::new(u.clone()).into_lua(lua)?, }) } } diff --git a/yazi-ffi/Cargo.toml b/yazi-ffi/Cargo.toml index 873f6545..ba9107c9 100644 --- a/yazi-ffi/Cargo.toml +++ b/yazi-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-ffi" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -9,7 +9,7 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 360e1e7b..9752b874 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-fm" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -13,27 +13,30 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-adapter = { path = "../yazi-adapter", version = "25.3.7" } -yazi-boot = { path = "../yazi-boot", version = "25.3.7" } -yazi-codegen = { path = "../yazi-codegen", version = "25.3.7" } -yazi-config = { path = "../yazi-config", version = "25.3.7" } -yazi-core = { path = "../yazi-core", version = "25.3.7" } -yazi-dds = { path = "../yazi-dds", version = "25.3.7" } -yazi-fs = { path = "../yazi-fs", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" } -yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } -yazi-widgets = { path = "../yazi-widgets", version = "25.3.7" } +yazi-adapter = { path = "../yazi-adapter", version = "25.4.8" } +yazi-binding = { path = "../yazi-binding", version = "25.4.8" } +yazi-boot = { path = "../yazi-boot", version = "25.4.8" } +yazi-codegen = { path = "../yazi-codegen", version = "25.4.8" } +yazi-config = { path = "../yazi-config", version = "25.4.8" } +yazi-core = { path = "../yazi-core", version = "25.4.8" } +yazi-dds = { path = "../yazi-dds", version = "25.4.8" } +yazi-fs = { path = "../yazi-fs", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-plugin = { path = "../yazi-plugin", version = "25.4.8" } +yazi-proxy = { path = "../yazi-proxy", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } +yazi-widgets = { path = "../yazi-widgets", version = "25.4.8" } # External dependencies anyhow = { workspace = true } better-panic = "0.3.0" crossterm = { workspace = true } fdlimit = "0.3.0" +foldhash = { workspace = true } futures = { workspace = true } indexmap = { workspace = true } mlua = { workspace = true } +paste = { workspace = true } ratatui = { workspace = true } scopeguard = { workspace = true } tokio = { workspace = true } diff --git a/yazi-fm/build.rs b/yazi-fm/build.rs index ec41c9eb..9785ed08 100644 --- a/yazi-fm/build.rs +++ b/yazi-fm/build.rs @@ -2,7 +2,14 @@ use std::{env, error::Error}; fn main() -> Result<(), Box> { let dir = env::var("OUT_DIR").unwrap(); - if dir.contains("\\target\\release\\build\\yazi-fm-") { + + // cargo build + // C:\Users\Ika\Desktop\yazi\target\release\build\yazi-fm-cfc94820f71daa30\out + // cargo install + // C:\Users\Ika\AppData\Local\Temp\cargo-installTFU8cj\release\build\ + // yazi-fm-45dffef2500eecd0\out + + if dir.contains("\\release\\build\\yazi-fm-") { panic!( "Unwinding must be enabled for Windows. Please use `cargo build --profile release-windows --locked` instead to build Yazi." ); diff --git a/yazi-fm/src/app/app.rs b/yazi-fm/src/app/app.rs index 2a16e14c..8491c473 100644 --- a/yazi-fm/src/app/app.rs +++ b/yazi-fm/src/app/app.rs @@ -7,7 +7,7 @@ use yazi_macro::emit; use yazi_shared::event::{CmdCow, Event, NEED_RENDER}; use yazi_widgets::input::InputMode; -use crate::{Ctx, Executor, Router, Signals, Term, lives::Lives}; +use crate::{Ctx, Executor, Router, Signals, Term}; pub(crate) struct App { pub(crate) cx: Ctx, @@ -20,7 +20,6 @@ impl App { let term = Term::start()?; let (mut rx, signals) = (Event::take(), Signals::start()?); - Lives::register()?; let mut app = Self { cx: Ctx::make(), term: Some(term), signals }; app.render(); diff --git a/yazi-fm/src/app/commands/quit.rs b/yazi-fm/src/app/commands/quit.rs index 9b725a50..72a9f4ec 100644 --- a/yazi-fm/src/app/commands/quit.rs +++ b/yazi-fm/src/app/commands/quit.rs @@ -19,7 +19,7 @@ impl App { self.selected_to_file(selected); } - Term::goodbye(|| false); + Term::goodbye(|| opt.code); } fn cwd_to_file(&self) { diff --git a/yazi-fm/src/lives/context.rs b/yazi-fm/src/lives/context.rs index 0c13d2a9..6e548c46 100644 --- a/yazi-fm/src/lives/context.rs +++ b/yazi-fm/src/lives/context.rs @@ -1,38 +1,66 @@ use std::ops::Deref; use mlua::{AnyUserData, IntoLua, MetaMethod, UserData, Value}; +use paste::paste; -use super::Lives; +use super::{Lives, PtrCell}; pub(super) struct Ctx { - inner: *const crate::Ctx, + inner: PtrCell, + + c_active: Option, + c_tabs: Option, + c_tasks: Option, + c_yanked: Option, + c_layer: Option, } impl Deref for Ctx { type Target = crate::Ctx; - fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } + fn deref(&self) -> &Self::Target { &self.inner } } impl Ctx { #[inline] pub(super) fn make(inner: &crate::Ctx) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { + inner: inner.into(), + + c_active: None, + c_tabs: None, + c_tasks: None, + c_yanked: None, + c_layer: None, + }) } } impl UserData for Ctx { fn add_methods>(methods: &mut M) { - methods.add_meta_method(MetaMethod::Index, |lua, me, key: mlua::String| { - match key.as_bytes().as_ref() { - b"active" => super::Tab::make(me.active())?, - b"tabs" => super::Tabs::make(&me.mgr.tabs)?, - b"tasks" => super::Tasks::make(&me.tasks)?, - b"yanked" => super::Yanked::make(&me.mgr.yanked)?, - b"layer" => return yazi_plugin::bindings::Layer::from(me.layer()).into_lua(lua), - _ => return Ok(Value::Nil), + methods.add_meta_method_mut(MetaMethod::Index, |lua, me, key: mlua::String| { + macro_rules! reuse { + ($key:ident, $value:expr) => { + match paste! { &me.[] } { + Some(v) => v.clone(), + None => { + let v = $value?.into_lua(lua)?; + paste! { me.[] = Some(v.clone()); }; + v + } + } + }; } - .into_lua(lua) + Ok(match key.as_bytes().as_ref() { + b"active" => reuse!(active, super::Tab::make(me.active())), + b"tabs" => reuse!(tabs, super::Tabs::make(&me.mgr.tabs)), + b"tasks" => reuse!(tasks, super::Tasks::make(&me.tasks)), + b"yanked" => reuse!(yanked, super::Yanked::make(&me.mgr.yanked)), + b"layer" => { + reuse!(layer, Ok::<_, mlua::Error>(yazi_plugin::bindings::Layer::from(me.layer()))) + } + _ => Value::Nil, + }) }); } } diff --git a/yazi-fm/src/lives/file.rs b/yazi-fm/src/lives/file.rs index fc8b945e..d213db03 100644 --- a/yazi-fm/src/lives/file.rs +++ b/yazi-fm/src/lives/file.rs @@ -1,22 +1,27 @@ use std::ops::Deref; -use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods}; +use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Value}; use yazi_config::THEME; use yazi_plugin::{bindings::Range, elements::Style}; use super::Lives; -use crate::Ctx; +use crate::{Ctx, lives::PtrCell}; pub(super) struct File { idx: usize, - folder: *const yazi_core::tab::Folder, - tab: *const yazi_core::tab::Tab, + folder: PtrCell, + tab: PtrCell, + + v_cha: Option, + v_url: Option, + v_link_to: Option, + v_name: Option, } impl Deref for File { type Target = yazi_fs::File; - fn deref(&self) -> &Self::Target { &self.folder().files[self.idx] } + fn deref(&self) -> &Self::Target { &self.folder.files[self.idx] } } impl AsRef for File { @@ -30,14 +35,26 @@ impl File { folder: &yazi_core::tab::Folder, tab: &yazi_core::tab::Tab, ) -> mlua::Result { - Lives::scoped_userdata(Self { idx, folder, tab }) + use std::collections::hash_map::Entry; + + Ok(match super::FILE_CACHE.borrow_mut().entry(PtrCell(&folder.files[idx])) { + Entry::Occupied(oe) => oe.into_mut().clone(), + Entry::Vacant(ve) => { + let ud = Lives::scoped_userdata(Self { + idx, + folder: folder.into(), + tab: tab.into(), + + v_cha: None, + v_url: None, + v_link_to: None, + v_name: None, + })?; + ve.insert(ud.clone()); + ud + } + }) } - - #[inline] - fn folder(&self) -> &yazi_core::tab::Folder { unsafe { &*self.folder } } - - #[inline] - fn tab(&self) -> &yazi_core::tab::Tab { unsafe { &*self.tab } } } impl UserData for File { @@ -45,13 +62,17 @@ impl UserData for File { yazi_plugin::impl_file_fields!(fields); fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1)); + fields.add_field_method_get("is_hovered", |_, me| Ok(me.idx == me.folder.cursor)); + fields.add_field_method_get("in_preview", |_, me| { + Ok(me.tab.hovered().is_some_and(|f| f.url == me.folder.url)) + }); } fn add_methods>(methods: &mut M) { yazi_plugin::impl_file_methods!(methods); methods.add_method("size", |_, me, ()| { - Ok(if me.is_dir() { me.folder().files.sizes.get(me.urn()).copied() } else { Some(me.len) }) + Ok(if me.is_dir() { me.folder.files.sizes.get(me.urn()).copied() } else { Some(me.len) }) }); methods.add_method("mime", |lua, me, ()| { lua.named_registry_value::("cx")?.borrow_scoped(|cx: &Ctx| { @@ -59,11 +80,11 @@ impl UserData for File { })? }); methods.add_method("prefix", |lua, me, ()| { - if !me.folder().url.is_search() { + if !me.folder.url.is_search() { return Ok(None); } - let mut p = me.url.strip_prefix(&me.folder().url).unwrap_or(&me.url).components(); + let mut p = me.url.strip_prefix(&me.folder.url).unwrap_or(&me.url).components(); p.next_back(); Some(lua.create_string(p.as_path().as_os_str().as_encoded_bytes())).transpose() }); @@ -73,7 +94,6 @@ impl UserData for File { THEME.filetype.iter().find(|&x| x.matches(me, mime)).map(|x| Style::from(x.style)) }) }); - methods.add_method("is_hovered", |_, me, ()| Ok(me.idx == me.folder().cursor)); methods.add_method("is_yanked", |lua, me, ()| { lua.named_registry_value::("cx")?.borrow_scoped(|cx: &Ctx| { if !cx.mgr.yanked.contains(&me.url) { @@ -87,24 +107,17 @@ impl UserData for File { }); methods.add_method("is_marked", |_, me, ()| { use yazi_core::tab::Mode::*; - if !me.tab().mode.is_visual() || me.folder().url != me.tab().current.url { + if !me.tab.mode.is_visual() || me.folder.url != me.tab.current.url { return Ok(0u8); } - Ok(match &me.tab().mode { + Ok(match &me.tab.mode { Select(_, indices) if indices.contains(&me.idx) => 1u8, Unset(_, indices) if indices.contains(&me.idx) => 2u8, _ => 0u8, }) }); - methods.add_method("is_selected", |_, me, ()| Ok(me.tab().selected.contains_key(&me.url))); - methods.add_method("in_parent", |_, me, ()| { - Ok(me.tab().parent.as_ref().is_some_and(|f| me.folder().url == f.url)) - }); - methods.add_method("in_current", |_, me, ()| Ok(me.folder().url == me.tab().current.url)); - methods.add_method("in_preview", |_, me, ()| { - Ok(me.tab().hovered().is_some_and(|f| f.url == me.folder().url)) - }); + methods.add_method("is_selected", |_, me, ()| Ok(me.tab.selected.contains_key(&me.url))); methods.add_method("found", |lua, me, ()| { lua.named_registry_value::("cx")?.borrow_scoped(|cx: &Ctx| { let Some(finder) = &cx.active().finder else { @@ -124,7 +137,7 @@ impl UserData for File { let Some(finder) = &cx.active().finder else { return None; }; - if me.folder().url != me.tab().current.url { + if me.folder.url != me.tab.current.url { return None; } diff --git a/yazi-fm/src/lives/files.rs b/yazi-fm/src/lives/files.rs index 64a2de95..2f2ba645 100644 --- a/yazi-fm/src/lives/files.rs +++ b/yazi-fm/src/lives/files.rs @@ -1,19 +1,22 @@ use std::ops::{Deref, Range}; -use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods}; +use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, Value}; +use yazi_binding::cached_field; -use super::{File, Filter, Lives}; +use super::{File, Filter, Lives, PtrCell}; pub(super) struct Files { window: Range, - folder: *const yazi_core::tab::Folder, - tab: *const yazi_core::tab::Tab, + folder: PtrCell, + tab: PtrCell, + + v_filter: Option, } impl Deref for Files { type Target = yazi_fs::Files; - fn deref(&self) -> &Self::Target { &self.folder().files } + fn deref(&self) -> &Self::Target { &self.folder.files } } impl Files { @@ -23,19 +26,13 @@ impl Files { folder: &yazi_core::tab::Folder, tab: &yazi_core::tab::Tab, ) -> mlua::Result { - Lives::scoped_userdata(Self { window, folder, tab }) + Lives::scoped_userdata(Self { window, folder: folder.into(), tab: tab.into(), v_filter: None }) } - - #[inline] - fn folder(&self) -> &yazi_core::tab::Folder { unsafe { &*self.folder } } - - #[inline] - fn tab(&self) -> &yazi_core::tab::Tab { unsafe { &*self.tab } } } impl UserData for Files { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("filter", |_, me| me.filter().map(Filter::make).transpose()); + cached_field!(fields, filter, |_, me: &Self| me.filter().map(Filter::make).transpose()); } fn add_methods>(methods: &mut M) { @@ -46,7 +43,7 @@ impl UserData for Files { if idx > me.window.end || idx == 0 { Ok(None) } else { - Some(File::make(idx - 1, me.folder(), me.tab())).transpose() + Some(File::make(idx - 1, &me.folder, &me.tab)).transpose() } }); } diff --git a/yazi-fm/src/lives/filter.rs b/yazi-fm/src/lives/filter.rs index 4c6a6813..1bef8375 100644 --- a/yazi-fm/src/lives/filter.rs +++ b/yazi-fm/src/lives/filter.rs @@ -2,22 +2,22 @@ use std::ops::Deref; use mlua::{AnyUserData, MetaMethod, UserData, UserDataMethods}; -use super::Lives; +use super::{Lives, PtrCell}; pub(super) struct Filter { - inner: *const yazi_fs::Filter, + inner: PtrCell, } impl Deref for Filter { type Target = yazi_fs::Filter; - fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } + fn deref(&self) -> &Self::Target { &self.inner } } impl Filter { #[inline] pub(super) fn make(inner: &yazi_fs::Filter) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { inner: inner.into() }) } } diff --git a/yazi-fm/src/lives/finder.rs b/yazi-fm/src/lives/finder.rs index 2b53d9cf..df992983 100644 --- a/yazi-fm/src/lives/finder.rs +++ b/yazi-fm/src/lives/finder.rs @@ -2,22 +2,22 @@ use std::ops::Deref; use mlua::{AnyUserData, MetaMethod, UserData, UserDataMethods}; -use super::Lives; +use super::{Lives, PtrCell}; pub(super) struct Finder { - inner: *const yazi_core::tab::Finder, + inner: PtrCell, } impl Deref for Finder { type Target = yazi_core::tab::Finder; - fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } + fn deref(&self) -> &Self::Target { &self.inner } } impl Finder { #[inline] pub(super) fn make(inner: &yazi_core::tab::Finder) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { inner: inner.into() }) } } diff --git a/yazi-fm/src/lives/folder.rs b/yazi-fm/src/lives/folder.rs index f67b130b..22939195 100644 --- a/yazi-fm/src/lives/folder.rs +++ b/yazi-fm/src/lives/folder.rs @@ -1,21 +1,27 @@ use std::ops::{Deref, Range}; -use mlua::{AnyUserData, IntoLuaMulti, Lua, MetaMethod, UserData, UserDataFields, UserDataMethods}; +use mlua::{AnyUserData, Lua, UserData, UserDataFields, Value}; +use yazi_binding::{FolderStage, Url, cached_field}; use yazi_config::LAYOUT; -use yazi_plugin::url::Url; -use super::{File, Files, Lives}; +use super::{File, Files, Lives, PtrCell}; pub(super) struct Folder { window: Range, - inner: *const yazi_core::tab::Folder, - tab: *const yazi_core::tab::Tab, + inner: PtrCell, + tab: PtrCell, + + v_cwd: Option, + v_files: Option, + v_stage: Option, + v_window: Option, + v_hovered: Option, } impl Deref for Folder { type Target = yazi_core::tab::Folder; - fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } + fn deref(&self) -> &Self::Target { &self.inner } } impl Folder { @@ -33,38 +39,31 @@ impl Folder { } }; - Lives::scoped_userdata(Self { window, inner, tab }) + Lives::scoped_userdata(Self { + window, + inner: inner.into(), + tab: tab.into(), + + v_cwd: None, + v_files: None, + v_stage: None, + v_window: None, + v_hovered: None, + }) } - - pub(super) fn register(lua: &Lua) -> mlua::Result<()> { - lua.register_userdata_type::(|reg| { - use yazi_fs::FolderStage; - - reg.add_meta_method(MetaMethod::Call, |lua, me, ()| match me { - FolderStage::Loading => false.into_lua_multi(lua), - FolderStage::Loaded => true.into_lua_multi(lua), - FolderStage::Failed(kind) => (true, yazi_plugin::Error::IoKind(*kind)).into_lua_multi(lua), - }); - })?; - - Ok(()) - } - - #[inline] - fn tab(&self) -> &yazi_core::tab::Tab { unsafe { &*self.tab } } } impl UserData for Folder { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("cwd", |_, me| Ok(Url(me.url.to_owned()))); - fields.add_field_method_get("files", |_, me| Files::make(0..me.files.len(), me, me.tab())); - fields.add_field_method_get("stage", |lua, me| lua.create_any_userdata(me.stage)); - fields.add_field_method_get("window", |_, me| Files::make(me.window.clone(), me, me.tab())); + cached_field!(fields, cwd, |_, me: &Self| Ok(Url::new(me.url.to_owned()))); + cached_field!(fields, files, |_, me: &Self| Files::make(0..me.files.len(), me, &me.tab)); + cached_field!(fields, stage, |_: &Lua, me: &Self| Ok(FolderStage::new(me.stage))); + cached_field!(fields, window, |_, me: &Self| Files::make(me.window.clone(), me, &me.tab)); fields.add_field_method_get("offset", |_, me| Ok(me.offset)); fields.add_field_method_get("cursor", |_, me| Ok(me.cursor)); - fields.add_field_method_get("hovered", |_, me| { - me.hovered().map(|_| File::make(me.cursor, me, me.tab())).transpose() + cached_field!(fields, hovered, |_, me: &Self| { + me.hovered().map(|_| File::make(me.cursor, me, &me.tab)).transpose() }); } } diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs index 1a7ef8f8..ca3459d1 100644 --- a/yazi-fm/src/lives/lives.rs +++ b/yazi-fm/src/lives/lives.rs @@ -1,24 +1,26 @@ use std::cell::RefCell; +use foldhash::HashMap; use mlua::{AnyUserData, UserData}; +use scopeguard::defer; use tracing::error; use yazi_plugin::LUA; use yazi_shared::RoCell; +use super::PtrCell; use crate::Ctx; static TO_DESTROY: RoCell>> = RoCell::new_const(RefCell::new(Vec::new())); +pub(super) static FILE_CACHE: RoCell, AnyUserData>>> = + RoCell::new(); pub(crate) struct Lives; impl Lives { - pub(crate) fn register() -> mlua::Result<()> { - super::Folder::register(&LUA)?; - - Ok(()) - } - pub(crate) fn scope(cx: &Ctx, f: impl FnOnce() -> mlua::Result) -> mlua::Result { + FILE_CACHE.init(Default::default()); + defer! { FILE_CACHE.drop(); } + let result = LUA.scope(|scope| { scope.add_destructor(|| { for ud in TO_DESTROY.borrow_mut().drain(..) { diff --git a/yazi-fm/src/lives/mod.rs b/yazi-fm/src/lives/mod.rs index 6fcc4036..42ab437f 100644 --- a/yazi-fm/src/lives/mod.rs +++ b/yazi-fm/src/lives/mod.rs @@ -1,3 +1,3 @@ #![allow(clippy::module_inception)] -yazi_macro::mod_flat!(context file files filter finder folder iter lives mode preference preview selected tab tabs tasks yanked); +yazi_macro::mod_flat!(context file files filter finder folder iter lives mode preference preview ptr selected tab tabs tasks yanked); diff --git a/yazi-fm/src/lives/mode.rs b/yazi-fm/src/lives/mode.rs index 902bd991..8e1921f7 100644 --- a/yazi-fm/src/lives/mode.rs +++ b/yazi-fm/src/lives/mode.rs @@ -2,22 +2,22 @@ use std::ops::Deref; use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods}; -use super::Lives; +use super::{Lives, PtrCell}; pub(super) struct Mode { - inner: *const yazi_core::tab::Mode, + inner: PtrCell, } impl Deref for Mode { type Target = yazi_core::tab::Mode; - fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } + fn deref(&self) -> &Self::Target { &self.inner } } impl Mode { #[inline] pub(super) fn make(inner: &yazi_core::tab::Mode) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { inner: inner.into() }) } } diff --git a/yazi-fm/src/lives/preference.rs b/yazi-fm/src/lives/preference.rs index 4ee7c459..d12c0f7d 100644 --- a/yazi-fm/src/lives/preference.rs +++ b/yazi-fm/src/lives/preference.rs @@ -1,35 +1,39 @@ use std::ops::Deref; -use mlua::{AnyUserData, UserData, UserDataFields}; +use mlua::{AnyUserData, UserData, UserDataFields, Value}; +use yazi_binding::cached_field; -use super::Lives; +use super::{Lives, PtrCell}; pub(super) struct Preference { - inner: *const yazi_core::tab::Preference, + inner: PtrCell, + + v_sort_by: Option, + v_linemode: Option, } impl Deref for Preference { type Target = yazi_core::tab::Preference; - fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } + fn deref(&self) -> &Self::Target { &self.inner } } impl Preference { #[inline] pub(super) fn make(inner: &yazi_core::tab::Preference) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { inner: inner.into(), v_sort_by: None, v_linemode: None }) } } impl UserData for Preference { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("sort_by", |_, me| Ok(me.sort_by.to_string())); + cached_field!(fields, sort_by, |_, me: &Self| Ok(me.sort_by.to_string())); fields.add_field_method_get("sort_sensitive", |_, me| Ok(me.sort_sensitive)); fields.add_field_method_get("sort_reverse", |_, me| Ok(me.sort_reverse)); fields.add_field_method_get("sort_dir_first", |_, me| Ok(me.sort_dir_first)); fields.add_field_method_get("sort_translit", |_, me| Ok(me.sort_translit)); - fields.add_field_method_get("linemode", |_, me| Ok(me.linemode.to_owned())); + cached_field!(fields, linemode, |_, me: &Self| Ok(me.linemode.to_string())); fields.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden)); } } diff --git a/yazi-fm/src/lives/preview.rs b/yazi-fm/src/lives/preview.rs index cdc85d94..e629b197 100644 --- a/yazi-fm/src/lives/preview.rs +++ b/yazi-fm/src/lives/preview.rs @@ -1,39 +1,39 @@ use std::ops::Deref; -use mlua::{AnyUserData, UserData, UserDataFields}; +use mlua::{AnyUserData, UserData, UserDataFields, Value}; +use yazi_binding::cached_field; use yazi_config::LAYOUT; -use super::{Folder, Lives}; +use super::{Folder, Lives, PtrCell}; pub(super) struct Preview { - tab: *const yazi_core::tab::Tab, + tab: PtrCell, + + v_folder: Option, } impl Deref for Preview { type Target = yazi_core::tab::Preview; - fn deref(&self) -> &Self::Target { &self.tab().preview } + fn deref(&self) -> &Self::Target { &self.tab.preview } } impl Preview { #[inline] pub(super) fn make(tab: &yazi_core::tab::Tab) -> mlua::Result { - Lives::scoped_userdata(Self { tab }) + Lives::scoped_userdata(Self { tab: tab.into(), v_folder: None }) } - - #[inline] - fn tab(&self) -> &yazi_core::tab::Tab { unsafe { &*self.tab } } } impl UserData for Preview { fn add_fields>(fields: &mut F) { fields.add_field_method_get("skip", |_, me| Ok(me.skip)); - fields.add_field_method_get("folder", |_, me| { - me.tab() + cached_field!(fields, folder, |_, me: &Self| { + me.tab .hovered_folder() .map(|f| { let limit = LAYOUT.get().preview.height as usize; - Folder::make(Some(me.skip..f.files.len().min(me.skip + limit)), f, me.tab()) + Folder::make(Some(me.skip..f.files.len().min(me.skip + limit)), f, &me.tab) }) .transpose() }); diff --git a/yazi-fm/src/lives/ptr.rs b/yazi-fm/src/lives/ptr.rs new file mode 100644 index 00000000..9140254c --- /dev/null +++ b/yazi-fm/src/lives/ptr.rs @@ -0,0 +1,34 @@ +use std::{hash::{Hash, Hasher}, ops::Deref}; + +pub(super) struct PtrCell(pub(super) *const T); + +impl Deref for PtrCell { + type Target = T; + + fn deref(&self) -> &Self::Target { unsafe { &*self.0 } } +} + +impl From<&T> for PtrCell { + fn from(value: &T) -> Self { Self(value) } +} + +impl Clone for PtrCell { + fn clone(&self) -> Self { *self } +} + +impl Copy for PtrCell {} + +impl PartialEq for PtrCell { + fn eq(&self, other: &Self) -> bool { self.0.addr() == other.0.addr() } +} + +impl Eq for PtrCell {} + +impl Hash for PtrCell { + fn hash(&self, state: &mut H) { state.write_usize(self.0.addr()); } +} + +impl PtrCell { + #[inline] + pub(super) fn as_static(&self) -> &'static T { unsafe { &*self.0 } } +} diff --git a/yazi-fm/src/lives/selected.rs b/yazi-fm/src/lives/selected.rs index a6c7bb3c..3276569d 100644 --- a/yazi-fm/src/lives/selected.rs +++ b/yazi-fm/src/lives/selected.rs @@ -2,29 +2,26 @@ use std::ops::Deref; use indexmap::{IndexMap, map::Keys}; use mlua::{AnyUserData, IntoLuaMulti, MetaMethod, UserData, UserDataMethods, UserDataRefMut}; -use yazi_plugin::url::Url; +use yazi_binding::Url; -use super::{Iter, Lives}; +use super::{Iter, Lives, PtrCell}; #[derive(Clone, Copy)] pub(super) struct Selected { - inner: *const IndexMap, + inner: PtrCell>, } impl Deref for Selected { type Target = IndexMap; - fn deref(&self) -> &Self::Target { self.inner() } + fn deref(&self) -> &Self::Target { &self.inner } } impl Selected { #[inline] pub(super) fn make(inner: &IndexMap) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { inner: inner.into() }) } - - #[inline] - fn inner(&self) -> &'static IndexMap { unsafe { &*self.inner } } } impl UserData for Selected { @@ -35,14 +32,14 @@ impl UserData for Selected { let iter = lua.create_function( |lua, mut iter: UserDataRefMut, _>>| { if let Some(next) = iter.next() { - (next.0, Url(next.1.clone())).into_lua_multi(lua) + (next.0, Url::new(next.1.clone())).into_lua_multi(lua) } else { ().into_lua_multi(lua) } }, )?; - Ok((iter, Iter::make(me.inner().keys()))) + Ok((iter, Iter::make(me.inner.as_static().keys()))) }); } } diff --git a/yazi-fm/src/lives/tab.rs b/yazi-fm/src/lives/tab.rs index 69d34519..6bc147bd 100644 --- a/yazi-fm/src/lives/tab.rs +++ b/yazi-fm/src/lives/tab.rs @@ -1,47 +1,69 @@ use std::ops::Deref; -use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods}; -use yazi_plugin::url::UrlRef; +use mlua::{AnyUserData, Lua, UserData, UserDataFields, UserDataMethods, Value}; +use yazi_binding::{UrlRef, cached_field}; +use yazi_plugin::Id; -use super::{Finder, Folder, Lives, Mode, Preference, Preview, Selected}; +use super::{Finder, Folder, Lives, Mode, Preference, Preview, PtrCell, Selected}; pub(super) struct Tab { - inner: *const yazi_core::tab::Tab, + inner: PtrCell, + + v_name: Option, + v_mode: Option, + v_pref: Option, + v_current: Option, + v_parent: Option, + v_selected: Option, + v_preview: Option, + v_finder: Option, } impl Deref for Tab { type Target = yazi_core::tab::Tab; - fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } + fn deref(&self) -> &Self::Target { &self.inner } } impl Tab { #[inline] pub(super) fn make(inner: &yazi_core::tab::Tab) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { + inner: inner.into(), + + v_name: None, + v_mode: None, + v_pref: None, + v_current: None, + v_parent: None, + v_selected: None, + v_preview: None, + v_finder: None, + }) } } impl UserData for Tab { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("id", |_, me| Ok(me.id.get())); - fields.add_field_method_get("mode", |_, me| Mode::make(&me.mode)); - fields.add_field_method_get("pref", |_, me| Preference::make(&me.pref)); - fields.add_field_method_get("current", |_, me| Folder::make(None, &me.current, me)); - fields.add_field_method_get("parent", |_, me| { + fields.add_field_method_get("id", |_, me| Ok(Id(me.id))); + cached_field!(fields, name, |lua: &Lua, me: &Self| { + lua.create_string(me.current.url.name().as_encoded_bytes()) + }); + + cached_field!(fields, mode, |_, me: &Self| Mode::make(&me.mode)); + cached_field!(fields, pref, |_, me: &Self| Preference::make(&me.pref)); + cached_field!(fields, current, |_, me: &Self| Folder::make(None, &me.current, me)); + cached_field!(fields, parent, |_, me: &Self| { me.parent.as_ref().map(|f| Folder::make(None, f, me)).transpose() }); - fields.add_field_method_get("selected", |_, me| Selected::make(&me.selected)); + cached_field!(fields, selected, |_, me: &Self| Selected::make(&me.selected)); - fields.add_field_method_get("preview", |_, me| Preview::make(me)); - fields.add_field_method_get("finder", |_, me| me.finder.as_ref().map(Finder::make).transpose()); + cached_field!(fields, preview, |_, me: &Self| Preview::make(me)); + cached_field!(fields, finder, |_, me: &Self| me.finder.as_ref().map(Finder::make).transpose()); } fn add_methods>(methods: &mut M) { - methods.add_method("name", |lua, me, ()| { - lua.create_string(me.current.url.name().as_encoded_bytes()) - }); methods.add_method("history", |_, me, url: UrlRef| { me.history.get(&url).map(|f| Folder::make(None, f, me)).transpose() }); diff --git a/yazi-fm/src/lives/tabs.rs b/yazi-fm/src/lives/tabs.rs index 4bbf576e..2733d16e 100644 --- a/yazi-fm/src/lives/tabs.rs +++ b/yazi-fm/src/lives/tabs.rs @@ -2,22 +2,22 @@ use std::ops::Deref; use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods}; -use super::{Lives, Tab}; +use super::{Lives, PtrCell, Tab}; pub(super) struct Tabs { - inner: *const yazi_core::mgr::Tabs, + inner: PtrCell, } impl Deref for Tabs { type Target = yazi_core::mgr::Tabs; - fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } + fn deref(&self) -> &Self::Target { &self.inner } } impl Tabs { #[inline] pub(super) fn make(inner: &yazi_core::mgr::Tabs) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { inner: inner.into() }) } } diff --git a/yazi-fm/src/lives/tasks.rs b/yazi-fm/src/lives/tasks.rs index 60aac480..2a85aa2a 100644 --- a/yazi-fm/src/lives/tasks.rs +++ b/yazi-fm/src/lives/tasks.rs @@ -1,28 +1,31 @@ use std::ops::Deref; -use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields}; +use mlua::{AnyUserData, Lua, LuaSerdeExt, UserData, UserDataFields, Value}; +use yazi_binding::cached_field; -use super::Lives; +use super::{Lives, PtrCell}; pub(super) struct Tasks { - inner: *const yazi_core::tasks::Tasks, + inner: PtrCell, + + v_progress: Option, } impl Deref for Tasks { type Target = yazi_core::tasks::Tasks; - fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } + fn deref(&self) -> &Self::Target { &self.inner } } impl Tasks { #[inline] pub(super) fn make(inner: &yazi_core::tasks::Tasks) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { inner: inner.into(), v_progress: None }) } } impl UserData for Tasks { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("progress", |lua, me| lua.to_value(&me.progress)) + cached_field!(fields, progress, |lua: &Lua, me: &Self| lua.to_value(&me.progress)); } } diff --git a/yazi-fm/src/lives/yanked.rs b/yazi-fm/src/lives/yanked.rs index ab37d779..cdacb7c8 100644 --- a/yazi-fm/src/lives/yanked.rs +++ b/yazi-fm/src/lives/yanked.rs @@ -1,28 +1,25 @@ use std::{collections::hash_set, ops::Deref}; use mlua::{AnyUserData, IntoLuaMulti, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRefMut}; -use yazi_plugin::url::Url; +use yazi_binding::Url; -use super::{Iter, Lives}; +use super::{Iter, Lives, PtrCell}; pub(super) struct Yanked { - inner: *const yazi_core::mgr::Yanked, + inner: PtrCell, } impl Deref for Yanked { type Target = yazi_core::mgr::Yanked; - fn deref(&self) -> &Self::Target { self.inner() } + fn deref(&self) -> &Self::Target { &self.inner } } impl Yanked { #[inline] pub(super) fn make(inner: &yazi_core::mgr::Yanked) -> mlua::Result { - Lives::scoped_userdata(Self { inner }) + Lives::scoped_userdata(Self { inner: inner.into() }) } - - #[inline] - fn inner(&self) -> &'static yazi_core::mgr::Yanked { unsafe { &*self.inner } } } impl UserData for Yanked { @@ -37,14 +34,14 @@ impl UserData for Yanked { let iter = lua.create_function( |lua, mut iter: UserDataRefMut, _>>| { if let Some(next) = iter.next() { - (next.0, Url(next.1.clone())).into_lua_multi(lua) + (next.0, Url::new(next.1.clone())).into_lua_multi(lua) } else { ().into_lua_multi(lua) } }, )?; - Ok((iter, Iter::make(me.inner().iter()))) + Ok((iter, Iter::make(me.inner.as_static().iter()))) }); } } diff --git a/yazi-fm/src/panic.rs b/yazi-fm/src/panic.rs index ca8cedfe..3c832d78 100644 --- a/yazi-fm/src/panic.rs +++ b/yazi-fm/src/panic.rs @@ -10,7 +10,7 @@ impl Panic { std::panic::set_hook(Box::new(move |info| { Term::goodbye(|| { hook(info); - true + 1 }); })); } diff --git a/yazi-fm/src/term.rs b/yazi-fm/src/term.rs index f26a03fc..59bdeea0 100644 --- a/yazi-fm/src/term.rs +++ b/yazi-fm/src/term.rs @@ -89,7 +89,7 @@ impl Term { Ok(disable_raw_mode()?) } - pub(super) fn goodbye(f: impl FnOnce() -> bool) -> ! { + pub(super) fn goodbye(f: impl FnOnce() -> i32) -> ! { if CSI_U.swap(false, Ordering::Relaxed) { PopKeyboardEnhancementFlags.write_ansi(&mut TTY.writer()).ok(); } @@ -111,7 +111,7 @@ impl Term { disable_raw_mode().ok(); - std::process::exit(f() as i32); + std::process::exit(f()); } pub(super) fn draw(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result { diff --git a/yazi-fs/Cargo.toml b/yazi-fs/Cargo.toml index 4b530916..f85956bc 100644 --- a/yazi-fs/Cargo.toml +++ b/yazi-fs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-fs" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -9,9 +9,9 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-ffi = { path = "../yazi-ffi", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-ffi = { path = "../yazi-ffi", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-fs/src/step.rs b/yazi-fs/src/step.rs index 916060b4..ae5277dc 100644 --- a/yazi-fs/src/step.rs +++ b/yazi-fs/src/step.rs @@ -63,14 +63,13 @@ impl Step { Self::Percent(n) => n as isize * limit as isize / 100, }; - if fixed == 0 { - pos - } else if matches!(self, Self::Prev | Self::Next) { + if matches!(self, Self::Prev | Self::Next) { fixed.saturating_add_unsigned(pos).rem_euclid(len as _) as _ - } else if fixed > 0 { - pos.saturating_add_signed(fixed).min(len - 1) + } else if fixed >= 0 { + pos.saturating_add_signed(fixed) } else { pos.saturating_sub(fixed.unsigned_abs()) } + .min(len - 1) } } diff --git a/yazi-macro/Cargo.toml b/yazi-macro/Cargo.toml index 2479458d..ff85f2c1 100644 --- a/yazi-macro/Cargo.toml +++ b/yazi-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-macro" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] diff --git a/yazi-macro/src/event.rs b/yazi-macro/src/event.rs index c2f5c48b..745cdf42 100644 --- a/yazi-macro/src/event.rs +++ b/yazi-macro/src/event.rs @@ -24,6 +24,13 @@ macro_rules! render { render!(); } }; + ($left:expr, > $right:expr) => {{ + let val = $left; + if val > $right { + render!(); + } + val + }}; } #[macro_export] diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 137927ee..84c4113e 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-plugin" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -13,14 +13,15 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-adapter = { path = "../yazi-adapter", version = "25.3.7" } -yazi-boot = { path = "../yazi-boot", version = "25.3.7" } -yazi-config = { path = "../yazi-config", version = "25.3.7" } -yazi-dds = { path = "../yazi-dds", version = "25.3.7" } -yazi-fs = { path = "../yazi-fs", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-adapter = { path = "../yazi-adapter", version = "25.4.8" } +yazi-binding = { path = "../yazi-binding", version = "25.4.8" } +yazi-boot = { path = "../yazi-boot", version = "25.4.8" } +yazi-config = { path = "../yazi-config", version = "25.4.8" } +yazi-dds = { path = "../yazi-dds", version = "25.4.8" } +yazi-fs = { path = "../yazi-fs", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-proxy = { path = "../yazi-proxy", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies ansi-to-tui = { workspace = true } @@ -32,6 +33,7 @@ globset = { workspace = true } md-5 = { workspace = true } mlua = { workspace = true } parking_lot = { workspace = true } +paste = { workspace = true } ratatui = { workspace = true } serde_json = { workspace = true } syntect = { workspace = true } @@ -44,10 +46,12 @@ unicode-width = { workspace = true } yazi-prebuilt = "0.1.0" [target."cfg(unix)".dependencies] +libc = { workspace = true } uzers = { workspace = true } [target."cfg(windows)".dependencies] clipboard-win = "5.4.0" +windows-sys = { version = "0.59.0", features = [ "Win32_Security", "Win32_System_JobObjects", "Win32_System_Threading" ] } [target.'cfg(target_os = "macos")'.dependencies] crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } diff --git a/yazi-plugin/preset/components/entity.lua b/yazi-plugin/preset/components/entity.lua index 5f59f8f5..157e4907 100644 --- a/yazi-plugin/preset/components/entity.lua +++ b/yazi-plugin/preset/components/entity.lua @@ -18,7 +18,7 @@ function Entity:icon() local icon = self._file:icon() if not icon then return "" - elseif self._file:is_hovered() then + elseif self._file.is_hovered then return icon.text .. " " else return ui.Line(icon.text .. " "):style(icon.style) @@ -52,7 +52,7 @@ function Entity:highlights() end function Entity:found() - if not self._file:is_hovered() then + if not self._file.is_hovered then return "" end @@ -86,9 +86,9 @@ end function Entity:style() local s = self._file:style() - if not self._file:is_hovered() then + if not self._file.is_hovered then return s - elseif self._file:in_preview() then + elseif self._file.in_preview then return s and s:patch(th.mgr.preview_hovered) or th.mgr.preview_hovered else return s and s:patch(th.mgr.hovered) or th.mgr.hovered diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index 63af33fa..1dc851b9 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -38,7 +38,7 @@ function Header:flags() local t = {} if cwd.is_search then - t[#t + 1] = string.format("search: %s", cwd:frag()) + t[#t + 1] = string.format("search: %s", cwd.frag) end if filter then t[#t + 1] = string.format("filter: %s", filter) @@ -84,7 +84,7 @@ function Header:tabs() for i = 1, tabs do local text = i if th.mgr.tab_width > 2 then - text = ya.truncate(text .. " " .. cx.tabs[i]:name(), { max = th.mgr.tab_width }) + text = ya.truncate(text .. " " .. cx.tabs[i].name, { max = th.mgr.tab_width }) end if i == cx.tabs.idx then spans[#spans + 1] = ui.Span(" " .. text .. " "):style(th.mgr.tab_active) diff --git a/yazi-plugin/preset/plugins/extract.lua b/yazi-plugin/preset/plugins/extract.lua index 5775034d..7b690431 100644 --- a/yazi-plugin/preset/plugins/extract.lua +++ b/yazi-plugin/preset/plugins/extract.lua @@ -27,7 +27,7 @@ function M:entry(job) end local value, event = ya.input { - title = string.format('Password for "%s":', from:name()), + title = string.format('Password for "%s":', from.name), position = { "center", w = 50 }, } if event == 1 then @@ -39,7 +39,7 @@ function M:entry(job) end function M:try_with(from, pwd, to) - to = to or from:parent() + to = to or from.parent if not to then fail("Invalid URL '%s'", from) end @@ -90,7 +90,7 @@ function M:tidy(from, to, tmp) if only then target = to:join(outs[1].name) else - target = to:join(self.trim_ext(from:name())) + target = to:join(self.trim_ext(from.name)) end target = fs.unique_name(target) diff --git a/yazi-plugin/preset/plugins/fzf.lua b/yazi-plugin/preset/plugins/fzf.lua index f655a217..645675b0 100644 --- a/yazi-plugin/preset/plugins/fzf.lua +++ b/yazi-plugin/preset/plugins/fzf.lua @@ -1,29 +1,73 @@ -local state = ya.sync(function() return cx.active.current.cwd end) +local M = {} -local function fail(s, ...) ya.notify { title = "Fzf", content = string.format(s, ...), timeout = 5, level = "error" } end +local state = ya.sync(function() + local selected = {} + for _, url in pairs(cx.active.selected) do + selected[#selected + 1] = url + end + return cx.active.current.cwd, selected +end) + +function M:entry() + ya.mgr_emit("escape", { visual = true }) -local function entry() local _permit = ya.hide() - local cwd = tostring(state()) + local cwd, selected = state() - local child, err = - Command("fzf"):cwd(cwd):stdin(Command.INHERIT):stdout(Command.PIPED):stderr(Command.INHERIT):spawn() + local output, err = M.run_with(cwd, selected) + if not output then + return ya.notify { title = "Fzf", content = tostring(err), timeout = 5, level = "error" } + end + + local urls = M.split_urls(cwd, output) + if #urls == 1 then + local cha = #selected == 0 and fs.cha(urls[1]) + ya.mgr_emit(cha and cha.is_dir and "cd" or "reveal", { urls[1] }) + elseif #urls > 1 then + urls.state = #selected > 0 and "off" or "on" + ya.mgr_emit("toggle_all", urls) + end +end + +function M.run_with(cwd, selected) + local child, err = Command("fzf") + :arg("-m") + :cwd(tostring(cwd)) + :stdin(#selected > 0 and Command.PIPED or Command.INHERIT) + :stdout(Command.PIPED) + :spawn() if not child then - return fail("Failed to start `fzf`, error: " .. err) + return nil, Err("Failed to start `fzf`, error: %s", err) + end + + for _, u in ipairs(selected) do + child:write_all(string.format("%s\n", u)) + end + if #selected > 0 then + child:flush() end local output, err = child:wait_with_output() if not output then - return fail("Cannot read `fzf` output, error: " .. err) + return nil, Err("Cannot read `fzf` output, error: %s", err) elseif not output.status.success and output.status.code ~= 130 then - return fail("`fzf` exited with error code %s", output.status.code) - end - - local target = output.stdout:gsub("\n$", "") - if target ~= "" then - ya.mgr_emit(target:find("[/\\]$") and "cd" or "reveal", { target }) + return nil, Err("`fzf` exited with error code %s", output.status.code) end + return output.stdout, nil end -return { entry = entry } +function M.split_urls(cwd, output) + local t = {} + for line in output:gmatch("[^\r\n]+") do + local u = Url(line) + if u.is_absolute then + t[#t + 1] = u + else + t[#t + 1] = cwd:join(u) + end + end + return t +end + +return M diff --git a/yazi-plugin/preset/plugins/magick.lua b/yazi-plugin/preset/plugins/magick.lua index a9496cdd..b976f13d 100644 --- a/yazi-plugin/preset/plugins/magick.lua +++ b/yazi-plugin/preset/plugins/magick.lua @@ -24,7 +24,7 @@ function M:preload(job) return true end - local cmd = M.with_env() + local cmd = M.with_limit() if job.args.flatten then cmd = cmd:arg("-flatten") end @@ -46,10 +46,16 @@ end function M:spot(job) require("file"):spot(job) end -function M.with_env() - local cmd = Command("magick"):env("MAGICK_THREAD_LIMIT", 1) +function M.with_limit() + local cmd = Command("magick"):args { "-limit", "thread", 1 } if rt.tasks.image_alloc > 0 then - cmd = cmd:env("MAGICK_MEMORY_LIMIT", rt.tasks.image_alloc) + cmd = cmd:args({ "-limit", "memory", rt.tasks.image_alloc }):args { "-limit", "disk", "1MiB" } + end + if rt.tasks.image_bound[1] > 0 then + cmd = cmd:args { "-limit", "width", rt.tasks.image_bound[1] } + end + if rt.tasks.image_bound[2] > 0 then + cmd = cmd:args { "-limit", "height", rt.tasks.image_bound[2] } end return cmd end diff --git a/yazi-plugin/preset/plugins/svg.lua b/yazi-plugin/preset/plugins/svg.lua index aac9e127..7d046b17 100644 --- a/yazi-plugin/preset/plugins/svg.lua +++ b/yazi-plugin/preset/plugins/svg.lua @@ -25,19 +25,44 @@ function M:preload(job) end -- stylua: ignore - local cmd = require("magick").with_env():args { - "-density", 200, - tostring(job.file.url), "-strip", - "-resize", string.format("%dx%d^", rt.preview.max_width, rt.preview.max_height), - "-quality", rt.preview.image_quality, - string.format("JPG:%s", cache), + local cmd = Command("resvg"):args { + "-w", rt.preview.max_width, "-h", rt.preview.max_height, + "--image-rendering", "optimizeSpeed", + tostring(job.file.url), tostring(cache) } + if rt.tasks.image_alloc > 0 then + cmd = cmd:memory(rt.tasks.image_alloc) + end + + local child, err = cmd:spawn() + if not child then + return true, Err("Failed to start `resvg`, error: %s", err) + end + + local status, err + while true do + ya.sleep(0.2) + + status, err = child:try_wait() + if status or err then + break + end + + local id, mem = child:id(), nil + if id then + mem = ya.proc_info(id).mem_resident + end + if mem and mem > rt.tasks.image_alloc then + child:start_kill() + err = Err("memory limit exceeded, pid: %s, memory: %s", id, mem) + break + end + end - local status, err = cmd:status() if status then return status.success else - return true, Err("Failed to start `magick`, error: %s", err) + return true, Err("Error while running `resvg`: %s", err) end end diff --git a/yazi-plugin/preset/plugins/video.lua b/yazi-plugin/preset/plugins/video.lua index 1bc118a4..e9ca2f1d 100644 --- a/yazi-plugin/preset/plugins/video.lua +++ b/yazi-plugin/preset/plugins/video.lua @@ -60,7 +60,7 @@ function M:preload(job) "-i", tostring(job.file.url), "-vframes", 1, "-q:v", qv, - "-vf", string.format("scale=-1:'min(%d,ih)':flags=fast_bilinear", rt.preview.max_height), + "-vf", string.format("scale='min(%d,iw)':'min(%d,ih)':force_original_aspect_ratio=decrease:flags=fast_bilinear", rt.preview.max_width, rt.preview.max_height), "-f", "image2", "-y", tostring(cache), }):status() diff --git a/yazi-plugin/src/bindings/chan.rs b/yazi-plugin/src/bindings/chan.rs index 88067d98..206e9433 100644 --- a/yazi-plugin/src/bindings/chan.rs +++ b/yazi-plugin/src/bindings/chan.rs @@ -1,6 +1,5 @@ use mlua::{ExternalError, IntoLuaMulti, UserData, Value}; - -use crate::Error; +use yazi_binding::Error; pub struct MpscTx(pub tokio::sync::mpsc::Sender); pub struct MpscRx(pub tokio::sync::mpsc::Receiver); diff --git a/yazi-plugin/src/bindings/icon.rs b/yazi-plugin/src/bindings/icon.rs index c3fda16d..d27ddc03 100644 --- a/yazi-plugin/src/bindings/icon.rs +++ b/yazi-plugin/src/bindings/icon.rs @@ -1,24 +1,32 @@ use std::ops::Deref; -use mlua::{UserData, UserDataFields}; +use mlua::{Lua, UserData, UserDataFields, Value}; +use yazi_binding::cached_field; use crate::elements::Style; -pub struct Icon(&'static yazi_shared::theme::Icon); +pub struct Icon { + inner: &'static yazi_shared::theme::Icon, + + v_text: Option, + v_style: Option, +} impl Deref for Icon { type Target = yazi_shared::theme::Icon; - fn deref(&self) -> &Self::Target { self.0 } + fn deref(&self) -> &Self::Target { self.inner } } impl From<&'static yazi_shared::theme::Icon> for Icon { - fn from(icon: &'static yazi_shared::theme::Icon) -> Self { Self(icon) } + fn from(icon: &'static yazi_shared::theme::Icon) -> Self { + Self { inner: icon, v_text: None, v_style: None } + } } impl UserData for Icon { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("text", |lua, me| lua.create_string(&me.text)); - fields.add_field_method_get("style", |_, me| Ok(Style::from(me.style))); + cached_field!(fields, text, |lua: &Lua, me: &Self| lua.create_string(&me.text)); + cached_field!(fields, style, |_, me: &Self| Ok(Style::from(me.style))); } } diff --git a/yazi-plugin/src/config/plugin.rs b/yazi-plugin/src/config/plugin.rs index 478482ca..ec8b19ea 100644 --- a/yazi-plugin/src/config/plugin.rs +++ b/yazi-plugin/src/config/plugin.rs @@ -1,7 +1,8 @@ use mlua::{Function, IntoLua, Lua, UserData, Value}; +use yazi_binding::{UrlRef, cached_field}; use yazi_config::YAZI; -use crate::{Composer, file::FileRef, url::UrlRef}; +use crate::{Composer, file::FileRef}; pub(super) struct Plugin; @@ -21,61 +22,93 @@ impl Plugin { fn fetchers(lua: &Lua) -> mlua::Result { lua.create_function(|lua, (file, mime): (FileRef, mlua::String)| { - lua.create_sequence_from(YAZI.plugin.fetchers(&file.url, &mime.to_str()?).map(Fetcher)) + lua.create_sequence_from(YAZI.plugin.fetchers(&file.url, &mime.to_str()?).map(Fetcher::new)) }) } fn spotter(lua: &Lua) -> mlua::Result { lua.create_function(|_, (url, mime): (UrlRef, mlua::String)| { - Ok(YAZI.plugin.spotter(&url, &mime.to_str()?).map(Spotter)) + Ok(YAZI.plugin.spotter(&url, &mime.to_str()?).map(Spotter::new)) }) } fn preloaders(lua: &Lua) -> mlua::Result { lua.create_function(|lua, (url, mime): (UrlRef, mlua::String)| { - lua.create_sequence_from(YAZI.plugin.preloaders(&url, &mime.to_str()?).map(Preloader)) + lua.create_sequence_from(YAZI.plugin.preloaders(&url, &mime.to_str()?).map(Preloader::new)) }) } fn previewer(lua: &Lua) -> mlua::Result { lua.create_function(|_, (url, mime): (UrlRef, mlua::String)| { - Ok(YAZI.plugin.previewer(&url, &mime.to_str()?).map(Previewer)) + Ok(YAZI.plugin.previewer(&url, &mime.to_str()?).map(Previewer::new)) }) } } // --- Fetcher -struct Fetcher(&'static yazi_config::plugin::Fetcher); +struct Fetcher { + inner: &'static yazi_config::plugin::Fetcher, + + v_cmd: Option, +} + +impl Fetcher { + pub fn new(inner: &'static yazi_config::plugin::Fetcher) -> Self { Self { inner, v_cmd: None } } +} impl UserData for Fetcher { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("cmd", |lua, me| lua.create_string(&me.0.run.name)); + cached_field!(fields, cmd, |lua: &Lua, me: &Self| lua.create_string(&me.inner.run.name)); } } // --- Spotter -struct Spotter(&'static yazi_config::plugin::Spotter); +struct Spotter { + inner: &'static yazi_config::plugin::Spotter, + + v_cmd: Option, +} + +impl Spotter { + pub fn new(inner: &'static yazi_config::plugin::Spotter) -> Self { Self { inner, v_cmd: None } } +} impl UserData for Spotter { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("cmd", |lua, me| lua.create_string(&me.0.run.name)); + cached_field!(fields, cmd, |lua: &Lua, me: &Self| lua.create_string(&me.inner.run.name)); } } // --- Preloader -struct Preloader(&'static yazi_config::plugin::Preloader); +struct Preloader { + inner: &'static yazi_config::plugin::Preloader, + + v_cmd: Option, +} + +impl Preloader { + pub fn new(inner: &'static yazi_config::plugin::Preloader) -> Self { Self { inner, v_cmd: None } } +} impl UserData for Preloader { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("cmd", |lua, me| lua.create_string(&me.0.run.name)); + cached_field!(fields, cmd, |lua: &Lua, me: &Self| lua.create_string(&me.inner.run.name)); } } // --- Previewer -struct Previewer(&'static yazi_config::plugin::Previewer); +struct Previewer { + inner: &'static yazi_config::plugin::Previewer, + + v_cmd: Option, +} + +impl Previewer { + pub fn new(inner: &'static yazi_config::plugin::Previewer) -> Self { Self { inner, v_cmd: None } } +} impl UserData for Previewer { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("cmd", |lua, me| lua.create_string(&me.0.run.name)); + cached_field!(fields, cmd, |lua: &Lua, me: &Self| lua.create_string(&me.inner.run.name)); } } diff --git a/yazi-plugin/src/config/runtime.rs b/yazi-plugin/src/config/runtime.rs index 108d564c..ff008c35 100644 --- a/yazi-plugin/src/config/runtime.rs +++ b/yazi-plugin/src/config/runtime.rs @@ -1,18 +1,17 @@ use mlua::{IntoLua, Lua, LuaSerdeExt, SerializeOptions, Value}; use yazi_adapter::EMULATOR; +use yazi_binding::Url; use yazi_boot::ARGS; -use yazi_config::{THEME, YAZI}; +use yazi_config::YAZI; -use crate::{Composer, url::Url}; +use crate::Composer; pub const OPTS: SerializeOptions = SerializeOptions::new().serialize_none_to_null(false).serialize_unit_to_null(false); -pub struct Runtime<'a> { - lua: &'a Lua, -} +pub struct Runtime; -impl<'a> Runtime<'a> { +impl Runtime { pub fn compose(lua: &Lua) -> mlua::Result { Composer::make(lua, 10, |lua, key| { match key { @@ -30,9 +29,9 @@ impl<'a> Runtime<'a> { fn args(lua: &Lua) -> mlua::Result { Composer::make(lua, 5, |lua, key| match key { - b"entries" => lua.create_sequence_from(ARGS.entries.iter().map(Url::from))?.into_lua(lua), - b"cwd_file" => ARGS.cwd_file.as_ref().map(Url::from).into_lua(lua), - b"chooser_file" => ARGS.chooser_file.as_ref().map(Url::from).into_lua(lua), + b"entries" => lua.create_sequence_from(ARGS.entries.iter().map(Url::new))?.into_lua(lua), + b"cwd_file" => ARGS.cwd_file.as_ref().map(Url::new).into_lua(lua), + b"chooser_file" => ARGS.chooser_file.as_ref().map(Url::new).into_lua(lua), _ => Ok(Value::Nil), }) } @@ -106,24 +105,4 @@ impl<'a> Runtime<'a> { .into_lua(lua) }) } - - pub fn new(lua: &'a Lua) -> Self { Self { lua } } - - // TODO: remove this - pub fn install_manager(self) -> mlua::Result { - self.lua.globals().raw_set("MANAGER", self.lua.to_value_with(&YAZI.mgr, OPTS)?)?; - Ok(self) - } - - // TODO: remove this - pub fn install_theme(self) -> mlua::Result { - self.lua.globals().raw_set("THEME", self.lua.to_value_with(&*THEME, OPTS)?)?; - Ok(self) - } - - // TODO: remove this - pub fn install_preview(self) -> mlua::Result { - self.lua.globals().raw_set("PREVIEW", self.lua.to_value_with(&YAZI.preview, OPTS)?)?; - Ok(self) - } } diff --git a/yazi-plugin/src/elements/pad.rs b/yazi-plugin/src/elements/pad.rs index 141ccaf7..94d97e41 100644 --- a/yazi-plugin/src/elements/pad.rs +++ b/yazi-plugin/src/elements/pad.rs @@ -17,6 +17,13 @@ impl From for Pad { impl Pad { pub fn compose(lua: &Lua, v4: bool) -> mlua::Result { + if !v4 { + crate::deprecate!( + lua, + "The `ui.Padding` has been deprecated, please use `ui.Pad` instead, in your {}" + ); + } + let new = if v4 { lua.create_function(|_, (_, top, right, bottom, left): (Table, u16, u16, u16, u16)| { Ok(Self(ratatui::widgets::Padding::new(left, right, top, bottom))) diff --git a/yazi-plugin/src/elements/pos.rs b/yazi-plugin/src/elements/pos.rs index bafd6195..dc07cb51 100644 --- a/yazi-plugin/src/elements/pos.rs +++ b/yazi-plugin/src/elements/pos.rs @@ -64,6 +64,7 @@ impl Pos { impl UserData for Pos { fn add_fields>(fields: &mut F) { + // TODO: cache fields.add_field_method_get(1, |_, me| Ok(me.origin.to_string())); fields.add_field_method_get("x", |_, me| Ok(me.offset.x)); fields.add_field_method_get("y", |_, me| Ok(me.offset.y)); diff --git a/yazi-plugin/src/elements/rect.rs b/yazi-plugin/src/elements/rect.rs index d0d16db0..78b9690d 100644 --- a/yazi-plugin/src/elements/rect.rs +++ b/yazi-plugin/src/elements/rect.rs @@ -72,7 +72,10 @@ impl UserData for Rect { fn add_methods>(methods: &mut M) { methods.add_method("pad", |_, me, pad: Pad| Ok(me.pad(pad))); // TODO: deprecate this - methods.add_method("padding", |_, me, pad: Pad| Ok(me.pad(pad))); + methods.add_method("padding", |lua, me, pad: Pad| { + crate::deprecate!(lua, "The `padding()` method of `ui.Rect` has been deprecated, please use `pad()` instead, in your {}"); + Ok(me.pad(pad)) + }); methods.add_method("contains", |_, me, Rect(rect)| Ok(me.contains(rect.into()))); } } diff --git a/yazi-plugin/src/elements/text.rs b/yazi-plugin/src/elements/text.rs index d9014dbe..5aa0b355 100644 --- a/yazi-plugin/src/elements/text.rs +++ b/yazi-plugin/src/elements/text.rs @@ -23,9 +23,9 @@ pub struct Text { pub area: Area, // TODO: block - pub inner: ratatui::text::Text<'static>, - pub wrap: u8, - // TODO: scroll + pub inner: ratatui::text::Text<'static>, + pub wrap: u8, + pub scroll: ratatui::layout::Position, } impl Text { @@ -58,7 +58,7 @@ impl Text { trans: impl Fn(yazi_config::popup::Position) -> ratatui::layout::Rect, ) { let rect = self.area.transform(trans); - if self.wrap == WRAP_NO { + if self.wrap == WRAP_NO && self.scroll == Default::default() { self.inner.render(rect, buf); } else { ratatui::widgets::Paragraph::from(self).render(rect, buf); @@ -130,7 +130,7 @@ impl From for ratatui::widgets::Paragraph<'static> { if value.wrap != WRAP_NO { p = p.wrap(ratatui::widgets::Wrap { trim: value.wrap == WRAP_TRIM }); } - p + p.scroll((value.scroll.y, value.scroll.x)) } } @@ -155,6 +155,10 @@ impl UserData for Text { }; Ok(ud) }); + methods.add_function_mut("scroll", |_, (ud, x, y): (AnyUserData, u16, u16)| { + ud.borrow_mut::()?.scroll = ratatui::layout::Position { x, y }; + Ok(ud) + }); methods.add_method("max_width", |_, me, ()| { Ok(me.inner.lines.iter().take(me.area.size().height as usize).map(|l| l.width()).max()) }); diff --git a/yazi-plugin/src/file/file.rs b/yazi-plugin/src/file/file.rs index 53a1006f..e23db4b6 100644 --- a/yazi-plugin/src/file/file.rs +++ b/yazi-plugin/src/file/file.rs @@ -1,26 +1,44 @@ -use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, Table, UserDataRef, Value}; +use std::ops::Deref; + +use mlua::{ExternalError, FromLua, Lua, Table, UserData, UserDataFields, UserDataMethods, UserDataRef, Value}; +use yazi_binding::Url; use crate::{bindings::Cha, impl_file_fields, impl_file_methods}; -pub type FileRef = UserDataRef; +pub type FileRef = UserDataRef; -pub struct File(pub yazi_fs::File); +pub struct File { + inner: yazi_fs::File, + + v_cha: Option, + v_url: Option, + v_link_to: Option, + v_name: Option, +} + +impl Deref for File { + type Target = yazi_fs::File; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl From for yazi_fs::File { + fn from(value: File) -> Self { value.inner } +} impl File { - #[inline] - pub fn register(lua: &Lua) -> mlua::Result<()> { - lua.register_userdata_type::(|reg| { - impl_file_fields!(reg); - impl_file_methods!(reg); - }) + pub fn new(inner: yazi_fs::File) -> Self { + Self { inner, v_cha: None, v_url: None, v_link_to: None, v_name: None } } +} +impl File { pub fn install(lua: &Lua) -> mlua::Result<()> { lua.globals().raw_set( "File", lua.create_function(|_, t: Table| { - Ok(Self(yazi_fs::File { - url: t.raw_get::("url")?.take()?, + Ok(Self::new(yazi_fs::File { + url: t.raw_get::("url")?.into(), cha: *t.raw_get::("cha")?, ..Default::default() })) @@ -31,15 +49,19 @@ impl File { impl FromLua for File { fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::UserData(ud) => ud.take().map(Self), - _ => Err("Expected a File".into_lua_err()), - } + Ok(match value { + Value::UserData(ud) => Self::new(ud.take::()?.inner), + _ => Err("Expected a File".into_lua_err())?, + }) } } -impl IntoLua for File { - fn into_lua(self, lua: &Lua) -> mlua::Result { - lua.create_any_userdata(self.0)?.into_lua(lua) +impl UserData for File { + fn add_fields>(fields: &mut F) { + impl_file_fields!(fields); + } + + fn add_methods>(methods: &mut M) { + impl_file_methods!(methods); } } diff --git a/yazi-plugin/src/file/mod.rs b/yazi-plugin/src/file/mod.rs index 640debc4..b11417e9 100644 --- a/yazi-plugin/src/file/mod.rs +++ b/yazi-plugin/src/file/mod.rs @@ -1,12 +1,3 @@ #![allow(clippy::module_inception)] -use mlua::Lua; - yazi_macro::mod_flat!(file); - -pub fn pour(lua: &Lua) -> mlua::Result<()> { - file::File::register(lua)?; - file::File::install(lua)?; - - Ok(()) -} diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index 347a1547..f26db46f 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -1,9 +1,10 @@ use globset::GlobBuilder; use mlua::{ExternalError, ExternalResult, Function, IntoLua, IntoLuaMulti, Lua, Table, Value}; use tokio::fs; +use yazi_binding::{Error, Url, UrlRef}; use yazi_fs::{mounts::PARTITIONS, remove_dir_clean}; -use crate::{Composer, Error, bindings::Cha, file::File, url::{Url, UrlRef}}; +use crate::{Composer, bindings::Cha, file::File}; pub fn compose(lua: &Lua) -> mlua::Result { Composer::make(lua, 10, |lua, key| { @@ -34,7 +35,7 @@ fn op(lua: &Lua) -> mlua::Result { fn cwd(lua: &Lua) -> mlua::Result { lua.create_function(|lua, ()| match std::env::current_dir() { - Ok(p) => (Url::from(p), Value::Nil).into_lua_multi(lua), + Ok(p) => (Url::new(p), Value::Nil).into_lua_multi(lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(lua), }) } @@ -139,7 +140,7 @@ fn read_dir(lua: &Lua) -> mlua::Result { } else { yazi_fs::File::from_dummy(url, next.file_type().await.ok()) }; - files.push(File(file)); + files.push(File::new(file)); } let tbl = lua.create_table_with_capacity(files.len(), 0)?; @@ -154,9 +155,9 @@ fn read_dir(lua: &Lua) -> mlua::Result { fn expand_url(lua: &Lua) -> mlua::Result { lua.create_function(|_, value: Value| { use yazi_fs::expand_path; - Ok(Url::from(match value { + Ok(Url::new(match value { Value::String(s) => expand_path(s.to_str()?.as_ref()), - Value::UserData(ud) => expand_path(&*ud.borrow::()?), + Value::UserData(ud) => expand_path(&*ud.borrow::()?), _ => Err("must be a string or a Url".into_lua_err())?, })) }) @@ -165,7 +166,7 @@ fn expand_url(lua: &Lua) -> mlua::Result { fn unique_name(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, url: UrlRef| async move { match yazi_fs::unique_name(url.clone(), async { false }).await { - Ok(u) => (Url(u), Value::Nil).into_lua_multi(&lua), + Ok(u) => (Url::new(u), Value::Nil).into_lua_multi(&lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } }) diff --git a/yazi-plugin/src/fs/op.rs b/yazi-plugin/src/fs/op.rs index 31f687e2..f8e572d6 100644 --- a/yazi-plugin/src/fs/op.rs +++ b/yazi-plugin/src/fs/op.rs @@ -1,6 +1,7 @@ use mlua::{IntoLua, Lua, Table}; +use yazi_binding::Url; -use crate::{Id, bindings::Cha, file::File, url::Url}; +use crate::{Id, bindings::Cha, file::File}; pub(super) struct FilesOp(yazi_fs::FilesOp); @@ -11,8 +12,11 @@ impl FilesOp { let files: Table = t.raw_get("files")?; Ok(Self(yazi_fs::FilesOp::Part( - url.0, - files.sequence_values::().map(|f| f.map(|f| f.0)).collect::>>()?, + url.into(), + files + .sequence_values::() + .map(|f| f.map(Into::into)) + .collect::>>()?, *id, ))) } @@ -22,7 +26,7 @@ impl FilesOp { let cha: Cha = t.raw_get("cha")?; let url: Url = t.raw_get("url")?; - Ok(Self(yazi_fs::FilesOp::Done(url.0, *cha, *id))) + Ok(Self(yazi_fs::FilesOp::Done(url.into(), *cha, *id))) } } diff --git a/yazi-plugin/src/id.rs b/yazi-plugin/src/id.rs index db0e48dd..dd5470ca 100644 --- a/yazi-plugin/src/id.rs +++ b/yazi-plugin/src/id.rs @@ -24,6 +24,6 @@ impl FromLua for Id { impl UserData for Id { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("value", |_, me: &Self| Ok(me.0.get())); + fields.add_field_method_get("value", |_, me| Ok(me.0.get())); } } diff --git a/yazi-plugin/src/isolate/fetch.rs b/yazi-plugin/src/isolate/fetch.rs index 063d2f7f..279b53e4 100644 --- a/yazi-plugin/src/isolate/fetch.rs +++ b/yazi-plugin/src/isolate/fetch.rs @@ -1,10 +1,11 @@ use mlua::{ExternalResult, FromLua, IntoLua, Lua, ObjectLike, Value}; use tokio::runtime::Handle; +use yazi_binding::Error; use yazi_dds::Sendable; use yazi_shared::event::CmdCow; use super::slim_lua; -use crate::{Error, file::File, loader::LOADER}; +use crate::{file::File, loader::LOADER}; pub async fn fetch( cmd: CmdCow, @@ -23,7 +24,7 @@ pub async fn fetch( "fetch", lua.create_table_from([ ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), - ("files", lua.create_sequence_from(files.into_iter().map(File))?.into_lua(&lua)?), + ("files", lua.create_sequence_from(files.into_iter().map(File::new))?.into_lua(&lua)?), ])?, )) }) diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index 9f5e18fc..d3fae54c 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -6,7 +6,6 @@ use crate::runtime::Runtime; pub fn slim_lua(name: &str) -> mlua::Result { let lua = Lua::new(); lua.set_named_registry_value("ir", Runtime::new(name))?; - crate::config::Runtime::new(&lua).install_preview()?; // Base let globals = lua.globals(); @@ -17,10 +16,10 @@ pub fn slim_lua(name: &str) -> mlua::Result { globals.raw_set("th", crate::config::Theme::compose(&lua)?)?; crate::bindings::Cha::install(&lua)?; - crate::file::pour(&lua)?; - crate::url::pour(&lua)?; + crate::file::File::install(&lua)?; + yazi_binding::Url::install(&lua)?; - crate::Error::install(&lua)?; + yazi_binding::Error::install(&lua)?; crate::loader::install_isolate(&lua)?; crate::process::install(&lua)?; diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index 308e9a38..4866259c 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -51,7 +51,7 @@ fn peek_sync(cmd: &'static Cmd, file: yazi_fs::File, mime: Cow<'static, str>, sk let job = lua.create_table_from([ ("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?), ("args", Sendable::args_to_table_ref(lua, &cmd.args)?.into_lua(lua)?), - ("file", File(file).into_lua(lua)?), + ("file", File::new(file).into_lua(lua)?), ("mime", mime.into_lua(lua)?), ("skip", skip.into_lua(lua)?), ])?; @@ -88,7 +88,7 @@ fn peek_async( let job = lua.create_table_from([ ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), - ("file", File(file).into_lua(&lua)?), + ("file", File::new(file).into_lua(&lua)?), ("mime", mime.into_lua(&lua)?), ("skip", skip.into_lua(&lua)?), ])?; diff --git a/yazi-plugin/src/isolate/preload.rs b/yazi-plugin/src/isolate/preload.rs index 3bcaf596..d39f6784 100644 --- a/yazi-plugin/src/isolate/preload.rs +++ b/yazi-plugin/src/isolate/preload.rs @@ -1,11 +1,12 @@ use mlua::{ExternalResult, IntoLua, ObjectLike}; use tokio::runtime::Handle; +use yazi_binding::Error; use yazi_config::LAYOUT; use yazi_dds::Sendable; use yazi_shared::event::Cmd; use super::slim_lua; -use crate::{Error, elements::Rect, file::File, loader::LOADER}; +use crate::{elements::Rect, file::File, loader::LOADER}; pub async fn preload( cmd: &'static Cmd, @@ -20,7 +21,7 @@ pub async fn preload( let job = lua.create_table_from([ ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), - ("file", File(file).into_lua(&lua)?), + ("file", File::new(file).into_lua(&lua)?), ("skip", 0.into_lua(&lua)?), ])?; diff --git a/yazi-plugin/src/isolate/seek.rs b/yazi-plugin/src/isolate/seek.rs index a378c06d..464cd34d 100644 --- a/yazi-plugin/src/isolate/seek.rs +++ b/yazi-plugin/src/isolate/seek.rs @@ -8,7 +8,7 @@ use crate::{elements::Rect, file::File}; pub fn seek_sync(cmd: &'static Cmd, file: yazi_fs::File, units: i16) { let cb: PluginCallback = Box::new(move |lua, plugin| { let job = lua.create_table_from([ - ("file", File(file).into_lua(lua)?), + ("file", File::new(file).into_lua(lua)?), ("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?), ("units", units.into_lua(lua)?), ])?; diff --git a/yazi-plugin/src/isolate/spot.rs b/yazi-plugin/src/isolate/spot.rs index c3040085..0dab49b1 100644 --- a/yazi-plugin/src/isolate/spot.rs +++ b/yazi-plugin/src/isolate/spot.rs @@ -38,7 +38,7 @@ pub fn spot( let plugin = LOADER.load_once(&lua, &cmd.name)?; let job = lua.create_table_from([ ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), - ("file", File(file).into_lua(&lua)?), + ("file", File::new(file).into_lua(&lua)?), ("mime", mime.into_lua(&lua)?), ("skip", skip.into_lua(&lua)?), ])?; diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index 9cbf8e96..4a710d44 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -2,9 +2,9 @@ mod macros; -yazi_macro::mod_pub!(bindings config elements external file fs isolate loader process pubsub url utils); +yazi_macro::mod_pub!(bindings config elements external file fs isolate loader process pubsub utils); -yazi_macro::mod_flat!(clipboard composer error id lua runtime); +yazi_macro::mod_flat!(clipboard composer id lua runtime); pub fn init() -> anyhow::Result<()> { CLIPBOARD.with(<_>::default); diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/lua.rs index 3e932810..ec66c456 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/lua.rs @@ -18,7 +18,6 @@ pub(super) fn init_lua() -> Result<()> { fn stage_1(lua: &'static Lua) -> Result<()> { lua.set_named_registry_value("ir", Runtime::default())?; - crate::config::Runtime::new(lua).install_manager()?.install_theme()?; // Base let globals = lua.globals(); @@ -29,11 +28,11 @@ fn stage_1(lua: &'static Lua) -> Result<()> { globals.raw_set("rt", crate::config::Runtime::compose(lua)?)?; globals.raw_set("th", crate::config::Theme::compose(lua)?)?; - crate::Error::install(lua)?; + yazi_binding::Error::install(lua)?; crate::bindings::Cha::install(lua)?; crate::loader::install(lua)?; - crate::file::pour(lua)?; - crate::url::pour(lua)?; + crate::file::File::install(lua)?; + yazi_binding::Url::install(lua)?; // Addons lua.load(preset!("ya")).set_name("ya.lua").exec()?; diff --git a/yazi-plugin/src/macros.rs b/yazi-plugin/src/macros.rs index 74c9d40f..e6d284b7 100644 --- a/yazi-plugin/src/macros.rs +++ b/yazi-plugin/src/macros.rs @@ -81,14 +81,17 @@ macro_rules! impl_style_shorthands { #[macro_export] macro_rules! impl_file_fields { ($fields:ident) => { - use mlua::UserDataFields; + yazi_binding::cached_field!($fields, cha, |_, me: &Self| Ok($crate::bindings::Cha::from( + me.cha + ))); + yazi_binding::cached_field!($fields, url, |_, me: &Self| Ok(yazi_binding::Url::new( + me.url_owned() + ))); + yazi_binding::cached_field!($fields, link_to, |_, me: &Self| Ok( + me.link_to.clone().map(yazi_binding::Url::new) + )); - $fields.add_field_method_get("cha", |_, me| Ok($crate::bindings::Cha::from(me.cha))); - $fields.add_field_method_get("url", |_, me| Ok($crate::url::Url(me.url_owned()))); - $fields - .add_field_method_get("link_to", |_, me| Ok(me.link_to.clone().map(|u| $crate::url::Url(u)))); - - $fields.add_field_method_get("name", |lua, me| { + yazi_binding::cached_field!($fields, name, |lua: &mlua::Lua, me: &Self| { Some(me.name()) .filter(|s| !s.is_empty()) .map(|s| lua.create_string(s.as_encoded_bytes())) @@ -100,8 +103,6 @@ macro_rules! impl_file_fields { #[macro_export] macro_rules! impl_file_methods { ($methods:ident) => { - use mlua::UserDataMethods; - $methods.add_method("hash", |_, me, ()| Ok(me.hash())); $methods.add_method("icon", |_, me, ()| { diff --git a/yazi-plugin/src/process/child.rs b/yazi-plugin/src/process/child.rs index 26e14679..043ff670 100644 --- a/yazi-plugin/src/process/child.rs +++ b/yazi-plugin/src/process/child.rs @@ -1,50 +1,102 @@ -use std::{ops::DerefMut, time::Duration}; +use std::{ops::DerefMut, process::ExitStatus, time::Duration}; use futures::future::try_join3; -use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Table, UserData, Value}; -use tokio::{ - io::{self, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, - process::{ChildStderr, ChildStdin, ChildStdout}, - select, -}; +use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Table, UserData, UserDataMethods, Value}; +use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, process::{ChildStderr, ChildStdin, ChildStdout}, select}; +use yazi_binding::Error; use super::Status; -use crate::{Error, process::Output}; +use crate::process::Output; pub struct Child { - inner: tokio::process::Child, - stdin: Option>, - stdout: Option>, - stderr: Option>, + inner: tokio::process::Child, + stdin: Option>, + stdout: Option>, + stderr: Option>, + #[cfg(windows)] + job_handle: Option, +} + +#[cfg(windows)] +impl Drop for Child { + fn drop(&mut self) { + if let Some(h) = self.job_handle.take() { + unsafe { windows_sys::Win32::Foundation::CloseHandle(h) }; + } + } } impl Child { - pub fn new(mut inner: tokio::process::Child) -> Self { + pub fn new( + mut inner: tokio::process::Child, + #[cfg(windows)] job_handle: Option, + ) -> Self { let stdin = inner.stdin.take().map(BufWriter::new); let stdout = inner.stdout.take().map(BufReader::new); let stderr = inner.stderr.take().map(BufReader::new); - Self { inner, stdin, stdout, stderr } + Self { + inner, + stdin, + stdout, + stderr, + #[cfg(windows)] + job_handle, + } + } + + pub(super) async fn wait(&mut self) -> io::Result { + drop(self.stdin.take()); + self.inner.wait().await + } + + pub(super) async fn status(&mut self) -> io::Result { + drop(self.stdin.take()); + drop(self.stdout.take()); + drop(self.stderr.take()); + self.inner.wait().await + } + + async fn read_line(&mut self) -> (Option>, u8) { + async fn read(r: Option) -> Option> { + let mut buf = Vec::new(); + match r?.read_until(b'\n', &mut buf).await { + Ok(0) | Err(_) => None, + Ok(_) => Some(buf), + } + } + + select! { + r @ Some(_) = read(self.stdout.as_mut()) => (r, 0u8), + r @ Some(_) = read(self.stderr.as_mut()) => (r, 1u8), + else => (None, 2u8), + } + } + + pub(super) async fn wait_with_output(mut self) -> io::Result { + async fn read(r: &mut Option) -> io::Result> { + let mut vec = Vec::new(); + if let Some(r) = r.as_mut() { + r.read_to_end(&mut vec).await?; + } + Ok(vec) + } + + // Ensure stdin is closed so the child isn't stuck waiting on input while the + // parent is waiting for it to exit. + drop(self.stdin.take()); + + // Drop happens after `try_join` due to + let mut stdout = self.stdout.take(); + let mut stderr = self.stderr.take(); + + let result = try_join3(self.inner.wait(), read(&mut stdout), read(&mut stderr)).await?; + Ok(std::process::Output { status: result.0, stdout: result.1, stderr: result.2 }) } } impl UserData for Child { - fn add_methods>(methods: &mut M) { - #[inline] - async fn read_line(me: &mut Child) -> (Option>, u8) { - async fn read(r: Option) -> Option> { - let mut buf = Vec::new(); - match r?.read_until(b'\n', &mut buf).await { - Ok(0) | Err(_) => None, - Ok(_) => Some(buf), - } - } - - select! { - r @ Some(_) = read(me.stdout.as_mut()) => (r, 0u8), - r @ Some(_) = read(me.stderr.as_mut()) => (r, 1u8), - else => (None, 2u8), - } - } + fn add_methods>(methods: &mut M) { + methods.add_method("id", |_, me, ()| Ok(me.inner.id())); methods.add_async_method_mut("read", |_, mut me, len: usize| async move { async fn read(r: Option, len: usize) -> Option> { @@ -65,14 +117,15 @@ impl UserData for Child { }) }); methods.add_async_method_mut("read_line", |lua, mut me, ()| async move { - match read_line(&mut me).await { + match me.read_line().await { (Some(b), event) => (lua.create_string(b)?, event).into_lua_multi(&lua), (None, event) => (Value::Nil, event).into_lua_multi(&lua), } }); + // TODO: deprecate this method methods.add_async_method_mut("read_line_with", |lua, mut me, options: Table| async move { let timeout = Duration::from_millis(options.raw_get("timeout")?); - let Ok(result) = tokio::time::timeout(timeout, read_line(&mut me)).await else { + let Ok(result) = tokio::time::timeout(timeout, me.read_line()).await else { return (Value::Nil, 3u8).into_lua_multi(&lua); }; match result { @@ -101,38 +154,21 @@ impl UserData for Child { }); methods.add_async_method_mut("wait", |lua, mut me, ()| async move { - drop(me.stdin.take()); - match me.inner.wait().await { + match me.wait().await { Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(&lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } }); methods.add_async_function("wait_with_output", |lua, ud: AnyUserData| async move { - async fn read_to_end(r: &mut Option) -> io::Result> { - let mut vec = Vec::new(); - if let Some(r) = r.as_mut() { - r.read_to_end(&mut vec).await?; - } - Ok(vec) + match ud.take::()?.wait_with_output().await { + Ok(output) => (Output::new(output), Value::Nil).into_lua_multi(&lua), + Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } - - let mut me = ud.take::()?; - let mut stdout_pipe = me.stdout.take(); - let mut stderr_pipe = me.stderr.take(); - - let stdout_fut = read_to_end(&mut stdout_pipe); - let stderr_fut = read_to_end(&mut stderr_pipe); - - drop(me.stdin.take()); - let result = try_join3(me.inner.wait(), stdout_fut, stderr_fut).await; - drop(stdout_pipe); - drop(stderr_pipe); - - match result { - Ok((status, stdout, stderr)) => { - (Output::new(std::process::Output { status, stdout, stderr }), Value::Nil) - .into_lua_multi(&lua) - } + }); + methods.add_async_method_mut("try_wait", |lua, mut me, ()| async move { + match me.inner.try_wait() { + Ok(Some(status)) => (Status::new(status), Value::Nil).into_lua_multi(&lua), + Ok(None) => (Value::Nil, Value::Nil).into_lua_multi(&lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } }); diff --git a/yazi-plugin/src/process/command.rs b/yazi-plugin/src/process/command.rs index 574dc637..49f99f56 100644 --- a/yazi-plugin/src/process/command.rs +++ b/yazi-plugin/src/process/command.rs @@ -1,13 +1,15 @@ -use std::process::Stdio; +use std::{io, process::Stdio}; use mlua::{AnyUserData, ExternalError, IntoLuaMulti, Lua, MetaMethod, Table, UserData, Value}; use tokio::process::{ChildStderr, ChildStdin, ChildStdout}; +use yazi_binding::Error; use super::{Child, output::Output}; -use crate::{Error, process::Status}; +use crate::process::Status; pub struct Command { - inner: tokio::process::Command, + inner: tokio::process::Command, + memory: Option, } const NULL: u8 = 0; @@ -20,7 +22,7 @@ impl Command { let mut inner = tokio::process::Command::new(program); inner.kill_on_drop(true).stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null()); - Ok(Self { inner }) + Ok(Self { inner, memory: None }) })?; let command = lua.create_table_from([ @@ -34,6 +36,74 @@ impl Command { lua.globals().raw_set("Command", command) } + + #[cfg(unix)] + fn spawn(&mut self) -> io::Result { + if let Some(max) = self.memory { + unsafe { + self.inner.pre_exec(move || { + let rlp = libc::rlimit { rlim_cur: max as _, rlim_max: max as _ }; + libc::setrlimit(libc::RLIMIT_AS, &rlp); + Ok(()) + }); + } + } + self.inner.spawn().map(Child::new) + } + + #[cfg(windows)] + fn spawn(&mut self) -> io::Result { + use std::os::windows::io::RawHandle; + + use windows_sys::Win32::System::JobObjects::{AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_PROCESS_MEMORY, JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation, SetInformationJobObject}; + + fn assign_job(max: usize, handle: RawHandle) -> io::Result { + unsafe { + let job = CreateJobObjectW(std::ptr::null_mut(), std::ptr::null()); + if job.is_null() { + return Err(io::Error::last_os_error()); + } + + let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = std::mem::zeroed(); + info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_PROCESS_MEMORY; + info.ProcessMemoryLimit = max; + + let result = SetInformationJobObject( + job, + JobObjectExtendedLimitInformation, + &mut info as *mut _ as *mut _, + std::mem::size_of_val(&info) as u32, + ); + + if result == 0 { + Err(io::Error::last_os_error()) + } else if AssignProcessToJobObject(job, handle) == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(job) + } + } + } + + let child = self.inner.spawn()?; + if let (Some(max), Some(handle)) = (self.memory, child.raw_handle()) { + if let Ok(job) = assign_job(max, handle) { + return Ok(Child::new(child, Some(job))); + } + } + + Ok(Child::new(child, None)) + } + + async fn output(&mut self) -> io::Result { + self.inner.stdin(Stdio::piped()); + self.inner.stdout(Stdio::piped()); + self.spawn()?.wait_with_output().await + } + + async fn status(&mut self) -> io::Result { + self.spawn()?.status().await + } } impl UserData for Command { @@ -101,18 +171,22 @@ impl UserData for Command { ud.borrow_mut::()?.inner.stderr(make_stdio(stdio)?); Ok(ud) }); - methods.add_method_mut("spawn", |lua, me, ()| match me.inner.spawn() { - Ok(child) => (Child::new(child), Value::Nil).into_lua_multi(lua), + methods.add_function_mut("memory", |_, (ud, max): (AnyUserData, usize)| { + ud.borrow_mut::()?.memory = Some(max); + Ok(ud) + }); + methods.add_method_mut("spawn", |lua, me, ()| match me.spawn() { + Ok(child) => (child, Value::Nil).into_lua_multi(lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(lua), }); methods.add_async_method_mut("output", |lua, mut me, ()| async move { - match me.inner.output().await { + match me.output().await { Ok(output) => (Output::new(output), Value::Nil).into_lua_multi(&lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } }); methods.add_async_method_mut("status", |lua, mut me, ()| async move { - match me.inner.status().await { + match me.status().await { Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(&lua), Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), } diff --git a/yazi-plugin/src/process/output.rs b/yazi-plugin/src/process/output.rs index 42178658..8b1a5af0 100644 --- a/yazi-plugin/src/process/output.rs +++ b/yazi-plugin/src/process/output.rs @@ -1,19 +1,26 @@ -use mlua::UserData; +use mlua::{Lua, UserData, Value}; +use yazi_binding::cached_field; use super::Status; pub struct Output { inner: std::process::Output, + + v_status: Option, + v_stdout: Option, + v_stderr: Option, } impl Output { - pub fn new(inner: std::process::Output) -> Self { Self { inner } } + pub fn new(inner: std::process::Output) -> Self { + Self { inner, v_status: None, v_stdout: None, v_stderr: None } + } } impl UserData for Output { fn add_fields>(fields: &mut F) { - fields.add_field_method_get("status", |_, me| Ok(Status::new(me.inner.status))); - fields.add_field_method_get("stdout", |lua, me| lua.create_string(&me.inner.stdout)); - fields.add_field_method_get("stderr", |lua, me| lua.create_string(&me.inner.stderr)); + cached_field!(fields, status, |_, me: &Self| Ok(Status::new(me.inner.status))); + cached_field!(fields, stdout, |lua: &Lua, me: &Self| lua.create_string(&me.inner.stdout)); + cached_field!(fields, stderr, |lua: &Lua, me: &Self| lua.create_string(&me.inner.stderr)); } } diff --git a/yazi-plugin/src/url/mod.rs b/yazi-plugin/src/url/mod.rs deleted file mode 100644 index 4afe04c7..00000000 --- a/yazi-plugin/src/url/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![allow(clippy::module_inception)] - -use mlua::Lua; - -yazi_macro::mod_flat!(url); - -pub fn pour(lua: &Lua) -> mlua::Result<()> { - url::Url::register(lua)?; - url::Url::install(lua)?; - - Ok(()) -} diff --git a/yazi-plugin/src/url/url.rs b/yazi-plugin/src/url/url.rs deleted file mode 100644 index 9942f7bd..00000000 --- a/yazi-plugin/src/url/url.rs +++ /dev/null @@ -1,108 +0,0 @@ -use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, MetaMethod, UserDataFields, UserDataMethods, UserDataRef, Value}; - -pub type UrlRef = UserDataRef; - -pub struct Url(pub yazi_shared::url::Url); - -impl Url { - pub fn register(lua: &Lua) -> mlua::Result<()> { - lua.register_userdata_type::(|reg| { - reg.add_method("frag", |lua, me, ()| lua.create_string(me.frag())); - reg.add_field_method_get("base", |_, me| { - Ok(if me.base().as_os_str().is_empty() { None } else { Some(Self::from(me.base())) }) - }); - reg.add_field_method_get("is_regular", |_, me| Ok(me.is_regular())); - reg.add_field_method_get("is_search", |_, me| Ok(me.is_search())); - reg.add_field_method_get("is_archive", |_, me| Ok(me.is_archive())); - reg.add_field_method_get("is_absolute", |_, me| Ok(me.is_absolute())); - reg.add_field_method_get("has_root", |_, me| Ok(me.has_root())); - - reg.add_method("name", |lua, me, ()| { - Some(me.name()) - .filter(|&s| !s.is_empty()) - .map(|s| lua.create_string(s.as_encoded_bytes())) - .transpose() - }); - reg.add_method("stem", |lua, me, ()| { - me.file_stem().map(|s| lua.create_string(s.as_encoded_bytes())).transpose() - }); - reg.add_method("ext", |lua, me, ()| { - me.extension().map(|s| lua.create_string(s.as_encoded_bytes())).transpose() - }); - reg.add_method("join", |_, me, other: Value| { - Ok(Self(match other { - Value::String(s) => me.join(s.to_str()?.as_ref()), - Value::UserData(ud) => me.join(&*ud.borrow::()?), - _ => Err("must be a string or a Url".into_lua_err())?, - })) - }); - reg.add_method("parent", |_, me, ()| Ok(me.parent_url().map(Self))); - reg.add_method("starts_with", |_, me, base: Value| { - Ok(match base { - Value::String(s) => me.starts_with(s.to_str()?.as_ref()), - Value::UserData(ud) => me.starts_with(&*ud.borrow::()?), - _ => Err("must be a string or a Url".into_lua_err())?, - }) - }); - reg.add_method("ends_with", |_, me, child: Value| { - Ok(match child { - Value::String(s) => me.ends_with(s.to_str()?.as_ref()), - Value::UserData(ud) => me.ends_with(&*ud.borrow::()?), - _ => Err("must be a string or a Url".into_lua_err())?, - }) - }); - reg.add_method("strip_prefix", |_, me, base: Value| { - let path = match base { - Value::String(s) => me.strip_prefix(s.to_str()?.as_ref()), - Value::UserData(ud) => me.strip_prefix(&*ud.borrow::()?), - _ => Err("must be a string or a Url".into_lua_err())?, - }; - Ok(path.ok().map(Self::from)) - }); - - reg.add_function_mut("into_search", |_, (ud, frag): (AnyUserData, mlua::String)| { - Ok(Self(ud.take::()?.into_search(&frag.to_str()?))) - }); - - reg.add_meta_method(MetaMethod::Eq, |_, me, other: UrlRef| Ok(me == &*other)); - reg.add_meta_method(MetaMethod::ToString, |lua, me, ()| { - lua.create_string(me.as_os_str().as_encoded_bytes()) - }); - reg.add_meta_method(MetaMethod::Concat, |lua, lhs, rhs: mlua::String| { - lua.create_string([lhs.as_os_str().as_encoded_bytes(), rhs.as_bytes().as_ref()].concat()) - }); - }) - } - - pub fn install(lua: &Lua) -> mlua::Result<()> { - lua.globals().raw_set( - "Url", - lua.create_function(|_, value: Value| { - Ok(match value { - Value::String(s) => Self::from(s.to_str()?.as_ref()), - Value::UserData(ud) => Self(ud.borrow::()?.clone()), - _ => Err("Expected a string or a Url".into_lua_err())?, - }) - })?, - ) - } -} - -impl> From for Url { - fn from(value: T) -> Self { Self(value.into()) } -} - -impl FromLua for Url { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - match value { - Value::UserData(ud) => ud.take().map(Self), - _ => Err("Expected a Url".into_lua_err()), - } - } -} - -impl IntoLua for Url { - fn into_lua(self, lua: &Lua) -> mlua::Result { - lua.create_any_userdata(self.0)?.into_lua(lua) - } -} diff --git a/yazi-plugin/src/utils/cache.rs b/yazi-plugin/src/utils/cache.rs index 085a2cdb..b5edc9b6 100644 --- a/yazi-plugin/src/utils/cache.rs +++ b/yazi-plugin/src/utils/cache.rs @@ -1,9 +1,10 @@ use mlua::{Function, Lua, Table}; use twox_hash::XxHash3_128; +use yazi_binding::Url; use yazi_config::YAZI; use super::Utils; -use crate::{file::FileRef, url::Url}; +use crate::file::FileRef; impl Utils { pub(super) fn file_cache(lua: &Lua) -> mlua::Result { @@ -20,7 +21,7 @@ impl Utils { format!("{:x}", h.finish_128()) }; - Ok(Some(Url::from(YAZI.preview.cache_dir.join(hex)))) + Ok(Some(Url::new(YAZI.preview.cache_dir.join(hex)))) }) } } diff --git a/yazi-plugin/src/utils/image.rs b/yazi-plugin/src/utils/image.rs index 0e8ada59..1bbc2931 100644 --- a/yazi-plugin/src/utils/image.rs +++ b/yazi-plugin/src/utils/image.rs @@ -1,8 +1,9 @@ use mlua::{Function, IntoLua, Lua, Value}; use yazi_adapter::{ADAPTOR, Image}; +use yazi_binding::UrlRef; use super::Utils; -use crate::{bindings::ImageInfo, elements::Rect, url::UrlRef}; +use crate::{bindings::ImageInfo, elements::Rect}; impl Utils { pub(super) fn image_info(lua: &Lua) -> mlua::Result { diff --git a/yazi-plugin/src/utils/json.rs b/yazi-plugin/src/utils/json.rs index 4766536f..5d3574b6 100644 --- a/yazi-plugin/src/utils/json.rs +++ b/yazi-plugin/src/utils/json.rs @@ -1,7 +1,8 @@ use mlua::{Function, IntoLuaMulti, Lua, LuaSerdeExt, Value}; +use yazi_binding::Error; use super::Utils; -use crate::{Error, config::OPTS}; +use crate::config::OPTS; impl Utils { pub(super) fn json_encode(lua: &Lua) -> mlua::Result { diff --git a/yazi-plugin/src/utils/mod.rs b/yazi-plugin/src/utils/mod.rs index 5a320167..c1f3a585 100644 --- a/yazi-plugin/src/utils/mod.rs +++ b/yazi-plugin/src/utils/mod.rs @@ -1,5 +1,5 @@ #![allow(clippy::module_inception)] yazi_macro::mod_flat!( - app cache call image json layer log preview spot sync target text time user utils + app cache call image json layer log preview process spot sync target text time user utils ); diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 4452b4dd..2136d1c2 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -52,6 +52,7 @@ impl Utils { area, inner, wrap: if YAZI.preview.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO }, + scroll: Default::default(), })]; emit!(Call(Cmd::new("mgr:update_peeked").with_any("lock", lock))); diff --git a/yazi-plugin/src/utils/process.rs b/yazi-plugin/src/utils/process.rs new file mode 100644 index 00000000..9d8d1008 --- /dev/null +++ b/yazi-plugin/src/utils/process.rs @@ -0,0 +1,29 @@ +use mlua::{Function, Lua}; + +use super::Utils; + +impl Utils { + #[cfg(target_os = "macos")] + pub(super) fn proc_info(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, pid: usize| { + let info = unsafe { + let mut info: libc::proc_taskinfo = std::mem::zeroed(); + libc::proc_pidinfo( + pid as _, + libc::PROC_PIDTASKINFO, + 0, + &mut info as *mut _ as *mut _, + std::mem::size_of_val(&info) as _, + ); + info + }; + + lua.create_table_from([("mem_resident", info.pti_resident_size)]) + }) + } + + #[cfg(not(target_os = "macos"))] + pub(super) fn proc_info(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, ()| lua.create_table()) + } +} diff --git a/yazi-plugin/src/utils/utils.rs b/yazi-plugin/src/utils/utils.rs index 839e3e46..a4d6a490 100644 --- a/yazi-plugin/src/utils/utils.rs +++ b/yazi-plugin/src/utils/utils.rs @@ -45,6 +45,9 @@ pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result { b"preview_code" => Utils::preview_code(lua)?, b"preview_widgets" => Utils::preview_widgets(lua)?, + // Process + b"proc_info" => Utils::proc_info(lua)?, + // Spot b"spot_table" => Utils::spot_table(lua)?, b"spot_widgets" => Utils::spot_widgets(lua)?, diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index 40b77b6c..0284314a 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-proxy" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -13,9 +13,9 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-config = { path = "../yazi-config", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-config = { path = "../yazi-config", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-proxy/src/options/search.rs b/yazi-proxy/src/options/search.rs index 7c829109..abbaf1a0 100644 --- a/yazi-proxy/src/options/search.rs +++ b/yazi-proxy/src/options/search.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt::Display}; +use std::borrow::Cow; use yazi_shared::event::CmdCow; @@ -35,31 +35,27 @@ impl TryFrom for SearchOpt { // Via #[derive(PartialEq, Eq)] pub enum SearchOptVia { - // TODO: remove `None` in the future - None, Rg, - Fd, Rga, + Fd, } impl From<&str> for SearchOptVia { fn from(value: &str) -> Self { match value { "rg" => Self::Rg, - "fd" => Self::Fd, "rga" => Self::Rga, - _ => Self::None, + _ => Self::Fd, } } } -impl Display for SearchOptVia { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { +impl AsRef for SearchOptVia { + fn as_ref(&self) -> &str { + match self { Self::Rg => "rg", - Self::Fd => "fd", Self::Rga => "rga", - Self::None => "none", - }) + Self::Fd => "fd", + } } } diff --git a/yazi-proxy/src/tab.rs b/yazi-proxy/src/tab.rs index 7c3c2dc4..cf7fee1c 100644 --- a/yazi-proxy/src/tab.rs +++ b/yazi-proxy/src/tab.rs @@ -25,7 +25,9 @@ impl TabProxy { pub fn search_do(opt: SearchOpt) { emit!(Call( // TODO: use second positional argument instead of `args` parameter - Cmd::args("mgr:search_do", &[opt.subject]).with("via", opt.via).with("args", opt.args_raw) + Cmd::args("mgr:search_do", &[opt.subject]) + .with("via", opt.via.as_ref()) + .with("args", opt.args_raw) )); } } diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index 85958a41..dcd59ab4 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-scheduler" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -9,13 +9,13 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-config = { path = "../yazi-config", version = "25.3.7" } -yazi-dds = { path = "../yazi-dds", version = "25.3.7" } -yazi-fs = { path = "../yazi-fs", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" } -yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-config = { path = "../yazi-config", version = "25.4.8" } +yazi-dds = { path = "../yazi-dds", version = "25.4.8" } +yazi-fs = { path = "../yazi-fs", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-plugin = { path = "../yazi-plugin", version = "25.4.8" } +yazi-proxy = { path = "../yazi-proxy", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 0afc9b26..802f9520 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-shared" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -10,7 +10,7 @@ repository = "https://github.com/sxyazi/yazi" rust-version = "1.83.0" [dependencies] -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-shared/src/event/cmd.rs b/yazi-shared/src/event/cmd.rs index 1fc84615..340528e5 100644 --- a/yazi-shared/src/event/cmd.rs +++ b/yazi-shared/src/event/cmd.rs @@ -33,6 +33,12 @@ impl Cmd { me } + #[inline] + pub fn len(&self) -> usize { self.args.len() } + + #[inline] + pub fn is_empty(&self) -> bool { self.args.is_empty() } + // --- With #[inline] pub fn with(mut self, name: impl Into, value: impl ToString) -> Self { diff --git a/yazi-shared/src/event/data.rs b/yazi-shared/src/event/data.rs index fac8cc4a..1eab3f7b 100644 --- a/yazi-shared/src/event/data.rs +++ b/yazi-shared/src/event/data.rs @@ -182,6 +182,7 @@ macro_rules! impl_number_as { impl_integer_as!(usize, as_usize); impl_integer_as!(isize, as_isize); impl_integer_as!(i16, as_i16); +impl_integer_as!(i32, as_i32); impl_number_as!(f64, as_f64); diff --git a/yazi-shared/src/event/event.rs b/yazi-shared/src/event/event.rs index 9bb24f48..b95fc305 100644 --- a/yazi-shared/src/event/event.rs +++ b/yazi-shared/src/event/event.rs @@ -23,6 +23,7 @@ pub enum Event { #[derive(Debug, Default)] pub struct EventQuit { + pub code: i32, pub no_cwd_file: bool, pub selected: Option, } diff --git a/yazi-widgets/Cargo.toml b/yazi-widgets/Cargo.toml index 806ad867..8160bf6a 100644 --- a/yazi-widgets/Cargo.toml +++ b/yazi-widgets/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-widgets" -version = "25.3.7" +version = "25.4.8" edition = "2021" license = "MIT" authors = [ "sxyazi " ] @@ -10,11 +10,11 @@ repository = "https://github.com/sxyazi/yazi" rust-version = "1.83.0" [dependencies] -yazi-codegen = { path = "../yazi-codegen", version = "25.3.7" } -yazi-config = { path = "../yazi-config", version = "25.3.7" } -yazi-macro = { path = "../yazi-macro", version = "25.3.7" } -yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" } -yazi-shared = { path = "../yazi-shared", version = "25.3.7" } +yazi-codegen = { path = "../yazi-codegen", version = "25.4.8" } +yazi-config = { path = "../yazi-config", version = "25.4.8" } +yazi-macro = { path = "../yazi-macro", version = "25.4.8" } +yazi-plugin = { path = "../yazi-plugin", version = "25.4.8" } +yazi-shared = { path = "../yazi-shared", version = "25.4.8" } # External dependencies futures = { workspace = true }