feat: mount manager (#2199)

This commit is contained in:
三咲雅 · Misaki Masa 2025-01-17 18:37:18 +08:00 committed by sxyazi
parent 6c94227d40
commit 3e1c8950f2
No known key found for this signature in database
40 changed files with 1068 additions and 340 deletions

352
Cargo.lock generated
View file

@ -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",
]

View file

@ -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"

View file

@ -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.
<!-- Protocols -->
[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

View file

@ -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":[]}
{"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":[]}

158
flake.lock generated
View file

@ -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
}

View file

@ -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" ] }

View file

@ -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" ] }

View file

@ -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"

View file

@ -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 }

View file

@ -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 }
}

View file

@ -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 =

View file

@ -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 }

View file

@ -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),
}
}

View file

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

View file

@ -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<BodyMount> for Body<'_> {
fn from(value: BodyMount) -> Self { Self::Mount(value) }
}
impl IntoLua for BodyMount {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Ok(Value::Nil) }
}

View file

@ -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),
};

View file

@ -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<RwLock<HashMap<String, HashMap<String, Function>>>> = 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

22
yazi-ffi/Cargo.toml Normal file
View file

@ -0,0 +1,22 @@
[package]
name = "yazi-ffi"
version = "0.4.3"
edition = "2021"
license = "MIT"
authors = [ "sxyazi <sxyazi@gmail.com>" ]
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 }

53
yazi-ffi/src/cf_dict.rs Normal file
View file

@ -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<Self> {
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<i64> {
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<OsString> {
ManuallyDrop::new(CFString(self.value(key)? as CFStringRef)).os_string()
}
pub fn path_buf(&self, key: &str) -> Result<PathBuf> {
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 _) } }
}

58
yazi-ffi/src/cf_string.rs Normal file
View file

@ -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<Self> {
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<OsString> {
let len = self.len();
let capacity =
unsafe { CFStringGetMaximumSizeForEncoding(len as _, kCFStringEncodingUTF8) } + 1;
let mut out: Vec<u8> = 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 _) }; }
}

View file

@ -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,
);
}

26
yazi-ffi/src/io_kit.rs Normal file
View file

@ -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;
}

2
yazi-ffi/src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
#[cfg(target_os = "macos")]
yazi_macro::mod_flat!(cf_dict cf_string disk_arbitration io_kit);

View file

@ -1 +1 @@
yazi_macro::mod_flat!(preview);
yazi_macro::mod_flat!(modal preview);

View file

@ -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::<Table>("Modal")?.call_method::<Table>("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}");
}
}
}

View file

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

View file

@ -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 }

View file

@ -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<Metadata> 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 _

View file

@ -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<Cha> {
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 {

View file

@ -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();
}

151
yazi-fs/src/mounts/linux.rs Normal file
View file

@ -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<F>(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<Vec<Partition>> {
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<Vec<Partition>> {
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<HashSet<String>> {
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::<u16>()) else { continue };
let Some(Ok(_minor)) = it.next().map(|s| s.parse::<u16>()) else { continue };
let Some(Ok(_blocks)) = it.next().map(|s| s.parse::<u16>()) else { continue };
if let Some(name) = it.next() {
set.insert(Self::unmangle_octal(name).into_owned());
}
}
Ok(set)
}
fn labels() -> Result<HashMap<(u64, u64), OsString>> {
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<str> {
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
}
}

169
yazi-fs/src/mounts/macos.rs Normal file
View file

@ -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<F>(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<dyn Fn()>;
unsafe { (*boxed)() }
}
extern "C" fn on_changed(_disk: *const c_void, _keys: CFArrayRef, context: *mut c_void) {
let boxed = context as *mut Box<dyn Fn()>;
unsafe { (*boxed)() }
}
extern "C" fn on_disappeared(_disk: *const c_void, context: *mut c_void) {
let boxed = context as *mut Box<dyn Fn()>;
unsafe { (*boxed)() }
}
let create_context = || {
let me = me.clone();
let boxed: Box<dyn Fn()> = 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<CString>) -> Result<Vec<Partition>> {
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<Vec<CString>> {
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<CString> {
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) })
})
}
}

View file

@ -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()); }

View file

@ -0,0 +1,50 @@
use std::{ffi::{OsStr, OsString}, path::PathBuf};
#[derive(Debug, Default)]
pub struct Partition {
pub src: OsString,
pub dist: Option<PathBuf>,
#[cfg(unix)]
pub rdev: Option<libc::dev_t>,
pub label: Option<OsString>,
pub fstype: Option<OsString>,
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())
}
}

View file

@ -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<RwLock<Partitions>>;
pub static PARTITIONS: RoCell<Locked> = RoCell::new();
#[derive(Default)]
pub struct Partitions {
pub(super) inner: Vec<Partition>,
#[cfg(target_os = "linux")]
pub(super) linux_cache: std::collections::HashSet<String>,
#[cfg(target_os = "macos")]
pub(super) need_update: bool,
}
impl Deref for Partitions {
type Target = Vec<Partition>;
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
}
}
}

View file

@ -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

View file

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

View file

@ -33,8 +33,8 @@ pub struct MpscUnboundedRx(pub tokio::sync::mpsc::UnboundedReceiver<Value>);
impl UserData for MpscUnboundedTx {
fn add_methods<M: mlua::UserDataMethods<Self>>(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),
}
});
}

View file

@ -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<Table> {
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<Function> {
}
})
}
fn partitions(lua: &Lua) -> mlua::Result<Function> {
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::<mlua::Result<Vec<Table>>>()
})
}

View file

@ -7,7 +7,7 @@ authors = [ "sxyazi <sxyazi@gmail.com>" ]
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" }