diff --git a/Cargo.lock b/Cargo.lock index 5bf0d1ca..c3a2668a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,7 +63,7 @@ dependencies = [ "ratatui", "simdutf8", "smallvec", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -107,11 +107,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -200,7 +201,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -242,9 +243,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "bitstream-io" @@ -333,9 +334,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.7" +version = "1.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b" dependencies = [ "jobserver", "libc", @@ -367,14 +368,14 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] name = "clap" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", "clap_derive", @@ -382,9 +383,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -394,9 +395,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.40" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9" +checksum = "33a7e468e750fa4b6be660e8b5651ad47372e8fb114030b594c2d75d48c5ffd0" dependencies = [ "clap", ] @@ -423,9 +424,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck", "proc-macro2", @@ -550,7 +551,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm_winapi", "filedescriptor", "futures-core", @@ -675,23 +676,23 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -791,7 +792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182f7dbc2ef73d9ef67351c5fbbea084729c48362d3ce9dd44c28e32e277fe5" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -801,7 +802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" dependencies = [ "libc", - "thiserror", + "thiserror 1.0.69", "winapi", ] @@ -1223,9 +1224,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" dependencies = [ "byteorder-lite", "quick-error", @@ -1256,11 +1257,11 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "inotify" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.8.0", "inotify-sys", "libc", ] @@ -1276,9 +1277,9 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894813a444908c0c8c0e221b041771d107c4a21de1d317dc49bcc66e3c9e5b3f" +checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" dependencies = [ "darling", "indoc", @@ -1287,15 +1288,6 @@ dependencies = [ "syn", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "interpolate_name" version = "0.2.4" @@ -1348,9 +1340,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -1410,16 +1402,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -1439,9 +1431,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "loop9" @@ -1480,6 +1472,15 @@ dependencies = [ "which", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "matchers" version = "0.1.0" @@ -1522,9 +1523,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", "simd-adler32", @@ -1614,11 +1615,11 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "notify" -version = "7.0.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "filetime", "fsevent-sys", "inotify", @@ -1628,17 +1629,14 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "notify-types" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585d3cb5e12e01aed9e8a1f70d5c6b5e86fe2a6e48fc8cd0b3e0b8df6f6eb174" -dependencies = [ - "instant", -] +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" [[package]] name = "nu-ansi-term" @@ -1715,6 +1713,15 @@ dependencies = [ "libc", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + [[package]] name = "objc-sys" version = "0.3.5" @@ -1743,7 +1750,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "libc", "objc2", @@ -1833,7 +1840,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1850,9 +1857,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1931,9 +1938,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -2026,7 +2033,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cassowary", "compact_str", "crossterm", @@ -2071,7 +2078,7 @@ dependencies = [ "rand_chacha", "simd_helpers", "system-deps", - "thiserror", + "thiserror 1.0.69", "v_frame", "wasm-bindgen", ] @@ -2106,18 +2113,18 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] name = "redox_users" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 2.0.11", ] [[package]] @@ -2193,11 +2200,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -2269,9 +2276,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", "memchr", @@ -2439,9 +2446,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.95" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -2476,7 +2483,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 1.0.69", "walkdir", ] @@ -2505,7 +2512,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -2519,6 +2535,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -2594,9 +2621,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -2612,9 +2639,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -2697,7 +2724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.69", "time", "tracing-subscriber", ] @@ -2920,9 +2947,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vergen" -version = "9.0.2" +version = "9.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f25fc8f8f05df455c7941e87f093ad22522a9ff33d7a027774815acf6f0639" +checksum = "e0d2f179f8075b805a43a2a21728a46f0cc2921b3c58695b28fa8817e103cd9a" dependencies = [ "anyhow", "derive_builder", @@ -2934,9 +2961,9 @@ dependencies = [ [[package]] name = "vergen-gitcl" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0227006d09f98ab00ea69e9a5e055e676a813cfbed4232986176c86a6080b997" +checksum = "b2f89d70a58a4506a6079cedf575c64cf51649ccbb4e02a63dac539b264b7711" dependencies = [ "anyhow", "derive_builder", @@ -2948,9 +2975,9 @@ dependencies = [ [[package]] name = "vergen-lib" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0c767e6751c09fc85cde58722cf2f1007e80e4c8d5a4321fc90d83dc54ca147" +checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" dependencies = [ "anyhow", "derive_builder", @@ -2987,20 +3014,21 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -3012,9 +3040,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3022,9 +3050,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -3035,9 +3063,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "weezl" @@ -3095,7 +3126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" dependencies = [ "windows-core 0.56.0", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3104,7 +3135,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3116,7 +3147,7 @@ dependencies = [ "windows-implement", "windows-interface", "windows-result", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3147,16 +3178,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -3165,7 +3187,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3174,22 +3196,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -3198,46 +3205,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3250,48 +3239,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3300,9 +3265,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.22" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -3399,7 +3364,7 @@ name = "yazi-config" version = "0.4.3" dependencies = [ "anyhow", - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm", "globset", "indexmap", @@ -3419,7 +3384,7 @@ name = "yazi-core" version = "0.4.3" dependencies = [ "anyhow", - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm", "dirs", "futures", @@ -3468,6 +3433,17 @@ dependencies = [ "yazi-shared", ] +[[package]] +name = "yazi-ffi" +version = "0.4.3" +dependencies = [ + "anyhow", + "core-foundation-sys", + "libc", + "objc", + "yazi-macro", +] + [[package]] name = "yazi-fm" version = "0.4.3" @@ -3508,15 +3484,21 @@ version = "0.4.3" dependencies = [ "anyhow", "arc-swap", - "bitflags 2.6.0", + "bitflags 2.8.0", + "core-foundation-sys", "dirs", "futures", "libc", + "objc", + "parking_lot", "regex", + "scopeguard", "serde", "tokio", + "tracing", "uzers", "windows-sys 0.59.0", + "yazi-ffi", "yazi-macro", "yazi-shared", ] diff --git a/Cargo.toml b/Cargo.toml index d412fd2c..5dce8430 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,30 +10,32 @@ panic = "abort" strip = true [workspace.dependencies] -ansi-to-tui = "7.0.0" -anyhow = "1.0.95" -base64 = "0.22.1" -bitflags = "2.6.0" -clap = { version = "4.5.23", features = [ "derive" ] } -crossterm = { version = "0.28.1", features = [ "event-stream" ] } -dirs = "5.0.1" -futures = "0.3.31" -globset = "0.4.15" -libc = "0.2.169" -md-5 = "0.10.6" -mlua = { version = "0.10.2", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serialize" ] } -parking_lot = "0.12.3" -ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] } -regex = "1.11.1" -scopeguard = "1.2.0" -serde = { version = "1.0.217", features = [ "derive" ] } -serde_json = "1.0.134" -shell-words = "1.1.0" -tokio = { version = "1.42.0", features = [ "full" ] } -tokio-stream = "0.1.17" -tokio-util = "0.7.13" -toml = { version = "0.8.19" } -tracing = { version = "0.1.41", features = [ "max_level_debug", "release_max_level_debug" ] } -twox-hash = { version = "2.1.0", default-features = false, features = [ "std", "random", "xxhash3_128" ] } -unicode-width = "0.2.0" -uzers = "0.12.1" +ansi-to-tui = "7.0.0" +anyhow = "1.0.95" +base64 = "0.22.1" +bitflags = "2.8.0" +clap = { version = "4.5.26", features = [ "derive" ] } +core-foundation-sys = "0.8.7" +crossterm = { version = "0.28.1", features = [ "event-stream" ] } +dirs = "6.0.0" +futures = "0.3.31" +globset = "0.4.15" +libc = "0.2.169" +md-5 = "0.10.6" +mlua = { version = "0.10.2", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serialize" ] } +objc = "0.2.7" +parking_lot = "0.12.3" +ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] } +regex = "1.11.1" +scopeguard = "1.2.0" +serde = { version = "1.0.217", features = [ "derive" ] } +serde_json = "1.0.135" +shell-words = "1.1.0" +tokio = { version = "1.43.0", features = [ "full" ] } +tokio-stream = "0.1.17" +tokio-util = "0.7.13" +toml = { version = "0.8.19" } +tracing = { version = "0.1.41", features = [ "max_level_debug", "release_max_level_debug" ] } +twox-hash = { version = "2.1.0", default-features = false, features = [ "std", "random", "xxhash3_128" ] } +unicode-width = "0.2.0" +uzers = "0.12.1" diff --git a/README.md b/README.md index 4b76406e..1cc4eab8 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo - 🧰 Integration with ripgrep, fd, fzf, zoxide - 💫 Vim-like input/pick/confirm/which/notify component, auto-completion for cd paths - 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, code, directories, etc.) -- 🔄 Bulk Renaming, Visual Mode, File Chooser +- 🔄 Bulk Renaming, Visual Mode, File Chooser, [Git Integration](https://github.com/yazi-rs/plugins/tree/main/git.yazi), [Mount Manager](https://github.com/yazi-rs/plugins/tree/main/mount.yazi) - 🎨 Theme System, Mouse Support, Trash Bin, Custom Layouts, CSI u, OSC 52 - ... and more! @@ -61,7 +61,7 @@ See https://yazi-rs.github.io/docs/image-preview for details. [kgp]: https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders -[kgp-old]: https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/kgp_old.rs +[kgp-old]: https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/drivers/kgp_old.rs [iip]: https://iterm2.com/documentation-images.html [sixel]: https://www.vt100.net/docs/vt3xx-gp/chapter14.html [ascii-art]: https://en.wikipedia.org/wiki/ASCII_art diff --git a/cspell.json b/cspell.json index 31dfeb26..1b1c902d 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox"],"version":"0.2","language":"en","flagWords":[]} \ No newline at end of file +{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone"],"version":"0.2","language":"en","flagWords":[]} \ No newline at end of file diff --git a/flake.lock b/flake.lock index 0ce60016..791801ac 100644 --- a/flake.lock +++ b/flake.lock @@ -1,80 +1,82 @@ { - "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1729265718, - "narHash": "sha256-4HQI+6LsO3kpWTYuVGIzhJs1cetFcwT7quWCk/6rqeo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "ccc0c2126893dd20963580b6478d1a10a4512185", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" - } - }, - "rust-overlay": { - "inputs": { - "nixpkgs": ["nixpkgs"] - }, - "locked": { - "lastModified": 1729391507, - "narHash": "sha256-as0I9xieJUHf7kiK2a9znDsVZQTFWhM1pLivII43Gi0=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "784981a9feeba406de38c1c9a3decf966d853cca", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1737003892, + "narHash": "sha256-RCzJE9wKByLCXmRBp+z8LK9EgdW+K+W/DXnJS4S/NVo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ae06b9c2d83cb5c8b12d7d0e32692e93d1379713", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1737080704, + "narHash": "sha256-n+J2h9GM9ZpFOQUmtZoCr1+DFF/iO5UlmLJeHIxbZGY=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "f9953fe89f8b65401fc4d4a288940bc2cb072949", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 } diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 3593fd17..54f8d675 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -22,7 +22,7 @@ serde = { workspace = true } [build-dependencies] clap = { workspace = true } -clap_complete = "4.5.40" +clap_complete = "4.5.42" clap_complete_fig = "4.5.2" clap_complete_nushell = "4.5.5" -vergen-gitcl = { version = "1.0.2", features = [ "build", "rustc" ] } +vergen-gitcl = { version = "1.0.5", features = [ "build", "rustc" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 725a6aa9..f92450e1 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -31,11 +31,11 @@ yazi-shared = { path = "../yazi-shared", version = "0.4.3" } # External build dependencies anyhow = { workspace = true } clap = { workspace = true } -clap_complete = "4.5.40" +clap_complete = "4.5.42" clap_complete_fig = "4.5.2" clap_complete_nushell = "4.5.5" serde_json = { workspace = true } -vergen-gitcl = { version = "1.0.2", features = [ "build" ] } +vergen-gitcl = { version = "1.0.5", features = [ "build" ] } [target.'cfg(target_os = "macos")'.dependencies] crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } diff --git a/yazi-codegen/Cargo.toml b/yazi-codegen/Cargo.toml index ade0565e..7884d0fb 100644 --- a/yazi-codegen/Cargo.toml +++ b/yazi-codegen/Cargo.toml @@ -13,5 +13,5 @@ proc-macro = true [dependencies] # External dependencies -syn = { version = "2.0.95", features = [ "full" ] } +syn = { version = "2.0.96", features = [ "full" ] } quote = "1.0.38" diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 1020d4eb..e99bd6dc 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -27,7 +27,7 @@ bitflags = { workspace = true } crossterm = { workspace = true } dirs = { workspace = true } futures = { workspace = true } -notify = { version = "7.0.0", default-features = false, features = [ "macos_fsevent" ] } +notify = { version = "8.0.0", default-features = false, features = [ "macos_fsevent" ] } parking_lot = { workspace = true } ratatui = { workspace = true } scopeguard = { workspace = true } diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index d73b6dfa..6c9f40d1 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -7,7 +7,8 @@ use tokio::{fs, pin, sync::{mpsc::{self, UnboundedReceiver}, watch}}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use tracing::error; use yazi_config::PLUGIN; -use yazi_fs::{Cha, File, Files, FilesOp, realname_unchecked}; +use yazi_dds::Pubsub; +use yazi_fs::{Cha, File, Files, FilesOp, mounts::PARTITIONS, realname_unchecked}; use yazi_plugin::isolate; use yazi_proxy::WATCHER; use yazi_shared::{RoCell, event::CmdCow, url::Url}; @@ -44,6 +45,9 @@ impl Watcher { tokio::spawn(Self::fan_in(in_rx, RecommendedWatcher::new(handler, config).unwrap())); } + #[cfg(any(target_os = "linux", target_os = "macos"))] + yazi_fs::mounts::Partitions::monitor(PARTITIONS.clone(), Pubsub::pub_from_mount); + tokio::spawn(Self::fan_out(out_rx)); Self { in_tx, out_tx } } diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index 86d90e09..dd900219 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -46,9 +46,7 @@ impl Preview { let Some(cwd) = cwd else { return }; self.folder_loader.take().map(|h| h.abort()); self.folder_loader = Some(tokio::spawn(async move { - let Some(new) = Files::assert_stale(&cwd, dir.unwrap_or(Cha::dummy())).await else { - return; - }; + let Some(new) = Files::assert_stale(&cwd, dir.unwrap_or(Cha::dummy())).await else { return }; let Ok(rx) = Files::from_dir(&cwd).await else { return }; let stream = diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index a6e69180..ab48d947 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -30,7 +30,7 @@ tokio-util = { workspace = true } tracing = { workspace = true } [build-dependencies] -vergen-gitcl = { version = "1.0.2", features = [ "build" ] } +vergen-gitcl = { version = "1.0.5", features = [ "build" ] } [target."cfg(unix)".dependencies] uzers = { workspace = true } diff --git a/yazi-dds/src/body/body.rs b/yazi-dds/src/body/body.rs index cbe0dfac..2081e3e0 100644 --- a/yazi-dds/src/body/body.rs +++ b/yazi-dds/src/body/body.rs @@ -2,7 +2,7 @@ use anyhow::{Result, bail}; use mlua::{ExternalResult, IntoLua, Lua, Value}; use serde::Serialize; -use super::{BodyBulk, BodyBye, BodyCd, BodyCustom, BodyDelete, BodyHey, BodyHi, BodyHover, BodyLoad, BodyMove, BodyRename, BodyTab, BodyTrash, BodyYank}; +use super::{BodyBulk, BodyBye, BodyCd, BodyCustom, BodyDelete, BodyHey, BodyHi, BodyHover, BodyLoad, BodyMount, BodyMove, BodyRename, BodyTab, BodyTrash, BodyYank}; use crate::Payload; #[derive(Debug, Serialize)] @@ -21,6 +21,7 @@ pub enum Body<'a> { Move(BodyMove<'a>), Trash(BodyTrash<'a>), Delete(BodyDelete<'a>), + Mount(BodyMount), Custom(BodyCustom), } @@ -40,6 +41,7 @@ impl Body<'static> { "move" => Self::Move(serde_json::from_str(body)?), "trash" => Self::Trash(serde_json::from_str(body)?), "delete" => Self::Delete(serde_json::from_str(body)?), + "mount" => Self::Mount(serde_json::from_str(body)?), _ => BodyCustom::from_str(kind, body)?, }) } @@ -65,6 +67,7 @@ impl Body<'static> { | "move" | "trash" | "delete" + | "mount" ) { bail!("Cannot construct system event"); } @@ -98,6 +101,7 @@ impl<'a> Body<'a> { Self::Move(_) => "move", Self::Trash(_) => "trash", Self::Delete(_) => "delete", + Self::Mount(_) => "mount", Self::Custom(b) => b.kind.as_str(), } } @@ -127,6 +131,7 @@ impl IntoLua for Body<'static> { Self::Move(b) => b.into_lua(lua), Self::Trash(b) => b.into_lua(lua), Self::Delete(b) => b.into_lua(lua), + Self::Mount(b) => b.into_lua(lua), Self::Custom(b) => b.into_lua(lua), } } diff --git a/yazi-dds/src/body/mod.rs b/yazi-dds/src/body/mod.rs index 72c24a86..b016c0c5 100644 --- a/yazi-dds/src/body/mod.rs +++ b/yazi-dds/src/body/mod.rs @@ -1,5 +1,5 @@ #![allow(clippy::module_inception)] yazi_macro::mod_flat!( - body bulk bye cd custom delete hey hi hover load move_ rename tab trash yank + body bulk bye cd custom delete hey hi hover load mount move_ rename tab trash yank ); diff --git a/yazi-dds/src/body/mount.rs b/yazi-dds/src/body/mount.rs new file mode 100644 index 00000000..50c44b6e --- /dev/null +++ b/yazi-dds/src/body/mount.rs @@ -0,0 +1,20 @@ +use mlua::{IntoLua, Lua, Value}; +use serde::{Deserialize, Serialize}; + +use super::Body; + +#[derive(Debug, Serialize, Deserialize)] +pub struct BodyMount; + +impl BodyMount { + #[inline] + pub fn owned() -> Body<'static> { Self.into() } +} + +impl From for Body<'_> { + fn from(value: BodyMount) -> Self { Self::Mount(value) } +} + +impl IntoLua for BodyMount { + fn into_lua(self, _: &Lua) -> mlua::Result { Ok(Value::Nil) } +} diff --git a/yazi-dds/src/payload.rs b/yazi-dds/src/payload.rs index d819dd18..29a5bf41 100644 --- a/yazi-dds/src/payload.rs +++ b/yazi-dds/src/payload.rs @@ -91,6 +91,7 @@ impl Display for Payload<'_> { Body::Move(b) => serde_json::to_string(b), Body::Trash(b) => serde_json::to_string(b), Body::Delete(b) => serde_json::to_string(b), + Body::Mount(b) => serde_json::to_string(b), Body::Custom(b) => serde_json::to_string(b), }; diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index a1113775..2ebf461f 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -6,7 +6,7 @@ use yazi_boot::BOOT; use yazi_fs::FolderStage; use yazi_shared::{Id, RoCell, url::Url}; -use crate::{Client, ID, PEERS, body::{Body, BodyBulk, BodyCd, BodyDelete, BodyHi, BodyHover, BodyLoad, BodyMove, BodyMoveItem, BodyRename, BodyTab, BodyTrash, BodyYank}}; +use crate::{Client, ID, PEERS, body::{Body, BodyBulk, BodyCd, BodyDelete, BodyHi, BodyHover, BodyLoad, BodyMount, BodyMove, BodyMoveItem, BodyRename, BodyTab, BodyTrash, BodyYank}}; pub static LOCAL: RoCell>>> = RoCell::new(); @@ -205,6 +205,18 @@ impl Pubsub { } } + pub fn pub_from_mount() { + if LOCAL.read().contains_key("mount") { + Self::pub_(BodyMount::owned()); + } + if PEERS.read().values().any(|p| p.able("mount")) { + Client::push(BodyMount::owned()); + } + if BOOT.local_events.contains("mount") { + BodyMount::owned().with_receiver(*ID).flush(); + } + } + #[inline] fn any_remote_own(kind: &str) -> bool { REMOTE.read().contains_key(kind) // Owned abilities diff --git a/yazi-ffi/Cargo.toml b/yazi-ffi/Cargo.toml new file mode 100644 index 00000000..91624065 --- /dev/null +++ b/yazi-ffi/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "yazi-ffi" +version = "0.4.3" +edition = "2021" +license = "MIT" +authors = [ "sxyazi " ] +description = "Yazi foreign function interface" +homepage = "https://yazi-rs.github.io" +repository = "https://github.com/sxyazi/yazi" + +[dependencies] +yazi-macro = { path = "../yazi-macro", version = "0.4.3" } + +# External dependencies +anyhow = { workspace = true } + +[target."cfg(unix)".dependencies] +libc = { workspace = true } + +[target.'cfg(target_os = "macos")'.dependencies] +core-foundation-sys = { workspace = true } +objc = { workspace = true } diff --git a/yazi-ffi/src/cf_dict.rs b/yazi-ffi/src/cf_dict.rs new file mode 100644 index 00000000..b535e2ed --- /dev/null +++ b/yazi-ffi/src/cf_dict.rs @@ -0,0 +1,53 @@ +use std::{ffi::{CStr, OsStr, OsString, c_char, c_void}, mem::ManuallyDrop, os::unix::ffi::OsStrExt, path::PathBuf}; + +use anyhow::{Result, bail}; +use core_foundation_sys::{base::{CFRelease, TCFTypeRef}, dictionary::{CFDictionaryGetValueIfPresent, CFDictionaryRef}, string::CFStringRef}; +use objc::{msg_send, runtime::Object, sel, sel_impl}; + +use super::cf_string::CFString; + +pub struct CFDict(CFDictionaryRef); + +impl CFDict { + pub fn take(dict: CFDictionaryRef) -> Result { + if dict.is_null() { + bail!("Cannot take a null pointer"); + } + Ok(Self(dict)) + } + + pub fn value(&self, key: &str) -> Result<*const c_void> { + let key_ = CFString::new(key)?; + let mut value = std::ptr::null(); + if unsafe { CFDictionaryGetValueIfPresent(self.0, key_.as_void_ptr(), &mut value) } == 0 + || value.is_null() + { + bail!("Cannot get the value for the key `{key}`"); + } + Ok(value) + } + + pub fn integer(&self, key: &str) -> Result { + let value = self.value(key)?; + #[allow(unexpected_cfgs)] + Ok(unsafe { msg_send![value as *const Object, longLongValue] }) + } + + pub fn os_string(&self, key: &str) -> Result { + ManuallyDrop::new(CFString(self.value(key)? as CFStringRef)).os_string() + } + + pub fn path_buf(&self, key: &str) -> Result { + let url = self.value(key)? as *const Object; + #[allow(unexpected_cfgs)] + let cstr: *const c_char = unsafe { + let nss: *const Object = msg_send![url, path]; + msg_send![nss, UTF8String] + }; + Ok(OsStr::from_bytes(unsafe { CStr::from_ptr(cstr) }.to_bytes()).into()) + } +} + +impl Drop for CFDict { + fn drop(&mut self) { unsafe { CFRelease(self.0 as _) } } +} diff --git a/yazi-ffi/src/cf_string.rs b/yazi-ffi/src/cf_string.rs new file mode 100644 index 00000000..3821c176 --- /dev/null +++ b/yazi-ffi/src/cf_string.rs @@ -0,0 +1,58 @@ +use std::{ffi::OsString, ops::Deref, os::unix::ffi::OsStringExt}; + +use anyhow::{Result, bail}; +use core_foundation_sys::{base::{CFRelease, kCFAllocatorDefault, kCFAllocatorNull}, string::{CFStringCreateWithBytesNoCopy, CFStringGetCString, CFStringGetLength, CFStringGetMaximumSizeForEncoding, CFStringRef, kCFStringEncodingUTF8}}; +use libc::strlen; + +pub struct CFString(pub(super) CFStringRef); + +impl CFString { + pub fn new(s: &str) -> Result { + let key = unsafe { + CFStringCreateWithBytesNoCopy( + kCFAllocatorDefault, + s.as_ptr(), + s.len() as _, + kCFStringEncodingUTF8, + false as _, + kCFAllocatorNull, + ) + }; + if key.is_null() { + bail!("Allocation failed while creating CFString"); + } + Ok(Self(key)) + } + + pub fn len(&self) -> usize { unsafe { CFStringGetLength(self.0) as _ } } + + pub fn is_empty(&self) -> bool { self.len() == 0 } + + pub fn os_string(&self) -> Result { + let len = self.len(); + let capacity = + unsafe { CFStringGetMaximumSizeForEncoding(len as _, kCFStringEncodingUTF8) } + 1; + + let mut out: Vec = Vec::with_capacity(capacity as usize); + let result = unsafe { + CFStringGetCString(self.0, out.as_mut_ptr().cast(), capacity, kCFStringEncodingUTF8) + }; + if result == 0 { + bail!("Failed to get the C string from CFString"); + } + + unsafe { out.set_len(strlen(out.as_ptr().cast())) }; + out.shrink_to_fit(); + Ok(OsString::from_vec(out)) + } +} + +impl Deref for CFString { + type Target = CFStringRef; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl Drop for CFString { + fn drop(&mut self) { unsafe { CFRelease(self.0 as _) }; } +} diff --git a/yazi-ffi/src/disk_arbitration.rs b/yazi-ffi/src/disk_arbitration.rs new file mode 100644 index 00000000..77666f65 --- /dev/null +++ b/yazi-ffi/src/disk_arbitration.rs @@ -0,0 +1,44 @@ +use std::ffi::{c_char, c_void}; + +use core_foundation_sys::{array::CFArrayRef, base::CFAllocatorRef, dictionary::CFDictionaryRef, runloop::CFRunLoopRef, string::CFStringRef}; + +#[link(name = "DiskArbitration", kind = "framework")] +extern "C" { + pub fn DASessionCreate(allocator: CFAllocatorRef) -> *const c_void; + + pub fn DADiskCreateFromBSDName( + allocator: CFAllocatorRef, + session: *const c_void, + path: *const c_char, + ) -> *const c_void; + + pub fn DADiskCopyDescription(disk: *const c_void) -> CFDictionaryRef; + + pub fn DARegisterDiskAppearedCallback( + session: *const c_void, + match_: CFDictionaryRef, + callback: extern "C" fn(disk: *const c_void, context: *mut c_void), + context: *mut c_void, + ); + + pub fn DARegisterDiskDescriptionChangedCallback( + session: *const c_void, + match_: CFDictionaryRef, + watch: CFArrayRef, + callback: extern "C" fn(disk: *const c_void, keys: CFArrayRef, context: *mut c_void), + context: *mut c_void, + ); + + pub fn DARegisterDiskDisappearedCallback( + session: *const c_void, + match_: CFDictionaryRef, + callback: extern "C" fn(disk: *const c_void, context: *mut c_void), + context: *mut c_void, + ); + + pub fn DASessionScheduleWithRunLoop( + session: *const c_void, + runLoop: CFRunLoopRef, + runLoopMode: CFStringRef, + ); +} diff --git a/yazi-ffi/src/io_kit.rs b/yazi-ffi/src/io_kit.rs new file mode 100644 index 00000000..60d30759 --- /dev/null +++ b/yazi-ffi/src/io_kit.rs @@ -0,0 +1,26 @@ +use std::ffi::c_char; + +use core_foundation_sys::{base::{CFAllocatorRef, CFTypeRef, mach_port_t}, dictionary::CFMutableDictionaryRef, string::CFStringRef}; +use libc::kern_return_t; + +#[link(name = "IOKit", kind = "framework")] +extern "C" { + pub fn IOServiceGetMatchingServices( + mainPort: mach_port_t, + matching: CFMutableDictionaryRef, + existing: *mut mach_port_t, + ) -> kern_return_t; + + pub fn IOServiceMatching(a: *const c_char) -> CFMutableDictionaryRef; + + pub fn IOIteratorNext(iterator: mach_port_t) -> mach_port_t; + + pub fn IORegistryEntryCreateCFProperty( + entry: mach_port_t, + key: CFStringRef, + allocator: CFAllocatorRef, + options: u32, + ) -> CFTypeRef; + + pub fn IOObjectRelease(obj: mach_port_t) -> kern_return_t; +} diff --git a/yazi-ffi/src/lib.rs b/yazi-ffi/src/lib.rs new file mode 100644 index 00000000..182356db --- /dev/null +++ b/yazi-ffi/src/lib.rs @@ -0,0 +1,2 @@ +#[cfg(target_os = "macos")] +yazi_macro::mod_flat!(cf_dict cf_string disk_arbitration io_kit); diff --git a/yazi-fm/src/manager/mod.rs b/yazi-fm/src/manager/mod.rs index b2788a42..fb49cbea 100644 --- a/yazi-fm/src/manager/mod.rs +++ b/yazi-fm/src/manager/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(preview); +yazi_macro::mod_flat!(modal preview); diff --git a/yazi-fm/src/manager/modal.rs b/yazi-fm/src/manager/modal.rs new file mode 100644 index 00000000..4ad52d0e --- /dev/null +++ b/yazi-fm/src/manager/modal.rs @@ -0,0 +1,30 @@ +use mlua::{ObjectLike, Table}; +use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; +use tracing::error; +use yazi_plugin::{LUA, elements::render_once}; + +use crate::Ctx; + +pub(crate) struct Modal<'a> { + cx: &'a Ctx, +} + +impl<'a> Modal<'a> { + #[inline] + pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } +} + +impl Widget for Modal<'_> { + fn render(self, area: Rect, buf: &mut Buffer) { + let mut f = || { + let area = yazi_plugin::elements::Rect::from(area); + let root = LUA.globals().raw_get::("Modal")?.call_method::
("new", area)?; + + render_once(root.call_method("children_redraw", ())?, buf, |p| self.cx.manager.area(p)); + Ok::<_, mlua::Error>(()) + }; + if let Err(e) = f() { + error!("Failed to redraw the `Modal` component:\n{e}"); + } + } +} diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs index 844cc483..d997db9c 100644 --- a/yazi-fm/src/root.rs +++ b/yazi-fm/src/root.rs @@ -34,6 +34,7 @@ impl Widget for Root<'_> { } manager::Preview::new(self.cx).render(area, buf); + manager::Modal::new(self.cx).render(area, buf); if self.cx.tasks.visible { tasks::Tasks::new(self.cx).render(area, buf); diff --git a/yazi-fs/Cargo.toml b/yazi-fs/Cargo.toml index e5467d6b..490e8382 100644 --- a/yazi-fs/Cargo.toml +++ b/yazi-fs/Cargo.toml @@ -9,18 +9,22 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] +yazi-ffi = { path = "../yazi-ffi", version = "0.4.3" } yazi-macro = { path = "../yazi-macro", version = "0.4.3" } yazi-shared = { path = "../yazi-shared", version = "0.4.3" } # External dependencies -anyhow = { workspace = true } -arc-swap = "1.7.1" -bitflags = { workspace = true } -dirs = { workspace = true } -futures = { workspace = true } -regex = { workspace = true } -serde = { workspace = true } -tokio = { workspace = true } +anyhow = { workspace = true } +arc-swap = "1.7.1" +bitflags = { workspace = true } +dirs = { workspace = true } +futures = { workspace = true } +parking_lot = { workspace = true } +regex = { workspace = true } +scopeguard = { workspace = true } +serde = { workspace = true } +tokio = { workspace = true } +tracing = { workspace = true } [target."cfg(unix)".dependencies] libc = { workspace = true } @@ -28,3 +32,7 @@ uzers = { workspace = true } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.59.0", features = [ "Win32_Storage_FileSystem" ] } + +[target.'cfg(target_os = "macos")'.dependencies] +core-foundation-sys = { workspace = true } +objc = { workspace = true } diff --git a/yazi-fs/src/cha.rs b/yazi-fs/src/cha.rs index 201ca4a5..386f8f42 100644 --- a/yazi-fs/src/cha.rs +++ b/yazi-fs/src/cha.rs @@ -30,6 +30,8 @@ pub struct Cha { #[cfg(unix)] pub mode: libc::mode_t, #[cfg(unix)] + pub dev: libc::dev_t, + #[cfg(unix)] pub uid: libc::uid_t, #[cfg(unix)] pub gid: libc::gid_t, @@ -64,6 +66,11 @@ impl From for Cha { m.permissions().mode() as _ }, #[cfg(unix)] + dev: { + use std::os::unix::fs::MetadataExt; + m.dev() as _ + }, + #[cfg(unix)] uid: { use std::os::unix::fs::MetadataExt; m.uid() as _ diff --git a/yazi-fs/src/files.rs b/yazi-fs/src/files.rs index 280699af..bed8bc7b 100644 --- a/yazi-fs/src/files.rs +++ b/yazi-fs/src/files.rs @@ -4,7 +4,7 @@ use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}} use yazi_shared::{Id, url::{Url, Urn, UrnBuf}}; use super::{FilesSorter, Filter}; -use crate::{Cha, FILES_TICKET, File, FilesOp, SortBy, maybe_exists}; +use crate::{Cha, FILES_TICKET, File, FilesOp, SortBy, maybe_exists, mounts::PARTITIONS}; #[derive(Default)] pub struct Files { @@ -84,10 +84,9 @@ impl Files { pub async fn assert_stale(cwd: &Url, cha: Cha) -> Option { match fs::metadata(cwd).await.map(Cha::from) { Ok(c) if !c.is_dir() => { - // TODO: use `ErrorKind::NotADirectory` instead once it gets stabilized - FilesOp::IOErr(cwd.clone(), std::io::ErrorKind::AlreadyExists).emit(); + FilesOp::IOErr(cwd.clone(), std::io::ErrorKind::NotADirectory).emit(); } - Ok(c) if c.hits(cha) => {} + Ok(c) if c.hits(cha) && PARTITIONS.read().heuristic(cha) => {} Ok(c) => return Some(c), Err(e) => { if maybe_exists(cwd).await { diff --git a/yazi-fs/src/lib.rs b/yazi-fs/src/lib.rs index aa5f90c5..e028bc45 100644 --- a/yazi-fs/src/lib.rs +++ b/yazi-fs/src/lib.rs @@ -1,5 +1,11 @@ #![allow(clippy::if_same_then_else, clippy::option_map_unit_fn)] +yazi_macro::mod_pub!(mounts); + yazi_macro::mod_flat!(cha cwd file files filter fns op path sorter sorting stage step xdg); -pub fn init() { CWD.init(<_>::default()); } +pub fn init() { + CWD.init(<_>::default()); + + mounts::init(); +} diff --git a/yazi-fs/src/mounts/linux.rs b/yazi-fs/src/mounts/linux.rs new file mode 100644 index 00000000..0f305153 --- /dev/null +++ b/yazi-fs/src/mounts/linux.rs @@ -0,0 +1,151 @@ +use std::{borrow::Cow, collections::{HashMap, HashSet}, ffi::{OsStr, OsString}, os::{fd::AsFd, unix::{ffi::OsStrExt, fs::MetadataExt}}, time::Duration}; + +use anyhow::Result; +use tokio::{io::{Interest, unix::AsyncFd}, time::sleep}; +use tracing::error; +use yazi_shared::replace_cow; + +use super::{Locked, Partition, Partitions}; + +impl Partitions { + pub fn monitor(me: Locked, cb: F) + where + F: Fn() + Copy + Send + 'static, + { + async fn wait_mounts(me: Locked, cb: impl Fn()) -> Result<()> { + let f = std::fs::File::open("/proc/mounts")?; + let fd = AsyncFd::with_interest(f.as_fd(), Interest::READABLE)?; + loop { + let mut guard = fd.readable().await?; + guard.clear_ready(); + Partitions::update(me.clone()).await; + cb(); + } + } + + async fn wait_partitions(me: Locked, cb: impl Fn()) -> Result<()> { + loop { + let partitions = Partitions::partitions()?; + if me.read().linux_cache == partitions { + sleep(Duration::from_secs(3)).await; + continue; + } + + me.write().linux_cache = partitions; + Partitions::update(me.clone()).await; + + cb(); + sleep(Duration::from_secs(3)).await; + } + } + + let me_ = me.clone(); + tokio::spawn(async move { + loop { + if let Err(e) = wait_mounts(me_.clone(), cb).await { + error!("Error encountered while monitoring `/proc/mounts`: {e:?}"); + } + sleep(Duration::from_secs(5)).await; + } + }); + + tokio::spawn(async move { + loop { + if let Err(e) = wait_partitions(me.clone(), cb).await { + error!("Error encountered while monitoring `/proc/partitions`: {e:?}"); + } + sleep(Duration::from_secs(5)).await; + } + }); + } + + async fn update(me: Locked) { + _ = tokio::task::spawn_blocking(move || { + let mut guard = me.write(); + match Self::all(&guard) { + Ok(new) => guard.inner = new, + Err(e) => error!("Error encountered while updating mount points: {e:?}"), + }; + }) + .await; + } + + fn all(&self) -> Result> { + let mut mounts = Self::mounts()?; + { + let set = &self.linux_cache; + let mut set: HashSet<&OsStr> = set.iter().map(AsRef::as_ref).collect(); + mounts.iter().filter_map(|p| p.dev_name()).for_each(|s| _ = set.remove(s)); + mounts.extend(set.into_iter().map(Partition::new)); + }; + + let labels = Self::labels()?; + for mount in &mut mounts { + if !mount.src.as_bytes().starts_with(b"/dev/") { + continue; + } + if let Ok(meta) = std::fs::metadata(&mount.src) { + mount.rdev = Some(meta.rdev() as _); + mount.label = labels.get(&(meta.dev(), meta.ino())).cloned(); + } + } + Ok(mounts) + } + + fn mounts() -> Result> { + let mut vec = vec![]; + let s = std::fs::read_to_string("/proc/mounts")?; + for line in s.lines() { + let mut it = line.split_whitespace(); + let Some(src) = it.next() else { continue }; + let Some(dist) = it.next() else { continue }; + let Some(fstype) = it.next() else { continue }; + vec.push(Partition { + src: Self::unmangle_octal(src).into_owned().into(), + dist: Some(Self::unmangle_octal(dist).into_owned().into()), + fstype: Some(Self::unmangle_octal(fstype).into_owned().into()), + ..Default::default() + }); + } + Ok(vec) + } + + fn partitions() -> Result> { + let mut set = HashSet::new(); + let s = std::fs::read_to_string("/proc/partitions")?; + for line in s.lines().skip(2) { + let mut it = line.split_whitespace(); + let Some(Ok(_major)) = it.next().map(|s| s.parse::()) else { continue }; + let Some(Ok(_minor)) = it.next().map(|s| s.parse::()) else { continue }; + let Some(Ok(_blocks)) = it.next().map(|s| s.parse::()) else { continue }; + if let Some(name) = it.next() { + set.insert(Self::unmangle_octal(name).into_owned()); + } + } + Ok(set) + } + + fn labels() -> Result> { + let mut map = HashMap::new(); + for entry in std::fs::read_dir("/dev/disk/by-label")?.flatten() { + let meta = std::fs::metadata(entry.path())?; + map.insert((meta.dev(), meta.ino()), entry.file_name()); + } + Ok(map) + } + + // Unmangle '\t', '\n', ' ', '#', and r'\' + // https://elixir.bootlin.com/linux/v6.13-rc3/source/fs/proc_namespace.c#L89 + fn unmangle_octal(s: &str) -> Cow { + let mut s = Cow::Borrowed(s); + for (a, b) in + [(r"\011", "\t"), (r"\012", "\n"), (r"\040", " "), (r"\043", "#"), (r"\134", r"\")] + { + s = match replace_cow(&s, a, b) { + Cow::Borrowed(_) => s, + Cow::Owned(new) => Cow::Owned(new), + }; + } + s + } +} diff --git a/yazi-fs/src/mounts/macos.rs b/yazi-fs/src/mounts/macos.rs new file mode 100644 index 00000000..bb95ca06 --- /dev/null +++ b/yazi-fs/src/mounts/macos.rs @@ -0,0 +1,169 @@ +use std::{ffi::{CStr, CString, OsString, c_void}, mem, os::unix::{ffi::OsStringExt, fs::MetadataExt}}; + +use anyhow::{Result, bail}; +use core_foundation_sys::{array::CFArrayRef, base::{CFRelease, kCFAllocatorDefault}, runloop::{CFRunLoopGetCurrent, CFRunLoopRun, kCFRunLoopDefaultMode}}; +use libc::{c_char, mach_port_t}; +use objc::{msg_send, runtime::Object, sel, sel_impl}; +use scopeguard::defer; +use tracing::error; +use yazi_ffi::{CFDict, CFString, DADiskCopyDescription, DADiskCreateFromBSDName, DARegisterDiskAppearedCallback, DARegisterDiskDescriptionChangedCallback, DARegisterDiskDisappearedCallback, DASessionCreate, DASessionScheduleWithRunLoop, IOIteratorNext, IOObjectRelease, IORegistryEntryCreateCFProperty, IOServiceGetMatchingServices, IOServiceMatching}; + +use super::{Locked, Partition, Partitions}; + +impl Partitions { + pub fn monitor(me: Locked, cb: F) + where + F: Fn() + Copy + Send + 'static, + { + tokio::task::spawn_blocking(move || { + let session = unsafe { DASessionCreate(kCFAllocatorDefault) }; + if session.is_null() { + return error!("Cannot create a disk arbitration session"); + } + defer! { unsafe { CFRelease(session) } }; + + extern "C" fn on_appeared(_disk: *const c_void, context: *mut c_void) { + let boxed = context as *mut Box; + unsafe { (*boxed)() } + } + + extern "C" fn on_changed(_disk: *const c_void, _keys: CFArrayRef, context: *mut c_void) { + let boxed = context as *mut Box; + unsafe { (*boxed)() } + } + + extern "C" fn on_disappeared(_disk: *const c_void, context: *mut c_void) { + let boxed = context as *mut Box; + unsafe { (*boxed)() } + } + + let create_context = || { + let me = me.clone(); + let boxed: Box = Box::new(move || { + if mem::replace(&mut me.write().need_update, true) { + return; + } + Self::update(me.clone(), cb); + }); + Box::into_raw(Box::new(boxed)) as *mut c_void + }; + + unsafe { + DARegisterDiskAppearedCallback(session, std::ptr::null(), on_appeared, create_context()); + DARegisterDiskDescriptionChangedCallback( + session, + std::ptr::null(), + std::ptr::null(), + on_changed, + create_context(), + ); + DARegisterDiskDisappearedCallback( + session, + std::ptr::null(), + on_disappeared, + create_context(), + ); + DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFRunLoopRun(); + } + }); + } + + fn update(me: Locked, cb: impl Fn() + Send + 'static) { + _ = tokio::task::spawn_blocking(move || { + let result = Self::all_names().and_then(Self::all_partitions); + if let Err(ref e) = result { + error!("Error encountered while updating mount points: {e:?}"); + } + + let mut guard = me.write(); + if let Ok(new) = result { + guard.inner = new; + } + guard.need_update = false; + + drop(guard); + cb(); + }); + } + + fn all_partitions(names: Vec) -> Result> { + let session = unsafe { DASessionCreate(kCFAllocatorDefault) }; + if session.is_null() { + bail!("Cannot create a disk arbitration session"); + } + defer! { unsafe { CFRelease(session) } }; + + let mut disks = Vec::with_capacity(names.len()); + for name in names { + let disk = unsafe { DADiskCreateFromBSDName(kCFAllocatorDefault, session, name.as_ptr()) }; + if disk.is_null() { + continue; + } + + defer! { unsafe { CFRelease(disk) } }; + let Ok(dict) = CFDict::take(unsafe { DADiskCopyDescription(disk) }) else { + continue; + }; + + let partition = Partition::new(&OsString::from_vec(name.into_bytes())); + let rdev = std::fs::metadata(&partition.src).map(|m| m.rdev() as _).ok(); + disks.push(Partition { + dist: dict.path_buf("DAVolumePath").ok(), + rdev, + fstype: dict.os_string("DAVolumeKind").ok(), + label: dict.os_string("DAVolumeName").ok(), + capacity: dict.integer("DAMediaSize").unwrap_or_default() as u64, + ..partition + }); + } + + Ok(disks) + } + + fn all_names() -> Result> { + let mut iterator: mach_port_t = 0; + let result = unsafe { + IOServiceGetMatchingServices(0, IOServiceMatching(c"IOService".as_ptr()), &mut iterator) + }; + + if result != 0 { + bail!("Cannot get the IO matching services"); + } + defer! { unsafe { IOObjectRelease(iterator); } }; + + let mut names = vec![]; + loop { + let service = unsafe { IOIteratorNext(iterator) }; + if service == 0 { + break; + } + defer! { unsafe { IOObjectRelease(service); } }; + if let Some(name) = Self::bsd_name(service).ok().filter(|s| s.as_bytes().starts_with(b"disk")) + { + names.push(name); + } + } + + names.sort_unstable(); + Ok(names) + } + + fn bsd_name(service: mach_port_t) -> Result { + let key = CFString::new("BSD Name")?; + let property = + unsafe { IORegistryEntryCreateCFProperty(service, *key, kCFAllocatorDefault, 1) }; + if property.is_null() { + bail!("Cannot get the name property"); + } + defer! { unsafe { CFRelease(property) } }; + + #[allow(unexpected_cfgs)] + let cstr: *const c_char = unsafe { msg_send![property as *const Object, UTF8String] }; + Ok(if cstr.is_null() { + bail!("Invalid value for the name property"); + } else { + CString::from(unsafe { CStr::from_ptr(cstr) }) + }) + } +} diff --git a/yazi-fs/src/mounts/mod.rs b/yazi-fs/src/mounts/mod.rs new file mode 100644 index 00000000..ef5ff71d --- /dev/null +++ b/yazi-fs/src/mounts/mod.rs @@ -0,0 +1,9 @@ +yazi_macro::mod_flat!(partition partitions); + +#[cfg(target_os = "linux")] +yazi_macro::mod_flat!(linux); + +#[cfg(target_os = "macos")] +yazi_macro::mod_flat!(macos); + +pub(super) fn init() { PARTITIONS.init(<_>::default()); } diff --git a/yazi-fs/src/mounts/partition.rs b/yazi-fs/src/mounts/partition.rs new file mode 100644 index 00000000..ccf453aa --- /dev/null +++ b/yazi-fs/src/mounts/partition.rs @@ -0,0 +1,50 @@ +use std::{ffi::{OsStr, OsString}, path::PathBuf}; + +#[derive(Debug, Default)] +pub struct Partition { + pub src: OsString, + pub dist: Option, + #[cfg(unix)] + pub rdev: Option, + pub label: Option, + pub fstype: Option, + pub capacity: u64, +} + +impl Partition { + pub fn heuristic(&self) -> bool { + let b: &[u8] = self.fstype.as_ref().map_or(b"", |s| s.as_encoded_bytes()); + !matches!(b, b"exfat" | b"fuse.rclone") + } + + #[rustfmt::skip] + pub fn systemic(&self) -> bool { + let b: &[u8] = self.fstype.as_ref().map_or(b"", |s| s.as_encoded_bytes()); + #[cfg(target_os = "linux")] + { + matches!(b, b"autofs" | b"binfmt_misc" | b"bpf" | b"cgroup2" | b"configfs" | b"debugfs" | b"devpts" | b"devtmpfs" | b"fuse.gvfsd-fuse" | b"fusectl" | b"hugetlbfs" | b"mqueue" | b"proc" | b"ramfs" | b"securityfs" | b"sysfs" | b"tmpfs") + } + #[cfg(target_os = "macos")] + { + b.is_empty() + } + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + { + false + } + } +} + +impl Partition { + #[inline] + #[cfg(any(target_os = "linux", target_os = "macos"))] + pub(super) fn new(name: &OsStr) -> Self { + Self { src: std::path::Path::new("/dev/").join(name).into(), ..Default::default() } + } + + #[inline] + #[cfg(target_os = "linux")] + pub(super) fn dev_name(&self) -> Option<&OsStr> { + std::path::Path::new(&self.src).strip_prefix("/dev/").ok().map(|p| p.as_os_str()) + } +} diff --git a/yazi-fs/src/mounts/partitions.rs b/yazi-fs/src/mounts/partitions.rs new file mode 100644 index 00000000..536de288 --- /dev/null +++ b/yazi-fs/src/mounts/partitions.rs @@ -0,0 +1,45 @@ +use std::{ops::Deref, sync::Arc}; + +use parking_lot::RwLock; +use yazi_shared::RoCell; + +use super::Partition; +use crate::Cha; + +pub(super) type Locked = Arc>; + +pub static PARTITIONS: RoCell = RoCell::new(); + +#[derive(Default)] +pub struct Partitions { + pub(super) inner: Vec, + #[cfg(target_os = "linux")] + pub(super) linux_cache: std::collections::HashSet, + #[cfg(target_os = "macos")] + pub(super) need_update: bool, +} + +impl Deref for Partitions { + type Target = Vec; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl Partitions { + #[inline] + #[cfg(unix)] + pub fn by_dev(&self, dev: libc::dev_t) -> Option<&Partition> { + self.inner.iter().find(|p| p.rdev == Some(dev)) + } + + pub fn heuristic(&self, cha: Cha) -> bool { + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + self.by_dev(cha.dev).is_none_or(|p| p.heuristic()) + } + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + { + true + } + } +} diff --git a/yazi-plugin/preset/components/modal.lua b/yazi-plugin/preset/components/modal.lua index 0041c831..7d19f87a 100644 --- a/yazi-plugin/preset/components/modal.lua +++ b/yazi-plugin/preset/components/modal.lua @@ -15,13 +15,7 @@ function Modal:reflow() return components end -function Modal:redraw() - local elements = {} - for _, child in ipairs(self._children) do - elements = ya.list_merge(elements, ya.redraw_with(child[1]:new(self._area))) - end - return elements -end +function Modal:redraw() return {} end -- Children function Modal:children_add(tbl, order) @@ -40,3 +34,11 @@ function Modal:children_remove(id) end end end + +function Modal:children_redraw() + local elements = {} + for _, child in ipairs(self._children) do + elements = ya.list_merge(elements, ya.redraw_with(child[1]:new(self._area))) + end + return elements +end diff --git a/yazi-plugin/src/bindings/cha.rs b/yazi-plugin/src/bindings/cha.rs index 11e68c9b..75f70845 100644 --- a/yazi-plugin/src/bindings/cha.rs +++ b/yazi-plugin/src/bindings/cha.rs @@ -1,4 +1,4 @@ -use std::{ops::Deref, time::{Duration, SystemTime, UNIX_EPOCH}}; +use std::{ops::{Deref, Not}, time::{Duration, SystemTime, UNIX_EPOCH}}; use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, UserDataMethods}; use yazi_fs::ChaKind; @@ -44,6 +44,8 @@ impl Cha { #[cfg(unix)] mode: t.raw_get("mode").unwrap_or_default(), #[cfg(unix)] + dev: t.raw_get("dev").unwrap_or_default(), + #[cfg(unix)] uid: t.raw_get("uid").unwrap_or_default(), #[cfg(unix)] gid: t.raw_get("gid").unwrap_or_default(), @@ -72,9 +74,10 @@ impl UserData for Cha { #[cfg(unix)] { - fields.add_field_method_get("uid", |_, me| Ok((!me.is_dummy()).then_some(me.uid))); - fields.add_field_method_get("gid", |_, me| Ok((!me.is_dummy()).then_some(me.gid))); - fields.add_field_method_get("nlink", |_, me| Ok((!me.is_dummy()).then_some(me.nlink))); + fields.add_field_method_get("dev", |_, me| Ok(me.is_dummy().not().then_some(me.dev))); + fields.add_field_method_get("uid", |_, me| Ok(me.is_dummy().not().then_some(me.uid))); + fields.add_field_method_get("gid", |_, me| Ok(me.is_dummy().not().then_some(me.gid))); + fields.add_field_method_get("nlink", |_, me| Ok(me.is_dummy().not().then_some(me.nlink))); } fields.add_field_method_get("len", |_, me| Ok(me.len)); diff --git a/yazi-plugin/src/bindings/chan.rs b/yazi-plugin/src/bindings/chan.rs index 9eef85cc..88067d98 100644 --- a/yazi-plugin/src/bindings/chan.rs +++ b/yazi-plugin/src/bindings/chan.rs @@ -33,8 +33,8 @@ pub struct MpscUnboundedRx(pub tokio::sync::mpsc::UnboundedReceiver); impl UserData for MpscUnboundedTx { fn add_methods>(methods: &mut M) { methods.add_method("send", |lua, me, value: Value| match me.0.send(value) { - Ok(()) => (true, Value::Nil).into_lua_multi(&lua), - Err(e) => (false, Error::Custom(e.to_string())).into_lua_multi(&lua), + Ok(()) => (true, Value::Nil).into_lua_multi(lua), + Err(e) => (false, Error::Custom(e.to_string())).into_lua_multi(lua), }); } } @@ -60,10 +60,8 @@ impl UserData for OneshotTx { return Err("Oneshot sender already used".into_lua_err()); }; match tx.send(value) { - Ok(()) => (true, Value::Nil).into_lua_multi(&lua), - Err(_) => { - (false, Error::Custom("Oneshot receiver closed".to_string())).into_lua_multi(&lua) - } + Ok(()) => (true, Value::Nil).into_lua_multi(lua), + Err(_) => (false, Error::Custom("Oneshot receiver closed".to_string())).into_lua_multi(lua), } }); } diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index daab60d2..e22a2711 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -1,7 +1,7 @@ use globset::GlobBuilder; use mlua::{ExternalError, ExternalResult, Function, IntoLua, IntoLuaMulti, Lua, MetaMethod, Table, Value}; use tokio::fs; -use yazi_fs::remove_dir_clean; +use yazi_fs::{mounts::PARTITIONS, remove_dir_clean}; use crate::{Error, bindings::{Cast, Cha}, file::File, url::{Url, UrlRef}}; @@ -15,6 +15,7 @@ pub fn compose(lua: &Lua) -> mlua::Result
{ b"remove" => remove(lua)?, b"read_dir" => read_dir(lua)?, b"unique_name" => unique_name(lua)?, + b"partitions" => partitions(lua)?, _ => return Ok(Value::Nil), } .into_lua(lua)?; @@ -156,3 +157,21 @@ fn unique_name(lua: &Lua) -> mlua::Result { } }) } + +fn partitions(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, ()| async move { + PARTITIONS + .read() + .iter() + .filter(|&p| !p.systemic()) + .map(|p| { + lua.create_table_from([ + ("src", p.src.clone().into_lua(&lua)?), + ("dist", p.dist.clone().into_lua(&lua)?), + ("label", p.label.clone().into_lua(&lua)?), + ("fstype", p.fstype.clone().into_lua(&lua)?), + ]) + }) + .collect::>>() + }) +} diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 2e4281af..56861c56 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -7,7 +7,7 @@ authors = [ "sxyazi " ] description = "Yazi shared library" homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" -rust-version = "1.78.0" +rust-version = "1.83.0" [dependencies] yazi-macro = { path = "../yazi-macro", version = "0.4.3" }