From 35ad31180f6e414cef5738e1fe87e9552b32f364 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 31 Mar 2026 18:25:23 +0800 Subject: [PATCH] refactor: prepare for custom VFS provider --- Cargo.lock | 421 ++++++++-------- Cargo.toml | 5 +- cspell.json | 2 +- yazi-actor/Cargo.toml | 1 + yazi-actor/src/actor.rs | 2 +- yazi-actor/src/app/deprecate.rs | 7 +- yazi-actor/src/app/plugin.rs | 13 +- yazi-actor/src/app/plugin_do.rs | 13 +- yazi-actor/src/app/quit.rs | 6 +- yazi-actor/src/app/title.rs | 3 +- yazi-actor/src/cmp/show.rs | 7 +- yazi-actor/src/cmp/trigger.rs | 7 +- yazi-actor/src/context.rs | 5 +- yazi-actor/src/core/preflight.rs | 3 +- yazi-actor/src/mgr/arrow.rs | 1 + yazi-actor/src/mgr/bulk_rename.rs | 3 +- yazi-actor/src/mgr/close.rs | 8 +- yazi-actor/src/mgr/displace.rs | 5 +- yazi-actor/src/mgr/displace_do.rs | 6 +- yazi-actor/src/mgr/download.rs | 3 +- yazi-actor/src/mgr/escape.rs | 2 +- yazi-actor/src/mgr/filter.rs | 7 +- yazi-actor/src/mgr/filter_do.rs | 6 +- yazi-actor/src/mgr/find.rs | 3 +- yazi-actor/src/mgr/find_do.rs | 6 +- yazi-actor/src/mgr/hidden.rs | 3 +- yazi-actor/src/mgr/hover.rs | 6 +- yazi-actor/src/mgr/open.rs | 7 +- yazi-actor/src/mgr/open_do.rs | 13 +- yazi-actor/src/mgr/paste.rs | 2 +- yazi-actor/src/mgr/quit.rs | 10 +- yazi-actor/src/mgr/refresh.rs | 4 +- yazi-actor/src/mgr/remove.rs | 6 +- yazi-actor/src/mgr/search.rs | 20 +- yazi-actor/src/mgr/seek.rs | 12 +- yazi-actor/src/mgr/shell.rs | 6 +- yazi-actor/src/mgr/sort.rs | 3 +- yazi-actor/src/mgr/stash.rs | 3 +- yazi-actor/src/mgr/tab_create.rs | 2 +- yazi-actor/src/mgr/toggle.rs | 2 +- yazi-actor/src/mgr/toggle_all.rs | 2 +- yazi-actor/src/mgr/update_mimes.rs | 2 +- yazi-actor/src/mgr/update_paged.rs | 4 +- yazi-actor/src/mgr/update_peeked.rs | 10 +- yazi-actor/src/mgr/update_spotted.rs | 18 +- yazi-actor/src/mgr/update_yanked.rs | 2 +- yazi-actor/src/notify/push.rs | 9 +- yazi-actor/src/notify/tick.rs | 4 +- yazi-actor/src/tasks/cancel.rs | 2 +- yazi-actor/src/tasks/inspect.rs | 4 +- yazi-actor/src/tasks/open_shell_compat.rs | 6 +- yazi-actor/src/tasks/process_open.rs | 16 +- yazi-actor/src/tasks/update_succeed.rs | 15 + yazi-actor/src/which/activate.rs | 7 +- yazi-cli/src/dds/shot.rs | 6 +- yazi-core/Cargo.toml | 13 +- yazi-core/src/app/mod.rs | 1 + yazi-core/src/app/quit.rs | 32 ++ yazi-core/src/cmp/cmp.rs | 3 +- yazi-core/src/cmp/item.rs | 7 + yazi-core/src/cmp/mod.rs | 2 +- yazi-core/src/cmp/opt.rs | 11 + .../external => yazi-core/src}/highlighter.rs | 11 +- yazi-core/src/lib.rs | 4 +- yazi-core/src/mgr/displace.rs | 7 + yazi-core/src/mgr/filter.rs | 21 + yazi-core/src/mgr/find.rs | 22 + yazi-core/src/mgr/mod.rs | 2 +- yazi-core/src/mgr/open.rs | 30 ++ yazi-core/src/mgr/search.rs | 66 +++ yazi-core/src/mgr/yanked.rs | 22 +- yazi-core/src/notify/level.rs | 29 ++ yazi-core/src/notify/message.rs | 20 +- yazi-core/src/notify/mod.rs | 2 +- yazi-core/src/notify/notify.rs | 4 +- yazi-core/src/notify/opt.rs | 41 ++ yazi-core/src/proxy.rs | 59 +++ yazi-core/src/spot/lock.rs | 57 +++ yazi-core/src/spot/mod.rs | 2 +- yazi-core/src/spot/spot.rs | 7 +- yazi-core/src/tab/folder.rs | 5 +- yazi-core/src/tab/mod.rs | 2 +- yazi-core/src/tab/preview.rs | 21 +- yazi-core/src/tab/preview_lock.rs | 31 ++ yazi-core/src/tasks/file.rs | 6 +- yazi-core/src/tasks/process.rs | 7 +- yazi-core/src/tasks/tasks.rs | 12 +- yazi-core/src/which/mod.rs | 2 +- yazi-core/src/which/opt.rs | 78 +++ yazi-dds/Cargo.toml | 2 +- yazi-dds/src/ember/yank.rs | 68 ++- yazi-dds/src/lib.rs | 4 +- yazi-dds/src/payload.rs | 15 +- yazi-dds/src/pubsub.rs | 5 +- yazi-dds/src/sendable.rs | 10 +- yazi-dds/src/server.rs | 2 +- yazi-dds/src/spark/spark.rs | 447 ----------------- yazi-fm/Cargo.toml | 40 +- yazi-fm/src/dispatcher.rs | 5 +- yazi-fm/src/main.rs | 2 + yazi-fm/src/signals.rs | 11 +- yazi-macro/src/actor.rs | 2 +- yazi-macro/src/asset.rs | 10 +- yazi-parser/Cargo.toml | 24 +- yazi-parser/src/app/plugin.rs | 115 +---- yazi-parser/src/app/quit.rs | 39 +- yazi-parser/src/app/update_progress.rs | 12 +- yazi-parser/src/cmp/show.rs | 29 +- yazi-parser/src/lib.rs | 4 +- {yazi-dds => yazi-parser}/src/macros.rs | 0 yazi-parser/src/mgr/close.rs | 23 +- yazi-parser/src/mgr/displace_do.rs | 22 +- yazi-parser/src/mgr/filter.rs | 26 +- yazi-parser/src/mgr/find_do.rs | 27 +- yazi-parser/src/mgr/open.rs | 31 +- yazi-parser/src/mgr/open_do.rs | 27 +- yazi-parser/src/mgr/search.rs | 71 +-- yazi-parser/src/mgr/update_peeked.rs | 43 +- yazi-parser/src/mgr/update_spotted.rs | 75 +-- yazi-parser/src/mgr/update_yanked.rs | 76 +-- yazi-parser/src/notify/push.rs | 74 +-- {yazi-dds => yazi-parser}/src/spark/kind.rs | 0 {yazi-dds => yazi-parser}/src/spark/mod.rs | 0 yazi-parser/src/spark/spark.rs | 448 ++++++++++++++++++ yazi-parser/src/tasks/process_open.rs | 30 +- yazi-parser/src/tasks/update_succeed.rs | 8 +- yazi-parser/src/which/activate.rs | 79 +-- yazi-plugin/Cargo.toml | 34 +- yazi-plugin/preset/components/status.lua | 4 +- yazi-plugin/preset/plugins/null.lua | 7 +- yazi-plugin/preset/plugins/session.lua | 2 +- yazi-plugin/src/elements/elements.rs | 2 +- yazi-plugin/src/external/mod.rs | 2 +- yazi-plugin/src/isolate/entry.rs | 21 - yazi-plugin/src/isolate/fetch.rs | 62 --- yazi-plugin/src/isolate/mod.rs | 1 - yazi-plugin/src/isolate/peek.rs | 134 ------ yazi-plugin/src/isolate/preload.rs | 61 --- yazi-plugin/src/isolate/seek.rs | 20 - yazi-plugin/src/isolate/spot.rs | 63 --- yazi-plugin/src/lib.rs | 8 +- .../src/{isolate/isolate.rs => slim.rs} | 10 +- yazi-plugin/src/{lua.rs => standard.rs} | 17 +- yazi-plugin/src/utils/call.rs | 6 +- yazi-plugin/src/utils/layer.rs | 4 +- yazi-plugin/src/utils/preview.rs | 20 +- yazi-plugin/src/utils/spot.rs | 6 +- yazi-plugin/src/utils/sync.rs | 5 +- yazi-proxy/Cargo.toml | 14 +- yazi-proxy/src/app.rs | 24 +- yazi-proxy/src/cmp.rs | 4 +- yazi-proxy/src/mgr.rs | 31 +- yazi-proxy/src/notify.rs | 22 +- yazi-proxy/src/tasks.rs | 34 +- yazi-proxy/src/which.rs | 4 +- yazi-runner/Cargo.toml | 34 ++ yazi-runner/README.md | 5 + yazi-runner/src/entry.rs | 21 + yazi-runner/src/fetcher/fetcher.rs | 23 + yazi-runner/src/fetcher/job.rs | 20 + yazi-runner/src/fetcher/mod.rs | 1 + yazi-runner/src/fetcher/state.rs | 29 ++ yazi-runner/src/lib.rs | 11 + .../src/loader/chunk.rs | 0 .../src/loader/loader.rs | 0 .../src/loader/mod.rs | 2 +- .../src/loader/require.rs | 0 yazi-runner/src/plugin/mod.rs | 1 + yazi-runner/src/plugin/option.rs | 105 ++++ yazi-runner/src/preload.rs | 63 +++ yazi-runner/src/previewer/error.rs | 30 ++ yazi-runner/src/previewer/job.rs | 46 ++ yazi-runner/src/previewer/mod.rs | 1 + yazi-runner/src/previewer/previewer.rs | 65 +++ yazi-runner/src/runner.rs | 15 + yazi-runner/src/spot.rs | 65 +++ yazi-scheduler/Cargo.toml | 4 +- yazi-scheduler/src/fetch/fetch.rs | 12 +- yazi-scheduler/src/fetch/in.rs | 5 + yazi-scheduler/src/fetch/progress.rs | 3 +- yazi-scheduler/src/file/file.rs | 15 +- yazi-scheduler/src/file/out.rs | 10 + yazi-scheduler/src/file/progress.rs | 25 +- yazi-scheduler/src/hook/hook.rs | 31 +- yazi-scheduler/src/hook/in.rs | 135 +++++- yazi-scheduler/src/lib.rs | 2 +- yazi-scheduler/src/ongoing.rs | 43 +- yazi-scheduler/src/out.rs | 8 +- yazi-scheduler/src/plugin/in.rs | 2 +- yazi-scheduler/src/plugin/plugin.rs | 7 +- yazi-scheduler/src/plugin/progress.rs | 3 +- yazi-scheduler/src/preload/preload.rs | 4 +- yazi-scheduler/src/preload/progress.rs | 3 +- yazi-scheduler/src/process/mod.rs | 2 +- yazi-scheduler/src/process/opt.rs | 15 + yazi-scheduler/src/process/process.rs | 10 +- yazi-scheduler/src/process/progress.rs | 3 +- yazi-scheduler/src/progress.rs | 3 +- yazi-scheduler/src/proxy.rs | 55 +++ yazi-scheduler/src/runner.rs | 2 + yazi-scheduler/src/scheduler.rs | 232 ++++----- yazi-scheduler/src/size/progress.rs | 3 +- yazi-scheduler/src/summary.rs | 48 ++ yazi-scheduler/src/task.rs | 12 +- yazi-shared/src/data/data.rs | 43 +- yazi-shared/src/errors/mod.rs | 1 - yazi-shared/src/errors/peek.rs | 34 -- yazi-shared/src/event/action.rs | 14 + yazi-shared/src/event/cow.rs | 8 + yazi-shared/src/id.rs | 4 +- yazi-shared/src/lib.rs | 2 +- yazi-shared/src/pool/symbol.rs | 4 + yazi-shared/src/url/cov.rs | 14 +- yazi-vfs/src/provider/sftp/conn.rs | 23 +- yazi-watcher/Cargo.toml | 1 - yazi-watcher/src/backend.rs | 4 +- yazi-watcher/src/lib.rs | 2 +- yazi-watcher/src/proxy.rs | 21 + yazi-watcher/src/remote/remote.rs | 3 +- 219 files changed, 2916 insertions(+), 2414 deletions(-) create mode 100644 yazi-core/src/app/mod.rs create mode 100644 yazi-core/src/app/quit.rs create mode 100644 yazi-core/src/cmp/item.rs create mode 100644 yazi-core/src/cmp/opt.rs rename {yazi-plugin/src/external => yazi-core/src}/highlighter.rs (96%) create mode 100644 yazi-core/src/mgr/displace.rs create mode 100644 yazi-core/src/mgr/filter.rs create mode 100644 yazi-core/src/mgr/find.rs create mode 100644 yazi-core/src/mgr/open.rs create mode 100644 yazi-core/src/mgr/search.rs create mode 100644 yazi-core/src/notify/level.rs create mode 100644 yazi-core/src/notify/opt.rs create mode 100644 yazi-core/src/proxy.rs create mode 100644 yazi-core/src/spot/lock.rs create mode 100644 yazi-core/src/tab/preview_lock.rs create mode 100644 yazi-core/src/which/opt.rs delete mode 100644 yazi-dds/src/spark/spark.rs rename {yazi-dds => yazi-parser}/src/macros.rs (100%) rename {yazi-dds => yazi-parser}/src/spark/kind.rs (100%) rename {yazi-dds => yazi-parser}/src/spark/mod.rs (100%) create mode 100644 yazi-parser/src/spark/spark.rs delete mode 100644 yazi-plugin/src/isolate/entry.rs delete mode 100644 yazi-plugin/src/isolate/fetch.rs delete mode 100644 yazi-plugin/src/isolate/mod.rs delete mode 100644 yazi-plugin/src/isolate/peek.rs delete mode 100644 yazi-plugin/src/isolate/preload.rs delete mode 100644 yazi-plugin/src/isolate/seek.rs delete mode 100644 yazi-plugin/src/isolate/spot.rs rename yazi-plugin/src/{isolate/isolate.rs => slim.rs} (77%) rename yazi-plugin/src/{lua.rs => standard.rs} (88%) create mode 100644 yazi-runner/Cargo.toml create mode 100644 yazi-runner/README.md create mode 100644 yazi-runner/src/entry.rs create mode 100644 yazi-runner/src/fetcher/fetcher.rs create mode 100644 yazi-runner/src/fetcher/job.rs create mode 100644 yazi-runner/src/fetcher/mod.rs create mode 100644 yazi-runner/src/fetcher/state.rs create mode 100644 yazi-runner/src/lib.rs rename {yazi-plugin => yazi-runner}/src/loader/chunk.rs (100%) rename {yazi-plugin => yazi-runner}/src/loader/loader.rs (100%) rename {yazi-plugin => yazi-runner}/src/loader/mod.rs (53%) rename {yazi-plugin => yazi-runner}/src/loader/require.rs (100%) create mode 100644 yazi-runner/src/plugin/mod.rs create mode 100644 yazi-runner/src/plugin/option.rs create mode 100644 yazi-runner/src/preload.rs create mode 100644 yazi-runner/src/previewer/error.rs create mode 100644 yazi-runner/src/previewer/job.rs create mode 100644 yazi-runner/src/previewer/mod.rs create mode 100644 yazi-runner/src/previewer/previewer.rs create mode 100644 yazi-runner/src/runner.rs create mode 100644 yazi-runner/src/spot.rs create mode 100644 yazi-scheduler/src/process/opt.rs create mode 100644 yazi-scheduler/src/proxy.rs create mode 100644 yazi-scheduler/src/summary.rs delete mode 100644 yazi-shared/src/errors/mod.rs delete mode 100644 yazi-shared/src/errors/peek.rs create mode 100644 yazi-watcher/src/proxy.rs diff --git a/Cargo.lock b/Cargo.lock index 5c275da5..889aa490 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -197,7 +197,7 @@ checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" dependencies = [ "base64ct", "blake2", - "cpufeatures", + "cpufeatures 0.2.17", "password-hash", ] @@ -433,7 +433,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" dependencies = [ - "hybrid-array", + "hybrid-array 0.4.8", ] [[package]] @@ -541,9 +541,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.57" +version = "1.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" dependencies = [ "find-msvc-tools", "jobserver", @@ -571,7 +571,7 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -757,17 +757,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core-models" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0940496e5c83c54f3b753d5317daec82e8edac71c33aaa1f666d76f518de2444" -dependencies = [ - "hax-lib", - "pastey", - "rand 0.9.2", -] - [[package]] name = "core2" version = "0.4.0" @@ -786,6 +775,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -924,7 +922,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" dependencies = [ - "hybrid-array", + "hybrid-array 0.4.8", ] [[package]] @@ -973,7 +971,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", @@ -1800,43 +1798,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "hax-lib" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86" -dependencies = [ - "hax-lib-macros", - "num-bigint", - "num-traits", -] - -[[package]] -name = "hax-lib-macros" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1" -dependencies = [ - "hax-lib-macros-types", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "hax-lib-macros-types" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_json", - "uuid", -] - [[package]] name = "heck" version = "0.5.0" @@ -1879,6 +1840,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hybrid-array" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9" +dependencies = [ + "typenum", +] + [[package]] name = "hybrid-array" version = "0.4.8" @@ -2059,7 +2029,7 @@ dependencies = [ "rsa", "sec1", "sha1 0.10.6", - "sha1 0.11.0-rc.5", + "sha1 0.11.0", "sha2 0.10.9", "signature 2.2.0", "signature 3.0.0-rc.6", @@ -2113,10 +2083,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -2132,6 +2104,25 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + +[[package]] +name = "kem" +version = "0.3.0-pre.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8645470337db67b01a7f966decf7d0bafedbae74147d33e641c67a91df239f" +dependencies = [ + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "kqueue" version = "1.1.1" @@ -2185,72 +2176,6 @@ version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" -[[package]] -name = "libcrux-intrinsics" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9ee7ef66569dd7516454fe26de4e401c0c62073929803486b96744594b9632" -dependencies = [ - "core-models", - "hax-lib", -] - -[[package]] -name = "libcrux-ml-kem" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6a88086bf11bd2ec90926c749c4a427f2e59841437dbdede8cde8a96334ab" -dependencies = [ - "hax-lib", - "libcrux-intrinsics", - "libcrux-platform", - "libcrux-secrets", - "libcrux-sha3", - "libcrux-traits", - "rand 0.9.2", - "tls_codec", -] - -[[package]] -name = "libcrux-platform" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db82d058aa76ea315a3b2092f69dfbd67ddb0e462038a206e1dcd73f058c0778" -dependencies = [ - "libc", -] - -[[package]] -name = "libcrux-secrets" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4dbbf6bc9f2bc0f20dc3bea3e5c99adff3bdccf6d2a40488963da69e2ec307" -dependencies = [ - "hax-lib", -] - -[[package]] -name = "libcrux-sha3" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2400bec764d1c75b8a496d5747cffe32f1fb864a12577f0aca2f55a92021c962" -dependencies = [ - "hax-lib", - "libcrux-intrinsics", - "libcrux-platform", - "libcrux-traits", -] - -[[package]] -name = "libcrux-traits" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9adfd58e79d860f6b9e40e35127bfae9e5bd3ade33201d1347459011a2add034" -dependencies = [ - "libcrux-secrets", - "rand 0.9.2", -] - [[package]] name = "libfuzzer-sys" version = "0.4.12" @@ -2278,9 +2203,9 @@ dependencies = [ [[package]] name = "line-clipping" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" +checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" dependencies = [ "bitflags 2.11.0", ] @@ -2428,9 +2353,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "log", @@ -2438,6 +2363,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "ml-kem" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de49b3df74c35498c0232031bb7e85f9389f913e2796169c8ab47a53993a18f" +dependencies = [ + "hybrid-array 0.2.3", + "kem", + "rand_core 0.6.4", + "sha3", +] + [[package]] name = "mlua" version = "0.11.6" @@ -2789,9 +2726,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" +checksum = "0218004a4aae742209bee9c3cef05672f6b2708be36a50add8eb613b1f2a4008" dependencies = [ "num-traits", "rand 0.8.5", @@ -3151,7 +3088,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] @@ -3163,7 +3100,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] @@ -3283,7 +3220,7 @@ dependencies = [ "bytemuck", "libm", "num-traits", - "ordered-float 5.1.0", + "ordered-float 5.2.0", "palette", "ref-cast", "wide", @@ -3428,7 +3365,7 @@ dependencies = [ "kasuari", "lru", "serde", - "strum", + "strum 0.27.2", "thiserror 2.0.18", "unicode-segmentation", "unicode-truncate", @@ -3493,7 +3430,7 @@ dependencies = [ "line-clipping", "ratatui-core", "serde", - "strum", + "strum 0.27.2", "time", "unicode-segmentation", "unicode-width", @@ -3680,7 +3617,7 @@ dependencies = [ "pkcs1", "pkcs8 0.11.0-rc.11", "rand_core 0.10.0-rc-3", - "sha2 0.11.0-rc.5", + "sha2 0.11.0", "signature 3.0.0-rc.6", "spki 0.8.0-rc.4", "zeroize", @@ -3688,9 +3625,9 @@ dependencies = [ [[package]] name = "russh" -version = "0.58.0" +version = "0.58.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30f6ce4f5d5105b934cfb4b8b3028aab4d5dcdff863cb8dda9edd06d39b8c4e8" +checksum = "68d53bd2e1d6c49e32ae183c09bdc710ace41e7c8c564cc8a2286aad3bffe10d" dependencies = [ "aes", "bitflags 2.11.0", @@ -3715,9 +3652,9 @@ dependencies = [ "hmac", "inout", "internal-russh-forked-ssh-key", - "libcrux-ml-kem", "log", "md5", + "ml-kem", "num-bigint", "p256", "p384", @@ -4027,18 +3964,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] [[package]] name = "sha1" -version = "0.11.0-rc.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b167252f3c126be0d8926639c4c4706950f01445900c4b3db0fd7e89fcb750a" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.3.0", "digest 0.11.2", ] @@ -4049,21 +3986,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] [[package]] name = "sha2" -version = "0.11.0-rc.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.3.0", "digest 0.11.2", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -4154,9 +4101,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simd_helpers" @@ -4280,7 +4227,16 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros", + "strum_macros 0.27.2", +] + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros 0.28.0", ] [[package]] @@ -4295,6 +4251,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "subtle" version = "2.6.1" @@ -4540,27 +4508,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tls_codec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" -dependencies = [ - "tls_codec_derive", - "zeroize", -] - -[[package]] -name = "tls_codec_derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "tokio" version = "1.50.0" @@ -4784,9 +4731,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da36089a805484bcccfffe0739803392c8298778a2d2f09febf76fac5ad9025b" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-truncate" @@ -4841,9 +4788,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "atomic", "getrandom 0.4.2", @@ -4969,9 +4916,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a" dependencies = [ "cfg-if", "once_cell", @@ -4982,23 +4929,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "2d1faf851e778dfa54db7cd438b70758eba9755cb47403f3496edd7c8fc212f0" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5006,9 +4949,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf" dependencies = [ "bumpalo", "proc-macro2", @@ -5019,9 +4962,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93" dependencies = [ "unicode-ident", ] @@ -5060,16 +5003,6 @@ dependencies = [ "semver", ] -[[package]] -name = "web-sys" -version = "0.3.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "weezl" version = "0.1.12" @@ -5665,6 +5598,7 @@ dependencies = [ "yazi-parser", "yazi-plugin", "yazi-proxy", + "yazi-runner", "yazi-scheduler", "yazi-shared", "yazi-term", @@ -5814,10 +5748,17 @@ version = "26.2.2" dependencies = [ "anyhow", "crossterm 0.29.0", + "dyn-clone", "hashbrown 0.16.1", "indexmap 2.13.0", + "mlua", + "ordered-float 5.2.0", "parking_lot", "ratatui", + "serde", + "serde_with", + "strum 0.28.0", + "syntect", "tokio", "tokio-stream", "tokio-util", @@ -5830,11 +5771,11 @@ dependencies = [ "yazi-emulator", "yazi-fs", "yazi-macro", - "yazi-parser", - "yazi-plugin", - "yazi-proxy", + "yazi-prebuilt", + "yazi-runner", "yazi-scheduler", "yazi-shared", + "yazi-shim", "yazi-vfs", "yazi-watcher", "yazi-widgets", @@ -5846,8 +5787,9 @@ version = "26.2.2" dependencies = [ "anyhow", "hashbrown 0.16.1", + "indexmap 2.13.0", "mlua", - "ordered-float 5.1.0", + "ordered-float 5.2.0", "parking_lot", "paste", "serde", @@ -5861,7 +5803,6 @@ dependencies = [ "yazi-boot", "yazi-fs", "yazi-macro", - "yazi-parser", "yazi-shared", "yazi-widgets", ] @@ -5926,6 +5867,8 @@ dependencies = [ "yazi-parser", "yazi-plugin", "yazi-proxy", + "yazi-runner", + "yazi-scheduler", "yazi-shared", "yazi-term", "yazi-tty", @@ -5983,8 +5926,10 @@ dependencies = [ "crossterm 0.29.0", "dyn-clone", "hashbrown 0.16.1", + "indexmap 2.13.0", "mlua", - "ordered-float 5.1.0", + "ordered-float 5.2.0", + "paste", "ratatui", "serde", "serde_with", @@ -5992,8 +5937,12 @@ dependencies = [ "yazi-binding", "yazi-boot", "yazi-config", + "yazi-core", + "yazi-dds", "yazi-fs", "yazi-macro", + "yazi-runner", + "yazi-scheduler", "yazi-shared", "yazi-vfs", "yazi-widgets", @@ -6026,13 +5975,15 @@ dependencies = [ "yazi-binding", "yazi-boot", "yazi-config", + "yazi-core", "yazi-dds", "yazi-emulator", "yazi-fs", "yazi-macro", "yazi-parser", - "yazi-prebuilt", "yazi-proxy", + "yazi-runner", + "yazi-scheduler", "yazi-shared", "yazi-shim", "yazi-term", @@ -6050,14 +6001,41 @@ checksum = "33232d9116df6415ddfcdf72701b1b7439ce87f240a14723e8dd5d17e7ed5f98" name = "yazi-proxy" version = "26.2.2" dependencies = [ + "mlua", "tokio", + "yazi-binding", "yazi-config", + "yazi-core", "yazi-macro", - "yazi-parser", + "yazi-runner", + "yazi-scheduler", "yazi-shared", "yazi-widgets", ] +[[package]] +name = "yazi-runner" +version = "26.2.2" +dependencies = [ + "anyhow", + "dyn-clone", + "hashbrown 0.16.1", + "mlua", + "parking_lot", + "serde", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tracing", + "yazi-binding", + "yazi-boot", + "yazi-config", + "yazi-dds", + "yazi-fs", + "yazi-macro", + "yazi-shared", +] + [[package]] name = "yazi-scheduler" version = "26.2.2" @@ -6069,7 +6047,7 @@ dependencies = [ "libc", "lru", "mlua", - "ordered-float 5.1.0", + "ordered-float 5.2.0", "parking_lot", "serde", "tokio", @@ -6080,9 +6058,7 @@ dependencies = [ "yazi-dds", "yazi-fs", "yazi-macro", - "yazi-parser", - "yazi-plugin", - "yazi-proxy", + "yazi-runner", "yazi-shared", "yazi-term", "yazi-vfs", @@ -6112,7 +6088,7 @@ dependencies = [ "hashbrown 0.16.1", "libc", "memchr", - "ordered-float 5.1.0", + "ordered-float 5.2.0", "parking_lot", "percent-encoding", "serde", @@ -6202,7 +6178,6 @@ dependencies = [ "yazi-dds", "yazi-fs", "yazi-macro", - "yazi-proxy", "yazi-shared", "yazi-vfs", ] @@ -6253,20 +6228,6 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] [[package]] name = "zmij" @@ -6291,9 +6252,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7a1c0af6e5d8d1363f4994b7a091ccf963d8b694f7da5b0b9cceb82da2c0a6" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index 4b423321..47a2026c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,18 +55,19 @@ libc = "0.2.183" lru = "0.16.3" mlua = { version = "0.11.6", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] } objc2 = "0.6.4" -ordered-float = { version = "5.1.0", features = [ "serde" ] } +ordered-float = { version = "5.2.0", features = [ "serde" ] } parking_lot = "0.12.5" paste = "1.0.15" percent-encoding = "2.3.2" rand = { version = "0.9.2", default-features = false, features = [ "os_rng", "small_rng", "std" ] } ratatui = { version = "0.30.0", features = [ "serde", "unstable-rendered-line-info", "unstable-widget-ref" ] } regex = "1.12.3" -russh = { version = "0.58.0", default-features = false, features = [ "ring", "rsa" ] } +russh = { version = "0.58.1", default-features = false, features = [ "ring", "rsa" ] } scopeguard = "1.2.0" serde = { version = "1.0.228", features = [ "derive" ] } serde_json = "1.0.149" serde_with = "3.18.0" +strum = { version = "0.28.0", features = [ "derive" ] } syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } thiserror = "2.0.18" tokio = { version = "1.50.0", features = [ "full" ] } diff --git a/cspell.json b/cspell.json index 098cad59..3fc3d927 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","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","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","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","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike"],"flagWords":[],"version":"0.2"} \ No newline at end of file +{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","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","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","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","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr"],"flagWords":[],"language":"en","version":"0.2"} \ No newline at end of file diff --git a/yazi-actor/Cargo.toml b/yazi-actor/Cargo.toml index 41b869fe..14c4fc12 100644 --- a/yazi-actor/Cargo.toml +++ b/yazi-actor/Cargo.toml @@ -28,6 +28,7 @@ yazi-macro = { path = "../yazi-macro", version = "26.2.2" } yazi-parser = { path = "../yazi-parser", version = "26.2.2" } yazi-plugin = { path = "../yazi-plugin", version = "26.2.2" } yazi-proxy = { path = "../yazi-proxy", version = "26.2.2" } +yazi-runner = { path = "../yazi-runner", version = "26.2.2" } yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" } yazi-shared = { path = "../yazi-shared", version = "26.2.2" } yazi-term = { path = "../yazi-term", version = "26.2.2" } diff --git a/yazi-actor/src/actor.rs b/yazi-actor/src/actor.rs index ed8e15ef..a09cbfd9 100644 --- a/yazi-actor/src/actor.rs +++ b/yazi-actor/src/actor.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use yazi_dds::spark::SparkKind; +use yazi_parser::spark::SparkKind; use yazi_shared::data::Data; use crate::Ctx; diff --git a/yazi-actor/src/app/deprecate.rs b/yazi-actor/src/app/deprecate.rs index 4f24b9b7..712863cf 100644 --- a/yazi-actor/src/app/deprecate.rs +++ b/yazi-actor/src/app/deprecate.rs @@ -1,6 +1,7 @@ use anyhow::Result; +use yazi_core::notify::{MessageLevel, MessageOpt}; use yazi_macro::act; -use yazi_parser::{app::DeprecateOpt, notify::{PushLevel, PushOpt}}; +use yazi_parser::app::DeprecateOpt; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -13,10 +14,10 @@ impl Actor for Deprecate { const NAME: &str = "deprecate"; fn act(cx: &mut Ctx, opt: Self::Options) -> Result { - act!(notify:push, cx, PushOpt { + act!(notify:push, cx, MessageOpt { title: "Deprecated API".to_owned(), content: opt.content.into_owned(), - level: PushLevel::Warn, + level: MessageLevel::Warn, timeout: std::time::Duration::from_secs(20), }) } diff --git a/yazi-actor/src/app/plugin.rs b/yazi-actor/src/app/plugin.rs index 0bb77a82..c3437dc4 100644 --- a/yazi-actor/src/app/plugin.rs +++ b/yazi-actor/src/app/plugin.rs @@ -1,8 +1,9 @@ use anyhow::Result; use yazi_macro::{act, succ}; -use yazi_parser::app::{PluginMode, PluginOpt}; -use yazi_plugin::loader::LOADER; -use yazi_proxy::{AppProxy, NotifyProxy}; +use yazi_parser::app::PluginForm; +use yazi_proxy::AppProxy; +use yazi_runner::{loader::LOADER, plugin::PluginMode}; +use yazi_scheduler::NotifyProxy; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -10,11 +11,11 @@ use crate::{Actor, Ctx}; pub struct Plugin; impl Actor for Plugin { - type Options = PluginOpt; + type Options = PluginForm; const NAME: &str = "plugin"; - fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { mut opt }: Self::Options) -> Result { let mut hits = false; if let Some(chunk) = LOADER.read().get(&*opt.id) { hits = true; @@ -30,7 +31,7 @@ impl Actor for Plugin { tokio::spawn(async move { match LOADER.ensure(&opt.id, |_| ()).await { Ok(()) => AppProxy::plugin_do(opt), - Err(e) => NotifyProxy::push_error("Plugin load failed", e), + Err(e) => NotifyProxy::push_error("Plugin load failed", e.to_string()), } }); succ!(); diff --git a/yazi-actor/src/app/plugin_do.rs b/yazi-actor/src/app/plugin_do.rs index 5c29c281..204d7590 100644 --- a/yazi-actor/src/app/plugin_do.rs +++ b/yazi-actor/src/app/plugin_do.rs @@ -5,9 +5,10 @@ use tracing::{error, warn}; use yazi_binding::runtime_mut; use yazi_dds::Sendable; use yazi_macro::succ; -use yazi_parser::app::{PluginMode, PluginOpt}; -use yazi_plugin::{LUA, loader::{LOADER, Loader}}; -use yazi_proxy::NotifyProxy; +use yazi_parser::app::PluginForm; +use yazi_plugin::LUA; +use yazi_runner::{loader::{LOADER, Loader}, plugin::PluginMode}; +use yazi_scheduler::NotifyProxy; use yazi_shared::data::Data; use crate::{Actor, Ctx, lives::Lives}; @@ -15,18 +16,18 @@ use crate::{Actor, Ctx, lives::Lives}; pub struct PluginDo; impl Actor for PluginDo { - type Options = PluginOpt; + type Options = PluginForm; const NAME: &str = "plugin_do"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { let loader = LOADER.read(); let Some(chunk) = loader.get(&*opt.id) else { succ!(warn!("plugin `{}` not found", opt.id)); }; if let Err(e) = Loader::compatible_or_error(&opt.id, chunk) { - succ!(NotifyProxy::push_error("Incompatible plugin", e)); + succ!(NotifyProxy::push_error("Incompatible plugin", e.to_string())); } if opt.mode.auto_then(chunk.sync_entry) != PluginMode::Sync { diff --git a/yazi-actor/src/app/quit.rs b/yazi-actor/src/app/quit.rs index 98d61e19..db14a7ba 100644 --- a/yazi-actor/src/app/quit.rs +++ b/yazi-actor/src/app/quit.rs @@ -1,7 +1,7 @@ use anyhow::Result; use yazi_boot::ARGS; use yazi_fs::provider::{Provider, local::Local}; -use yazi_parser::app::QuitOpt; +use yazi_parser::app::QuitForm; use yazi_shared::{data::Data, strand::{StrandBuf, StrandLike, ToStrand}}; use yazi_term::Term; @@ -10,11 +10,11 @@ use crate::{Actor, Ctx}; pub struct Quit; impl Actor for Quit { - type Options = QuitOpt; + type Options = QuitForm; const NAME: &str = "quit"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { cx.tasks.shutdown(); cx.mgr.shutdown(); diff --git a/yazi-actor/src/app/title.rs b/yazi-actor/src/app/title.rs index 31387205..c3fac5a2 100644 --- a/yazi-actor/src/app/title.rs +++ b/yazi-actor/src/app/title.rs @@ -1,9 +1,8 @@ use anyhow::Result; use crossterm::{execute, terminal::SetTitle}; use yazi_actor::Ctx; -use yazi_dds::spark::SparkKind; use yazi_macro::succ; -use yazi_parser::app::TitleOpt; +use yazi_parser::{app::TitleOpt, spark::SparkKind}; use yazi_shared::{Source, data::Data}; use yazi_term::TermState; use yazi_tty::TTY; diff --git a/yazi-actor/src/cmp/show.rs b/yazi-actor/src/cmp/show.rs index e9c17593..b6671e39 100644 --- a/yazi-actor/src/cmp/show.rs +++ b/yazi-actor/src/cmp/show.rs @@ -1,8 +1,9 @@ use std::{mem, ops::ControlFlow}; use anyhow::Result; +use yazi_core::cmp::CmpItem; use yazi_macro::{render, succ}; -use yazi_parser::cmp::{CmpItem, ShowOpt}; +use yazi_parser::cmp::ShowForm; use yazi_shared::{data::Data, path::{AsPath, PathDyn}, strand::StrandLike}; use crate::{Actor, Ctx}; @@ -12,11 +13,11 @@ const LIMIT: usize = 30; pub struct Show; impl Actor for Show { - type Options = ShowOpt; + type Options = ShowForm; const NAME: &str = "show"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { let cmp = &mut cx.cmp; if cmp.ticket != opt.ticket { succ!(); diff --git a/yazi-actor/src/cmp/trigger.rs b/yazi-actor/src/cmp/trigger.rs index d79d036b..6d0ff3d7 100644 --- a/yazi-actor/src/cmp/trigger.rs +++ b/yazi-actor/src/cmp/trigger.rs @@ -1,9 +1,10 @@ use std::{io, mem}; use anyhow::Result; +use yazi_core::cmp::{CmpItem, CmpOpt}; use yazi_fs::{path::clean_url, provider::{DirReader, FileHolder}}; use yazi_macro::{act, render, succ}; -use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt}; +use yazi_parser::cmp::TriggerOpt; use yazi_proxy::CmpProxy; use yazi_shared::{AnyAsciiChar, BytePredictor, data::Data, natsort, path::{AsPath, PathBufDyn, PathLike}, scheme::{SchemeCow, SchemeLike}, strand::{AsStrand, StrandLike}, url::{UrlBuf, UrlCow, UrlLike}}; use yazi_vfs::provider; @@ -31,7 +32,7 @@ impl Actor for Trigger { let ticket = cx.cmp.ticket; if cx.cmp.caches.contains_key(&parent) { - return act!(cmp:show, cx, ShowOpt { cache: vec![], cache_name: parent, word, ticket }); + return act!(cmp:show, cx, CmpOpt { cache: vec![], cache_name: parent, word, ticket }); } cx.cmp.handle = Some(tokio::spawn(async move { @@ -53,7 +54,7 @@ impl Actor for Trigger { if !cache.is_empty() { cache .sort_unstable_by(|a, b| natsort(a.name.encoded_bytes(), b.name.encoded_bytes(), false)); - CmpProxy::show(ShowOpt { cache, cache_name: parent, word, ticket }); + CmpProxy::show(CmpOpt { cache, cache_name: parent, word, ticket }); } Ok::<_, io::Error>(()) diff --git a/yazi-actor/src/context.rs b/yazi-actor/src/context.rs index 2c460256..24cd8bc1 100644 --- a/yazi-actor/src/context.rs +++ b/yazi-actor/src/context.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut}; use anyhow::{Result, anyhow}; -use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}, tasks::Tasks}; +use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}}; use yazi_fs::File; use yazi_shared::{Id, Source, event::Action, url::UrlBuf}; use yazi_term::Term; @@ -119,9 +119,6 @@ impl<'a> Ctx<'a> { self.tab_mut().hovered_folder_mut() } - #[inline] - pub fn tasks(&self) -> &Tasks { &self.tasks } - #[inline] pub fn source(&self) -> Source { if self.level != 1 { Source::Ind } else { self.source } } } diff --git a/yazi-actor/src/core/preflight.rs b/yazi-actor/src/core/preflight.rs index b020bed9..51596aba 100644 --- a/yazi-actor/src/core/preflight.rs +++ b/yazi-actor/src/core/preflight.rs @@ -1,7 +1,8 @@ use anyhow::Result; use mlua::{ErrorContext, ExternalError, IntoLua, Value}; use yazi_binding::runtime_scope; -use yazi_dds::{LOCAL, spark::{Spark, SparkKind}}; +use yazi_dds::LOCAL; +use yazi_parser::spark::{Spark, SparkKind}; use yazi_plugin::LUA; use crate::{Ctx, lives::Lives}; diff --git a/yazi-actor/src/mgr/arrow.rs b/yazi-actor/src/mgr/arrow.rs index c2be4481..b6c60b9f 100644 --- a/yazi-actor/src/mgr/arrow.rs +++ b/yazi-actor/src/mgr/arrow.rs @@ -28,6 +28,7 @@ impl Actor for Arrow { act!(mgr:peek, cx)?; act!(mgr:watch, cx)?; + // cx.tasks.scheduler.batch.next(); // FIXME: clear user action succ!(render!()); } } diff --git a/yazi-actor/src/mgr/bulk_rename.rs b/yazi-actor/src/mgr/bulk_rename.rs index 2681b9e6..fa7b6977 100644 --- a/yazi-actor/src/mgr/bulk_rename.rs +++ b/yazi-actor/src/mgr/bulk_rename.rs @@ -11,7 +11,8 @@ use yazi_dds::Pubsub; use yazi_fs::{File, FilesOp, Splatter, max_common_root, path::skip_url, provider::{FileBuilder, Provider, local::{Gate, Local}}}; use yazi_macro::{err, succ}; use yazi_parser::VoidOpt; -use yazi_proxy::{AppProxy, NotifyProxy, TasksProxy}; +use yazi_proxy::TasksProxy; +use yazi_scheduler::{AppProxy, NotifyProxy}; use yazi_shared::{data::Data, path::PathDyn, strand::{AsStrand, AsStrandJoin, Strand, StrandBuf, StrandLike}, terminal_clear, url::{AsUrl, UrlBuf, UrlCow, UrlLike}}; use yazi_term::YIELD_TO_SUBPROCESS; use yazi_tty::TTY; diff --git a/yazi-actor/src/mgr/close.rs b/yazi-actor/src/mgr/close.rs index 2e0620b2..debe3aa7 100644 --- a/yazi-actor/src/mgr/close.rs +++ b/yazi-actor/src/mgr/close.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_macro::act; -use yazi_parser::mgr::CloseOpt; +use yazi_parser::mgr::CloseForm; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -8,15 +8,15 @@ use crate::{Actor, Ctx}; pub struct Close; impl Actor for Close { - type Options = CloseOpt; + type Options = CloseForm; const NAME: &str = "close"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { if cx.tabs().len() > 1 { act!(mgr:tab_close, cx, cx.tabs().cursor) } else { - act!(mgr:quit, cx, opt.0) + act!(mgr:quit, cx, opt) } } } diff --git a/yazi-actor/src/mgr/displace.rs b/yazi-actor/src/mgr/displace.rs index 6469ea73..82118dae 100644 --- a/yazi-actor/src/mgr/displace.rs +++ b/yazi-actor/src/mgr/displace.rs @@ -1,6 +1,7 @@ use anyhow::Result; +use yazi_core::mgr::DisplaceOpt; use yazi_macro::succ; -use yazi_parser::{VoidOpt, mgr::DisplaceDoOpt}; +use yazi_parser::VoidOpt; use yazi_proxy::MgrProxy; use yazi_shared::{data::Data, url::UrlLike}; use yazi_vfs::provider; @@ -22,7 +23,7 @@ impl Actor for Displace { let tab = cx.tab().id; let from = cx.cwd().to_owned(); tokio::spawn(async move { - MgrProxy::displace_do(tab, DisplaceDoOpt { + MgrProxy::displace_do(tab, DisplaceOpt { to: provider::canonicalize(&from).await.map_err(Into::into), from, }); diff --git a/yazi-actor/src/mgr/displace_do.rs b/yazi-actor/src/mgr/displace_do.rs index 3564c61c..2e488ad2 100644 --- a/yazi-actor/src/mgr/displace_do.rs +++ b/yazi-actor/src/mgr/displace_do.rs @@ -1,7 +1,7 @@ use anyhow::{Result, bail}; use yazi_fs::FilesOp; use yazi_macro::{act, succ}; -use yazi_parser::mgr::{CdSource, DisplaceDoOpt}; +use yazi_parser::mgr::{CdSource, DisplaceDoForm}; use yazi_shared::{data::Data, url::UrlLike}; use crate::{Actor, Ctx}; @@ -9,11 +9,11 @@ use crate::{Actor, Ctx}; pub struct DisplaceDo; impl Actor for DisplaceDo { - type Options = DisplaceDoOpt; + type Options = DisplaceDoForm; const NAME: &str = "displace_do"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { if cx.cwd() != opt.from { succ!() } diff --git a/yazi-actor/src/mgr/download.rs b/yazi-actor/src/mgr/download.rs index 8fcb2640..47b62433 100644 --- a/yazi-actor/src/mgr/download.rs +++ b/yazi-actor/src/mgr/download.rs @@ -3,9 +3,10 @@ use std::{mem, time::{Duration, Instant}}; use anyhow::Result; use futures::{StreamExt, stream::FuturesUnordered}; use hashbrown::HashSet; +use yazi_core::mgr::OpenOpt; use yazi_fs::{File, FsScheme, provider::{Provider, local::Local}}; use yazi_macro::succ; -use yazi_parser::mgr::{DownloadOpt, OpenOpt}; +use yazi_parser::mgr::DownloadOpt; use yazi_proxy::MgrProxy; use yazi_shared::{data::Data, url::{UrlCow, UrlLike}}; use yazi_vfs::VfsFile; diff --git a/yazi-actor/src/mgr/escape.rs b/yazi-actor/src/mgr/escape.rs index d0b3db95..fae730a3 100644 --- a/yazi-actor/src/mgr/escape.rs +++ b/yazi-actor/src/mgr/escape.rs @@ -1,7 +1,7 @@ use anyhow::{Result, bail}; use yazi_macro::{act, render, render_and, succ}; use yazi_parser::{VoidOpt, mgr::EscapeOpt}; -use yazi_proxy::NotifyProxy; +use yazi_scheduler::NotifyProxy; use yazi_shared::{data::Data, url::UrlLike}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/filter.rs b/yazi-actor/src/mgr/filter.rs index fd4b3eed..7c590819 100644 --- a/yazi-actor/src/mgr/filter.rs +++ b/yazi-actor/src/mgr/filter.rs @@ -4,8 +4,9 @@ use anyhow::Result; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_config::popup::InputCfg; +use yazi_core::mgr::FilterOpt; use yazi_macro::succ; -use yazi_parser::mgr::FilterOpt; +use yazi_parser::mgr::FilterForm; use yazi_proxy::{InputProxy, MgrProxy}; use yazi_shared::{Debounce, data::Data}; use yazi_widgets::input::InputEvent; @@ -15,11 +16,11 @@ use crate::{Actor, Ctx}; pub struct Filter; impl Actor for Filter { - type Options = FilterOpt; + type Options = FilterForm; const NAME: &str = "filter"; - fn act(_: &mut Ctx, opt: Self::Options) -> Result { + fn act(_: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { let input = InputProxy::show(InputCfg::filter()); tokio::spawn(async move { diff --git a/yazi-actor/src/mgr/filter_do.rs b/yazi-actor/src/mgr/filter_do.rs index 37062d6c..d0e97db5 100644 --- a/yazi-actor/src/mgr/filter_do.rs +++ b/yazi-actor/src/mgr/filter_do.rs @@ -1,7 +1,7 @@ use anyhow::Result; use yazi_fs::Filter; use yazi_macro::{act, render, succ}; -use yazi_parser::mgr::FilterOpt; +use yazi_parser::mgr::FilterForm; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -9,11 +9,11 @@ use crate::{Actor, Ctx}; pub struct FilterDo; impl Actor for FilterDo { - type Options = FilterOpt; + type Options = FilterForm; const NAME: &str = "filter_do"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { let filter = if opt.query.is_empty() { None } else { Some(Filter::new(&opt.query, opt.case)?) }; let hovered = cx.hovered().map(|f| f.urn().into()); diff --git a/yazi-actor/src/mgr/find.rs b/yazi-actor/src/mgr/find.rs index 49c8f72a..0f555160 100644 --- a/yazi-actor/src/mgr/find.rs +++ b/yazi-actor/src/mgr/find.rs @@ -4,8 +4,9 @@ use anyhow::Result; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_config::popup::InputCfg; +use yazi_core::mgr::FindDoOpt; use yazi_macro::succ; -use yazi_parser::mgr::{FindDoOpt, FindOpt}; +use yazi_parser::mgr::FindOpt; use yazi_proxy::{InputProxy, MgrProxy}; use yazi_shared::{Debounce, data::Data}; use yazi_widgets::input::InputEvent; diff --git a/yazi-actor/src/mgr/find_do.rs b/yazi-actor/src/mgr/find_do.rs index 296c09f6..5e4053c2 100644 --- a/yazi-actor/src/mgr/find_do.rs +++ b/yazi-actor/src/mgr/find_do.rs @@ -1,7 +1,7 @@ use anyhow::Result; use yazi_core::tab::Finder; use yazi_macro::{act, render, succ}; -use yazi_parser::mgr::FindDoOpt; +use yazi_parser::mgr::FindDoForm; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -9,11 +9,11 @@ use crate::{Actor, Ctx}; pub struct FindDo; impl Actor for FindDo { - type Options = FindDoOpt; + type Options = FindDoForm; const NAME: &str = "find_do"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { if opt.query.is_empty() { return act!(mgr:escape_find, cx); } diff --git a/yazi-actor/src/mgr/hidden.rs b/yazi-actor/src/mgr/hidden.rs index 1553833e..fc202a26 100644 --- a/yazi-actor/src/mgr/hidden.rs +++ b/yazi-actor/src/mgr/hidden.rs @@ -1,9 +1,8 @@ use anyhow::Result; use yazi_core::tab::Folder; -use yazi_dds::spark::SparkKind; use yazi_fs::FolderStage; use yazi_macro::{act, render, render_and, succ}; -use yazi_parser::mgr::HiddenOpt; +use yazi_parser::{mgr::HiddenOpt, spark::SparkKind}; use yazi_shared::{Source, data::Data}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/hover.rs b/yazi-actor/src/mgr/hover.rs index bb16eab4..579d4003 100644 --- a/yazi-actor/src/mgr/hover.rs +++ b/yazi-actor/src/mgr/hover.rs @@ -22,18 +22,20 @@ impl Actor for Hover { } // Repos CWD - tab.current.repos(opt.urn.as_ref().map(Into::into)); + render!(tab.current.repos(opt.urn.as_ref().map(Into::into))); // Turn on tracing if let (Some(h), Some(u)) = (tab.hovered(), opt.urn) && h.urn() == u { // `hover(Some)` occurs after user actions, such as create, rename, reveal, etc. - // At this point, it's intuitive to track the location of the file regardless. + // At this point, it's intuitive to track the file location regardless. tab.current.trace = Some(u.clone()); + // cx.tasks.scheduler.batch.next();// FIXME: clear user action } // Publish through DDS + let tab = tab!(cx); err!(Pubsub::pub_after_hover(tab.id, tab.hovered().map(|h| &h.url))); succ!(); } diff --git a/yazi-actor/src/mgr/open.rs b/yazi-actor/src/mgr/open.rs index 2e47e45b..0ccb6d16 100644 --- a/yazi-actor/src/mgr/open.rs +++ b/yazi-actor/src/mgr/open.rs @@ -1,8 +1,9 @@ use anyhow::Result; use yazi_boot::ARGS; +use yazi_core::mgr::OpenDoOpt; use yazi_fs::File; use yazi_macro::{act, succ}; -use yazi_parser::mgr::{OpenDoOpt, OpenOpt}; +use yazi_parser::mgr::OpenForm; use yazi_proxy::MgrProxy; use yazi_shared::data::Data; use yazi_vfs::VfsFile; @@ -12,11 +13,11 @@ use crate::{Actor, Ctx, mgr::Quit}; pub struct Open; impl Actor for Open { - type Options = OpenOpt; + type Options = OpenForm; const NAME: &str = "open"; - fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { mut opt }: Self::Options) -> Result { if !opt.interactive && ARGS.chooser_file.is_some() { succ!(if !opt.targets.is_empty() { Quit::with_selected(opt.targets) diff --git a/yazi-actor/src/mgr/open_do.rs b/yazi-actor/src/mgr/open_do.rs index 0a7cd1a7..84fba983 100644 --- a/yazi-actor/src/mgr/open_do.rs +++ b/yazi-actor/src/mgr/open_do.rs @@ -2,8 +2,9 @@ use anyhow::Result; use hashbrown::HashMap; use yazi_config::{YAZI, popup::PickCfg}; use yazi_macro::succ; -use yazi_parser::{mgr::OpenDoOpt, tasks::ProcessOpenOpt}; +use yazi_parser::mgr::OpenDoForm; use yazi_proxy::{PickProxy, TasksProxy}; +use yazi_scheduler::process::ProcessOpt; use yazi_shared::{data::Data, url::UrlCow}; use crate::{Actor, Ctx}; @@ -11,11 +12,11 @@ use crate::{Actor, Ctx}; pub struct OpenDo; impl Actor for OpenDo { - type Options = OpenDoOpt; + type Options = OpenDoForm; const NAME: &str = "open_do"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { let targets: Vec<_> = opt .targets .into_iter() @@ -42,13 +43,12 @@ impl Actor for OpenDo { [UrlCow::default()].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect(); tokio::spawn(async move { if let Some(choice) = pick.await { - TasksProxy::open_shell_compat(ProcessOpenOpt { + TasksProxy::open_shell_compat(ProcessOpt { cwd: opt.cwd, cmd: openers[choice].run.clone().into(), args: urls, block: openers[choice].block, orphan: openers[choice].orphan, - done: None, spread: openers[choice].spread, }); } @@ -67,13 +67,12 @@ impl OpenDo { } } for (opener, args) in openers { - cx.tasks.open_shell_compat(ProcessOpenOpt { + cx.tasks.open_shell_compat(ProcessOpt { cwd: cwd.clone(), cmd: opener.run.clone().into(), args, block: opener.block, orphan: opener.orphan, - done: None, spread: opener.spread, }); } diff --git a/yazi-actor/src/mgr/paste.rs b/yazi-actor/src/mgr/paste.rs index 7cfc647a..a5f86496 100644 --- a/yazi-actor/src/mgr/paste.rs +++ b/yazi-actor/src/mgr/paste.rs @@ -20,7 +20,7 @@ impl Actor for Paste { if mgr.yanked.cut { cx.core.tasks.file_cut(&mgr.yanked, dest, opt.force); - mgr.tabs.iter_mut().for_each(|t| _ = t.selected.remove_many(mgr.yanked.iter())); + mgr.tabs.iter_mut().for_each(|t| _ = t.selected.remove_many(&*mgr.yanked)); act!(mgr:unyank, cx) } else { succ!(cx.core.tasks.file_copy(&mgr.yanked, dest, opt.force, opt.follow)); diff --git a/yazi-actor/src/mgr/quit.rs b/yazi-actor/src/mgr/quit.rs index 70e72eb8..25932cd4 100644 --- a/yazi-actor/src/mgr/quit.rs +++ b/yazi-actor/src/mgr/quit.rs @@ -3,9 +3,9 @@ use std::time::Duration; use anyhow::Result; use tokio::{select, time}; use yazi_config::popup::ConfirmCfg; -use yazi_dds::spark::SparkKind; +use yazi_core::app::QuitOpt; use yazi_macro::{act, succ}; -use yazi_parser::app::QuitOpt; +use yazi_parser::{app::QuitForm, spark::SparkKind}; use yazi_proxy::{AppProxy, ConfirmProxy}; use yazi_shared::{data::Data, strand::{Strand, StrandLike, ToStrandJoin}, url::AsUrl}; @@ -14,12 +14,12 @@ use crate::{Actor, Ctx}; pub struct Quit; impl Actor for Quit { - type Options = QuitOpt; + type Options = QuitForm; const NAME: &str = "quit"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { - let ongoing = cx.tasks().ongoing().clone(); + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { + let ongoing = cx.tasks.scheduler.ongoing.clone(); let (left, left_names) = { let ongoing = ongoing.lock(); (ongoing.len(), ongoing.values().take(11).map(|t| t.name.clone()).collect()) diff --git a/yazi-actor/src/mgr/refresh.rs b/yazi-actor/src/mgr/refresh.rs index 141cb55d..6c3e3017 100644 --- a/yazi-actor/src/mgr/refresh.rs +++ b/yazi-actor/src/mgr/refresh.rs @@ -3,9 +3,9 @@ use yazi_core::tab::Folder; use yazi_fs::{CWD, Files, FilesOp, cha::Cha}; use yazi_macro::{act, succ}; use yazi_parser::VoidOpt; -use yazi_proxy::MgrProxy; use yazi_shared::{data::Data, url::{UrlBuf, UrlLike}}; use yazi_vfs::{VfsFiles, VfsFilesOp}; +use yazi_watcher::MgrProxy; use crate::{Actor, Ctx}; @@ -29,7 +29,7 @@ impl Actor for Refresh { act!(mgr:watch, cx)?; act!(mgr:update_paged, cx)?; - cx.tasks().prework_sorted(&cx.current().files); + cx.tasks.prework_sorted(&cx.current().files); succ!(); } } diff --git a/yazi-actor/src/mgr/remove.rs b/yazi-actor/src/mgr/remove.rs index ee69f344..e2bdd265 100644 --- a/yazi-actor/src/mgr/remove.rs +++ b/yazi-actor/src/mgr/remove.rs @@ -59,11 +59,9 @@ impl Actor for RemoveDo { t.selected.remove_many(&opt.targets); }); - for u in &opt.targets { - mgr.yanked.remove(u); - } - + mgr.yanked.remove_many(&opt.targets); mgr.yanked.catchup_revision(false); + cx.tasks.file_remove(opt.targets, opt.permanently); succ!(); } diff --git a/yazi-actor/src/mgr/search.rs b/yazi-actor/src/mgr/search.rs index a6252a5d..e9d87e46 100644 --- a/yazi-actor/src/mgr/search.rs +++ b/yazi-actor/src/mgr/search.rs @@ -4,11 +4,13 @@ use anyhow::Result; use tokio::pin; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_config::popup::InputCfg; +use yazi_core::mgr::SearchVia; use yazi_fs::{FilesOp, cha::Cha}; use yazi_macro::{act, succ}; -use yazi_parser::{VoidOpt, mgr::{CdSource, SearchOpt, SearchOptVia}}; +use yazi_parser::{VoidOpt, mgr::{CdSource, SearchForm}}; use yazi_plugin::external; -use yazi_proxy::{InputProxy, MgrProxy, NotifyProxy}; +use yazi_proxy::{InputProxy, MgrProxy}; +use yazi_scheduler::NotifyProxy; use yazi_shared::{data::Data, url::{AsUrl, UrlLike}}; use yazi_widgets::input::InputEvent; @@ -17,11 +19,11 @@ use crate::{Actor, Ctx}; pub struct Search; impl Actor for Search { - type Options = SearchOpt; + type Options = SearchForm; const NAME: &str = "search"; - fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { mut opt }: Self::Options) -> Result { if let Some(handle) = cx.tab_mut().search.take() { handle.abort(); } @@ -43,11 +45,11 @@ impl Actor for Search { pub struct SearchDo; impl Actor for SearchDo { - type Options = SearchOpt; + type Options = SearchForm; const NAME: &str = "search_do"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt }: Self::Options) -> Result { let tab = cx.tab_mut(); if let Some(handle) = tab.search.take() { handle.abort(); @@ -61,19 +63,19 @@ impl Actor for SearchDo { tab.search = Some(tokio::spawn(async move { let rx = match opt.via { - SearchOptVia::Rg => external::rg(external::RgOpt { + SearchVia::Rg => external::rg(external::RgOpt { cwd: cwd.clone(), hidden, subject: opt.subject.into_owned(), args: opt.args, }), - SearchOptVia::Rga => external::rga(external::RgaOpt { + SearchVia::Rga => external::rga(external::RgaOpt { cwd: cwd.clone(), hidden, subject: opt.subject.into_owned(), args: opt.args, }), - SearchOptVia::Fd => external::fd(external::FdOpt { + SearchVia::Fd => external::fd(external::FdOpt { cwd: cwd.clone(), hidden, subject: opt.subject.into_owned(), diff --git a/yazi-actor/src/mgr/seek.rs b/yazi-actor/src/mgr/seek.rs index dc1f13f5..2822e89c 100644 --- a/yazi-actor/src/mgr/seek.rs +++ b/yazi-actor/src/mgr/seek.rs @@ -1,8 +1,9 @@ use anyhow::Result; +use mlua::ObjectLike; use yazi_config::YAZI; -use yazi_macro::succ; +use yazi_macro::{act, succ}; use yazi_parser::mgr::SeekOpt; -use yazi_plugin::isolate; +use yazi_runner::{plugin::PluginOpt, previewer::SeekJob}; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -27,7 +28,10 @@ impl Actor for Seek { succ!(cx.tab_mut().preview.reset()); }; - isolate::seek_sync(&previewer.run, hovered.clone(), opt.units); - succ!(); + let job = SeekJob { file: hovered.clone(), units: opt.units }; + let opt = PluginOpt::new_callback(&*previewer.run.name, move |_, plugin| { + plugin.call_method("seek", job) + }); + act!(app:plugin, cx, opt) } } diff --git a/yazi-actor/src/mgr/shell.rs b/yazi-actor/src/mgr/shell.rs index c7967ed7..dae4205c 100644 --- a/yazi-actor/src/mgr/shell.rs +++ b/yazi-actor/src/mgr/shell.rs @@ -3,8 +3,9 @@ use std::borrow::Cow; use anyhow::Result; use yazi_config::popup::InputCfg; use yazi_macro::{act, succ}; -use yazi_parser::{mgr::ShellOpt, tasks::ProcessOpenOpt}; +use yazi_parser::mgr::ShellOpt; use yazi_proxy::{InputProxy, TasksProxy}; +use yazi_scheduler::process::ProcessOpt; use yazi_shared::data::Data; use yazi_widgets::input::InputEvent; @@ -38,13 +39,12 @@ impl Actor for Shell { return; } - TasksProxy::open_shell_compat(ProcessOpenOpt { + TasksProxy::open_shell_compat(ProcessOpt { cwd: cwd.into(), cmd: opt.run.to_string().into(), args: selected, block: opt.block, orphan: opt.orphan, - done: None, spread: true, }); }); diff --git a/yazi-actor/src/mgr/sort.rs b/yazi-actor/src/mgr/sort.rs index 81b77676..f5da8a14 100644 --- a/yazi-actor/src/mgr/sort.rs +++ b/yazi-actor/src/mgr/sort.rs @@ -1,9 +1,8 @@ use anyhow::Result; use yazi_core::tab::Folder; -use yazi_dds::spark::SparkKind; use yazi_fs::{FilesSorter, FolderStage}; use yazi_macro::{act, render, render_and, succ}; -use yazi_parser::mgr::SortOpt; +use yazi_parser::{mgr::SortOpt, spark::SparkKind}; use yazi_shared::{Source, data::Data}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/stash.rs b/yazi-actor/src/mgr/stash.rs index 2dede4a5..f149809f 100644 --- a/yazi-actor/src/mgr/stash.rs +++ b/yazi-actor/src/mgr/stash.rs @@ -1,7 +1,6 @@ use anyhow::Result; -use yazi_dds::spark::SparkKind; use yazi_macro::succ; -use yazi_parser::mgr::StashOpt; +use yazi_parser::{mgr::StashOpt, spark::SparkKind}; use yazi_shared::{Source, data::Data, url::{AsUrl, UrlLike}}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/tab_create.rs b/yazi-actor/src/mgr/tab_create.rs index 848b176e..f5f78bab 100644 --- a/yazi-actor/src/mgr/tab_create.rs +++ b/yazi-actor/src/mgr/tab_create.rs @@ -2,7 +2,7 @@ use anyhow::Result; use yazi_core::tab::Tab; use yazi_macro::{act, render, succ}; use yazi_parser::mgr::{CdSource, TabCreateOpt}; -use yazi_proxy::NotifyProxy; +use yazi_scheduler::NotifyProxy; use yazi_shared::{data::Data, url::UrlLike}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/toggle.rs b/yazi-actor/src/mgr/toggle.rs index 8dd6b729..39314078 100644 --- a/yazi-actor/src/mgr/toggle.rs +++ b/yazi-actor/src/mgr/toggle.rs @@ -1,7 +1,7 @@ use anyhow::Result; use yazi_macro::{render_and, succ}; use yazi_parser::mgr::ToggleOpt; -use yazi_proxy::NotifyProxy; +use yazi_scheduler::NotifyProxy; use yazi_shared::{data::Data, url::UrlCow}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/toggle_all.rs b/yazi-actor/src/mgr/toggle_all.rs index 70b968cc..5e4b3acd 100644 --- a/yazi-actor/src/mgr/toggle_all.rs +++ b/yazi-actor/src/mgr/toggle_all.rs @@ -1,7 +1,7 @@ use anyhow::Result; use yazi_macro::{render, succ}; use yazi_parser::mgr::ToggleAllOpt; -use yazi_proxy::NotifyProxy; +use yazi_scheduler::NotifyProxy; use yazi_shared::data::Data; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/mgr/update_mimes.rs b/yazi-actor/src/mgr/update_mimes.rs index ecb89060..539e55fe 100644 --- a/yazi-actor/src/mgr/update_mimes.rs +++ b/yazi-actor/src/mgr/update_mimes.rs @@ -19,7 +19,7 @@ impl Actor for UpdateMimes { let updates = opt .updates .into_iter() - .flat_map(|(key, value)| key.into_url().zip(value.into_string())) + .flat_map(|(key, value)| key.into_url().zip(value.into_sstr())) .filter(|(url, mime)| cx.mgr.mimetype.get(url) != Some(mime)) .fold(HashMap::new(), |mut map, (u, m)| { for u in linked.from_file(u.as_url()) { diff --git a/yazi-actor/src/mgr/update_paged.rs b/yazi-actor/src/mgr/update_paged.rs index 90d501b3..f2188215 100644 --- a/yazi-actor/src/mgr/update_paged.rs +++ b/yazi-actor/src/mgr/update_paged.rs @@ -19,8 +19,8 @@ impl Actor for UpdatePaged { let targets = cx.current().paginate(opt.page.unwrap_or(cx.current().page)); if !targets.is_empty() { - cx.tasks().fetch_paged(targets, &cx.mgr.mimetype); - cx.tasks().preload_paged(targets, &cx.mgr.mimetype); + cx.tasks.fetch_paged(targets, &cx.mgr.mimetype); + cx.tasks.preload_paged(targets, &cx.mgr.mimetype); } succ!(); } diff --git a/yazi-actor/src/mgr/update_peeked.rs b/yazi-actor/src/mgr/update_peeked.rs index ab32a2e0..499e3669 100644 --- a/yazi-actor/src/mgr/update_peeked.rs +++ b/yazi-actor/src/mgr/update_peeked.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_macro::{render, succ}; -use yazi_parser::mgr::UpdatePeekedOpt; +use yazi_parser::mgr::UpdatePeekedForm; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -8,17 +8,17 @@ use crate::{Actor, Ctx}; pub struct UpdatePeeked; impl Actor for UpdatePeeked { - type Options = UpdatePeekedOpt; + type Options = UpdatePeekedForm; const NAME: &str = "update_peeked"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, form: Self::Options) -> Result { let Some(hovered) = cx.hovered().map(|h| &h.url) else { succ!(cx.tab_mut().preview.reset()); }; - if opt.lock.url == *hovered { - cx.tab_mut().preview.lock = Some(opt.lock); + if form.lock.url == *hovered { + cx.tab_mut().preview.lock = Some(form.lock); render!(); } diff --git a/yazi-actor/src/mgr/update_spotted.rs b/yazi-actor/src/mgr/update_spotted.rs index 9c15d0cc..ad3471c1 100644 --- a/yazi-actor/src/mgr/update_spotted.rs +++ b/yazi-actor/src/mgr/update_spotted.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_macro::{render, succ}; -use yazi_parser::mgr::UpdateSpottedOpt; +use yazi_parser::mgr::UpdateSpottedForm; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -8,29 +8,29 @@ use crate::{Actor, Ctx}; pub struct UpdateSpotted; impl Actor for UpdateSpotted { - type Options = UpdateSpottedOpt; + type Options = UpdateSpottedForm; const NAME: &str = "update_spotted"; - fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, mut form: Self::Options) -> Result { let tab = cx.tab_mut(); let Some(hovered) = tab.hovered().map(|h| &h.url) else { succ!(tab.spot.reset()); }; - if opt.lock.url != *hovered { + if form.lock.url != *hovered { succ!(); } - if tab.spot.lock.as_ref().is_none_or(|l| l.id != opt.lock.id) { - tab.spot.skip = opt.lock.selected().unwrap_or_default(); - } else if let Some(s) = opt.lock.selected() { + if tab.spot.lock.as_ref().is_none_or(|l| l.id != form.lock.id) { + tab.spot.skip = form.lock.selected().unwrap_or_default(); + } else if let Some(s) = form.lock.selected() { tab.spot.skip = s; } else { - opt.lock.select(Some(tab.spot.skip)); + form.lock.select(Some(tab.spot.skip)); } - tab.spot.lock = Some(opt.lock); + tab.spot.lock = Some(form.lock); succ!(render!()); } } diff --git a/yazi-actor/src/mgr/update_yanked.rs b/yazi-actor/src/mgr/update_yanked.rs index 2b3120e9..77f31174 100644 --- a/yazi-actor/src/mgr/update_yanked.rs +++ b/yazi-actor/src/mgr/update_yanked.rs @@ -18,7 +18,7 @@ impl Actor for UpdateYanked { succ!(); } - cx.mgr.yanked = Yanked::new(opt.cut, opt.urls.into_owned()); + cx.mgr.yanked = Yanked::new(opt.cut, opt.0.urls.into_owned()); succ!(render!()); } } diff --git a/yazi-actor/src/notify/push.rs b/yazi-actor/src/notify/push.rs index 568ac9b2..90274661 100644 --- a/yazi-actor/src/notify/push.rs +++ b/yazi-actor/src/notify/push.rs @@ -2,9 +2,8 @@ use std::time::Instant; use anyhow::Result; use yazi_core::notify::Message; -use yazi_dds::spark::SparkKind; use yazi_macro::{act, succ}; -use yazi_parser::notify::PushOpt; +use yazi_parser::{notify::PushForm, spark::SparkKind}; use yazi_shared::{Source, data::Data}; use crate::{Actor, Ctx}; @@ -12,14 +11,14 @@ use crate::{Actor, Ctx}; pub struct Push; impl Actor for Push { - type Options = PushOpt; + type Options = PushForm; const NAME: &str = "push"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, form: Self::Options) -> Result { let instant = Instant::now(); - let mut msg = Message::from(opt); + let mut msg = Message::from(form.opt); msg.timeout += instant - cx.notify.messages.first().map_or(instant, |m| m.instant); if cx.notify.messages.iter().all(|m| m != &msg) { diff --git a/yazi-actor/src/notify/tick.rs b/yazi-actor/src/notify/tick.rs index ccf40299..5575713c 100644 --- a/yazi-actor/src/notify/tick.rs +++ b/yazi-actor/src/notify/tick.rs @@ -19,7 +19,7 @@ impl Actor for Tick { const NAME: &str = "tick"; fn act(cx: &mut Ctx, opt: Self::Options) -> Result { - cx.notify.tick_handle.take().map(|h| h.abort()); + cx.notify.ticker.take().map(|h| h.abort()); let Dimension { rows, columns, .. } = Dimension::available(); let area = Notify::available(Rect { x: 0, y: 0, width: columns, height: rows }); @@ -55,7 +55,7 @@ impl Actor for Tick { succ!(render!()); }; - cx.notify.tick_handle = Some(tokio::spawn(async move { + cx.notify.ticker = Some(tokio::spawn(async move { tokio::time::sleep(interval).await; NotifyProxy::tick(interval); })); diff --git a/yazi-actor/src/tasks/cancel.rs b/yazi-actor/src/tasks/cancel.rs index bcb0219f..ec137486 100644 --- a/yazi-actor/src/tasks/cancel.rs +++ b/yazi-actor/src/tasks/cancel.rs @@ -15,7 +15,7 @@ impl Actor for Cancel { fn act(cx: &mut Ctx, _: Self::Options) -> Result { let tasks = &mut cx.tasks; - let id = tasks.ongoing().lock().get_id(tasks.cursor); + let id = tasks.scheduler.ongoing.lock().get_id(tasks.cursor); if id.map(|id| tasks.scheduler.cancel(id)) != Some(true) { succ!(); } diff --git a/yazi-actor/src/tasks/inspect.rs b/yazi-actor/src/tasks/inspect.rs index ddfb8d65..e4a4bc7d 100644 --- a/yazi-actor/src/tasks/inspect.rs +++ b/yazi-actor/src/tasks/inspect.rs @@ -7,7 +7,7 @@ use tokio::{io::{AsyncReadExt, stdin}, select, sync::mpsc, time}; use yazi_binding::Permit; use yazi_macro::succ; use yazi_parser::VoidOpt; -use yazi_proxy::AppProxy; +use yazi_scheduler::AppProxy; use yazi_shared::{data::Data, terminal_clear}; use yazi_term::YIELD_TO_SUBPROCESS; use yazi_tty::TTY; @@ -22,7 +22,7 @@ impl Actor for Inspect { const NAME: &str = "inspect"; fn act(cx: &mut Ctx, _: Self::Options) -> Result { - let ongoing = cx.tasks.ongoing().clone(); + let ongoing = cx.tasks.scheduler.ongoing.clone(); let Some(id) = ongoing.lock().get_id(cx.tasks.cursor) else { succ!(); }; diff --git a/yazi-actor/src/tasks/open_shell_compat.rs b/yazi-actor/src/tasks/open_shell_compat.rs index 3f287f72..0cc0f1cb 100644 --- a/yazi-actor/src/tasks/open_shell_compat.rs +++ b/yazi-actor/src/tasks/open_shell_compat.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_macro::succ; -use yazi_parser::tasks::ProcessOpenOpt; +use yazi_parser::tasks::ProcessOpenForm; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -9,11 +9,11 @@ pub struct OpenShellCompat; // TODO: remove impl Actor for OpenShellCompat { - type Options = ProcessOpenOpt; + type Options = ProcessOpenForm; const NAME: &str = "open_shell_compat"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { opt, .. }: Self::Options) -> Result { succ!(cx.tasks.open_shell_compat(opt)); } } diff --git a/yazi-actor/src/tasks/process_open.rs b/yazi-actor/src/tasks/process_open.rs index 502f789a..ffa61184 100644 --- a/yazi-actor/src/tasks/process_open.rs +++ b/yazi-actor/src/tasks/process_open.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_macro::succ; -use yazi_parser::tasks::ProcessOpenOpt; +use yazi_parser::tasks::ProcessOpenForm; use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -8,11 +8,19 @@ use crate::{Actor, Ctx}; pub struct ProcessOpen; impl Actor for ProcessOpen { - type Options = ProcessOpenOpt; + type Options = ProcessOpenForm; const NAME: &str = "process_open"; - fn act(cx: &mut Ctx, opt: Self::Options) -> Result { - succ!(cx.tasks.scheduler.process_open(opt)); + fn act(cx: &mut Ctx, form: Self::Options) -> Result { + let done = cx.tasks.scheduler.process_open(form.opt); + + if let Some(replier) = form.replier { + tokio::spawn(async move { + replier.send(Ok(done.future().await.into())).ok(); + }); + } + + succ!(); } } diff --git a/yazi-actor/src/tasks/update_succeed.rs b/yazi-actor/src/tasks/update_succeed.rs index 24bf2a59..21e0ebd4 100644 --- a/yazi-actor/src/tasks/update_succeed.rs +++ b/yazi-actor/src/tasks/update_succeed.rs @@ -13,6 +13,21 @@ impl Actor for UpdateSucceed { const NAME: &str = "update_succeed"; fn act(cx: &mut Ctx, opt: Self::Options) -> Result { + if opt.urls.is_empty() { + succ!(); + } + + // FIXME: todo!(); + // if opt.track + // && opt.batch == cx.tasks.scheduler.batch.current() + // && let Some((parent, urn)) = opt.urls[0].pair() + // && parent == *cx.cwd() + // { + // cx.tasks.scheduler.batch.next(); + // cx.current_mut().trace = Some(urn.into()); + // act!(mgr:hover, cx)?; + // } + cx.mgr.watcher.report(opt.urls); succ!(); } diff --git a/yazi-actor/src/which/activate.rs b/yazi-actor/src/which/activate.rs index 796851af..fd975825 100644 --- a/yazi-actor/src/which/activate.rs +++ b/yazi-actor/src/which/activate.rs @@ -1,8 +1,7 @@ use anyhow::Result; use yazi_core::which::WhichSorter; -use yazi_dds::spark::SparkKind; use yazi_macro::{render, succ}; -use yazi_parser::which::ActivateOpt; +use yazi_parser::{spark::SparkKind, which::ActivateForm}; use yazi_shared::{Source, data::Data}; use crate::{Actor, Ctx}; @@ -10,11 +9,11 @@ use crate::{Actor, Ctx}; pub struct Activate; impl Actor for Activate { - type Options = ActivateOpt; + type Options = ActivateForm; const NAME: &str = "activate"; - fn act(cx: &mut Ctx, mut opt: Self::Options) -> Result { + fn act(cx: &mut Ctx, Self::Options { mut opt }: Self::Options) -> Result { opt.cands.retain(|c| c.on.len() > opt.times); WhichSorter::default().sort(&mut opt.cands); diff --git a/yazi-cli/src/dds/shot.rs b/yazi-cli/src/dds/shot.rs index dcc81955..ed039ed0 100644 --- a/yazi-cli/src/dds/shot.rs +++ b/yazi-cli/src/dds/shot.rs @@ -57,13 +57,13 @@ impl Dds { pub(super) fn ensure_ability(peers: &HashMap, kind: &str, receiver: Id) -> Result<()> { match (receiver, peers.get(&receiver).map(|p| p.able(kind))) { // Send to all receivers - (Id(0), _) if peers.is_empty() => { + (Id::ZERO, _) if peers.is_empty() => { bail!("No receiver found. Check if any receivers are running.") } - (Id(0), _) if peers.values().all(|p| !p.able(kind)) => { + (Id::ZERO, _) if peers.values().all(|p| !p.able(kind)) => { bail!("No receiver has the ability to receive `{kind}` messages.") } - (Id(0), _) => Ok(()), + (Id::ZERO, _) => Ok(()), // Send to a specific receiver (_, Some(true)) => Ok(()), diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 2a5f8ccc..c22ba48d 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -20,11 +20,10 @@ yazi-dds = { path = "../yazi-dds", version = "26.2.2" } yazi-emulator = { path = "../yazi-emulator", version = "26.2.2" } yazi-fs = { path = "../yazi-fs", version = "26.2.2" } yazi-macro = { path = "../yazi-macro", version = "26.2.2" } -yazi-parser = { path = "../yazi-parser", version = "26.2.2" } -yazi-plugin = { path = "../yazi-plugin", version = "26.2.2" } -yazi-proxy = { path = "../yazi-proxy", version = "26.2.2" } +yazi-runner = { path = "../yazi-runner", version = "26.2.2" } yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" } yazi-shared = { path = "../yazi-shared", version = "26.2.2" } +yazi-shim = { path = "../yazi-shim", version = "26.2.2" } yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } yazi-watcher = { path = "../yazi-watcher", version = "26.2.2" } yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } @@ -32,15 +31,23 @@ yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } # External dependencies anyhow = { workspace = true } crossterm = { workspace = true } +dyn-clone = { workspace = true } hashbrown = { workspace = true } indexmap = { workspace = true } +mlua = { workspace = true } +ordered-float = { workspace = true } parking_lot = { workspace = true } ratatui = { workspace = true } +serde = { workspace = true } +serde_with = { workspace = true } +strum = { workspace = true } +syntect = { workspace = true } tokio = { workspace = true } tokio-stream = { workspace = true } tokio-util = { workspace = true } tracing = { workspace = true } unicode-width = { workspace = true } +yazi-prebuilt = "0.1.0" [target.'cfg(target_os = "macos")'.dependencies] crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } diff --git a/yazi-core/src/app/mod.rs b/yazi-core/src/app/mod.rs new file mode 100644 index 00000000..a696534f --- /dev/null +++ b/yazi-core/src/app/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(quit); diff --git a/yazi-core/src/app/quit.rs b/yazi-core/src/app/quit.rs new file mode 100644 index 00000000..f7544fcf --- /dev/null +++ b/yazi-core/src/app/quit.rs @@ -0,0 +1,32 @@ +use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; +use serde::{Deserialize, Serialize}; +use yazi_binding::SER_OPT; +use yazi_shared::{event::ActionCow, strand::StrandBuf}; + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct QuitOpt { + pub code: i32, + #[serde(skip)] + pub selected: Option, + pub no_cwd_file: bool, +} + +impl TryFrom for QuitOpt { + type Error = anyhow::Error; + + fn try_from(a: ActionCow) -> Result { + Ok(Self { + code: a.get("code").unwrap_or_default(), + selected: None, + no_cwd_file: a.bool("no-cwd-file"), + }) + } +} + +impl FromLua for QuitOpt { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { lua.from_value(value) } +} + +impl IntoLua for QuitOpt { + fn into_lua(self, lua: &Lua) -> mlua::Result { lua.to_value_with(&self, SER_OPT) } +} diff --git a/yazi-core/src/cmp/cmp.rs b/yazi-core/src/cmp/cmp.rs index 1ff188f4..00c78268 100644 --- a/yazi-core/src/cmp/cmp.rs +++ b/yazi-core/src/cmp/cmp.rs @@ -2,10 +2,11 @@ use std::io; use hashbrown::HashMap; use tokio::task::JoinHandle; -use yazi_parser::cmp::CmpItem; use yazi_shared::{Id, url::UrlBuf}; use yazi_widgets::Scrollable; +use crate::cmp::CmpItem; + #[derive(Default)] pub struct Cmp { pub caches: HashMap>, diff --git a/yazi-core/src/cmp/item.rs b/yazi-core/src/cmp/item.rs new file mode 100644 index 00000000..069c41d2 --- /dev/null +++ b/yazi-core/src/cmp/item.rs @@ -0,0 +1,7 @@ +use yazi_shared::strand::StrandBuf; + +#[derive(Debug, Clone)] +pub struct CmpItem { + pub name: StrandBuf, + pub is_dir: bool, +} diff --git a/yazi-core/src/cmp/mod.rs b/yazi-core/src/cmp/mod.rs index 4fc18fd3..b3ab4cee 100644 --- a/yazi-core/src/cmp/mod.rs +++ b/yazi-core/src/cmp/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(cmp); +yazi_macro::mod_flat!(cmp item opt); diff --git a/yazi-core/src/cmp/opt.rs b/yazi-core/src/cmp/opt.rs new file mode 100644 index 00000000..9e1543c2 --- /dev/null +++ b/yazi-core/src/cmp/opt.rs @@ -0,0 +1,11 @@ +use yazi_shared::{Id, path::PathBufDyn, url::UrlBuf}; + +use crate::cmp::CmpItem; + +#[derive(Clone, Debug)] +pub struct CmpOpt { + pub cache: Vec, + pub cache_name: UrlBuf, + pub word: PathBufDyn, + pub ticket: Id, +} diff --git a/yazi-plugin/src/external/highlighter.rs b/yazi-core/src/highlighter.rs similarity index 96% rename from yazi-plugin/src/external/highlighter.rs rename to yazi-core/src/highlighter.rs index 3dd15a49..4011d284 100644 --- a/yazi-plugin/src/external/highlighter.rs +++ b/yazi-core/src/highlighter.rs @@ -1,10 +1,11 @@ use std::{io::{BufRead, BufReader, Cursor, Seek}, path::PathBuf, sync::OnceLock}; -use anyhow::{Result, bail}; +use anyhow::{Result, anyhow, bail}; use ratatui::{layout::Size, text::{Line, Span, Text}}; use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}}; use yazi_config::{THEME, YAZI}; -use yazi_shared::{Id, Ids, errors::PeekError, replace_to_printable}; +use yazi_runner::previewer::PeekError; +use yazi_shared::{Id, Ids, replace_to_printable}; use yazi_shim::ratatui::LineIter; static INCR: Ids = Ids::new(); @@ -68,7 +69,7 @@ impl Highlighter { let mut inspected = 0u16; while self.reader.read_until(b'\n', &mut buf).is_ok_and(|n| n > 0) { if Self::is_binary(&buf, &mut inspected) { - return Err("Binary file".into()); + Err(anyhow!("Binary file"))?; } let remaining = Self::normalize_control_chars(&mut buf); @@ -86,7 +87,7 @@ impl Highlighter { } if self.skip > 0 && i < self.skip + self.size.height as usize { - return Err(PeekError::Exceed(i.saturating_sub(self.size.height as _))); + return Err(PeekError::Exceeded(i.saturating_sub(self.size.height as _))); } Ok(Text::from(lines)) @@ -139,7 +140,7 @@ impl Highlighter { #[inline] fn ensure_not_cancelled(&self) -> Result<(), PeekError> { - if self.ticket != INCR.current() { Err("Highlighting cancelled".into()) } else { Ok(()) } + if self.ticket != INCR.current() { Err(anyhow!("Highlighting cancelled"))? } else { Ok(()) } } fn load() -> (Theme, SyntaxSet) { diff --git a/yazi-core/src/lib.rs b/yazi-core/src/lib.rs index b95d01c7..ae95253b 100644 --- a/yazi-core/src/lib.rs +++ b/yazi-core/src/lib.rs @@ -1,3 +1,3 @@ -yazi_macro::mod_pub!(cmp confirm help input mgr notify pick spot tab tasks which); +yazi_macro::mod_pub!(app cmp confirm help input mgr notify pick spot tab tasks which); -yazi_macro::mod_flat!(core); +yazi_macro::mod_flat!(core highlighter proxy); diff --git a/yazi-core/src/mgr/displace.rs b/yazi-core/src/mgr/displace.rs new file mode 100644 index 00000000..11d63448 --- /dev/null +++ b/yazi-core/src/mgr/displace.rs @@ -0,0 +1,7 @@ +use yazi_shared::url::UrlBuf; + +#[derive(Clone, Debug)] +pub struct DisplaceOpt { + pub to: Result, + pub from: UrlBuf, +} diff --git a/yazi-core/src/mgr/filter.rs b/yazi-core/src/mgr/filter.rs new file mode 100644 index 00000000..f7fdc413 --- /dev/null +++ b/yazi-core/src/mgr/filter.rs @@ -0,0 +1,21 @@ +use yazi_fs::FilterCase; +use yazi_shared::{SStr, event::ActionCow}; + +#[derive(Clone, Debug, Default)] +pub struct FilterOpt { + pub query: SStr, + pub case: FilterCase, + pub done: bool, +} + +impl TryFrom for FilterOpt { + type Error = anyhow::Error; + + fn try_from(mut a: ActionCow) -> Result { + Ok(Self { + query: a.take_first().unwrap_or_default(), + case: FilterCase::from(&*a), + done: a.bool("done"), + }) + } +} diff --git a/yazi-core/src/mgr/find.rs b/yazi-core/src/mgr/find.rs new file mode 100644 index 00000000..18caf255 --- /dev/null +++ b/yazi-core/src/mgr/find.rs @@ -0,0 +1,22 @@ +use anyhow::bail; +use yazi_fs::FilterCase; +use yazi_shared::{SStr, event::ActionCow}; + +#[derive(Clone, Debug)] +pub struct FindDoOpt { + pub query: SStr, + pub prev: bool, + pub case: FilterCase, +} + +impl TryFrom for FindDoOpt { + type Error = anyhow::Error; + + fn try_from(mut a: ActionCow) -> Result { + let Ok(query) = a.take_first() else { + bail!("Invalid 'query' in FindDoOpt"); + }; + + Ok(Self { query, prev: a.bool("previous"), case: FilterCase::from(&*a) }) + } +} diff --git a/yazi-core/src/mgr/mod.rs b/yazi-core/src/mgr/mod.rs index 19f0aa52..8a89291d 100644 --- a/yazi-core/src/mgr/mod.rs +++ b/yazi-core/src/mgr/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(batcher mgr mimetype tabs yanked); +yazi_macro::mod_flat!(batcher displace filter find mgr mimetype open search tabs yanked); diff --git a/yazi-core/src/mgr/open.rs b/yazi-core/src/mgr/open.rs new file mode 100644 index 00000000..3038a6b6 --- /dev/null +++ b/yazi-core/src/mgr/open.rs @@ -0,0 +1,30 @@ +use yazi_shared::{event::ActionCow, url::UrlCow}; + +#[derive(Clone, Debug)] +pub struct OpenOpt { + pub cwd: Option>, + pub targets: Vec>, + pub interactive: bool, + pub hovered: bool, +} + +impl TryFrom for OpenOpt { + type Error = anyhow::Error; + + fn try_from(mut a: ActionCow) -> Result { + Ok(Self { + cwd: a.take("cwd").ok(), + targets: a.take_seq(), + interactive: a.bool("interactive"), + hovered: a.bool("hovered"), + }) + } +} + +// OpenDoOpt +#[derive(Clone, Debug, Default)] +pub struct OpenDoOpt { + pub cwd: UrlCow<'static>, + pub targets: Vec>, + pub interactive: bool, +} diff --git a/yazi-core/src/mgr/search.rs b/yazi-core/src/mgr/search.rs new file mode 100644 index 00000000..fcd81774 --- /dev/null +++ b/yazi-core/src/mgr/search.rs @@ -0,0 +1,66 @@ +use std::str::FromStr; + +use anyhow::bail; +use serde::Deserialize; +use yazi_shared::{SStr, event::ActionCow, url::{UrlCow, UrlLike}}; + +#[derive(Clone, Debug)] +pub struct SearchOpt { + pub via: SearchVia, + pub subject: SStr, + pub args: Vec, + pub args_raw: SStr, + pub r#in: Option>, +} + +impl TryFrom for SearchOpt { + type Error = anyhow::Error; + + fn try_from(mut a: ActionCow) -> Result { + let r#in = a.take::("in").ok(); + if let Some(u) = &r#in + && (!u.is_absolute() || u.is_search()) + { + bail!("invalid 'in' in SearchOpt"); + } + + let Ok(args) = yazi_shared::shell::unix::split(a.str("args"), false) else { + bail!("invalid 'args' in SearchOpt"); + }; + + Ok(Self { + via: a.str("via").parse()?, + subject: a.take_first().unwrap_or_default(), + args: args.0, + args_raw: a.take("args").unwrap_or_default(), + r#in, + }) + } +} + +// Via +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "kebab-case")] +pub enum SearchVia { + Rg, + Rga, + Fd, +} + +impl FromStr for SearchVia { + type Err = serde::de::value::Error; + + fn from_str(s: &str) -> Result { + Self::deserialize(serde::de::value::StrDeserializer::new(s)) + } +} + +impl SearchVia { + pub fn into_str(self) -> &'static str { + match self { + Self::Rg => "rg", + Self::Rga => "rga", + Self::Fd => "fd", + } + } +} diff --git a/yazi-core/src/mgr/yanked.rs b/yazi-core/src/mgr/yanked.rs index b8a69af1..22db97b9 100644 --- a/yazi-core/src/mgr/yanked.rs +++ b/yazi-core/src/mgr/yanked.rs @@ -1,6 +1,7 @@ use std::ops::Deref; use hashbrown::HashSet; +use indexmap::IndexSet; use yazi_dds::Pubsub; use yazi_fs::FilesOp; use yazi_macro::err; @@ -9,27 +10,36 @@ use yazi_shared::url::{Url, UrlBuf, UrlBufCov, UrlCov, UrlLike}; #[derive(Debug, Default)] pub struct Yanked { pub cut: bool, - urls: HashSet, + urls: IndexSet, version: u64, revision: u64, } impl Deref for Yanked { - type Target = HashSet; + type Target = IndexSet; fn deref(&self) -> &Self::Target { &self.urls } } impl Yanked { - pub fn new(cut: bool, urls: HashSet) -> Self { + pub fn new(cut: bool, urls: IndexSet) -> Self { Self { cut, urls, ..Default::default() } } - pub fn remove<'a>(&mut self, url: impl Into>) { - if self.urls.remove(&UrlCov::new(url)) { - self.revision += 1; + pub fn remove_many<'a, I, T>(&mut self, urls: I) + where + I: IntoIterator, + T: Into>, + { + let urls: HashSet<_> = urls.into_iter().map(Into::into).collect(); + if urls.is_empty() { + return; } + + let old = self.urls.len(); + self.urls.retain(|u| !urls.contains(&UrlCov::new(u))); + self.revision += (old != self.urls.len()) as u64; } pub fn clear(&mut self) { diff --git a/yazi-core/src/notify/level.rs b/yazi-core/src/notify/level.rs new file mode 100644 index 00000000..5a2591dc --- /dev/null +++ b/yazi-core/src/notify/level.rs @@ -0,0 +1,29 @@ +use serde::{Deserialize, Serialize}; +use yazi_config::{Style, THEME}; + +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum MessageLevel { + #[default] + Info, + Warn, + Error, +} + +impl MessageLevel { + pub fn icon(self) -> &'static str { + match self { + Self::Info => &THEME.notify.icon_info, + Self::Warn => &THEME.notify.icon_warn, + Self::Error => &THEME.notify.icon_error, + } + } + + pub fn style(self) -> Style { + match self { + Self::Info => THEME.notify.title_info, + Self::Warn => THEME.notify.title_warn, + Self::Error => THEME.notify.title_error, + } + } +} diff --git a/yazi-core/src/notify/message.rs b/yazi-core/src/notify/message.rs index a2a3499d..b532e80f 100644 --- a/yazi-core/src/notify/message.rs +++ b/yazi-core/src/notify/message.rs @@ -1,14 +1,14 @@ use std::time::{Duration, Instant}; use unicode_width::UnicodeWidthStr; -use yazi_parser::notify::{PushLevel, PushOpt}; use super::NOTIFY_BORDER; +use crate::notify::{MessageLevel, MessageOpt}; pub struct Message { pub title: String, pub content: String, - pub level: PushLevel, + pub level: MessageLevel, pub timeout: Duration, pub instant: Instant, @@ -16,8 +16,8 @@ pub struct Message { pub max_width: usize, } -impl From for Message { - fn from(opt: PushOpt) -> Self { +impl From for Message { + fn from(opt: MessageOpt) -> Self { let title = opt.title.lines().next().unwrap_or_default(); let title_width = title.width() + (opt.level.icon().width() + /* Space */ 1); @@ -36,6 +36,12 @@ impl From for Message { } } +impl PartialEq for Message { + fn eq(&self, other: &Self) -> bool { + self.level == other.level && self.content == other.content && self.title == other.title + } +} + impl Message { pub fn height(&self, width: u16) -> usize { let lines = ratatui::widgets::Paragraph::new(self.content.as_str()) @@ -45,9 +51,3 @@ impl Message { lines + NOTIFY_BORDER as usize } } - -impl PartialEq for Message { - fn eq(&self, other: &Self) -> bool { - self.level == other.level && self.content == other.content && self.title == other.title - } -} diff --git a/yazi-core/src/notify/mod.rs b/yazi-core/src/notify/mod.rs index e5e385b7..e62af76c 100644 --- a/yazi-core/src/notify/mod.rs +++ b/yazi-core/src/notify/mod.rs @@ -1,4 +1,4 @@ -yazi_macro::mod_flat!(message notify); +yazi_macro::mod_flat!(level message notify opt); pub const NOTIFY_BORDER: u16 = 2; pub const NOTIFY_SPACING: u16 = 1; diff --git a/yazi-core/src/notify/notify.rs b/yazi-core/src/notify/notify.rs index 44cbb515..c8032a90 100644 --- a/yazi-core/src/notify/notify.rs +++ b/yazi-core/src/notify/notify.rs @@ -7,8 +7,8 @@ use super::{Message, NOTIFY_SPACING}; #[derive(Default)] pub struct Notify { - pub tick_handle: Option>, - pub messages: Vec, + pub ticker: Option>, + pub messages: Vec, } impl Notify { diff --git a/yazi-core/src/notify/opt.rs b/yazi-core/src/notify/opt.rs new file mode 100644 index 00000000..646384b2 --- /dev/null +++ b/yazi-core/src/notify/opt.rs @@ -0,0 +1,41 @@ +use std::time::Duration; + +use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; +use serde::{Deserialize, Serialize}; +use serde_with::{DurationSecondsWithFrac, serde_as}; +use yazi_binding::SER_OPT; +use yazi_shared::{data::Data, event::ActionCow}; + +use crate::notify::MessageLevel; + +#[serde_as] +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct MessageOpt { + pub title: String, + pub content: String, + #[serde(default)] + pub level: MessageLevel, + #[serde_as(as = "DurationSecondsWithFrac")] + pub timeout: Duration, +} + +impl TryFrom for MessageOpt { + type Error = anyhow::Error; + + fn try_from(mut a: ActionCow) -> Result { + Ok(Self { + title: a.take_second()?, + content: a.take_first()?, + level: <_>::deserialize(a.get::<&Data>("level")?)?, + timeout: <_>::deserialize(a.get::<&Data>("timeout")?)?, + }) + } +} + +impl FromLua for MessageOpt { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { lua.from_value(value) } +} + +impl IntoLua for MessageOpt { + fn into_lua(self, lua: &Lua) -> mlua::Result { lua.to_value_with(&self, SER_OPT) } +} diff --git a/yazi-core/src/proxy.rs b/yazi-core/src/proxy.rs new file mode 100644 index 00000000..a26a778a --- /dev/null +++ b/yazi-core/src/proxy.rs @@ -0,0 +1,59 @@ +use mlua::ObjectLike; +use yazi_binding::{Error, elements::Renderable}; +use yazi_config::LAYOUT; +use yazi_macro::{emit, relay}; +use yazi_runner::{plugin::PluginOpt, previewer::PeekJob}; +use yazi_scheduler::TaskSummary; +use yazi_shared::url::AsUrl; + +use crate::tab::PreviewLock; + +pub struct AppProxy; + +impl AppProxy { + pub fn plugin(opt: PluginOpt) { + emit!(Call(relay!(app:plugin).with_any("opt", opt))); + } + + pub fn plugin_peek(job: PeekJob) { + Self::plugin(PluginOpt::new_callback(&*job.action.name, move |_, plugin| { + plugin.call_method("peek", job) + })); + } + + pub fn update_progress(summary: TaskSummary) { + emit!(Call(relay!(app:update_progress).with_any("summary", summary))); + } +} + +// --- Mgr +pub struct MgrProxy; + +impl MgrProxy { + pub fn update_paged_by(page: usize, only_if: U) + where + U: AsUrl, + { + emit!(Call(relay!(mgr:update_paged, [page]).with("only-if", only_if.as_url()))); + } + + pub fn update_peeked(lock: PreviewLock) { + emit!(Call(relay!(mgr:update_peeked).with_any("lock", lock))); + } + + pub fn update_peeked_error(job: PeekJob, error: String) { + let area = LAYOUT.get().preview; + Self::update_peeked(PreviewLock { + url: job.file.url, + cha: job.file.cha, + mime: job.mime, + + skip: job.skip, + area: area.into(), + data: vec![ + Renderable::Clear(yazi_binding::elements::Clear { area: area.into() }), + Renderable::from(Error::custom(error)).with_area(area), + ], + }); + } +} diff --git a/yazi-core/src/spot/lock.rs b/yazi-core/src/spot/lock.rs new file mode 100644 index 00000000..86c84c20 --- /dev/null +++ b/yazi-core/src/spot/lock.rs @@ -0,0 +1,57 @@ +use mlua::Table; +use yazi_binding::{FileRef, elements::Renderable}; +use yazi_shared::Id; + +#[derive(Clone, Debug)] +pub struct SpotLock { + pub url: yazi_shared::url::UrlBuf, + pub cha: yazi_fs::cha::Cha, + pub mime: String, + + pub id: Id, + pub skip: usize, + pub data: Vec, +} + +impl TryFrom for SpotLock { + type Error = mlua::Error; + + fn try_from(t: Table) -> Result { + let file: FileRef = t.raw_get("file")?; + Ok(Self { + url: file.url_owned(), + cha: file.cha, + mime: t.raw_get("mime")?, + + id: *t.raw_get::("id")?, + skip: t.raw_get("skip")?, + data: Default::default(), + }) + } +} + +impl SpotLock { + pub fn len(&self) -> Option { Some(self.table()?.len()) } + + pub fn select(&mut self, idx: Option) { + if let Some(t) = self.table_mut() { + t.select(idx); + } + } + + pub fn selected(&self) -> Option { self.table()?.selected() } + + pub fn table(&self) -> Option<&yazi_binding::elements::Table> { + self.data.iter().rev().find_map(|r| match r { + Renderable::Table(t) => Some(t.as_ref()), + _ => None, + }) + } + + pub fn table_mut(&mut self) -> Option<&mut yazi_binding::elements::Table> { + self.data.iter_mut().rev().find_map(|r| match r { + Renderable::Table(t) => Some(t.as_mut()), + _ => None, + }) + } +} diff --git a/yazi-core/src/spot/mod.rs b/yazi-core/src/spot/mod.rs index 9681606b..2a58c911 100644 --- a/yazi-core/src/spot/mod.rs +++ b/yazi-core/src/spot/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(spot); +yazi_macro::mod_flat!(lock spot); diff --git a/yazi-core/src/spot/spot.rs b/yazi-core/src/spot/spot.rs index 34034168..db871bbf 100644 --- a/yazi-core/src/spot/spot.rs +++ b/yazi-core/src/spot/spot.rs @@ -2,10 +2,11 @@ use tokio_util::sync::CancellationToken; use yazi_config::YAZI; use yazi_fs::File; use yazi_macro::render; -use yazi_parser::mgr::SpotLock; -use yazi_plugin::isolate; +use yazi_runner::RUNNER; use yazi_shared::{pool::Symbol, url::UrlBuf}; +use crate::spot::SpotLock; + #[derive(Default)] pub struct Spot { pub lock: Option, @@ -27,7 +28,7 @@ impl Spot { }; self.abort(); - self.ct = Some(isolate::spot(&spotter.run, file, mime, self.skip)); + self.ct = Some(RUNNER.spot(&spotter.run, file, mime, self.skip)); } pub fn visible(&self) -> bool { self.lock.is_some() } diff --git a/yazi-core/src/tab/folder.rs b/yazi-core/src/tab/folder.rs index 08c4d107..b6c402a3 100644 --- a/yazi-core/src/tab/folder.rs +++ b/yazi-core/src/tab/folder.rs @@ -4,10 +4,11 @@ use yazi_config::{LAYOUT, YAZI}; use yazi_dds::Pubsub; use yazi_fs::{File, Files, FilesOp, FolderStage, cha::Cha}; use yazi_macro::err; -use yazi_proxy::MgrProxy; use yazi_shared::{Id, path::{AsPath, PathBufDyn, PathDyn}, url::UrlBuf}; use yazi_widgets::{Scrollable, Step}; +use crate::MgrProxy; + pub struct Folder { pub url: UrlBuf, pub cha: Cha, @@ -79,7 +80,7 @@ impl Folder { FilesOp::Upserting(_, files) => self.files.update_upserting(files), } - self.trace = self.trace.take_if(|_| !self.files.is_empty() || self.stage.is_loading()); + self.trace.take_if(|_| self.files.is_empty() && !self.stage.is_loading()); self.repos(None); (&stage, revision) != (&self.stage, self.files.revision) diff --git a/yazi-core/src/tab/mod.rs b/yazi-core/src/tab/mod.rs index c4a014ae..4678c783 100644 --- a/yazi-core/src/tab/mod.rs +++ b/yazi-core/src/tab/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(backstack finder folder history mode preference preview selected tab); +yazi_macro::mod_flat!(backstack finder folder history mode preference preview preview_lock selected tab); diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index cc7143cb..293be3de 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -2,22 +2,22 @@ use std::time::Duration; use tokio::{pin, task::JoinHandle}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; -use tokio_util::sync::CancellationToken; use yazi_adapter::ADAPTOR; use yazi_config::{LAYOUT, YAZI}; use yazi_fs::{File, Files, FilesOp, cha::Cha}; use yazi_macro::render; -use yazi_parser::mgr::PreviewLock; -use yazi_plugin::{external::Highlighter, isolate}; +use yazi_runner::{RUNNER, previewer::{PeekError, PeekJob}}; use yazi_shared::{pool::Symbol, url::{UrlBuf, UrlLike}}; use yazi_vfs::{VfsFiles, VfsFilesOp}; +use crate::{AppProxy, Highlighter, MgrProxy, tab::PreviewLock}; + #[derive(Default)] pub struct Preview { pub lock: Option, pub skip: usize, - previewer_ct: Option, + handle: Option>, pub folder_lock: Option, folder_loader: Option>, } @@ -35,7 +35,16 @@ impl Preview { }; self.abort(); - self.previewer_ct = isolate::peek(&previewer.run, file, mime, self.skip); + let job = PeekJob { action: &previewer.run, file, mime, skip: self.skip }; + + self.handle = Some(tokio::spawn(async move { + let mut rx = RUNNER.peek(&job).await; + match rx.recv().await.unwrap_or(Err(PeekError::Cancelled)) { + Ok(()) | Err(PeekError::Cancelled) => {} + Err(PeekError::ShouldSync) => AppProxy::plugin_peek(job), + Err(e) => MgrProxy::update_peeked_error(job, e.to_string()), + } + })); } pub fn go_folder(&mut self, file: File, dir: Option, mime: Symbol, force: bool) { @@ -71,7 +80,7 @@ impl Preview { } pub fn abort(&mut self) { - self.previewer_ct.take().map(|ct| ct.cancel()); + self.handle.take().map(|ct| ct.abort()); Highlighter::abort(); } diff --git a/yazi-core/src/tab/preview_lock.rs b/yazi-core/src/tab/preview_lock.rs new file mode 100644 index 00000000..b643ceae --- /dev/null +++ b/yazi-core/src/tab/preview_lock.rs @@ -0,0 +1,31 @@ +use mlua::Table; +use yazi_binding::{FileRef, elements::{Rect, Renderable}}; +use yazi_shared::pool::{InternStr, Symbol}; + +#[derive(Clone, Debug, Default)] +pub struct PreviewLock { + pub url: yazi_shared::url::UrlBuf, + pub cha: yazi_fs::cha::Cha, + pub mime: Symbol, + + pub skip: usize, + pub area: Rect, + pub data: Vec, +} + +impl TryFrom
for PreviewLock { + type Error = mlua::Error; + + fn try_from(t: Table) -> Result { + let file: FileRef = t.raw_get("file")?; + Ok(Self { + url: file.url_owned(), + cha: file.cha, + mime: t.raw_get::("mime")?.to_str()?.intern(), + + skip: t.raw_get("skip")?, + area: t.raw_get("area")?, + data: Default::default(), + }) + } +} diff --git a/yazi-core/src/tasks/file.rs b/yazi-core/src/tasks/file.rs index 1935a014..e6f6bcb8 100644 --- a/yazi-core/src/tasks/file.rs +++ b/yazi-core/src/tasks/file.rs @@ -1,4 +1,4 @@ -use hashbrown::HashSet; +use indexmap::IndexSet; use tracing::debug; use yazi_shared::url::{UrlBuf, UrlBufCov, UrlLike}; @@ -34,7 +34,7 @@ impl Tasks { } } - pub fn file_link(&self, src: &HashSet, dest: &UrlBuf, relative: bool, force: bool) { + pub fn file_link(&self, src: &IndexSet, dest: &UrlBuf, relative: bool, force: bool) { for u in src { let Some(Ok(to)) = u.name().map(|n| dest.try_join(n)) else { debug!("file_link: cannot join {u:?} with {dest:?}"); @@ -48,7 +48,7 @@ impl Tasks { } } - pub fn file_hardlink(&self, src: &HashSet, dest: &UrlBuf, force: bool, follow: bool) { + pub fn file_hardlink(&self, src: &IndexSet, dest: &UrlBuf, force: bool, follow: bool) { for u in src { let Some(Ok(to)) = u.name().map(|n| dest.try_join(n)) else { debug!("file_hardlink: cannot join {u:?} with {dest:?}"); diff --git a/yazi-core/src/tasks/process.rs b/yazi-core/src/tasks/process.rs index a3b71487..c403d2a7 100644 --- a/yazi-core/src/tasks/process.rs +++ b/yazi-core/src/tasks/process.rs @@ -1,13 +1,13 @@ use std::mem; use yazi_fs::Splatter; -use yazi_parser::tasks::ProcessOpenOpt; +use yazi_scheduler::process::ProcessOpt; use super::Tasks; impl Tasks { // TODO: remove - pub fn open_shell_compat(&self, mut opt: ProcessOpenOpt) { + pub fn open_shell_compat(&self, mut opt: ProcessOpt) { if opt.spread { opt.cmd = Splatter::new(&opt.args).splat(opt.cmd); self.scheduler.process_open(opt); @@ -24,13 +24,12 @@ impl Tasks { let hovered = mem::take(&mut opt.args[0]); for target in opt.args.into_iter().skip(1) { let args = vec![hovered.clone(), target]; - self.scheduler.process_open(ProcessOpenOpt { + self.scheduler.process_open(ProcessOpt { cwd: opt.cwd.clone(), cmd: Splatter::new(&args).splat(&opt.cmd), args, block: opt.block, orphan: opt.orphan, - done: None, spread: opt.spread, }); } diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 8cded4af..7c46c599 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -1,13 +1,11 @@ use std::{sync::Arc, time::Duration}; -use parking_lot::Mutex; use tokio::{task::JoinHandle, time::sleep}; use yazi_emulator::Dimension; -use yazi_parser::app::TaskSummary; -use yazi_proxy::AppProxy; -use yazi_scheduler::{Ongoing, Scheduler, TaskSnap}; +use yazi_scheduler::{Scheduler, TaskSnap, TaskSummary}; use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT}; +use crate::AppProxy; pub struct Tasks { pub scheduler: Arc, @@ -29,7 +27,7 @@ impl Tasks { loop { sleep(Duration::from_millis(500)).await; - let new = ongoing.lock().summary(); + let new = TaskSummary::from(&*ongoing.lock()); if last != new { last = new; AppProxy::update_progress(new); @@ -60,8 +58,6 @@ impl Tasks { } pub fn paginate(&self) -> Vec { - self.ongoing().lock().values().take(Self::limit()).map(Into::into).collect() + self.scheduler.ongoing.lock().values().take(Self::limit()).map(Into::into).collect() } - - pub fn ongoing(&self) -> &Arc> { &self.scheduler.ongoing } } diff --git a/yazi-core/src/which/mod.rs b/yazi-core/src/which/mod.rs index add09c1a..d9966ab6 100644 --- a/yazi-core/src/which/mod.rs +++ b/yazi-core/src/which/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(sorter which); +yazi_macro::mod_flat!(opt sorter which); diff --git a/yazi-core/src/which/opt.rs b/yazi-core/src/which/opt.rs new file mode 100644 index 00000000..4a95f83e --- /dev/null +++ b/yazi-core/src/which/opt.rs @@ -0,0 +1,78 @@ +use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; +use tokio::sync::mpsc; +use yazi_config::{KEYMAP, keymap::{ChordCow, Key}}; +use yazi_shared::{Layer, event::ActionCow}; + +#[derive(Clone, Debug)] +pub struct WhichOpt { + pub tx: Option>>, + pub cands: Vec, + pub silent: bool, + pub times: usize, +} + +impl TryFrom for WhichOpt { + type Error = anyhow::Error; + + fn try_from(mut a: ActionCow) -> Result { + if let Some(opt) = a.take_any2("opt") { + return opt; + } + + Ok(Self { + tx: a.take_any2("tx").transpose()?, + cands: a.take_any_iter::().map(Into::into).collect(), + silent: a.bool("silent"), + times: a.get("times").unwrap_or(0), + }) + } +} + +impl From<(Layer, Key)> for WhichOpt { + fn from((layer, key): (Layer, Key)) -> Self { + Self { + tx: None, + cands: KEYMAP + .get(layer) + .iter() + .filter(|c| c.on.len() > 1 && c.on[0] == key) + .map(Into::into) + .collect(), + times: 1, + silent: false, + } + } +} + +impl FromLua for WhichOpt { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + let Value::Table(t) = value else { + return Err("expected a table".into_lua_err()); + }; + + Ok(Self { + tx: t.raw_get::>("tx").ok().map(|t| t.0), + cands: t + .raw_get::
("cands")? + .sequence_values::() + .map(|c| c.map(Into::into)) + .collect::>>()?, + times: t.raw_get("times").unwrap_or_default(), + silent: t.raw_get("silent")?, + }) + } +} + +impl IntoLua for WhichOpt { + #[rustfmt::skip] + fn into_lua(self, lua: &Lua) -> mlua::Result { + lua + .create_table_from([ + ("tx", self.tx.map(yazi_binding::MpscUnboundedTx).into_lua(lua)?), + ("cands", lua.create_sequence_from(self.cands.into_iter().map(yazi_binding::ChordCow))?.into_lua(lua)?), + ("times", self.times.into_lua(lua)?), + ("silent", self.silent.into_lua(lua)?), + ])? + .into_lua(lua) + } +} diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index 14792f01..503d60e1 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -21,13 +21,13 @@ yazi-binding = { path = "../yazi-binding", version = "26.2.2" } yazi-boot = { path = "../yazi-boot", version = "26.2.2" } yazi-fs = { path = "../yazi-fs", version = "26.2.2" } yazi-macro = { path = "../yazi-macro", version = "26.2.2" } -yazi-parser = { path = "../yazi-parser", version = "26.2.2" } yazi-shared = { path = "../yazi-shared", version = "26.2.2" } yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } # External dependencies anyhow = { workspace = true } hashbrown = { workspace = true } +indexmap = { workspace = true } mlua = { workspace = true } ordered-float = { workspace = true } parking_lot = { workspace = true } diff --git a/yazi-dds/src/ember/yank.rs b/yazi-dds/src/ember/yank.rs index 5360830e..a22eb4f1 100644 --- a/yazi-dds/src/ember/yank.rs +++ b/yazi-dds/src/ember/yank.rs @@ -1,25 +1,33 @@ use std::borrow::Cow; -use hashbrown::HashSet; -use mlua::{IntoLua, Lua, Value}; +use indexmap::IndexSet; +use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods, Value}; use serde::{Deserialize, Serialize}; -use yazi_parser::mgr::UpdateYankedOpt; +use yazi_binding::get_metatable; use yazi_shared::url::UrlBufCov; use super::Ember; +type Iter = yazi_binding::Iter< + std::iter::Map, fn(UrlBufCov) -> yazi_binding::Url>, + yazi_binding::Url, +>; + #[derive(Clone, Debug, Deserialize, Serialize)] -pub struct EmberYank<'a>(UpdateYankedOpt<'a>); +pub struct EmberYank<'a> { + pub cut: bool, + pub urls: Cow<'a, IndexSet>, +} impl<'a> EmberYank<'a> { - pub fn borrowed(cut: bool, urls: &'a HashSet) -> Ember<'a> { - Self(UpdateYankedOpt { cut, urls: Cow::Borrowed(urls) }).into() + pub fn borrowed(cut: bool, urls: &'a IndexSet) -> Ember<'a> { + Self { cut, urls: Cow::Borrowed(urls) }.into() } } impl EmberYank<'static> { - pub fn owned(cut: bool, _: &HashSet) -> Ember<'static> { - Self(UpdateYankedOpt { cut, urls: Default::default() }).into() + pub fn owned(cut: bool, _: &IndexSet) -> Ember<'static> { + Self { cut, urls: Default::default() }.into() } } @@ -28,5 +36,47 @@ impl<'a> From> for Ember<'a> { } impl IntoLua for EmberYank<'_> { - fn into_lua(self, lua: &Lua) -> mlua::Result { self.0.into_lua(lua) } + fn into_lua(self, lua: &Lua) -> mlua::Result { + let len = self.urls.len(); + let iter = Iter::new(self.urls.into_owned().into_iter().map(yazi_binding::Url::new), Some(len)); + EmberYankIter { cut: self.cut, len, inner: lua.create_userdata(iter)? }.into_lua(lua) + } +} + +// --- Iter +pub struct EmberYankIter { + cut: bool, + len: usize, + inner: AnyUserData, +} + +impl EmberYankIter { + pub fn collect(self, lua: &Lua) -> mlua::Result> { + Ok(EmberYank { + cut: self.cut, + urls: Cow::Owned( + self + .inner + .take::()? + .into_iter(lua) + .map(|result| result.map(Into::into)) + .collect::>()?, + ), + }) + } +} + +impl UserData for EmberYankIter { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("cut", |_, me| Ok(me.cut)); + } + + fn add_methods>(methods: &mut M) { + methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len)); + + methods.add_meta_method(MetaMethod::Pairs, |lua, me, ()| { + get_metatable(lua, &me.inner)? + .call_function::(MetaMethod::Pairs.name(), me.inner.clone()) + }); + } } diff --git a/yazi-dds/src/lib.rs b/yazi-dds/src/lib.rs index 652bf3c9..aa41ee8f 100644 --- a/yazi-dds/src/lib.rs +++ b/yazi-dds/src/lib.rs @@ -1,6 +1,4 @@ -mod macros; - -yazi_macro::mod_pub!(ember spark); +yazi_macro::mod_pub!(ember); yazi_macro::mod_flat!(client payload pubsub pump sendable server state stream); diff --git a/yazi-dds/src/payload.rs b/yazi-dds/src/payload.rs index c1281358..1e72f1a6 100644 --- a/yazi-dds/src/payload.rs +++ b/yazi-dds/src/payload.rs @@ -6,7 +6,7 @@ use yazi_boot::BOOT; use yazi_macro::{emit, relay}; use yazi_shared::{Id, event::ActionCow}; -use crate::{ID, ember::Ember, spark::Spark}; +use crate::{ID, ember::Ember}; #[derive(Clone, Debug)] pub struct Payload<'a> { @@ -16,7 +16,7 @@ pub struct Payload<'a> { } impl<'a> Payload<'a> { - pub fn new(body: Ember<'a>) -> Self { Self { receiver: Id(0), sender: *ID, body } } + pub fn new(body: Ember<'a>) -> Self { Self { receiver: Id::ZERO, sender: *ID, body } } pub(super) fn flush(&self) -> Result<()> { writeln!(std::io::stdout(), "{self}")?; @@ -75,17 +75,6 @@ impl<'a> From> for Payload<'a> { fn from(value: Ember<'a>) -> Self { Self::new(value) } } -impl<'a> TryFrom> for Payload<'a> { - type Error = (); - - fn try_from(value: Spark<'a>) -> Result { - match value { - Spark::AppAcceptPayload(payload) => Ok(payload), - _ => Err(()), - } - } -} - impl TryFrom for Payload<'_> { type Error = anyhow::Error; diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index 7e4efdc1..6c719870 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -1,5 +1,6 @@ use anyhow::Result; -use hashbrown::{HashMap, HashSet}; +use hashbrown::HashMap; +use indexmap::IndexSet; use mlua::Function; use parking_lot::RwLock; use yazi_boot::BOOT; @@ -160,7 +161,7 @@ impl Pubsub { pub_after!(rename(tab: Id, from: &UrlBuf, to: &UrlBuf), (tab, from, to)); - pub_after!(@yank(cut: bool, urls: &HashSet), (cut, urls)); + pub_after!(@yank(cut: bool, urls: &IndexSet), (cut, urls)); pub_after!(duplicate(items: Vec), (&items), (items)); diff --git a/yazi-dds/src/sendable.rs b/yazi-dds/src/sendable.rs index 71c42550..860213e7 100644 --- a/yazi-dds/src/sendable.rs +++ b/yazi-dds/src/sendable.rs @@ -5,6 +5,8 @@ use mlua::{ExternalError, IntoLua, Lua, MultiValue, Table, Value}; use ordered_float::OrderedFloat; use yazi_shared::{data::{Data, DataKey}, replace_cow}; +use crate::ember; + pub struct Sendable; impl Sendable { @@ -55,8 +57,8 @@ impl Sendable { Some(t) if t == TypeId::of::() => { Data::Any(Box::new(ud.take::()?)) } - Some(t) if t == TypeId::of::() => { - Data::Any(Box::new(ud.take::()?.into_opt(lua)?)) + Some(t) if t == TypeId::of::() => { + Data::Any(Box::new(ud.take::()?.collect(lua)?)) } Some(t) if t == TypeId::of::() => { Data::Any(Box::new(ud.take::()?)) @@ -102,7 +104,7 @@ impl Sendable { } try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v)); - try_cast!(|v: yazi_parser::mgr::UpdateYankedOpt| v.into_lua(lua)); + try_cast!(|v: ember::EmberYank| v.into_lua(lua)); try_cast!(|v: yazi_binding::ChordCow| v.into_lua(lua)); Err("unsupported DataAny included".into_lua_err())? } @@ -146,7 +148,7 @@ impl Sendable { } try_cast!(|v: yazi_fs::FilesOp| lua.create_any_userdata(v)); - try_cast!(|v: yazi_parser::mgr::UpdateYankedOpt| v.into_lua(lua)); + try_cast!(|v: ember::EmberYank| v.into_lua(lua)); try_cast!(|v: yazi_binding::ChordCow| v.into_lua(lua)); Err("unsupported DataAny included".into_lua_err())? } diff --git a/yazi-dds/src/server.rs b/yazi-dds/src/server.rs index 1d845063..cf7eae6d 100644 --- a/yazi-dds/src/server.rs +++ b/yazi-dds/src/server.rs @@ -125,7 +125,7 @@ impl Server { } } - let bye = EmberBye::borrowed().with_receiver(id).with_sender(Id(0)); + let bye = EmberBye::borrowed().with_receiver(id).with_sender(Id::ZERO); if let Ok(s) = try_format!("{bye}") { writer.write_all(s.as_bytes()).await.ok(); writer.flush().await.ok(); diff --git a/yazi-dds/src/spark/spark.rs b/yazi-dds/src/spark/spark.rs deleted file mode 100644 index 9fddb178..00000000 --- a/yazi-dds/src/spark/spark.rs +++ /dev/null @@ -1,447 +0,0 @@ -use mlua::{FromLua, IntoLua, Lua, Value}; - -use crate::{spark::SparkKind, try_from_spark}; - -#[derive(Debug)] -pub enum Spark<'a> { - // Void - Void(yazi_parser::VoidOpt), - - // App - AppAcceptPayload(crate::Payload<'a>), - AppBootstrap(yazi_parser::VoidOpt), - AppDeprecate(yazi_parser::app::DeprecateOpt), - AppFocus(yazi_parser::VoidOpt), - AppLua(yazi_parser::app::LuaOpt), - AppMouse(yazi_parser::app::MouseOpt), - AppPlugin(yazi_parser::app::PluginOpt), - AppPluginDo(yazi_parser::app::PluginOpt), - AppQuit(yazi_parser::app::QuitOpt), - AppReflow(yazi_parser::app::ReflowOpt), - AppResize(yazi_parser::app::ReflowOpt), - AppResume(yazi_parser::app::ResumeOpt), - AppStop(yazi_parser::app::StopOpt), - AppTitle(yazi_parser::app::TitleOpt), - AppUpdateProgress(yazi_parser::app::UpdateProgressOpt), - - // Mgr - Arrow(yazi_parser::ArrowOpt), - Back(yazi_parser::VoidOpt), - BulkExit(yazi_parser::mgr::BulkExitOpt), - BulkRename(yazi_parser::VoidOpt), - Cd(yazi_parser::mgr::CdOpt), - Close(yazi_parser::mgr::CloseOpt), - Copy(yazi_parser::mgr::CopyOpt), - Create(yazi_parser::mgr::CreateOpt), - Displace(yazi_parser::VoidOpt), - DisplaceDo(yazi_parser::mgr::DisplaceDoOpt), - Download(yazi_parser::mgr::DownloadOpt), - Enter(yazi_parser::VoidOpt), - Escape(yazi_parser::mgr::EscapeOpt), - EscapeFilter(yazi_parser::VoidOpt), - EscapeFind(yazi_parser::VoidOpt), - EscapeSearch(yazi_parser::VoidOpt), - EscapeSelect(yazi_parser::VoidOpt), - EscapeVisual(yazi_parser::VoidOpt), - Filter(yazi_parser::mgr::FilterOpt), - FilterDo(yazi_parser::mgr::FilterOpt), - Find(yazi_parser::mgr::FindOpt), - FindArrow(yazi_parser::mgr::FindArrowOpt), - FindDo(yazi_parser::mgr::FindDoOpt), - Follow(yazi_parser::VoidOpt), - Forward(yazi_parser::VoidOpt), - Hardlink(yazi_parser::mgr::HardlinkOpt), - Hidden(yazi_parser::mgr::HiddenOpt), - Hover(yazi_parser::mgr::HoverOpt), - Leave(yazi_parser::VoidOpt), - Linemode(yazi_parser::mgr::LinemodeOpt), - Link(yazi_parser::mgr::LinkOpt), - Open(yazi_parser::mgr::OpenOpt), - OpenDo(yazi_parser::mgr::OpenDoOpt), - Paste(yazi_parser::mgr::PasteOpt), - Peek(yazi_parser::mgr::PeekOpt), - Quit(yazi_parser::app::QuitOpt), - Refresh(yazi_parser::VoidOpt), - Remove(yazi_parser::mgr::RemoveOpt), - RemoveDo(yazi_parser::mgr::RemoveOpt), - Rename(yazi_parser::mgr::RenameOpt), - Reveal(yazi_parser::mgr::RevealOpt), - Search(yazi_parser::mgr::SearchOpt), - SearchDo(yazi_parser::mgr::SearchOpt), - SearchStop(yazi_parser::VoidOpt), - Seek(yazi_parser::mgr::SeekOpt), - Shell(yazi_parser::mgr::ShellOpt), - Sort(yazi_parser::mgr::SortOpt), - Spot(yazi_parser::mgr::SpotOpt), - Stash(yazi_parser::mgr::StashOpt), - Suspend(yazi_parser::VoidOpt), - TabClose(yazi_parser::mgr::TabCloseOpt), - TabCreate(yazi_parser::mgr::TabCreateOpt), - TabRename(yazi_parser::mgr::TabRenameOpt), - TabSwap(yazi_parser::ArrowOpt), - TabSwitch(yazi_parser::mgr::TabSwitchOpt), - Toggle(yazi_parser::mgr::ToggleOpt), - ToggleAll(yazi_parser::mgr::ToggleAllOpt), - Unyank(yazi_parser::VoidOpt), - UpdateFiles(yazi_parser::mgr::UpdateFilesOpt), - UpdateMimes(yazi_parser::mgr::UpdateMimesOpt), - UpdatePaged(yazi_parser::mgr::UpdatePagedOpt), - UpdatePeeked(yazi_parser::mgr::UpdatePeekedOpt), - UpdateSpotted(yazi_parser::mgr::UpdateSpottedOpt), - UpdateYanked(yazi_parser::mgr::UpdateYankedOpt<'a>), - Upload(yazi_parser::mgr::UploadOpt), - VisualMode(yazi_parser::mgr::VisualModeOpt), - Watch(yazi_parser::VoidOpt), - Yank(yazi_parser::mgr::YankOpt), - - // Cmp - CmpArrow(yazi_parser::ArrowOpt), - CmpClose(yazi_parser::cmp::CloseOpt), - CmpShow(yazi_parser::cmp::ShowOpt), - CmpTrigger(yazi_parser::cmp::TriggerOpt), - - // Confirm - ConfirmArrow(yazi_parser::ArrowOpt), - ConfirmClose(yazi_parser::confirm::CloseOpt), - ConfirmShow(Box), - - // Help - HelpArrow(yazi_parser::ArrowOpt), - HelpEscape(yazi_parser::VoidOpt), - HelpFilter(yazi_parser::VoidOpt), - HelpToggle(yazi_parser::help::ToggleOpt), - - // Input - InputBackspace(yazi_widgets::input::parser::BackspaceOpt), - InputBackward(yazi_widgets::input::parser::BackwardOpt), - InputClose(yazi_parser::input::CloseOpt), - InputComplete(yazi_widgets::input::parser::CompleteOpt), - InputDelete(yazi_widgets::input::parser::DeleteOpt), - InputEscape(yazi_parser::VoidOpt), - InputForward(yazi_widgets::input::parser::ForwardOpt), - InputInsert(yazi_widgets::input::parser::InsertOpt), - InputKill(yazi_widgets::input::parser::KillOpt), - InputMove(yazi_widgets::input::parser::MoveOpt), - InputPaste(yazi_widgets::input::parser::PasteOpt), - InputShow(yazi_widgets::input::InputOpt), - - // Notify - NotifyPush(yazi_parser::notify::PushOpt), - NotifyTick(yazi_parser::notify::TickOpt), - - // Pick - PickArrow(yazi_parser::ArrowOpt), - PickClose(yazi_parser::pick::CloseOpt), - PickShow(yazi_parser::pick::ShowOpt), - - // Spot - SpotArrow(yazi_parser::ArrowOpt), - SpotClose(yazi_parser::VoidOpt), - SpotCopy(yazi_parser::spot::CopyOpt), - SpotSwipe(yazi_parser::ArrowOpt), - - // Tasks - TasksArrow(yazi_parser::ArrowOpt), - TasksCancel(yazi_parser::VoidOpt), - TasksClose(yazi_parser::VoidOpt), - TasksInspect(yazi_parser::VoidOpt), - TasksOpenShellCompat(yazi_parser::tasks::ProcessOpenOpt), - TasksProcessOpen(yazi_parser::tasks::ProcessOpenOpt), - TasksShow(yazi_parser::VoidOpt), - TasksUpdateSucceed(yazi_parser::tasks::UpdateSucceedOpt), - - // Which - WhichActivate(yazi_parser::which::ActivateOpt), - WhichDismiss(yazi_parser::VoidOpt), -} - -impl<'a> Spark<'a> { - pub fn from_lua(lua: &Lua, kind: SparkKind, value: Value) -> mlua::Result { - use SparkKind::*; - - Ok(match kind { - // app:title - IndAppTitle => Self::AppTitle(<_>::from_lua(value, lua)?), - - // mgr:hidden - KeyHidden => Self::Hidden(<_>::from_lua(value, lua)?), - IndHidden => Self::Hidden(<_>::from_lua(value, lua)?), - // mgr:sort - KeySort => Self::Sort(<_>::from_lua(value, lua)?), - IndSort => Self::Sort(<_>::from_lua(value, lua)?), - // mgr:stash - IndStash => Self::Stash(<_>::from_lua(value, lua)?), - RelayStash => Self::Stash(<_>::from_lua(value, lua)?), - // mgr:quit - KeyQuit => Self::Quit(<_>::from_lua(value, lua)?), - - // which:activate - IndWhichActivate => Self::WhichActivate(<_>::from_lua(value, lua)?), - - // notify:push - RelayNotifyPush => Self::NotifyPush(<_>::from_lua(value, lua)?), - }) - } -} - -impl<'a> IntoLua for Spark<'a> { - fn into_lua(self, lua: &Lua) -> mlua::Result { - match self { - // Void - Self::Void(b) => b.into_lua(lua), - - // App - Self::AppAcceptPayload(b) => b.into_lua(lua), - Self::AppBootstrap(b) => b.into_lua(lua), - Self::AppDeprecate(b) => b.into_lua(lua), - Self::AppFocus(b) => b.into_lua(lua), - Self::AppLua(b) => b.into_lua(lua), - Self::AppMouse(b) => b.into_lua(lua), - Self::AppPlugin(b) => b.into_lua(lua), - Self::AppPluginDo(b) => b.into_lua(lua), - Self::AppQuit(b) => b.into_lua(lua), - Self::AppReflow(b) => b.into_lua(lua), - Self::AppResize(b) => b.into_lua(lua), - Self::AppResume(b) => b.into_lua(lua), - Self::AppStop(b) => b.into_lua(lua), - Self::AppTitle(b) => b.into_lua(lua), - Self::AppUpdateProgress(b) => b.into_lua(lua), - - // Mgr - Self::Arrow(b) => b.into_lua(lua), - Self::Back(b) => b.into_lua(lua), - Self::BulkExit(b) => b.into_lua(lua), - Self::BulkRename(b) => b.into_lua(lua), - Self::Cd(b) => b.into_lua(lua), - Self::Close(b) => b.into_lua(lua), - Self::Copy(b) => b.into_lua(lua), - Self::Create(b) => b.into_lua(lua), - Self::Displace(b) => b.into_lua(lua), - Self::DisplaceDo(b) => b.into_lua(lua), - Self::Download(b) => b.into_lua(lua), - Self::Enter(b) => b.into_lua(lua), - Self::Escape(b) => b.into_lua(lua), - Self::EscapeFilter(b) => b.into_lua(lua), - Self::EscapeFind(b) => b.into_lua(lua), - Self::EscapeSearch(b) => b.into_lua(lua), - Self::EscapeSelect(b) => b.into_lua(lua), - Self::EscapeVisual(b) => b.into_lua(lua), - Self::Filter(b) => b.into_lua(lua), - Self::FilterDo(b) => b.into_lua(lua), - Self::Find(b) => b.into_lua(lua), - Self::FindArrow(b) => b.into_lua(lua), - Self::FindDo(b) => b.into_lua(lua), - Self::Follow(b) => b.into_lua(lua), - Self::Forward(b) => b.into_lua(lua), - Self::Hardlink(b) => b.into_lua(lua), - Self::Hidden(b) => b.into_lua(lua), - Self::Hover(b) => b.into_lua(lua), - Self::Leave(b) => b.into_lua(lua), - Self::Linemode(b) => b.into_lua(lua), - Self::Link(b) => b.into_lua(lua), - Self::Open(b) => b.into_lua(lua), - Self::OpenDo(b) => b.into_lua(lua), - Self::Paste(b) => b.into_lua(lua), - Self::Peek(b) => b.into_lua(lua), - Self::Quit(b) => b.into_lua(lua), - Self::Refresh(b) => b.into_lua(lua), - Self::Remove(b) => b.into_lua(lua), - Self::RemoveDo(b) => b.into_lua(lua), - Self::Rename(b) => b.into_lua(lua), - Self::Reveal(b) => b.into_lua(lua), - Self::Search(b) => b.into_lua(lua), - Self::SearchDo(b) => b.into_lua(lua), - Self::SearchStop(b) => b.into_lua(lua), - Self::Seek(b) => b.into_lua(lua), - Self::Shell(b) => b.into_lua(lua), - Self::Sort(b) => b.into_lua(lua), - Self::Spot(b) => b.into_lua(lua), - Self::Stash(b) => b.into_lua(lua), - Self::Suspend(b) => b.into_lua(lua), - Self::TabClose(b) => b.into_lua(lua), - Self::TabCreate(b) => b.into_lua(lua), - Self::TabRename(b) => b.into_lua(lua), - Self::TabSwap(b) => b.into_lua(lua), - Self::TabSwitch(b) => b.into_lua(lua), - Self::Toggle(b) => b.into_lua(lua), - Self::ToggleAll(b) => b.into_lua(lua), - Self::Unyank(b) => b.into_lua(lua), - Self::UpdateFiles(b) => b.into_lua(lua), - Self::UpdateMimes(b) => b.into_lua(lua), - Self::UpdatePaged(b) => b.into_lua(lua), - Self::UpdatePeeked(b) => b.into_lua(lua), - Self::UpdateSpotted(b) => b.into_lua(lua), - Self::UpdateYanked(b) => b.into_lua(lua), - Self::Upload(b) => b.into_lua(lua), - Self::VisualMode(b) => b.into_lua(lua), - Self::Watch(b) => b.into_lua(lua), - Self::Yank(b) => b.into_lua(lua), - - // Cmp - Self::CmpArrow(b) => b.into_lua(lua), - Self::CmpClose(b) => b.into_lua(lua), - Self::CmpShow(b) => b.into_lua(lua), - Self::CmpTrigger(b) => b.into_lua(lua), - - // Confirm - Self::ConfirmArrow(b) => b.into_lua(lua), - Self::ConfirmClose(b) => b.into_lua(lua), - Self::ConfirmShow(b) => b.into_lua(lua), - - // Help - Self::HelpArrow(b) => b.into_lua(lua), - Self::HelpEscape(b) => b.into_lua(lua), - Self::HelpFilter(b) => b.into_lua(lua), - Self::HelpToggle(b) => b.into_lua(lua), - - // Input - Self::InputBackspace(b) => b.into_lua(lua), - Self::InputBackward(b) => b.into_lua(lua), - Self::InputClose(b) => b.into_lua(lua), - Self::InputComplete(b) => b.into_lua(lua), - Self::InputDelete(b) => b.into_lua(lua), - Self::InputEscape(b) => b.into_lua(lua), - Self::InputForward(b) => b.into_lua(lua), - Self::InputInsert(b) => b.into_lua(lua), - Self::InputKill(b) => b.into_lua(lua), - Self::InputMove(b) => b.into_lua(lua), - Self::InputPaste(b) => b.into_lua(lua), - Self::InputShow(b) => b.into_lua(lua), - - // Notify - Self::NotifyPush(b) => b.into_lua(lua), - Self::NotifyTick(b) => b.into_lua(lua), - - // Pick - Self::PickArrow(b) => b.into_lua(lua), - Self::PickClose(b) => b.into_lua(lua), - Self::PickShow(b) => b.into_lua(lua), - - // Spot - Self::SpotArrow(b) => b.into_lua(lua), - Self::SpotClose(b) => b.into_lua(lua), - Self::SpotCopy(b) => b.into_lua(lua), - Self::SpotSwipe(b) => b.into_lua(lua), - - // Tasks - Self::TasksArrow(b) => b.into_lua(lua), - Self::TasksCancel(b) => b.into_lua(lua), - Self::TasksClose(b) => b.into_lua(lua), - Self::TasksInspect(b) => b.into_lua(lua), - Self::TasksOpenShellCompat(b) => b.into_lua(lua), - Self::TasksProcessOpen(b) => b.into_lua(lua), - Self::TasksShow(b) => b.into_lua(lua), - Self::TasksUpdateSucceed(b) => b.into_lua(lua), - - // Which - Self::WhichActivate(b) => b.into_lua(lua), - Self::WhichDismiss(b) => b.into_lua(lua), - } - } -} - -try_from_spark!( - yazi_parser::VoidOpt, - app:bootstrap, - app:focus, - mgr:back, - mgr:bulk_rename, - mgr:enter, - mgr:escape_filter, - mgr:escape_find, - mgr:escape_search, - mgr:escape_select, - mgr:escape_visual, - mgr:follow, - mgr:forward, - mgr:leave, - mgr:refresh, - mgr:search_stop, - mgr:suspend, - mgr:unyank, - mgr:watch, - which:dismiss -); - -// App -try_from_spark!(yazi_parser::ArrowOpt, mgr:arrow, mgr:tab_swap); -try_from_spark!(yazi_parser::app::DeprecateOpt, app:deprecate); -try_from_spark!(yazi_parser::app::LuaOpt, app:lua); -try_from_spark!(yazi_parser::app::MouseOpt, app:mouse); -try_from_spark!(yazi_parser::app::PluginOpt, app:plugin, app:plugin_do); -try_from_spark!(yazi_parser::app::QuitOpt, app:quit, mgr:quit); -try_from_spark!(yazi_parser::app::ReflowOpt, app:reflow, app:resize); -try_from_spark!(yazi_parser::app::ResumeOpt, app:resume); -try_from_spark!(yazi_parser::app::StopOpt, app:stop); -try_from_spark!(yazi_parser::app::TitleOpt, app:title); -try_from_spark!(yazi_parser::app::UpdateProgressOpt, app:update_progress); -try_from_spark!(yazi_parser::cmp::CloseOpt, cmp:close); -try_from_spark!(yazi_parser::cmp::ShowOpt, cmp:show); -try_from_spark!(yazi_parser::cmp::TriggerOpt, cmp:trigger); -try_from_spark!(yazi_parser::confirm::CloseOpt, confirm:close); -try_from_spark!(yazi_parser::confirm::ShowOpt, confirm:show); -try_from_spark!(yazi_parser::help::ToggleOpt, help:toggle); -try_from_spark!(yazi_parser::input::CloseOpt, input:close); -try_from_spark!(yazi_widgets::input::InputOpt, input:show); -try_from_spark!(yazi_parser::mgr::BulkExitOpt, mgr:bulk_exit); -try_from_spark!(yazi_parser::mgr::CdOpt, mgr:cd); -try_from_spark!(yazi_parser::mgr::CloseOpt, mgr:close); -try_from_spark!(yazi_parser::mgr::CopyOpt, mgr:copy); -try_from_spark!(yazi_parser::mgr::CreateOpt, mgr:create); -try_from_spark!(yazi_parser::mgr::DisplaceDoOpt, mgr:displace_do); -try_from_spark!(yazi_parser::mgr::DownloadOpt, mgr:download); -try_from_spark!(yazi_parser::mgr::EscapeOpt, mgr:escape); -try_from_spark!(yazi_parser::mgr::FilterOpt, mgr:filter, mgr:filter_do); -try_from_spark!(yazi_parser::mgr::FindArrowOpt, mgr:find_arrow); -try_from_spark!(yazi_parser::mgr::FindDoOpt, mgr:find_do); -try_from_spark!(yazi_parser::mgr::FindOpt, mgr:find); -try_from_spark!(yazi_parser::mgr::HardlinkOpt, mgr:hardlink); -try_from_spark!(yazi_parser::mgr::HiddenOpt, mgr:hidden); -try_from_spark!(yazi_parser::mgr::HoverOpt, mgr:hover); -try_from_spark!(yazi_parser::mgr::LinemodeOpt, mgr:linemode); -try_from_spark!(yazi_parser::mgr::LinkOpt, mgr:link); -try_from_spark!(yazi_parser::mgr::OpenDoOpt, mgr:open_do); -try_from_spark!(yazi_parser::mgr::OpenOpt, mgr:open); -try_from_spark!(yazi_parser::mgr::PasteOpt, mgr:paste); -try_from_spark!(yazi_parser::mgr::PeekOpt, mgr:peek); -try_from_spark!(yazi_parser::mgr::RemoveOpt, mgr:remove, mgr:remove_do); -try_from_spark!(yazi_parser::mgr::RenameOpt, mgr:rename); -try_from_spark!(yazi_parser::mgr::RevealOpt, mgr:reveal); -try_from_spark!(yazi_parser::mgr::SearchOpt, mgr:search, mgr:search_do); -try_from_spark!(yazi_parser::mgr::SeekOpt, mgr:seek); -try_from_spark!(yazi_parser::mgr::ShellOpt, mgr:shell); -try_from_spark!(yazi_parser::mgr::SortOpt, mgr:sort); -try_from_spark!(yazi_parser::mgr::SpotOpt, mgr:spot); -try_from_spark!(yazi_parser::mgr::StashOpt, mgr:stash); -try_from_spark!(yazi_parser::mgr::TabCloseOpt, mgr:tab_close); -try_from_spark!(yazi_parser::mgr::TabCreateOpt, mgr:tab_create); -try_from_spark!(yazi_parser::mgr::TabRenameOpt, mgr:tab_rename); -try_from_spark!(yazi_parser::mgr::TabSwitchOpt, mgr:tab_switch); -try_from_spark!(yazi_parser::mgr::ToggleAllOpt, mgr:toggle_all); -try_from_spark!(yazi_parser::mgr::ToggleOpt, mgr:toggle); -try_from_spark!(yazi_parser::mgr::UpdateFilesOpt, mgr:update_files); -try_from_spark!(yazi_parser::mgr::UpdateMimesOpt, mgr:update_mimes); -try_from_spark!(yazi_parser::mgr::UpdatePagedOpt, mgr:update_paged); -try_from_spark!(yazi_parser::mgr::UpdatePeekedOpt, mgr:update_peeked); -try_from_spark!(yazi_parser::mgr::UpdateSpottedOpt, mgr:update_spotted); -try_from_spark!(yazi_parser::mgr::UpdateYankedOpt<'a>, mgr:update_yanked); -try_from_spark!(yazi_parser::mgr::UploadOpt, mgr:upload); -try_from_spark!(yazi_parser::mgr::VisualModeOpt, mgr:visual_mode); -try_from_spark!(yazi_parser::mgr::YankOpt, mgr:yank); -try_from_spark!(yazi_parser::notify::PushOpt, notify:push); -try_from_spark!(yazi_parser::notify::TickOpt, notify:tick); -try_from_spark!(yazi_parser::pick::CloseOpt, pick:close); -try_from_spark!(yazi_parser::pick::ShowOpt, pick:show); -try_from_spark!(yazi_parser::spot::CopyOpt, spot:copy); -try_from_spark!(yazi_parser::tasks::ProcessOpenOpt, tasks:process_open); -try_from_spark!(yazi_parser::tasks::UpdateSucceedOpt, tasks:update_succeed); -try_from_spark!(yazi_parser::which::ActivateOpt, which:activate); -try_from_spark!(yazi_widgets::input::parser::BackspaceOpt, input:backspace); -try_from_spark!(yazi_widgets::input::parser::BackwardOpt, input:backward); -try_from_spark!(yazi_widgets::input::parser::CompleteOpt, input:complete); -try_from_spark!(yazi_widgets::input::parser::DeleteOpt, input:delete); -try_from_spark!(yazi_widgets::input::parser::ForwardOpt, input:forward); -try_from_spark!(yazi_widgets::input::parser::InsertOpt, input:insert); -try_from_spark!(yazi_widgets::input::parser::KillOpt, input:kill); -try_from_spark!(yazi_widgets::input::parser::MoveOpt, input:move); -try_from_spark!(yazi_widgets::input::parser::PasteOpt, input:paste); diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 516b44f0..79599465 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -27,25 +27,27 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-actor = { path = "../yazi-actor", version = "26.2.2" } -yazi-adapter = { path = "../yazi-adapter", version = "26.2.2" } -yazi-binding = { path = "../yazi-binding", version = "26.2.2" } -yazi-boot = { path = "../yazi-boot", version = "26.2.2" } -yazi-config = { path = "../yazi-config", version = "26.2.2" } -yazi-core = { path = "../yazi-core", version = "26.2.2" } -yazi-dds = { path = "../yazi-dds", version = "26.2.2" } -yazi-emulator = { path = "../yazi-emulator", version = "26.2.2" } -yazi-fs = { path = "../yazi-fs", version = "26.2.2" } -yazi-macro = { path = "../yazi-macro", version = "26.2.2" } -yazi-parser = { path = "../yazi-parser", version = "26.2.2" } -yazi-plugin = { path = "../yazi-plugin", version = "26.2.2" } -yazi-proxy = { path = "../yazi-proxy", version = "26.2.2" } -yazi-shared = { path = "../yazi-shared", version = "26.2.2" } -yazi-term = { path = "../yazi-term", version = "26.2.2" } -yazi-tty = { path = "../yazi-tty", version = "26.2.2" } -yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } -yazi-watcher = { path = "../yazi-watcher", version = "26.2.2" } -yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } +yazi-actor = { path = "../yazi-actor", version = "26.2.2" } +yazi-adapter = { path = "../yazi-adapter", version = "26.2.2" } +yazi-binding = { path = "../yazi-binding", version = "26.2.2" } +yazi-boot = { path = "../yazi-boot", version = "26.2.2" } +yazi-config = { path = "../yazi-config", version = "26.2.2" } +yazi-core = { path = "../yazi-core", version = "26.2.2" } +yazi-dds = { path = "../yazi-dds", version = "26.2.2" } +yazi-emulator = { path = "../yazi-emulator", version = "26.2.2" } +yazi-fs = { path = "../yazi-fs", version = "26.2.2" } +yazi-macro = { path = "../yazi-macro", version = "26.2.2" } +yazi-parser = { path = "../yazi-parser", version = "26.2.2" } +yazi-plugin = { path = "../yazi-plugin", version = "26.2.2" } +yazi-proxy = { path = "../yazi-proxy", version = "26.2.2" } +yazi-runner = { path = "../yazi-runner", version = "26.2.2" } +yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" } +yazi-shared = { path = "../yazi-shared", version = "26.2.2" } +yazi-term = { path = "../yazi-term", version = "26.2.2" } +yazi-tty = { path = "../yazi-tty", version = "26.2.2" } +yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } +yazi-watcher = { path = "../yazi-watcher", version = "26.2.2" } +yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } # External dependencies anyhow = { workspace = true } diff --git a/yazi-fm/src/dispatcher.rs b/yazi-fm/src/dispatcher.rs index 2b5af3d6..d6cc43d2 100644 --- a/yazi-fm/src/dispatcher.rs +++ b/yazi-fm/src/dispatcher.rs @@ -2,12 +2,11 @@ use std::sync::atomic::Ordering; use anyhow::Result; use crossterm::event::{KeyEvent, MouseEvent}; -use tokio::sync::mpsc; use tracing::warn; use yazi_actor::Ctx; use yazi_config::keymap::Key; use yazi_macro::{act, emit}; -use yazi_shared::{data::Data, event::{ActionCow, Event, NEED_RENDER}}; +use yazi_shared::event::{ActionCow, Event, NEED_RENDER}; use yazi_widgets::input::InputMode; use crate::{Executor, Router, app::App}; @@ -39,7 +38,7 @@ impl<'a> Dispatcher<'a> { } fn dispatch_call(&mut self, action: ActionCow) -> Result<()> { - let tx = action.any::>>("__reply").cloned(); + let tx = action.replier().cloned(); let result = Executor::new(self.app).execute(action); if let Err(e) = &result { diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index 6f1446c6..eee14d5a 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -34,6 +34,8 @@ async fn main() -> anyhow::Result<()> { yazi_watcher::init(); + yazi_runner::init(yazi_plugin::slim_lua); + yazi_plugin::init()?; yazi_dds::serve(); diff --git a/yazi-fm/src/signals.rs b/yazi-fm/src/signals.rs index 6fe2641b..75bc9e5e 100644 --- a/yazi-fm/src/signals.rs +++ b/yazi-fm/src/signals.rs @@ -21,25 +21,26 @@ impl Signals { fn handle_sys(n: libc::c_int) -> bool { use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGSTOP, SIGTERM, SIGTSTP}; use tracing::error; - use yazi_proxy::AppProxy; use yazi_term::YIELD_TO_SUBPROCESS; match n { SIGINT => { /* ignored */ } SIGQUIT | SIGHUP | SIGTERM => { - AppProxy::quit(Default::default()); + yazi_proxy::AppProxy::quit(Default::default()); return false; } SIGTSTP => { tokio::spawn(async move { - AppProxy::stop().await; + yazi_scheduler::AppProxy::stop().await; if unsafe { libc::kill(0, SIGSTOP) } != 0 { error!("Failed to stop the process:\n{}", std::io::Error::last_os_error()); - AppProxy::quit(Default::default()); + yazi_proxy::AppProxy::quit(Default::default()); } }); } - SIGCONT if YIELD_TO_SUBPROCESS.try_acquire().is_ok() => _ = tokio::spawn(AppProxy::resume()), + SIGCONT if YIELD_TO_SUBPROCESS.try_acquire().is_ok() => { + tokio::spawn(yazi_scheduler::AppProxy::resume()); + } _ => {} } true diff --git a/yazi-macro/src/actor.rs b/yazi-macro/src/actor.rs index c7daf069..28ade8c7 100644 --- a/yazi-macro/src/actor.rs +++ b/yazi-macro/src/actor.rs @@ -2,7 +2,7 @@ macro_rules! act { (@pre $layer:ident : $name:ident, $cx:ident, $opt:ident) => { if let Some(hook) = ::hook($cx, &$opt) { - ::act($cx, (hook, yazi_dds::spark!($layer:$name, $opt))).map(|spark| spark.try_into().unwrap()) + ::act($cx, (hook, yazi_parser::spark!($layer:$name, $opt))).map(|spark| spark.try_into().unwrap()) } else { Ok($opt) } diff --git a/yazi-macro/src/asset.rs b/yazi-macro/src/asset.rs index 4058f8bc..e3d137e6 100644 --- a/yazi-macro/src/asset.rs +++ b/yazi-macro/src/asset.rs @@ -30,15 +30,13 @@ macro_rules! plugin_preset { ($name:literal) => {{ #[cfg(debug_assertions)] { - std::fs::read(concat!(env!("CARGO_MANIFEST_DIR"), "/preset/", $name, ".lua")).expect(concat!( - "Failed to read 'yazi-plugin/preset/", - $name, - ".lua'" - )) + std::fs::read(concat!(env!("CARGO_MANIFEST_DIR"), "/../yazi-plugin/preset/", $name, ".lua")) + .expect(concat!("Failed to read 'yazi-plugin/preset/", $name, ".lua'")) } #[cfg(not(debug_assertions))] { - &include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/preset/", $name, ".lua"))[..] + &include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../yazi-plugin/preset/", $name, ".lua")) + [..] } }}; } diff --git a/yazi-parser/Cargo.toml b/yazi-parser/Cargo.toml index d23b520e..93f518d6 100644 --- a/yazi-parser/Cargo.toml +++ b/yazi-parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yazi-parser" -description = "Yazi command parser" +description = "Yazi form parser" version.workspace = true edition.workspace = true license.workspace = true @@ -17,14 +17,18 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-binding = { path = "../yazi-binding", version = "26.2.2" } -yazi-boot = { path = "../yazi-boot", version = "26.2.2" } -yazi-config = { path = "../yazi-config", version = "26.2.2" } -yazi-fs = { path = "../yazi-fs", version = "26.2.2" } -yazi-macro = { path = "../yazi-macro", version = "26.2.2" } -yazi-shared = { path = "../yazi-shared", version = "26.2.2" } -yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } -yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } +yazi-binding = { path = "../yazi-binding", version = "26.2.2" } +yazi-boot = { path = "../yazi-boot", version = "26.2.2" } +yazi-config = { path = "../yazi-config", version = "26.2.2" } +yazi-core = { path = "../yazi-core", version = "26.2.2" } +yazi-dds = { path = "../yazi-dds", version = "26.2.2" } +yazi-fs = { path = "../yazi-fs", version = "26.2.2" } +yazi-macro = { path = "../yazi-macro", version = "26.2.2" } +yazi-runner = { path = "../yazi-runner", version = "26.2.2" } +yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" } +yazi-shared = { path = "../yazi-shared", version = "26.2.2" } +yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } +yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } # External dependencies anyhow = { workspace = true } @@ -32,8 +36,10 @@ bitflags = { workspace = true } crossterm = { workspace = true } dyn-clone = { workspace = true } hashbrown = { workspace = true } +indexmap = { workspace = true } mlua = { workspace = true } ordered-float = { workspace = true } +paste = { workspace = true } ratatui = { workspace = true } serde = { workspace = true } serde_with = { workspace = true } diff --git a/yazi-parser/src/app/plugin.rs b/yazi-parser/src/app/plugin.rs index 30d34575..e2d0eb97 100644 --- a/yazi-parser/src/app/plugin.rs +++ b/yazi-parser/src/app/plugin.rs @@ -1,117 +1,30 @@ -use std::{borrow::Cow, fmt::{self, Debug}, str::FromStr}; +use std::fmt::Debug; -use anyhow::bail; -use dyn_clone::DynClone; -use hashbrown::HashMap; -use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; -use serde::Deserialize; -use yazi_shared::{SStr, data::{Data, DataKey}, event::{Action, ActionCow}}; +use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use yazi_runner::plugin::PluginOpt; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug, Default)] -pub struct PluginOpt { - pub id: SStr, - pub args: HashMap, - pub mode: PluginMode, - pub callback: Option>, +pub struct PluginForm { + pub opt: PluginOpt, } -impl TryFrom for PluginOpt { +impl From for PluginForm { + fn from(opt: PluginOpt) -> Self { Self { opt } } +} + +impl TryFrom for PluginForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any("opt") { - return Ok(opt); - } - - let Some(id) = a.take_first::().ok().filter(|s| !s.is_empty()) else { - bail!("plugin id cannot be empty"); - }; - - let args = if let Ok(s) = a.second() { - let (words, last) = yazi_shared::shell::unix::split(s, true)?; - Action::parse_args(words, last)? - } else { - Default::default() - }; - - let mode = a.str("mode").parse().unwrap_or_default(); - Ok(Self { id: Self::normalize_id(id), args, mode, callback: a.take_any("callback") }) + Ok(Self { opt: if let Some(opt) = a.take_any("opt") { opt } else { a.try_into()? } }) } } -impl FromLua for PluginOpt { +impl FromLua for PluginForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for PluginOpt { +impl IntoLua for PluginForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } - -impl PluginOpt { - pub fn new_callback(id: impl Into, f: impl PluginCallback) -> Self { - Self { - id: Self::normalize_id(id.into()), - mode: PluginMode::Sync, - callback: Some(Box::new(f)), - ..Default::default() - } - } - - fn normalize_id(s: SStr) -> SStr { - match s { - Cow::Borrowed(s) => s.trim_end_matches(".main").into(), - Cow::Owned(mut s) => { - s.truncate(s.trim_end_matches(".main").len()); - s.into() - } - } - } -} - -// --- Mode -#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "kebab-case")] -pub enum PluginMode { - #[default] - Auto, - Sync, - Async, -} - -impl FromStr for PluginMode { - type Err = serde::de::value::Error; - - fn from_str(s: &str) -> Result { - Self::deserialize(serde::de::value::StrDeserializer::new(s)) - } -} - -impl PluginMode { - pub fn auto_then(self, sync: bool) -> Self { - if self != Self::Auto { - return self; - } - if sync { Self::Sync } else { Self::Async } - } -} - -// --- Callback -pub trait PluginCallback: - FnOnce(&Lua, Table) -> mlua::Result<()> + Send + Sync + DynClone + 'static -{ -} - -impl PluginCallback for T where - T: FnOnce(&Lua, Table) -> mlua::Result<()> + Send + Sync + DynClone + 'static -{ -} - -impl Clone for Box { - fn clone(&self) -> Self { dyn_clone::clone_box(&**self) } -} - -impl fmt::Debug for dyn PluginCallback { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PluginCallback").finish_non_exhaustive() - } -} diff --git a/yazi-parser/src/app/quit.rs b/yazi-parser/src/app/quit.rs index 283d8f38..59756cb1 100644 --- a/yazi-parser/src/app/quit.rs +++ b/yazi-parser/src/app/quit.rs @@ -1,36 +1,31 @@ -use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; +use mlua::{FromLua, IntoLua, Lua, Value}; use serde::{Deserialize, Serialize}; -use yazi_binding::SER_OPT; -use yazi_shared::{event::ActionCow, strand::StrandBuf}; +use yazi_core::app::QuitOpt; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct QuitOpt { - pub code: i32, - #[serde(skip)] - pub selected: Option, - pub no_cwd_file: bool, +pub struct QuitForm { + pub opt: QuitOpt, } -impl TryFrom for QuitOpt { +impl From for QuitForm { + fn from(opt: QuitOpt) -> Self { Self { opt } } +} + +impl TryFrom for QuitForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - return opt; - } - - Ok(Self { - code: a.get("code").unwrap_or_default(), - selected: None, - no_cwd_file: a.bool("no-cwd-file"), - }) + Ok(Self { opt: if let Some(opt) = a.take_any("opt") { opt } else { a.try_into()? } }) } } -impl FromLua for QuitOpt { - fn from_lua(value: Value, lua: &Lua) -> mlua::Result { lua.from_value(value) } +impl FromLua for QuitForm { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { + Ok(Self { opt: <_>::from_lua(value, lua)? }) + } } -impl IntoLua for QuitOpt { - fn into_lua(self, lua: &Lua) -> mlua::Result { lua.to_value_with(&self, SER_OPT) } +impl IntoLua for QuitForm { + fn into_lua(self, lua: &Lua) -> mlua::Result { self.opt.into_lua(lua) } } diff --git a/yazi-parser/src/app/update_progress.rs b/yazi-parser/src/app/update_progress.rs index 58386d48..fa5408e0 100644 --- a/yazi-parser/src/app/update_progress.rs +++ b/yazi-parser/src/app/update_progress.rs @@ -1,7 +1,6 @@ use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use ordered_float::OrderedFloat; -use serde::Serialize; +use yazi_scheduler::TaskSummary; use yazi_shared::event::ActionCow; #[derive(Debug)] @@ -28,12 +27,3 @@ impl FromLua for UpdateProgressOpt { impl IntoLua for UpdateProgressOpt { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } - -// --- Progress -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] -pub struct TaskSummary { - pub total: u32, - pub success: u32, - pub failed: u32, - pub percent: Option>, -} diff --git a/yazi-parser/src/cmp/show.rs b/yazi-parser/src/cmp/show.rs index 625b4cc0..682816c4 100644 --- a/yazi-parser/src/cmp/show.rs +++ b/yazi-parser/src/cmp/show.rs @@ -1,38 +1,33 @@ use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use yazi_shared::{Id, event::ActionCow, path::PathBufDyn, strand::StrandBuf, url::UrlBuf}; +use yazi_core::cmp::CmpOpt; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug)] -pub struct ShowOpt { - pub cache: Vec, - pub cache_name: UrlBuf, - pub word: PathBufDyn, - pub ticket: Id, +pub struct ShowForm { + pub opt: CmpOpt, } -impl TryFrom for ShowOpt { +impl From for ShowForm { + fn from(opt: CmpOpt) -> Self { Self { opt } } +} + +impl TryFrom for ShowForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { if let Some(opt) = a.take_any2("opt") { opt } else { - bail!("missing 'opt' argument"); + bail!("Invalid 'opt' in ShowForm"); } } } -impl FromLua for ShowOpt { +impl FromLua for ShowForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for ShowOpt { +impl IntoLua for ShowForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } - -// --- Item -#[derive(Debug, Clone)] -pub struct CmpItem { - pub name: StrandBuf, - pub is_dir: bool, -} diff --git a/yazi-parser/src/lib.rs b/yazi-parser/src/lib.rs index c86696b1..18288401 100644 --- a/yazi-parser/src/lib.rs +++ b/yazi-parser/src/lib.rs @@ -1,3 +1,5 @@ -yazi_macro::mod_pub!(app cmp confirm help input mgr notify pick spot tasks which); +mod macros; + +yazi_macro::mod_pub!(app cmp confirm help input mgr notify pick spark spot tasks which); yazi_macro::mod_flat!(arrow void); diff --git a/yazi-dds/src/macros.rs b/yazi-parser/src/macros.rs similarity index 100% rename from yazi-dds/src/macros.rs rename to yazi-parser/src/macros.rs diff --git a/yazi-parser/src/mgr/close.rs b/yazi-parser/src/mgr/close.rs index f1b074ce..2dee2cd2 100644 --- a/yazi-parser/src/mgr/close.rs +++ b/yazi-parser/src/mgr/close.rs @@ -1,21 +1,24 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use yazi_core::app::QuitOpt; use yazi_shared::event::ActionCow; -use crate::app::QuitOpt; - #[derive(Debug, Default)] -pub struct CloseOpt(pub QuitOpt); - -impl TryFrom for CloseOpt { - type Error = anyhow::Error; - - fn try_from(a: ActionCow) -> Result { a.try_into().map(Self) } +pub struct CloseForm { + pub opt: QuitOpt, } -impl FromLua for CloseOpt { +impl TryFrom for CloseForm { + type Error = anyhow::Error; + + fn try_from(mut a: ActionCow) -> Result { + Ok(Self { opt: if let Some(opt) = a.take_any("opt") { opt } else { a.try_into()? } }) + } +} + +impl FromLua for CloseForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for CloseOpt { +impl IntoLua for CloseForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } diff --git a/yazi-parser/src/mgr/displace_do.rs b/yazi-parser/src/mgr/displace_do.rs index b2ca6543..e2160a6f 100644 --- a/yazi-parser/src/mgr/displace_do.rs +++ b/yazi-parser/src/mgr/displace_do.rs @@ -1,29 +1,25 @@ -use anyhow::bail; +use anyhow::anyhow; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use yazi_shared::{event::ActionCow, url::UrlBuf}; +use yazi_core::mgr::DisplaceOpt; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug)] -pub struct DisplaceDoOpt { - pub to: Result, - pub from: UrlBuf, +pub struct DisplaceDoForm { + pub opt: DisplaceOpt, } -impl TryFrom for DisplaceDoOpt { +impl TryFrom for DisplaceDoForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - opt - } else { - bail!("Invalid 'opt' in DisplaceDoOpt"); - } + Ok(Self { opt: a.take_any("opt").ok_or_else(|| anyhow!("Invalid 'opt' in DisplaceDoForm"))? }) } } -impl FromLua for DisplaceDoOpt { +impl FromLua for DisplaceDoForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for DisplaceDoOpt { +impl IntoLua for DisplaceDoForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } diff --git a/yazi-parser/src/mgr/filter.rs b/yazi-parser/src/mgr/filter.rs index 0f58c47e..fbf7aac5 100644 --- a/yazi-parser/src/mgr/filter.rs +++ b/yazi-parser/src/mgr/filter.rs @@ -1,34 +1,24 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use yazi_fs::FilterCase; -use yazi_shared::{SStr, event::ActionCow}; +use yazi_core::mgr::FilterOpt; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug, Default)] -pub struct FilterOpt { - pub query: SStr, - pub case: FilterCase, - pub done: bool, +pub struct FilterForm { + pub opt: FilterOpt, } -impl TryFrom for FilterOpt { +impl TryFrom for FilterForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - return opt; - } - - Ok(Self { - query: a.take_first().unwrap_or_default(), - case: FilterCase::from(&*a), - done: a.bool("done"), - }) + Ok(Self { opt: if let Some(opt) = a.take_any("opt") { opt } else { a.try_into()? } }) } } -impl FromLua for FilterOpt { +impl FromLua for FilterForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for FilterOpt { +impl IntoLua for FilterForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } diff --git a/yazi-parser/src/mgr/find_do.rs b/yazi-parser/src/mgr/find_do.rs index 7d09823a..79064d00 100644 --- a/yazi-parser/src/mgr/find_do.rs +++ b/yazi-parser/src/mgr/find_do.rs @@ -1,35 +1,24 @@ -use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use yazi_fs::FilterCase; -use yazi_shared::{SStr, event::ActionCow}; +use yazi_core::mgr::FindDoOpt; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug)] -pub struct FindDoOpt { - pub query: SStr, - pub prev: bool, - pub case: FilterCase, +pub struct FindDoForm { + pub opt: FindDoOpt, } -impl TryFrom for FindDoOpt { +impl TryFrom for FindDoForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - return opt; - } - - let Ok(query) = a.take_first() else { - bail!("Invalid 'query' in FindDoOpt"); - }; - - Ok(Self { query, prev: a.bool("previous"), case: FilterCase::from(&*a) }) + Ok(Self { opt: if let Some(opt) = a.take_any("opt") { opt } else { a.try_into()? } }) } } -impl FromLua for FindDoOpt { +impl FromLua for FindDoForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for FindDoOpt { +impl IntoLua for FindDoForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } diff --git a/yazi-parser/src/mgr/open.rs b/yazi-parser/src/mgr/open.rs index 5086860e..f88da947 100644 --- a/yazi-parser/src/mgr/open.rs +++ b/yazi-parser/src/mgr/open.rs @@ -1,35 +1,28 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use yazi_shared::{event::ActionCow, url::UrlCow}; +use yazi_core::mgr::OpenOpt; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug)] -pub struct OpenOpt { - pub cwd: Option>, - pub targets: Vec>, - pub interactive: bool, - pub hovered: bool, +pub struct OpenForm { + pub opt: OpenOpt, } -impl TryFrom for OpenOpt { +impl From for OpenForm { + fn from(opt: OpenOpt) -> Self { Self { opt } } +} + +impl TryFrom for OpenForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - return opt; - } - - Ok(Self { - cwd: a.take("cwd").ok(), - targets: a.take_seq(), - interactive: a.bool("interactive"), - hovered: a.bool("hovered"), - }) + Ok(Self { opt: if let Some(opt) = a.take_any("opt") { opt } else { a.try_into()? } }) } } -impl FromLua for OpenOpt { +impl FromLua for OpenForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for OpenOpt { +impl IntoLua for OpenForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } diff --git a/yazi-parser/src/mgr/open_do.rs b/yazi-parser/src/mgr/open_do.rs index 9b86eec5..98b89221 100644 --- a/yazi-parser/src/mgr/open_do.rs +++ b/yazi-parser/src/mgr/open_do.rs @@ -1,30 +1,29 @@ -use anyhow::bail; +use anyhow::anyhow; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use yazi_shared::{event::ActionCow, url::UrlCow}; +use yazi_core::mgr::OpenDoOpt; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug, Default)] -pub struct OpenDoOpt { - pub cwd: UrlCow<'static>, - pub targets: Vec>, - pub interactive: bool, +pub struct OpenDoForm { + pub opt: OpenDoOpt, } -impl TryFrom for OpenDoOpt { +impl From for OpenDoForm { + fn from(opt: OpenDoOpt) -> Self { Self { opt } } +} + +impl TryFrom for OpenDoForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - opt - } else { - bail!("Invalid 'opt' in OpenDoOpt"); - } + Ok(Self { opt: a.take_any("opt").ok_or_else(|| anyhow!("Invalid 'opt' in OpenDoForm"))? }) } } -impl FromLua for OpenDoOpt { +impl FromLua for OpenDoForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for OpenDoOpt { +impl IntoLua for OpenDoForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } diff --git a/yazi-parser/src/mgr/search.rs b/yazi-parser/src/mgr/search.rs index f4110804..ffb378e3 100644 --- a/yazi-parser/src/mgr/search.rs +++ b/yazi-parser/src/mgr/search.rs @@ -1,79 +1,24 @@ -use std::str::FromStr; - -use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use serde::Deserialize; -use yazi_shared::{SStr, event::ActionCow, url::{UrlCow, UrlLike}}; +use yazi_core::mgr::SearchOpt; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug)] -pub struct SearchOpt { - pub via: SearchOptVia, - pub subject: SStr, - pub args: Vec, - pub args_raw: SStr, - pub r#in: Option>, +pub struct SearchForm { + pub opt: SearchOpt, } -impl TryFrom for SearchOpt { +impl TryFrom for SearchForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - return opt; - } - - let r#in = a.take::("in").ok(); - if let Some(u) = &r#in - && (!u.is_absolute() || u.is_search()) - { - bail!("invalid 'in' in SearchOpt"); - } - - let Ok(args) = yazi_shared::shell::unix::split(a.str("args"), false) else { - bail!("invalid 'args' in SearchOpt"); - }; - - Ok(Self { - via: a.str("via").parse()?, - subject: a.take_first().unwrap_or_default(), - args: args.0, - args_raw: a.take("args").unwrap_or_default(), - r#in, - }) + Ok(Self { opt: if let Some(opt) = a.take_any("opt") { opt } else { a.try_into()? } }) } } -impl FromLua for SearchOpt { +impl FromLua for SearchForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for SearchOpt { +impl IntoLua for SearchForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } - -// Via -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "kebab-case")] -pub enum SearchOptVia { - Rg, - Rga, - Fd, -} - -impl FromStr for SearchOptVia { - type Err = serde::de::value::Error; - - fn from_str(s: &str) -> Result { - Self::deserialize(serde::de::value::StrDeserializer::new(s)) - } -} - -impl SearchOptVia { - pub fn into_str(self) -> &'static str { - match self { - Self::Rg => "rg", - Self::Rga => "rga", - Self::Fd => "fd", - } - } -} diff --git a/yazi-parser/src/mgr/update_peeked.rs b/yazi-parser/src/mgr/update_peeked.rs index a56c960b..5ce7201b 100644 --- a/yazi-parser/src/mgr/update_peeked.rs +++ b/yazi-parser/src/mgr/update_peeked.rs @@ -1,14 +1,14 @@ use anyhow::bail; -use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; -use yazi_binding::{FileRef, elements::{Rect, Renderable}}; +use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use yazi_core::tab::PreviewLock; use yazi_shared::event::ActionCow; #[derive(Clone, Debug)] -pub struct UpdatePeekedOpt { +pub struct UpdatePeekedForm { pub lock: PreviewLock, } -impl TryFrom for UpdatePeekedOpt { +impl TryFrom for UpdatePeekedForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { @@ -17,46 +17,17 @@ impl TryFrom for UpdatePeekedOpt { } let Some(lock) = a.take_any("lock") else { - bail!("Invalid 'lock' in UpdatePeekedOpt"); + bail!("Invalid 'lock' in UpdatePeekedForm"); }; Ok(Self { lock }) } } -impl FromLua for UpdatePeekedOpt { +impl FromLua for UpdatePeekedForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for UpdatePeekedOpt { +impl IntoLua for UpdatePeekedForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } - -// --- Lock -#[derive(Clone, Debug, Default)] -pub struct PreviewLock { - pub url: yazi_shared::url::UrlBuf, - pub cha: yazi_fs::cha::Cha, - pub mime: String, - - pub skip: usize, - pub area: Rect, - pub data: Vec, -} - -impl TryFrom
for PreviewLock { - type Error = mlua::Error; - - fn try_from(t: Table) -> Result { - let file: FileRef = t.raw_get("file")?; - Ok(Self { - url: file.url_owned(), - cha: file.cha, - mime: t.raw_get("mime")?, - - skip: t.raw_get("skip")?, - area: t.raw_get("area")?, - data: Default::default(), - }) - } -} diff --git a/yazi-parser/src/mgr/update_spotted.rs b/yazi-parser/src/mgr/update_spotted.rs index 63fc765f..e25ceb50 100644 --- a/yazi-parser/src/mgr/update_spotted.rs +++ b/yazi-parser/src/mgr/update_spotted.rs @@ -1,88 +1,29 @@ use anyhow::bail; -use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; -use yazi_binding::{FileRef, elements::Renderable}; -use yazi_shared::{Id, event::ActionCow}; +use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; +use yazi_core::spot::SpotLock; +use yazi_shared::event::ActionCow; #[derive(Clone, Debug)] -pub struct UpdateSpottedOpt { +pub struct UpdateSpottedForm { pub lock: SpotLock, } -impl TryFrom for UpdateSpottedOpt { +impl TryFrom for UpdateSpottedForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - return opt; - } - let Some(lock) = a.take_any("lock") else { - bail!("Invalid 'lock' in UpdateSpottedOpt"); + bail!("Invalid 'lock' in UpdateSpottedForm"); }; Ok(Self { lock }) } } -impl FromLua for UpdateSpottedOpt { +impl FromLua for UpdateSpottedForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for UpdateSpottedOpt { +impl IntoLua for UpdateSpottedForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } - -// --- Lock -#[derive(Clone, Debug)] -pub struct SpotLock { - pub url: yazi_shared::url::UrlBuf, - pub cha: yazi_fs::cha::Cha, - pub mime: String, - - pub id: Id, - pub skip: usize, - pub data: Vec, -} - -impl TryFrom
for SpotLock { - type Error = mlua::Error; - - fn try_from(t: Table) -> Result { - let file: FileRef = t.raw_get("file")?; - Ok(Self { - url: file.url_owned(), - cha: file.cha, - mime: t.raw_get("mime")?, - - id: *t.raw_get::("id")?, - skip: t.raw_get("skip")?, - data: Default::default(), - }) - } -} - -impl SpotLock { - pub fn len(&self) -> Option { Some(self.table()?.len()) } - - pub fn select(&mut self, idx: Option) { - if let Some(t) = self.table_mut() { - t.select(idx); - } - } - - pub fn selected(&self) -> Option { self.table()?.selected() } - - pub fn table(&self) -> Option<&yazi_binding::elements::Table> { - self.data.iter().rev().find_map(|r| match r { - Renderable::Table(t) => Some(t.as_ref()), - _ => None, - }) - } - - pub fn table_mut(&mut self) -> Option<&mut yazi_binding::elements::Table> { - self.data.iter_mut().rev().find_map(|r| match r { - Renderable::Table(t) => Some(t.as_mut()), - _ => None, - }) - } -} diff --git a/yazi-parser/src/mgr/update_yanked.rs b/yazi-parser/src/mgr/update_yanked.rs index ed45f55a..0fe18475 100644 --- a/yazi-parser/src/mgr/update_yanked.rs +++ b/yazi-parser/src/mgr/update_yanked.rs @@ -1,32 +1,28 @@ -use std::borrow::Cow; +use std::ops::Deref; use anyhow::bail; -use hashbrown::HashSet; -use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, MetaMethod, MultiValue, ObjectLike, UserData, UserDataFields, UserDataMethods, Value}; +use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; use serde::{Deserialize, Serialize}; -use yazi_binding::get_metatable; -use yazi_shared::{event::ActionCow, url::UrlBufCov}; +use yazi_shared::event::ActionCow; -type Iter = yazi_binding::Iter< - std::iter::Map, fn(UrlBufCov) -> yazi_binding::Url>, - yazi_binding::Url, ->; +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct UpdateYankedOpt<'a>(pub yazi_dds::ember::EmberYank<'a>); -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct UpdateYankedOpt<'a> { - pub cut: bool, - pub urls: Cow<'a, HashSet>, +impl<'a> Deref for UpdateYankedOpt<'a> { + type Target = yazi_dds::ember::EmberYank<'a>; + + fn deref(&self) -> &Self::Target { &self.0 } } impl TryFrom for UpdateYankedOpt<'_> { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - opt - } else { - bail!("Invalid 'opt' in UpdateYankedOpt"); - } + let Some(state) = a.take_any("state") else { + bail!("Invalid 'state' in UpdateYankedOpt"); + }; + + Ok(Self(state)) } } @@ -35,47 +31,5 @@ impl FromLua for UpdateYankedOpt<'_> { } impl IntoLua for UpdateYankedOpt<'_> { - fn into_lua(self, lua: &Lua) -> mlua::Result { - let len = self.urls.len(); - let iter = Iter::new(self.urls.into_owned().into_iter().map(yazi_binding::Url::new), Some(len)); - UpdateYankedIter { cut: self.cut, len, inner: lua.create_userdata(iter)? }.into_lua(lua) - } -} - -// --- Iter -pub struct UpdateYankedIter { - cut: bool, - len: usize, - inner: AnyUserData, -} - -impl UpdateYankedIter { - pub fn into_opt(self, lua: &Lua) -> mlua::Result> { - Ok(UpdateYankedOpt { - cut: self.cut, - urls: Cow::Owned( - self - .inner - .take::()? - .into_iter(lua) - .map(|result| result.map(Into::into)) - .collect::>()?, - ), - }) - } -} - -impl UserData for UpdateYankedIter { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("cut", |_, me| Ok(me.cut)); - } - - fn add_methods>(methods: &mut M) { - methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len)); - - methods.add_meta_method(MetaMethod::Pairs, |lua, me, ()| { - get_metatable(lua, &me.inner)? - .call_function::(MetaMethod::Pairs.name(), me.inner.clone()) - }); - } + fn into_lua(self, lua: &Lua) -> mlua::Result { self.0.into_lua(lua) } } diff --git a/yazi-parser/src/notify/push.rs b/yazi-parser/src/notify/push.rs index 9e230705..c52c03d9 100644 --- a/yazi-parser/src/notify/push.rs +++ b/yazi-parser/src/notify/push.rs @@ -1,72 +1,30 @@ -use std::{str::FromStr, time::Duration}; - -use anyhow::anyhow; -use mlua::{FromLua, IntoLua, Lua, LuaSerdeExt, Value}; -use serde::{Deserialize, Serialize}; -use serde_with::{DurationSecondsWithFrac, serde_as}; -use yazi_binding::SER_OPT; -use yazi_config::{Style, THEME}; +use mlua::{FromLua, IntoLua, Lua, Value}; +use yazi_core::notify::MessageOpt; use yazi_shared::event::ActionCow; -#[serde_as] -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct PushOpt { - pub title: String, - pub content: String, - #[serde(default)] - pub level: PushLevel, - #[serde_as(as = "DurationSecondsWithFrac")] - pub timeout: Duration, +#[derive(Clone, Debug)] +pub struct PushForm { + pub opt: MessageOpt, } -impl TryFrom for PushOpt { +impl From for PushForm { + fn from(opt: MessageOpt) -> Self { Self { opt } } +} + +impl TryFrom for PushForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - a.take_any("opt").ok_or_else(|| anyhow!("Invalid 'opt' in NotifyOpt")) + Ok(Self { opt: if let Some(opt) = a.take_any("opt") { opt } else { a.try_into()? } }) } } -impl FromLua for PushOpt { - fn from_lua(value: Value, lua: &Lua) -> mlua::Result { lua.from_value(value) } -} - -impl IntoLua for PushOpt { - fn into_lua(self, lua: &Lua) -> mlua::Result { lua.to_value_with(&self, SER_OPT) } -} - -// --- Level -#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "kebab-case")] -pub enum PushLevel { - #[default] - Info, - Warn, - Error, -} - -impl PushLevel { - pub fn icon(self) -> &'static str { - match self { - Self::Info => &THEME.notify.icon_info, - Self::Warn => &THEME.notify.icon_warn, - Self::Error => &THEME.notify.icon_error, - } - } - - pub fn style(self) -> Style { - match self { - Self::Info => THEME.notify.title_info, - Self::Warn => THEME.notify.title_warn, - Self::Error => THEME.notify.title_error, - } +impl FromLua for PushForm { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { + Ok(Self { opt: MessageOpt::from_lua(value, lua)? }) } } -impl FromStr for PushLevel { - type Err = serde::de::value::Error; - - fn from_str(s: &str) -> Result { - Self::deserialize(serde::de::value::StrDeserializer::new(s)) - } +impl IntoLua for PushForm { + fn into_lua(self, lua: &Lua) -> mlua::Result { self.opt.into_lua(lua) } } diff --git a/yazi-dds/src/spark/kind.rs b/yazi-parser/src/spark/kind.rs similarity index 100% rename from yazi-dds/src/spark/kind.rs rename to yazi-parser/src/spark/kind.rs diff --git a/yazi-dds/src/spark/mod.rs b/yazi-parser/src/spark/mod.rs similarity index 100% rename from yazi-dds/src/spark/mod.rs rename to yazi-parser/src/spark/mod.rs diff --git a/yazi-parser/src/spark/spark.rs b/yazi-parser/src/spark/spark.rs new file mode 100644 index 00000000..cc7bf9f2 --- /dev/null +++ b/yazi-parser/src/spark/spark.rs @@ -0,0 +1,448 @@ +use mlua::{FromLua, IntoLua, Lua, Value}; + +use crate::{spark::SparkKind, try_from_spark}; + +#[derive(Debug)] +pub enum Spark<'a> { + // Void + Void(crate::VoidOpt), + + // App + AppAcceptPayload(yazi_dds::Payload<'a>), + AppBootstrap(crate::VoidOpt), + AppDeprecate(crate::app::DeprecateOpt), + AppFocus(crate::VoidOpt), + AppLua(crate::app::LuaOpt), + AppMouse(crate::app::MouseOpt), + AppPlugin(crate::app::PluginForm), + AppPluginDo(crate::app::PluginForm), + AppQuit(crate::app::QuitForm), + AppReflow(crate::app::ReflowOpt), + AppResize(crate::app::ReflowOpt), + AppResume(crate::app::ResumeOpt), + AppStop(crate::app::StopOpt), + AppTitle(crate::app::TitleOpt), + AppUpdateProgress(crate::app::UpdateProgressOpt), + + // Mgr + Arrow(crate::ArrowOpt), + Back(crate::VoidOpt), + BulkExit(crate::mgr::BulkExitOpt), + BulkRename(crate::VoidOpt), + Cd(crate::mgr::CdOpt), + Close(crate::mgr::CloseForm), + Copy(crate::mgr::CopyOpt), + Create(crate::mgr::CreateOpt), + Displace(crate::VoidOpt), + DisplaceDo(crate::mgr::DisplaceDoForm), + Download(crate::mgr::DownloadOpt), + Enter(crate::VoidOpt), + Escape(crate::mgr::EscapeOpt), + EscapeFilter(crate::VoidOpt), + EscapeFind(crate::VoidOpt), + EscapeSearch(crate::VoidOpt), + EscapeSelect(crate::VoidOpt), + EscapeVisual(crate::VoidOpt), + Filter(crate::mgr::FilterForm), + FilterDo(crate::mgr::FilterForm), + Find(crate::mgr::FindOpt), + FindArrow(crate::mgr::FindArrowOpt), + FindDo(crate::mgr::FindDoForm), + Follow(crate::VoidOpt), + Forward(crate::VoidOpt), + Hardlink(crate::mgr::HardlinkOpt), + Hidden(crate::mgr::HiddenOpt), + Hover(crate::mgr::HoverOpt), + Leave(crate::VoidOpt), + Linemode(crate::mgr::LinemodeOpt), + Link(crate::mgr::LinkOpt), + Open(crate::mgr::OpenForm), + OpenDo(crate::mgr::OpenDoForm), + Paste(crate::mgr::PasteOpt), + Peek(crate::mgr::PeekOpt), + Quit(crate::app::QuitForm), + Refresh(crate::VoidOpt), + Remove(crate::mgr::RemoveOpt), + RemoveDo(crate::mgr::RemoveOpt), + Rename(crate::mgr::RenameOpt), + Reveal(crate::mgr::RevealOpt), + Search(crate::mgr::SearchForm), + SearchDo(crate::mgr::SearchForm), + SearchStop(crate::VoidOpt), + Seek(crate::mgr::SeekOpt), + Shell(crate::mgr::ShellOpt), + Sort(crate::mgr::SortOpt), + Spot(crate::mgr::SpotOpt), + Stash(crate::mgr::StashOpt), + Suspend(crate::VoidOpt), + TabClose(crate::mgr::TabCloseOpt), + TabCreate(crate::mgr::TabCreateOpt), + TabRename(crate::mgr::TabRenameOpt), + TabSwap(crate::ArrowOpt), + TabSwitch(crate::mgr::TabSwitchOpt), + Toggle(crate::mgr::ToggleOpt), + ToggleAll(crate::mgr::ToggleAllOpt), + Unyank(crate::VoidOpt), + UpdateFiles(crate::mgr::UpdateFilesOpt), + UpdateMimes(crate::mgr::UpdateMimesOpt), + UpdatePaged(crate::mgr::UpdatePagedOpt), + UpdatePeeked(crate::mgr::UpdatePeekedForm), + UpdateSpotted(crate::mgr::UpdateSpottedForm), + UpdateYanked(crate::mgr::UpdateYankedOpt<'a>), + Upload(crate::mgr::UploadOpt), + VisualMode(crate::mgr::VisualModeOpt), + Watch(crate::VoidOpt), + Yank(crate::mgr::YankOpt), + + // Cmp + CmpArrow(crate::ArrowOpt), + CmpClose(crate::cmp::CloseOpt), + CmpShow(crate::cmp::ShowForm), + CmpTrigger(crate::cmp::TriggerOpt), + + // Confirm + ConfirmArrow(crate::ArrowOpt), + ConfirmClose(crate::confirm::CloseOpt), + ConfirmShow(Box), + + // Help + HelpArrow(crate::ArrowOpt), + HelpEscape(crate::VoidOpt), + HelpFilter(crate::VoidOpt), + HelpToggle(crate::help::ToggleOpt), + + // Input + InputBackspace(yazi_widgets::input::parser::BackspaceOpt), + InputBackward(yazi_widgets::input::parser::BackwardOpt), + InputClose(crate::input::CloseOpt), + InputComplete(yazi_widgets::input::parser::CompleteOpt), + InputDelete(yazi_widgets::input::parser::DeleteOpt), + InputEscape(crate::VoidOpt), + InputForward(yazi_widgets::input::parser::ForwardOpt), + InputInsert(yazi_widgets::input::parser::InsertOpt), + InputKill(yazi_widgets::input::parser::KillOpt), + InputMove(yazi_widgets::input::parser::MoveOpt), + InputPaste(yazi_widgets::input::parser::PasteOpt), + InputShow(yazi_widgets::input::InputOpt), + + // Notify + NotifyPush(crate::notify::PushForm), + NotifyTick(crate::notify::TickOpt), + + // Pick + PickArrow(crate::ArrowOpt), + PickClose(crate::pick::CloseOpt), + PickShow(crate::pick::ShowOpt), + + // Spot + SpotArrow(crate::ArrowOpt), + SpotClose(crate::VoidOpt), + SpotCopy(crate::spot::CopyOpt), + SpotSwipe(crate::ArrowOpt), + + // Tasks + TasksArrow(crate::ArrowOpt), + TasksCancel(crate::VoidOpt), + TasksClose(crate::VoidOpt), + TasksInspect(crate::VoidOpt), + TasksOpenShellCompat(crate::tasks::ProcessOpenForm), + TasksProcessOpen(crate::tasks::ProcessOpenForm), + TasksShow(crate::VoidOpt), + TasksUpdateSucceed(crate::tasks::UpdateSucceedOpt), + + // Which + WhichActivate(crate::which::ActivateForm), + WhichDismiss(crate::VoidOpt), +} + +impl<'a> Spark<'a> { + pub fn from_lua(lua: &Lua, kind: SparkKind, value: Value) -> mlua::Result { + use SparkKind::*; + + Ok(match kind { + // app:title + IndAppTitle => Self::AppTitle(<_>::from_lua(value, lua)?), + + // mgr:hidden + KeyHidden => Self::Hidden(<_>::from_lua(value, lua)?), + IndHidden => Self::Hidden(<_>::from_lua(value, lua)?), + // mgr:sort + KeySort => Self::Sort(<_>::from_lua(value, lua)?), + IndSort => Self::Sort(<_>::from_lua(value, lua)?), + // mgr:stash + IndStash => Self::Stash(<_>::from_lua(value, lua)?), + RelayStash => Self::Stash(<_>::from_lua(value, lua)?), + // mgr:quit + KeyQuit => Self::Quit(<_>::from_lua(value, lua)?), + + // which:activate + IndWhichActivate => Self::WhichActivate(<_>::from_lua(value, lua)?), + + // notify:push + RelayNotifyPush => Self::NotifyPush(<_>::from_lua(value, lua)?), + }) + } +} + +impl<'a> IntoLua for Spark<'a> { + fn into_lua(self, lua: &Lua) -> mlua::Result { + match self { + // Void + Self::Void(b) => b.into_lua(lua), + + // App + Self::AppAcceptPayload(b) => b.into_lua(lua), + Self::AppBootstrap(b) => b.into_lua(lua), + Self::AppDeprecate(b) => b.into_lua(lua), + Self::AppFocus(b) => b.into_lua(lua), + Self::AppLua(b) => b.into_lua(lua), + Self::AppMouse(b) => b.into_lua(lua), + Self::AppPlugin(b) => b.into_lua(lua), + Self::AppPluginDo(b) => b.into_lua(lua), + Self::AppQuit(b) => b.into_lua(lua), + Self::AppReflow(b) => b.into_lua(lua), + Self::AppResize(b) => b.into_lua(lua), + Self::AppResume(b) => b.into_lua(lua), + Self::AppStop(b) => b.into_lua(lua), + Self::AppTitle(b) => b.into_lua(lua), + Self::AppUpdateProgress(b) => b.into_lua(lua), + + // Mgr + Self::Arrow(b) => b.into_lua(lua), + Self::Back(b) => b.into_lua(lua), + Self::BulkExit(b) => b.into_lua(lua), + Self::BulkRename(b) => b.into_lua(lua), + Self::Cd(b) => b.into_lua(lua), + Self::Close(b) => b.into_lua(lua), + Self::Copy(b) => b.into_lua(lua), + Self::Create(b) => b.into_lua(lua), + Self::Displace(b) => b.into_lua(lua), + Self::DisplaceDo(b) => b.into_lua(lua), + Self::Download(b) => b.into_lua(lua), + Self::Enter(b) => b.into_lua(lua), + Self::Escape(b) => b.into_lua(lua), + Self::EscapeFilter(b) => b.into_lua(lua), + Self::EscapeFind(b) => b.into_lua(lua), + Self::EscapeSearch(b) => b.into_lua(lua), + Self::EscapeSelect(b) => b.into_lua(lua), + Self::EscapeVisual(b) => b.into_lua(lua), + Self::Filter(b) => b.into_lua(lua), + Self::FilterDo(b) => b.into_lua(lua), + Self::Find(b) => b.into_lua(lua), + Self::FindArrow(b) => b.into_lua(lua), + Self::FindDo(b) => b.into_lua(lua), + Self::Follow(b) => b.into_lua(lua), + Self::Forward(b) => b.into_lua(lua), + Self::Hardlink(b) => b.into_lua(lua), + Self::Hidden(b) => b.into_lua(lua), + Self::Hover(b) => b.into_lua(lua), + Self::Leave(b) => b.into_lua(lua), + Self::Linemode(b) => b.into_lua(lua), + Self::Link(b) => b.into_lua(lua), + Self::Open(b) => b.into_lua(lua), + Self::OpenDo(b) => b.into_lua(lua), + Self::Paste(b) => b.into_lua(lua), + Self::Peek(b) => b.into_lua(lua), + Self::Quit(b) => b.into_lua(lua), + Self::Refresh(b) => b.into_lua(lua), + Self::Remove(b) => b.into_lua(lua), + Self::RemoveDo(b) => b.into_lua(lua), + Self::Rename(b) => b.into_lua(lua), + Self::Reveal(b) => b.into_lua(lua), + Self::Search(b) => b.into_lua(lua), + Self::SearchDo(b) => b.into_lua(lua), + Self::SearchStop(b) => b.into_lua(lua), + Self::Seek(b) => b.into_lua(lua), + Self::Shell(b) => b.into_lua(lua), + Self::Sort(b) => b.into_lua(lua), + Self::Spot(b) => b.into_lua(lua), + Self::Stash(b) => b.into_lua(lua), + Self::Suspend(b) => b.into_lua(lua), + Self::TabClose(b) => b.into_lua(lua), + Self::TabCreate(b) => b.into_lua(lua), + Self::TabRename(b) => b.into_lua(lua), + Self::TabSwap(b) => b.into_lua(lua), + Self::TabSwitch(b) => b.into_lua(lua), + Self::Toggle(b) => b.into_lua(lua), + Self::ToggleAll(b) => b.into_lua(lua), + Self::Unyank(b) => b.into_lua(lua), + Self::UpdateFiles(b) => b.into_lua(lua), + Self::UpdateMimes(b) => b.into_lua(lua), + Self::UpdatePaged(b) => b.into_lua(lua), + Self::UpdatePeeked(b) => b.into_lua(lua), + Self::UpdateSpotted(b) => b.into_lua(lua), + Self::UpdateYanked(b) => b.into_lua(lua), + Self::Upload(b) => b.into_lua(lua), + Self::VisualMode(b) => b.into_lua(lua), + Self::Watch(b) => b.into_lua(lua), + Self::Yank(b) => b.into_lua(lua), + + // Cmp + Self::CmpArrow(b) => b.into_lua(lua), + Self::CmpClose(b) => b.into_lua(lua), + Self::CmpShow(b) => b.into_lua(lua), + Self::CmpTrigger(b) => b.into_lua(lua), + + // Confirm + Self::ConfirmArrow(b) => b.into_lua(lua), + Self::ConfirmClose(b) => b.into_lua(lua), + Self::ConfirmShow(b) => b.into_lua(lua), + + // Help + Self::HelpArrow(b) => b.into_lua(lua), + Self::HelpEscape(b) => b.into_lua(lua), + Self::HelpFilter(b) => b.into_lua(lua), + Self::HelpToggle(b) => b.into_lua(lua), + + // Input + Self::InputBackspace(b) => b.into_lua(lua), + Self::InputBackward(b) => b.into_lua(lua), + Self::InputClose(b) => b.into_lua(lua), + Self::InputComplete(b) => b.into_lua(lua), + Self::InputDelete(b) => b.into_lua(lua), + Self::InputEscape(b) => b.into_lua(lua), + Self::InputForward(b) => b.into_lua(lua), + Self::InputInsert(b) => b.into_lua(lua), + Self::InputKill(b) => b.into_lua(lua), + Self::InputMove(b) => b.into_lua(lua), + Self::InputPaste(b) => b.into_lua(lua), + Self::InputShow(b) => b.into_lua(lua), + + // Notify + Self::NotifyPush(b) => b.into_lua(lua), + Self::NotifyTick(b) => b.into_lua(lua), + + // Pick + Self::PickArrow(b) => b.into_lua(lua), + Self::PickClose(b) => b.into_lua(lua), + Self::PickShow(b) => b.into_lua(lua), + + // Spot + Self::SpotArrow(b) => b.into_lua(lua), + Self::SpotClose(b) => b.into_lua(lua), + Self::SpotCopy(b) => b.into_lua(lua), + Self::SpotSwipe(b) => b.into_lua(lua), + + // Tasks + Self::TasksArrow(b) => b.into_lua(lua), + Self::TasksCancel(b) => b.into_lua(lua), + Self::TasksClose(b) => b.into_lua(lua), + Self::TasksInspect(b) => b.into_lua(lua), + Self::TasksOpenShellCompat(b) => b.into_lua(lua), + Self::TasksProcessOpen(b) => b.into_lua(lua), + Self::TasksShow(b) => b.into_lua(lua), + Self::TasksUpdateSucceed(b) => b.into_lua(lua), + + // Which + Self::WhichActivate(b) => b.into_lua(lua), + Self::WhichDismiss(b) => b.into_lua(lua), + } + } +} + +try_from_spark!( + crate::VoidOpt, + app:bootstrap, + app:focus, + mgr:back, + mgr:bulk_rename, + mgr:enter, + mgr:escape_filter, + mgr:escape_find, + mgr:escape_search, + mgr:escape_select, + mgr:escape_visual, + mgr:follow, + mgr:forward, + mgr:leave, + mgr:refresh, + mgr:search_stop, + mgr:suspend, + mgr:unyank, + mgr:watch, + which:dismiss +); + +// App +try_from_spark!(crate::ArrowOpt, mgr:arrow, mgr:tab_swap); +try_from_spark!(crate::app::DeprecateOpt, app:deprecate); +try_from_spark!(crate::app::LuaOpt, app:lua); +try_from_spark!(crate::app::MouseOpt, app:mouse); +try_from_spark!(crate::app::PluginForm, app:plugin, app:plugin_do); +try_from_spark!(crate::app::QuitForm, app:quit, mgr:quit); +try_from_spark!(crate::app::ReflowOpt, app:reflow, app:resize); +try_from_spark!(crate::app::ResumeOpt, app:resume); +try_from_spark!(crate::app::StopOpt, app:stop); +try_from_spark!(crate::app::TitleOpt, app:title); +try_from_spark!(crate::app::UpdateProgressOpt, app:update_progress); +try_from_spark!(crate::cmp::CloseOpt, cmp:close); +try_from_spark!(crate::cmp::ShowForm, cmp:show); +try_from_spark!(crate::cmp::TriggerOpt, cmp:trigger); +try_from_spark!(crate::confirm::CloseOpt, confirm:close); +try_from_spark!(crate::confirm::ShowOpt, confirm:show); +try_from_spark!(crate::help::ToggleOpt, help:toggle); +try_from_spark!(crate::input::CloseOpt, input:close); +try_from_spark!(crate::mgr::BulkExitOpt, mgr:bulk_exit); +try_from_spark!(crate::mgr::CdOpt, mgr:cd); +try_from_spark!(crate::mgr::CloseForm, mgr:close); +try_from_spark!(crate::mgr::CopyOpt, mgr:copy); +try_from_spark!(crate::mgr::CreateOpt, mgr:create); +try_from_spark!(crate::mgr::DisplaceDoForm, mgr:displace_do); +try_from_spark!(crate::mgr::DownloadOpt, mgr:download); +try_from_spark!(crate::mgr::EscapeOpt, mgr:escape); +try_from_spark!(crate::mgr::FilterForm, mgr:filter, mgr:filter_do); +try_from_spark!(crate::mgr::FindArrowOpt, mgr:find_arrow); +try_from_spark!(crate::mgr::FindDoForm, mgr:find_do); +try_from_spark!(crate::mgr::FindOpt, mgr:find); +try_from_spark!(crate::mgr::HardlinkOpt, mgr:hardlink); +try_from_spark!(crate::mgr::HiddenOpt, mgr:hidden); +try_from_spark!(crate::mgr::HoverOpt, mgr:hover); +try_from_spark!(crate::mgr::LinemodeOpt, mgr:linemode); +try_from_spark!(crate::mgr::LinkOpt, mgr:link); +try_from_spark!(crate::mgr::OpenDoForm, mgr:open_do); +try_from_spark!(crate::mgr::OpenForm, mgr:open); +try_from_spark!(crate::mgr::PasteOpt, mgr:paste); +try_from_spark!(crate::mgr::PeekOpt, mgr:peek); +try_from_spark!(crate::mgr::RemoveOpt, mgr:remove, mgr:remove_do); +try_from_spark!(crate::mgr::RenameOpt, mgr:rename); +try_from_spark!(crate::mgr::RevealOpt, mgr:reveal); +try_from_spark!(crate::mgr::SearchForm, mgr:search, mgr:search_do); +try_from_spark!(crate::mgr::SeekOpt, mgr:seek); +try_from_spark!(crate::mgr::ShellOpt, mgr:shell); +try_from_spark!(crate::mgr::SortOpt, mgr:sort); +try_from_spark!(crate::mgr::SpotOpt, mgr:spot); +try_from_spark!(crate::mgr::StashOpt, mgr:stash); +try_from_spark!(crate::mgr::TabCloseOpt, mgr:tab_close); +try_from_spark!(crate::mgr::TabCreateOpt, mgr:tab_create); +try_from_spark!(crate::mgr::TabRenameOpt, mgr:tab_rename); +try_from_spark!(crate::mgr::TabSwitchOpt, mgr:tab_switch); +try_from_spark!(crate::mgr::ToggleAllOpt, mgr:toggle_all); +try_from_spark!(crate::mgr::ToggleOpt, mgr:toggle); +try_from_spark!(crate::mgr::UpdateFilesOpt, mgr:update_files); +try_from_spark!(crate::mgr::UpdateMimesOpt, mgr:update_mimes); +try_from_spark!(crate::mgr::UpdatePagedOpt, mgr:update_paged); +try_from_spark!(crate::mgr::UpdatePeekedForm, mgr:update_peeked); +try_from_spark!(crate::mgr::UpdateSpottedForm, mgr:update_spotted); +try_from_spark!(crate::mgr::UpdateYankedOpt<'a>, mgr:update_yanked); +try_from_spark!(crate::mgr::UploadOpt, mgr:upload); +try_from_spark!(crate::mgr::VisualModeOpt, mgr:visual_mode); +try_from_spark!(crate::mgr::YankOpt, mgr:yank); +try_from_spark!(crate::notify::PushForm, notify:push); +try_from_spark!(crate::notify::TickOpt, notify:tick); +try_from_spark!(crate::pick::CloseOpt, pick:close); +try_from_spark!(crate::pick::ShowOpt, pick:show); +try_from_spark!(crate::spot::CopyOpt, spot:copy); +try_from_spark!(crate::tasks::ProcessOpenForm, tasks:process_open); +try_from_spark!(crate::tasks::UpdateSucceedOpt, tasks:update_succeed); +try_from_spark!(crate::which::ActivateForm, which:activate); +try_from_spark!(yazi_dds::Payload<'a>, app:accept_payload); +try_from_spark!(yazi_widgets::input::InputOpt, input:show); +try_from_spark!(yazi_widgets::input::parser::BackspaceOpt, input:backspace); +try_from_spark!(yazi_widgets::input::parser::BackwardOpt, input:backward); +try_from_spark!(yazi_widgets::input::parser::CompleteOpt, input:complete); +try_from_spark!(yazi_widgets::input::parser::DeleteOpt, input:delete); +try_from_spark!(yazi_widgets::input::parser::ForwardOpt, input:forward); +try_from_spark!(yazi_widgets::input::parser::InsertOpt, input:insert); +try_from_spark!(yazi_widgets::input::parser::KillOpt, input:kill); +try_from_spark!(yazi_widgets::input::parser::MoveOpt, input:move); +try_from_spark!(yazi_widgets::input::parser::PasteOpt, input:paste); diff --git a/yazi-parser/src/tasks/process_open.rs b/yazi-parser/src/tasks/process_open.rs index a42c71b0..75a3ebb2 100644 --- a/yazi-parser/src/tasks/process_open.rs +++ b/yazi-parser/src/tasks/process_open.rs @@ -1,34 +1,30 @@ -use std::ffi::OsString; - use anyhow::anyhow; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use yazi_shared::{CompletionToken, event::ActionCow, url::UrlCow}; +use tokio::sync::mpsc; +use yazi_scheduler::process::ProcessOpt; +use yazi_shared::{data::Data, event::ActionCow}; -// --- Exec #[derive(Clone, Debug)] -pub struct ProcessOpenOpt { - pub cwd: UrlCow<'static>, - pub cmd: OsString, - pub args: Vec>, - pub block: bool, - pub orphan: bool, - pub done: Option, - - pub spread: bool, // TODO: remove +pub struct ProcessOpenForm { + pub opt: ProcessOpt, + pub replier: Option>>, } -impl TryFrom for ProcessOpenOpt { +impl TryFrom for ProcessOpenForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - a.take_any("opt").ok_or_else(|| anyhow!("Missing 'opt' in ProcessOpenOpt")) + Ok(Self { + opt: a.take_any("opt").ok_or_else(|| anyhow!("Invalid 'opt' in ProcessOpenForm"))?, + replier: a.take_replier(), + }) } } -impl FromLua for ProcessOpenOpt { +impl FromLua for ProcessOpenForm { fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } -impl IntoLua for ProcessOpenOpt { +impl IntoLua for ProcessOpenForm { fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } diff --git a/yazi-parser/src/tasks/update_succeed.rs b/yazi-parser/src/tasks/update_succeed.rs index 524f12e7..f7488e34 100644 --- a/yazi-parser/src/tasks/update_succeed.rs +++ b/yazi-parser/src/tasks/update_succeed.rs @@ -1,10 +1,12 @@ use anyhow::bail; use mlua::{ExternalError, FromLua, IntoLua, Lua, Value}; -use yazi_shared::{event::ActionCow, url::UrlBuf}; +use yazi_shared::{Id, event::ActionCow, url::UrlBuf}; #[derive(Debug)] pub struct UpdateSucceedOpt { - pub urls: Vec, + pub id: Id, + pub urls: Vec, + pub track: bool, } impl TryFrom for UpdateSucceedOpt { @@ -15,7 +17,7 @@ impl TryFrom for UpdateSucceedOpt { bail!("Invalid 'urls' in UpdateSucceedOpt"); }; - Ok(Self { urls }) + Ok(Self { id: a.first()?, urls, track: a.get("track").unwrap_or_default() }) } } diff --git a/yazi-parser/src/which/activate.rs b/yazi-parser/src/which/activate.rs index 7d741e2e..3f2acc31 100644 --- a/yazi-parser/src/which/activate.rs +++ b/yazi-parser/src/which/activate.rs @@ -1,78 +1,31 @@ -use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, Value}; -use tokio::sync::mpsc; -use yazi_config::{KEYMAP, keymap::{ChordCow, Key}}; +use mlua::{FromLua, IntoLua, Lua, Value}; +use yazi_config::keymap::Key; +use yazi_core::which::WhichOpt; use yazi_shared::{Layer, event::ActionCow}; #[derive(Clone, Debug)] -pub struct ActivateOpt { - pub tx: Option>>, - pub cands: Vec, - pub silent: bool, - pub times: usize, +pub struct ActivateForm { + pub opt: WhichOpt, } -impl TryFrom for ActivateOpt { +impl From<(Layer, Key)> for ActivateForm { + fn from(value: (Layer, Key)) -> Self { Self { opt: value.into() } } +} + +impl TryFrom for ActivateForm { type Error = anyhow::Error; fn try_from(mut a: ActionCow) -> Result { - if let Some(opt) = a.take_any2("opt") { - return opt; - } - - Ok(Self { - tx: a.take_any2("tx").transpose()?, - cands: a.take_any_iter::().map(Into::into).collect(), - silent: a.bool("silent"), - times: a.get("times").unwrap_or(0), - }) + Ok(Self { opt: if let Some(opt) = a.take_any("opt") { opt } else { a.try_into()? } }) } } -impl From<(Layer, Key)> for ActivateOpt { - fn from((layer, key): (Layer, Key)) -> Self { - Self { - tx: None, - cands: KEYMAP - .get(layer) - .iter() - .filter(|c| c.on.len() > 1 && c.on[0] == key) - .map(Into::into) - .collect(), - times: 1, - silent: false, - } +impl FromLua for ActivateForm { + fn from_lua(value: Value, lua: &Lua) -> mlua::Result { + Ok(Self { opt: WhichOpt::from_lua(value, lua)? }) } } -impl FromLua for ActivateOpt { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - let Value::Table(t) = value else { - return Err("expected a table".into_lua_err()); - }; - - Ok(Self { - tx: t.raw_get::>("tx").ok().map(|t| t.0), - cands: t - .raw_get::
("cands")? - .sequence_values::() - .map(|c| c.map(Into::into)) - .collect::>>()?, - times: t.raw_get("times").unwrap_or_default(), - silent: t.raw_get("silent")?, - }) - } -} - -impl IntoLua for ActivateOpt { - #[rustfmt::skip] - fn into_lua(self, lua: &Lua) -> mlua::Result { - lua - .create_table_from([ - ("tx", self.tx.map(yazi_binding::MpscUnboundedTx).into_lua(lua)?), - ("cands", lua.create_sequence_from(self.cands.into_iter().map(yazi_binding::ChordCow))?.into_lua(lua)?), - ("times", self.times.into_lua(lua)?), - ("silent", self.silent.into_lua(lua)?), - ])? - .into_lua(lua) - } +impl IntoLua for ActivateForm { + fn into_lua(self, lua: &Lua) -> mlua::Result { self.opt.into_lua(lua) } } diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 16493a9b..3af2bdf3 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -17,21 +17,24 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-adapter = { path = "../yazi-adapter", version = "26.2.2" } -yazi-binding = { path = "../yazi-binding", version = "26.2.2" } -yazi-boot = { path = "../yazi-boot", version = "26.2.2" } -yazi-config = { path = "../yazi-config", version = "26.2.2" } -yazi-dds = { path = "../yazi-dds", version = "26.2.2" } -yazi-emulator = { path = "../yazi-emulator", version = "26.2.2" } -yazi-fs = { path = "../yazi-fs", version = "26.2.2" } -yazi-macro = { path = "../yazi-macro", version = "26.2.2" } -yazi-parser = { path = "../yazi-parser", version = "26.2.2" } -yazi-proxy = { path = "../yazi-proxy", version = "26.2.2" } -yazi-shared = { path = "../yazi-shared", version = "26.2.2" } -yazi-shim = { path = "../yazi-shim", version = "26.2.2" } -yazi-term = { path = "../yazi-term", version = "26.2.2" } -yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } -yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } +yazi-adapter = { path = "../yazi-adapter", version = "26.2.2" } +yazi-binding = { path = "../yazi-binding", version = "26.2.2" } +yazi-boot = { path = "../yazi-boot", version = "26.2.2" } +yazi-config = { path = "../yazi-config", version = "26.2.2" } +yazi-core = { path = "../yazi-core", version = "26.2.2" } +yazi-dds = { path = "../yazi-dds", version = "26.2.2" } +yazi-emulator = { path = "../yazi-emulator", version = "26.2.2" } +yazi-fs = { path = "../yazi-fs", version = "26.2.2" } +yazi-macro = { path = "../yazi-macro", version = "26.2.2" } +yazi-parser = { path = "../yazi-parser", version = "26.2.2" } +yazi-proxy = { path = "../yazi-proxy", version = "26.2.2" } +yazi-runner = { path = "../yazi-runner", version = "26.2.2" } +yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" } +yazi-shared = { path = "../yazi-shared", version = "26.2.2" } +yazi-shim = { path = "../yazi-shim", version = "26.2.2" } +yazi-term = { path = "../yazi-term", version = "26.2.2" } +yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } +yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } # External dependencies ansi-to-tui = { workspace = true } @@ -50,7 +53,6 @@ tokio-util = { workspace = true } tracing = { workspace = true } twox-hash = { workspace = true } unicode-width = { workspace = true } -yazi-prebuilt = "0.1.0" [target."cfg(unix)".dependencies] libc = { workspace = true } diff --git a/yazi-plugin/preset/components/status.lua b/yazi-plugin/preset/components/status.lua index ba1a5070..c5a37928 100644 --- a/yazi-plugin/preset/components/status.lua +++ b/yazi-plugin/preset/components/status.lua @@ -49,11 +49,11 @@ end function Status:length() local h = self._current.hovered - local size = h and h.cha.len or 0 + local len = h and h.cha.len or 0 local style = self:style() return ui.Line { - ui.Span(" " .. ya.readable_size(size) .. " "):style(style.alt), + ui.Span(" " .. ya.readable_size(len) .. " "):style(style.alt), ui.Span(th.status.sep_left.close):fg(style.alt:bg()), } end diff --git a/yazi-plugin/preset/plugins/null.lua b/yazi-plugin/preset/plugins/null.lua index 279c9038..49cd001e 100644 --- a/yazi-plugin/preset/plugins/null.lua +++ b/yazi-plugin/preset/plugins/null.lua @@ -3,10 +3,9 @@ local M = {} function M:peek(job) local err if job.mime == "null/file1-not-found" then - err = string.format( - "Cannot find `%s` to detect the file's MIME type. Make sure it's installed and restart Yazi", - require("mime.local").file1_bin() - ) + local s = ya.target_family() == "windows" and "Set it up correctly as per the Windows Installation Guide" + or "Make sure it's installed and restart Yazi" + err = string.format("Cannot find `%s` to detect the file's MIME type. %s.", require("mime.local").file1_bin(), s) else err = "Unknown error occurred while detecting MIME type" end diff --git a/yazi-plugin/preset/plugins/session.lua b/yazi-plugin/preset/plugins/session.lua index 6d4e5b4d..b35e5261 100644 --- a/yazi-plugin/preset/plugins/session.lua +++ b/yazi-plugin/preset/plugins/session.lua @@ -1,6 +1,6 @@ local function setup(_, opts) if opts.sync_yanked then - ps.sub_remote("@yank", function(opt) ya.emit("update_yanked", { opt = opt }) end) + ps.sub_remote("@yank", function(state) ya.emit("update_yanked", { state = state }) end) end end diff --git a/yazi-plugin/src/elements/elements.rs b/yazi-plugin/src/elements/elements.rs index 2b7a96a4..bbcf8131 100644 --- a/yazi-plugin/src/elements/elements.rs +++ b/yazi-plugin/src/elements/elements.rs @@ -6,7 +6,7 @@ use tracing::error; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use yazi_binding::{Composer, ComposerGet, ComposerSet, Permit, PermitRef, elements::{Line, Rect, Span, Wrap}, runtime}; use yazi_config::LAYOUT; -use yazi_proxy::AppProxy; +use yazi_scheduler::AppProxy; use yazi_shared::replace_to_printable; use yazi_shim::ratatui::LineIter; use yazi_term::YIELD_TO_SUBPROCESS; diff --git a/yazi-plugin/src/external/mod.rs b/yazi-plugin/src/external/mod.rs index f974108b..d7e16bde 100644 --- a/yazi-plugin/src/external/mod.rs +++ b/yazi-plugin/src/external/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(fd highlighter rg rga); +yazi_macro::mod_flat!(fd rg rga); diff --git a/yazi-plugin/src/isolate/entry.rs b/yazi-plugin/src/isolate/entry.rs deleted file mode 100644 index fde8e6ab..00000000 --- a/yazi-plugin/src/isolate/entry.rs +++ /dev/null @@ -1,21 +0,0 @@ -use mlua::{ExternalResult, ObjectLike}; -use tokio::runtime::Handle; -use yazi_dds::Sendable; -use yazi_parser::app::PluginOpt; - -use super::slim_lua; -use crate::loader::LOADER; - -pub async fn entry(opt: PluginOpt) -> mlua::Result<()> { - LOADER.ensure(&opt.id, |_| ()).await.into_lua_err()?; - - tokio::task::spawn_blocking(move || { - let lua = slim_lua(&opt.id)?; - let job = lua.create_table_from([("args", Sendable::args_to_table(&lua, opt.args)?)])?; - - Handle::current() - .block_on(async { LOADER.load(&lua, &opt.id).await?.call_async_method("entry", job).await }) - }) - .await - .into_lua_err()? -} diff --git a/yazi-plugin/src/isolate/fetch.rs b/yazi-plugin/src/isolate/fetch.rs deleted file mode 100644 index 7482feaf..00000000 --- a/yazi-plugin/src/isolate/fetch.rs +++ /dev/null @@ -1,62 +0,0 @@ -use mlua::{ExternalResult, FromLua, IntoLua, Lua, ObjectLike, Value}; -use tokio::runtime::Handle; -use yazi_binding::{Error, File}; -use yazi_dds::Sendable; -use yazi_shared::event::ActionCow; - -use super::slim_lua; -use crate::loader::LOADER; - -pub async fn fetch( - action: ActionCow, - files: Vec, -) -> mlua::Result<(FetchState, Option)> { - if files.is_empty() { - return Ok((FetchState::Bool(true), None)); - } - LOADER.ensure(&action.name, |_| ()).await.into_lua_err()?; - - tokio::task::spawn_blocking(move || { - let lua = slim_lua(&action.name)?; - let job = lua.create_table_from([ - ("args", Sendable::args_to_table_ref(&lua, &action.args)?.into_lua(&lua)?), - ("files", lua.create_sequence_from(files.into_iter().map(File::new))?.into_lua(&lua)?), - ])?; - - Handle::current().block_on(async { - LOADER.load(&lua, &action.name).await?.call_async_method("fetch", job).await - }) - }) - .await - .into_lua_err()? -} - -// --- State -pub enum FetchState { - Bool(bool), - Vec(Vec), -} - -impl FetchState { - #[inline] - pub fn get(&self, idx: usize) -> bool { - match self { - Self::Bool(b) => *b, - Self::Vec(v) => v.get(idx).copied().unwrap_or(false), - } - } -} - -impl FromLua for FetchState { - fn from_lua(value: Value, _: &Lua) -> mlua::Result { - Ok(match value { - Value::Boolean(b) => Self::Bool(b), - Value::Table(tbl) => Self::Vec(tbl.sequence_values().collect::>()?), - _ => Err(mlua::Error::FromLuaConversionError { - from: value.type_name(), - to: "FetchState".to_owned(), - message: Some("expected a boolean or a table of booleans".to_owned()), - })?, - }) - } -} diff --git a/yazi-plugin/src/isolate/mod.rs b/yazi-plugin/src/isolate/mod.rs deleted file mode 100644 index a2b2e71a..00000000 --- a/yazi-plugin/src/isolate/mod.rs +++ /dev/null @@ -1 +0,0 @@ -yazi_macro::mod_flat!(entry fetch isolate peek preload seek spot); diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs deleted file mode 100644 index ca1c9a29..00000000 --- a/yazi-plugin/src/isolate/peek.rs +++ /dev/null @@ -1,134 +0,0 @@ -use mlua::{ExternalError, HookTriggers, IntoLua, Lua, ObjectLike, Table, VmState}; -use tokio::{runtime::Handle, select}; -use tokio_util::sync::CancellationToken; -use tracing::error; -use yazi_binding::{Error, File, elements::{Rect, Renderable}}; -use yazi_config::LAYOUT; -use yazi_dds::Sendable; -use yazi_parser::{app::PluginOpt, mgr::{PreviewLock, UpdatePeekedOpt}}; -use yazi_proxy::{AppProxy, MgrProxy}; -use yazi_shared::{event::Action, pool::Symbol}; - -use super::slim_lua; -use crate::loader::{LOADER, Loader}; - -pub fn peek( - action: &'static Action, - file: yazi_fs::File, - mime: Symbol, - skip: usize, -) -> Option { - let (id, ..) = Loader::normalize_id(&action.name).ok()?; - - let ct = CancellationToken::new(); - if let Some(c) = LOADER.read().get(id) { - if let Err(e) = Loader::compatible_or_error(id, c) { - peek_error(file, mime, skip, e); - return None; - } else if c.sync_peek { - peek_sync(action, file, mime, skip); - } else { - peek_async(action, file, mime, skip, ct.clone()); - } - return Some(ct).filter(|_| !c.sync_peek); - } - - let ct_ = ct.clone(); - tokio::spawn(async move { - select! { - _ = ct_.cancelled() => {}, - Ok(b) = LOADER.ensure(id, |c| c.sync_peek) => { - if b { - peek_sync(action, file, mime, skip); - } else { - peek_async(action, file, mime, skip, ct_); - } - }, - else => {} - } - }); - - Some(ct) -} - -fn peek_sync(action: &'static Action, file: yazi_fs::File, mime: Symbol, skip: usize) { - let cb = move |lua: &Lua, plugin: Table| { - let job = lua.create_table_from([ - ("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?), - ("args", Sendable::args_to_table_ref(lua, &action.args)?.into_lua(lua)?), - ("file", File::new(file).into_lua(lua)?), - ("mime", mime.into_lua(lua)?), - ("skip", skip.into_lua(lua)?), - ])?; - - plugin.call_method("peek", job) - }; - - AppProxy::plugin(PluginOpt::new_callback(&*action.name, cb)); -} - -fn peek_async( - action: &'static Action, - file: yazi_fs::File, - mime: Symbol, - skip: usize, - ct: CancellationToken, -) { - let ct_ = ct.clone(); - tokio::task::spawn_blocking(move || { - let future = async { - let lua = slim_lua(&action.name)?; - lua.set_hook( - HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000), - move |_, dbg| { - if ct.is_cancelled() && dbg.source().what != "C" { - Err("Peek task cancelled".into_lua_err()) - } else { - Ok(VmState::Continue) - } - }, - )?; - - let plugin = LOADER.load(&lua, &action.name).await?; - let job = lua.create_table_from([ - ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), - ("args", Sendable::args_to_table_ref(&lua, &action.args)?.into_lua(&lua)?), - ("file", File::new(file).into_lua(&lua)?), - ("mime", mime.into_lua(&lua)?), - ("skip", skip.into_lua(&lua)?), - ])?; - - if ct_.is_cancelled() { Ok(()) } else { plugin.call_async_method("peek", job).await } - }; - - let result = Handle::current().block_on(async { - select! { - _ = ct_.cancelled() => Ok(()), - r = future => r, - } - }); - - if let Err(e) = result - && !e.to_string().contains("Peek task cancelled") - { - error!("{e}"); - } - }); -} - -fn peek_error(file: yazi_fs::File, mime: Symbol, skip: usize, error: anyhow::Error) { - let area = LAYOUT.get().preview; - MgrProxy::update_peeked(UpdatePeekedOpt { - lock: PreviewLock { - url: file.url, - cha: file.cha, - mime: mime.to_string(), - skip, - area: area.into(), - data: vec![ - Renderable::Clear(yazi_binding::elements::Clear { area: area.into() }), - Renderable::from(Error::custom(error.to_string())).with_area(area), - ], - }, - }); -} diff --git a/yazi-plugin/src/isolate/preload.rs b/yazi-plugin/src/isolate/preload.rs deleted file mode 100644 index f6f85dfd..00000000 --- a/yazi-plugin/src/isolate/preload.rs +++ /dev/null @@ -1,61 +0,0 @@ -use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, VmState}; -use tokio::{runtime::Handle, select}; -use tokio_util::sync::CancellationToken; -use yazi_binding::{Error, File, elements::Rect}; -use yazi_config::LAYOUT; -use yazi_dds::Sendable; -use yazi_shared::event::Action; - -use super::slim_lua; -use crate::loader::LOADER; - -pub async fn preload( - action: &'static Action, - file: yazi_fs::File, - ct: CancellationToken, -) -> mlua::Result<(bool, Option)> { - let ct_ = ct.clone(); - tokio::task::spawn_blocking(move || { - let future = async { - LOADER.ensure(&action.name, |_| ()).await.into_lua_err()?; - - let lua = slim_lua(&action.name)?; - lua.set_hook( - HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000), - move |_, dbg| { - if ct.is_cancelled() && dbg.source().what != "C" { - Err("Preload task cancelled".into_lua_err()) - } else { - Ok(VmState::Continue) - } - }, - )?; - - let plugin = LOADER.load(&lua, &action.name).await?; - let job = lua.create_table_from([ - ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), - ("args", Sendable::args_to_table_ref(&lua, &action.args)?.into_lua(&lua)?), - ("file", File::new(file).into_lua(&lua)?), - ("skip", 0.into_lua(&lua)?), - ])?; - - if ct_.is_cancelled() { - Ok((false, None)) - } else { - plugin.call_async_method("preload", job).await - } - }; - - Handle::current().block_on(async { - select! { - _ = ct_.cancelled() => Ok((false, None)), - r = future => match r { - Err(e) if e.to_string().contains("Preload task cancelled") => Ok((false, None)), - Ok(_) | Err(_) => r, - }, - } - }) - }) - .await - .into_lua_err()? -} diff --git a/yazi-plugin/src/isolate/seek.rs b/yazi-plugin/src/isolate/seek.rs deleted file mode 100644 index 701da4b6..00000000 --- a/yazi-plugin/src/isolate/seek.rs +++ /dev/null @@ -1,20 +0,0 @@ -use mlua::{IntoLua, Lua, ObjectLike, Table}; -use yazi_binding::{File, elements::Rect}; -use yazi_config::LAYOUT; -use yazi_parser::app::PluginOpt; -use yazi_proxy::AppProxy; -use yazi_shared::event::Action; - -pub fn seek_sync(action: &'static Action, file: yazi_fs::File, units: i16) { - let cb = move |lua: &Lua, plugin: Table| { - let job = lua.create_table_from([ - ("file", File::new(file).into_lua(lua)?), - ("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?), - ("units", units.into_lua(lua)?), - ])?; - - plugin.call_method("seek", job) - }; - - AppProxy::plugin(PluginOpt::new_callback(&*action.name, cb)); -} diff --git a/yazi-plugin/src/isolate/spot.rs b/yazi-plugin/src/isolate/spot.rs deleted file mode 100644 index 183a2ae9..00000000 --- a/yazi-plugin/src/isolate/spot.rs +++ /dev/null @@ -1,63 +0,0 @@ -use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, VmState}; -use tokio::{runtime::Handle, select}; -use tokio_util::sync::CancellationToken; -use tracing::error; -use yazi_binding::{File, Id}; -use yazi_dds::Sendable; -use yazi_shared::{Ids, event::Action, pool::Symbol}; - -use super::slim_lua; -use crate::loader::LOADER; - -static IDS: Ids = Ids::new(); - -pub fn spot( - action: &'static Action, - file: yazi_fs::File, - mime: Symbol, - skip: usize, -) -> CancellationToken { - let ct = CancellationToken::new(); - let (ct1, ct2) = (ct.clone(), ct.clone()); - - tokio::task::spawn_blocking(move || { - let future = async { - LOADER.ensure(&action.name, |_| ()).await.into_lua_err()?; - - let lua = slim_lua(&action.name)?; - lua.set_hook( - HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000), - move |_, dbg| { - if ct1.is_cancelled() && dbg.source().what != "C" { - Err("Spot task cancelled".into_lua_err()) - } else { - Ok(VmState::Continue) - } - }, - )?; - - let plugin = LOADER.load(&lua, &action.name).await?; - let job = lua.create_table_from([ - ("id", Id(IDS.next()).into_lua(&lua)?), - ("args", Sendable::args_to_table_ref(&lua, &action.args)?.into_lua(&lua)?), - ("file", File::new(file).into_lua(&lua)?), - ("mime", mime.into_lua(&lua)?), - ("skip", skip.into_lua(&lua)?), - ])?; - - if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("spot", job).await } - }; - - Handle::current().block_on(async { - select! { - _ = ct2.cancelled() => {}, - Err(e) = future => if !e.to_string().contains("Spot task cancelled") { - error!("{e}"); - }, - else => {} - } - }); - }); - - ct -} diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index 0fd12c5b..247e1ad4 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -1,9 +1,9 @@ -yazi_macro::mod_pub!(elements external fs isolate loader process pubsub runtime theme utils); +yazi_macro::mod_pub!(elements external fs process pubsub runtime theme utils); -yazi_macro::mod_flat!(lua); +yazi_macro::mod_flat!(slim standard); pub fn init() -> anyhow::Result<()> { - crate::loader::init(); - crate::init_lua()?; + LUA.init(crate::standard_lua()?); + Ok(()) } diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/slim.rs similarity index 77% rename from yazi-plugin/src/isolate/isolate.rs rename to yazi-plugin/src/slim.rs index 319e75fc..4f909e01 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/slim.rs @@ -1,11 +1,7 @@ use mlua::{IntoLua, Lua}; -use yazi_binding::Runtime; use yazi_macro::plugin_preset as preset; -pub fn slim_lua(name: &str) -> mlua::Result { - let lua = Lua::new(); - lua.set_app_data(Runtime::new_isolate(name)); - +pub fn slim_lua(lua: &Lua) -> mlua::Result<()> { // Base let globals = lua.globals(); globals.raw_set("ui", crate::elements::compose())?; @@ -20,11 +16,11 @@ pub fn slim_lua(name: &str) -> mlua::Result { yazi_binding::Path::install(&lua)?; yazi_binding::Error::install(&lua)?; - crate::loader::install(&lua)?; crate::process::install(&lua)?; + yazi_runner::loader::install(&lua)?; // Addons lua.load(preset!("ya")).set_name("ya.lua").exec()?; - Ok(lua) + Ok(()) } diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/standard.rs similarity index 88% rename from yazi-plugin/src/lua.rs rename to yazi-plugin/src/standard.rs index c8a2b034..d6d5914c 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/standard.rs @@ -8,15 +8,16 @@ use yazi_shared::RoCell; pub static LUA: RoCell = RoCell::new(); -pub(super) fn init_lua() -> Result<()> { - LUA.init(Lua::new()); +pub(super) fn standard_lua() -> Result { + let lua = Lua::new(); - stage_1(&LUA).context("Lua setup failed")?; - stage_2(&LUA).context("Lua runtime failed")?; - Ok(()) + stage_1(&lua).context("Lua setup failed")?; + stage_2(&lua).context("Lua runtime failed")?; + + Ok(lua) } -fn stage_1(lua: &'static Lua) -> Result<()> { +fn stage_1(lua: &Lua) -> Result<()> { lua.set_app_data(Runtime::default()); // Base @@ -30,11 +31,11 @@ fn stage_1(lua: &'static Lua) -> Result<()> { yazi_binding::Error::install(lua)?; yazi_binding::Cha::install(lua)?; - crate::loader::install(lua)?; crate::process::install(lua)?; yazi_binding::File::install(lua)?; yazi_binding::Url::install(lua)?; yazi_binding::Path::install(lua)?; + yazi_runner::loader::install(lua)?; // Addons lua.load(preset!("ya")).set_name("ya.lua").exec()?; @@ -60,7 +61,7 @@ fn stage_1(lua: &'static Lua) -> Result<()> { Ok(()) } -fn stage_2(lua: &'static Lua) -> mlua::Result<()> { +fn stage_2(lua: &Lua) -> mlua::Result<()> { lua.load(preset!("setup")).set_name("setup.lua").exec()?; lua.load(preset!("compat")).set_name("compat.lua").exec()?; diff --git a/yazi-plugin/src/utils/call.rs b/yazi-plugin/src/utils/call.rs index ac3fb1d3..e22a9b1f 100644 --- a/yazi-plugin/src/utils/call.rs +++ b/yazi-plugin/src/utils/call.rs @@ -2,7 +2,7 @@ use mlua::{ExternalError, Function, Lua, Table}; use tokio::sync::mpsc; use yazi_dds::Sendable; use yazi_macro::emit; -use yazi_shared::{Layer, Source, data::Data, event::Action}; +use yazi_shared::{Layer, Source, event::Action}; use super::Utils; @@ -20,8 +20,8 @@ impl Utils { let mut action = Action::new(name, Source::Emit, Some(Layer::Mgr))?; action.args = Sendable::table_to_args(&lua, args)?; - let (tx, mut rx) = mpsc::unbounded_channel::>(); - emit!(Call(action.with_any("__reply", tx))); + let (tx, mut rx) = mpsc::unbounded_channel(); + emit!(Call(action.with_replier(tx))); Sendable::data_to_value( &lua, diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index d8f3f486..4cf02f00 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -4,8 +4,8 @@ use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, Table, Va use tokio_stream::wrappers::UnboundedReceiverStream; use yazi_binding::{InputRx, elements::{Line, Pos, Text}, runtime}; use yazi_config::{keymap::{Chord, ChordCow, Key}, popup::{ConfirmCfg, InputCfg}}; +use yazi_core::notify::MessageOpt; use yazi_macro::relay; -use yazi_parser::notify::PushOpt; use yazi_proxy::{ConfirmProxy, InputProxy, NotifyProxy, WhichProxy}; use yazi_shared::{Debounce, Layer}; @@ -94,7 +94,7 @@ impl Utils { } pub(super) fn notify(lua: &Lua) -> mlua::Result { - lua.create_function(|_, opt: PushOpt| Ok(NotifyProxy::push(opt))) + lua.create_function(|_, opt: MessageOpt| Ok(NotifyProxy::push(opt))) } fn parse_keys(value: Value) -> mlua::Result> { diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index cfc1cf53..5c996f74 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -1,14 +1,16 @@ use mlua::{ExternalError, Function, IntoLuaMulti, Lua, Table, Value}; use yazi_binding::{Error, elements::{Area, Renderable, Text}}; +use yazi_core::{Highlighter, MgrProxy, tab::PreviewLock}; use yazi_fs::FsUrl; -use yazi_parser::mgr::{PreviewLock, UpdatePeekedOpt}; -use yazi_proxy::MgrProxy; -use yazi_shared::{errors::PeekError, url::AsUrl}; +use yazi_runner::previewer::PeekError; +use yazi_shared::url::AsUrl; use super::Utils; -use crate::external::Highlighter; impl Utils { + // TODO: + // return (Text?, PeekError?) instead of (String, usize?) to align with other + // APIs, and allow users to use the Text for more flexible preview pub(super) fn preview_code(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, t: Table| async move { let area: Area = t.raw_get("area")?; @@ -17,15 +19,15 @@ impl Utils { let path = lock.url.as_url().unified_path(); let inner = match Highlighter::oneshot(path, lock.skip, area.size()).await { Ok(text) => text, - Err(e @ PeekError::Exceed(max)) => return (e.to_string(), max).into_lua_multi(&lua), - Err(e @ PeekError::Unexpected(_)) => { - return e.to_string().into_lua_multi(&lua); + Err(e @ PeekError::Exceeded(max)) => return (e, max).into_lua_multi(&lua), + Err(e) => { + return e.into_lua_multi(&lua); } }; lock.data = vec![Renderable::Text(Text { area, inner, ..Default::default() })]; - MgrProxy::update_peeked(UpdatePeekedOpt { lock }); + MgrProxy::update_peeked(lock); ().into_lua_multi(&lua) }) } @@ -52,7 +54,7 @@ impl Utils { _ => Err("preview widget must be a renderable element or a table of them".into_lua_err())?, }; - MgrProxy::update_peeked(UpdatePeekedOpt { lock }); + MgrProxy::update_peeked(lock); Ok(()) }) } diff --git a/yazi-plugin/src/utils/spot.rs b/yazi-plugin/src/utils/spot.rs index c8b68f77..803e4659 100644 --- a/yazi-plugin/src/utils/spot.rs +++ b/yazi-plugin/src/utils/spot.rs @@ -1,7 +1,7 @@ use mlua::{AnyUserData, Function, Lua, Table}; use yazi_binding::elements::{Edge, Renderable}; use yazi_config::THEME; -use yazi_parser::mgr::{SpotLock, UpdateSpottedOpt}; +use yazi_core::spot::SpotLock; use yazi_proxy::MgrProxy; use super::Utils; @@ -29,7 +29,7 @@ impl Utils { }), Renderable::Table(Box::new(table)), ]; - MgrProxy::update_spotted(UpdateSpottedOpt { lock }); + MgrProxy::update_spotted(lock); Ok(()) }) @@ -40,7 +40,7 @@ impl Utils { let mut lock = SpotLock::try_from(t)?; lock.data = widgets; - MgrProxy::update_spotted(UpdateSpottedOpt { lock }); + MgrProxy::update_spotted(lock); Ok(()) }) } diff --git a/yazi-plugin/src/utils/sync.rs b/yazi-plugin/src/utils/sync.rs index f149546d..dfc99129 100644 --- a/yazi-plugin/src/utils/sync.rs +++ b/yazi-plugin/src/utils/sync.rs @@ -3,13 +3,12 @@ use futures::future::join_all; use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Table, Value, Variadic}; use tokio::sync::mpsc; use yazi_binding::{Handle, MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx, runtime, runtime_mut}; +use yazi_core::AppProxy; use yazi_dds::Sendable; -use yazi_parser::app::PluginOpt; -use yazi_proxy::AppProxy; +use yazi_runner::{loader::LOADER, plugin::PluginOpt}; use yazi_shared::{LOCAL_SET, data::Data}; use super::Utils; -use crate::loader::LOADER; impl Utils { pub(super) fn co(lua: &Lua) -> mlua::Result { diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index dd3a7690..61b796a3 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -13,11 +13,15 @@ rust-version.workspace = true workspace = true [dependencies] -yazi-config = { path = "../yazi-config", version = "26.2.2" } -yazi-macro = { path = "../yazi-macro", version = "26.2.2" } -yazi-parser = { path = "../yazi-parser", version = "26.2.2" } -yazi-shared = { path = "../yazi-shared", version = "26.2.2" } -yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } +yazi-binding = { path = "../yazi-binding", version = "26.2.2" } +yazi-config = { path = "../yazi-config", version = "26.2.2" } +yazi-core = { path = "../yazi-core", version = "26.2.2" } +yazi-macro = { path = "../yazi-macro", version = "26.2.2" } +yazi-runner = { path = "../yazi-runner", version = "26.2.2" } +yazi-scheduler = { path = "../yazi-scheduler", version = "26.2.2" } +yazi-shared = { path = "../yazi-shared", version = "26.2.2" } +yazi-widgets = { path = "../yazi-widgets", version = "26.2.2" } # External dependencies +mlua = { workspace = true } tokio = { workspace = true } diff --git a/yazi-proxy/src/app.rs b/yazi-proxy/src/app.rs index e69d8ae5..a1d6d1fe 100644 --- a/yazi-proxy/src/app.rs +++ b/yazi-proxy/src/app.rs @@ -1,6 +1,6 @@ +use yazi_core::app::QuitOpt; use yazi_macro::{emit, relay}; -use yazi_parser::app::{PluginOpt, QuitOpt, TaskSummary}; -use yazi_shared::CompletionToken; +use yazi_runner::plugin::PluginOpt; pub struct AppProxy; @@ -9,27 +9,7 @@ impl AppProxy { emit!(Call(relay!(app:quit).with_any("opt", opt))); } - pub async fn stop() { - let token = CompletionToken::default(); - emit!(Call(relay!(app:stop).with_any("token", token.clone()))); - token.future().await; - } - - pub async fn resume() { - let token = CompletionToken::default(); - emit!(Call(relay!(app:resume).with_any("token", token.clone()))); - token.future().await; - } - - pub fn plugin(opt: PluginOpt) { - emit!(Call(relay!(app:plugin).with_any("opt", opt))); - } - pub fn plugin_do(opt: PluginOpt) { emit!(Call(relay!(app:plugin_do).with_any("opt", opt))); } - - pub fn update_progress(summary: TaskSummary) { - emit!(Call(relay!(app:update_progress).with_any("summary", summary))); - } } diff --git a/yazi-proxy/src/cmp.rs b/yazi-proxy/src/cmp.rs index b297f554..8c40abf6 100644 --- a/yazi-proxy/src/cmp.rs +++ b/yazi-proxy/src/cmp.rs @@ -1,11 +1,11 @@ +use yazi_core::cmp::CmpOpt; use yazi_macro::{emit, relay}; -use yazi_parser::cmp::ShowOpt; use yazi_shared::Id; pub struct CmpProxy; impl CmpProxy { - pub fn show(opt: ShowOpt) { + pub fn show(opt: CmpOpt) { emit!(Call(relay!(cmp:show).with_any("opt", opt))); } diff --git a/yazi-proxy/src/mgr.rs b/yazi-proxy/src/mgr.rs index 25777a0e..82aebadd 100644 --- a/yazi-proxy/src/mgr.rs +++ b/yazi-proxy/src/mgr.rs @@ -1,5 +1,5 @@ +use yazi_core::{mgr::{DisplaceOpt, FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt}, spot::SpotLock}; use yazi_macro::{emit, relay}; -use yazi_parser::mgr::{DisplaceDoOpt, FilterOpt, FindDoOpt, OpenDoOpt, OpenOpt, SearchOpt, UpdatePeekedOpt, UpdateSpottedOpt}; use yazi_shared::{Id, SStr, url::UrlBuf}; pub struct MgrProxy; @@ -13,7 +13,7 @@ impl MgrProxy { emit!(Call(relay!(mgr:cd, [target.into()]).with("raw", true))); } - pub fn displace_do(tab: Id, opt: DisplaceDoOpt) { + pub fn displace_do(tab: Id, opt: DisplaceOpt) { emit!(Call(relay!(mgr:displace_do).with("tab", tab).with_any("opt", opt))); } @@ -33,10 +33,6 @@ impl MgrProxy { emit!(Call(relay!(mgr:open_do).with_any("opt", opt))); } - pub fn refresh() { - emit!(Call(relay!(mgr:refresh))); - } - pub fn remove_do(targets: Vec, permanently: bool) { emit!(Call( relay!(mgr:remove_do).with("permanently", permanently).with_any("targets", targets) @@ -55,26 +51,7 @@ impl MgrProxy { emit!(Call(relay!(mgr:tab_rename, [name.into()]).with("tab", tab))); } - pub fn update_paged_by(page: usize, only_if: &UrlBuf) { - emit!(Call(relay!(mgr:update_paged, [page]).with("only-if", only_if))); - } - - pub fn update_peeked(opt: UpdatePeekedOpt) { - emit!(Call(relay!(mgr:update_peeked).with_any("opt", opt))); - } - - pub fn update_spotted(opt: UpdateSpottedOpt) { - emit!(Call(relay!(mgr:update_spotted).with_any("opt", opt))); - } - - pub fn upload(urls: I) - where - I: IntoIterator, - { - emit!(Call(relay!(mgr:upload).with_seq(urls))); - } - - pub fn watch() { - emit!(Call(relay!(mgr:watch))); + pub fn update_spotted(lock: SpotLock) { + emit!(Call(relay!(mgr:update_spotted).with_any("lock", lock))); } } diff --git a/yazi-proxy/src/notify.rs b/yazi-proxy/src/notify.rs index 7fad698d..5a1b24a0 100644 --- a/yazi-proxy/src/notify.rs +++ b/yazi-proxy/src/notify.rs @@ -1,33 +1,15 @@ use std::time::Duration; +use yazi_core::notify::MessageOpt; use yazi_macro::{emit, relay}; -use yazi_parser::notify::{PushLevel, PushOpt}; pub struct NotifyProxy; impl NotifyProxy { - pub fn push(opt: PushOpt) { + pub fn push(opt: MessageOpt) { emit!(Call(relay!(notify:push).with_any("opt", opt))); } - pub fn push_warn(title: impl Into, content: impl Into) { - Self::push(PushOpt { - title: title.into(), - content: content.into(), - level: PushLevel::Warn, - timeout: Duration::from_secs(5), - }); - } - - pub fn push_error(title: &str, content: impl ToString) { - Self::push(PushOpt { - title: title.to_owned(), - content: content.to_string(), - level: PushLevel::Error, - timeout: Duration::from_secs(10), - }); - } - pub fn tick(dur: Duration) { emit!(Call(relay!(notify:tick, [dur.as_secs_f64()]))); } diff --git a/yazi-proxy/src/tasks.rs b/yazi-proxy/src/tasks.rs index 79c69efe..cb0f5519 100644 --- a/yazi-proxy/src/tasks.rs +++ b/yazi-proxy/src/tasks.rs @@ -1,14 +1,15 @@ use std::ffi::OsString; +use tokio::sync::mpsc; use yazi_macro::{emit, relay}; -use yazi_parser::tasks::ProcessOpenOpt; -use yazi_shared::{CompletionToken, url::{UrlBuf, UrlCow}}; +use yazi_scheduler::process::ProcessOpt; +use yazi_shared::url::UrlCow; pub struct TasksProxy; impl TasksProxy { // TODO: remove - pub fn open_shell_compat(opt: ProcessOpenOpt) { + pub fn open_shell_compat(opt: ProcessOpt) { emit!(Call(relay!(tasks:open_shell_compat).with_any("opt", opt))); } @@ -19,25 +20,12 @@ impl TasksProxy { block: bool, orphan: bool, ) { - let done = CompletionToken::default(); - emit!(Call(relay!(tasks:process_open).with_any("opt", ProcessOpenOpt { - cwd, - cmd, - args, - block, - orphan, - done: Some(done.clone()), - spread: false - }))); - done.future().await; - } - - pub fn update_succeed(url: I) - where - I: IntoIterator, - I::Item: Into, - { - let urls: Vec<_> = url.into_iter().map(Into::into).collect(); - emit!(Call(relay!(tasks:update_succeed).with_any("urls", urls))); + let (tx, mut rx) = mpsc::unbounded_channel(); + emit!(Call( + relay!(tasks:process_open) + .with_any("opt", ProcessOpt { cwd, cmd, args, block, orphan, spread: false }) + .with_replier(tx) + )); + rx.recv().await; } } diff --git a/yazi-proxy/src/which.rs b/yazi-proxy/src/which.rs index 53250bde..3d52d8ab 100644 --- a/yazi-proxy/src/which.rs +++ b/yazi-proxy/src/which.rs @@ -1,14 +1,14 @@ use tokio::sync::mpsc; use yazi_config::keymap::ChordCow; +use yazi_core::which::WhichOpt; use yazi_macro::{emit, relay}; -use yazi_parser::which::ActivateOpt; pub struct WhichProxy; impl WhichProxy { pub async fn activate(cands: Vec, silent: bool) -> Option { let (tx, mut rx) = mpsc::unbounded_channel(); - emit!(Call(relay!(which:activate).with_any("opt", ActivateOpt { + emit!(Call(relay!(which:activate).with_any("opt", WhichOpt { tx: Some(tx), cands, silent, diff --git a/yazi-runner/Cargo.toml b/yazi-runner/Cargo.toml new file mode 100644 index 00000000..3fa21886 --- /dev/null +++ b/yazi-runner/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "yazi-runner" +description = "Yazi Lua runner" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +homepage.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lints] +workspace = true + +[dependencies] +yazi-binding = { path = "../yazi-binding", version = "26.2.2" } +yazi-boot = { path = "../yazi-boot", version = "26.2.2" } +yazi-config = { path = "../yazi-config", version = "26.2.2" } +yazi-dds = { path = "../yazi-dds", version = "26.2.2" } +yazi-fs = { path = "../yazi-fs", version = "26.2.2" } +yazi-macro = { path = "../yazi-macro", version = "26.2.2" } +yazi-shared = { path = "../yazi-shared", version = "26.2.2" } + +# External dependencies +anyhow = { workspace = true } +dyn-clone = { workspace = true } +hashbrown = { workspace = true } +mlua = { workspace = true } +parking_lot = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } +tokio = { workspace = true } +tokio-util = { workspace = true } +tracing = { workspace = true } diff --git a/yazi-runner/README.md b/yazi-runner/README.md new file mode 100644 index 00000000..bea35b9a --- /dev/null +++ b/yazi-runner/README.md @@ -0,0 +1,5 @@ +# yazi-proxy + +This crate is part of [Yazi][source], and it is not supposed to be used outside, as there are no guarantees about the stability of its API. + +[source]: https://github.com/sxyazi/yazi diff --git a/yazi-runner/src/entry.rs b/yazi-runner/src/entry.rs new file mode 100644 index 00000000..6e410281 --- /dev/null +++ b/yazi-runner/src/entry.rs @@ -0,0 +1,21 @@ +use mlua::{ExternalResult, ObjectLike}; +use tokio::runtime::Handle; +use yazi_dds::Sendable; + +use crate::{Runner, loader::LOADER, plugin::PluginOpt}; + +impl Runner { + pub async fn entry(&'static self, opt: PluginOpt) -> mlua::Result<()> { + LOADER.ensure(&opt.id, |_| ()).await.into_lua_err()?; + + tokio::task::spawn_blocking(move || { + let lua = self.spawn(&opt.id)?; + let job = lua.create_table_from([("args", Sendable::args_to_table(&lua, opt.args)?)])?; + + Handle::current() + .block_on(async { LOADER.load(&lua, &opt.id).await?.call_async_method("entry", job).await }) + }) + .await + .into_lua_err()? + } +} diff --git a/yazi-runner/src/fetcher/fetcher.rs b/yazi-runner/src/fetcher/fetcher.rs new file mode 100644 index 00000000..de5c97fa --- /dev/null +++ b/yazi-runner/src/fetcher/fetcher.rs @@ -0,0 +1,23 @@ +use mlua::{ExternalResult, ObjectLike}; +use tokio::runtime::Handle; +use yazi_binding::Error; + +use crate::{Runner, fetcher::{FetchJob, FetchState}, loader::LOADER}; + +impl Runner { + pub async fn fetch(&'static self, job: FetchJob) -> mlua::Result<(FetchState, Option)> { + if job.files.is_empty() { + return Ok((FetchState::Bool(true), None)); + } + LOADER.ensure(&job.action.name, |_| ()).await.into_lua_err()?; + + tokio::task::spawn_blocking(move || { + let lua = self.spawn(&job.action.name)?; + Handle::current().block_on(async { + LOADER.load(&lua, &job.action.name).await?.call_async_method("fetch", job).await + }) + }) + .await + .into_lua_err()? + } +} diff --git a/yazi-runner/src/fetcher/job.rs b/yazi-runner/src/fetcher/job.rs new file mode 100644 index 00000000..e10516e4 --- /dev/null +++ b/yazi-runner/src/fetcher/job.rs @@ -0,0 +1,20 @@ +use mlua::{IntoLua, Value}; +use yazi_binding::File; +use yazi_dds::Sendable; +use yazi_shared::event::Action; + +pub struct FetchJob { + pub action: &'static Action, + pub files: Vec, +} + +impl IntoLua for FetchJob { + fn into_lua(self, lua: &mlua::Lua) -> mlua::Result { + lua + .create_table_from([ + ("args", Sendable::args_to_table_ref(&lua, &self.action.args)?.into_lua(&lua)?), + ("files", lua.create_sequence_from(self.files.into_iter().map(File::new))?.into_lua(&lua)?), + ])? + .into_lua(lua) + } +} diff --git a/yazi-runner/src/fetcher/mod.rs b/yazi-runner/src/fetcher/mod.rs new file mode 100644 index 00000000..3943905d --- /dev/null +++ b/yazi-runner/src/fetcher/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(fetcher job state); diff --git a/yazi-runner/src/fetcher/state.rs b/yazi-runner/src/fetcher/state.rs new file mode 100644 index 00000000..66dbe55c --- /dev/null +++ b/yazi-runner/src/fetcher/state.rs @@ -0,0 +1,29 @@ +use mlua::{FromLua, Lua, Value}; + +pub enum FetchState { + Bool(bool), + Vec(Vec), +} + +impl FetchState { + pub fn get(&self, idx: usize) -> bool { + match self { + Self::Bool(b) => *b, + Self::Vec(v) => v.get(idx).copied().unwrap_or(false), + } + } +} + +impl FromLua for FetchState { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + Ok(match value { + Value::Boolean(b) => Self::Bool(b), + Value::Table(tbl) => Self::Vec(tbl.sequence_values().collect::>()?), + _ => Err(mlua::Error::FromLuaConversionError { + from: value.type_name(), + to: "FetchState".to_owned(), + message: Some("expected a boolean or a table of booleans".to_owned()), + })?, + }) + } +} diff --git a/yazi-runner/src/lib.rs b/yazi-runner/src/lib.rs new file mode 100644 index 00000000..5f14b835 --- /dev/null +++ b/yazi-runner/src/lib.rs @@ -0,0 +1,11 @@ +yazi_macro::mod_pub!(fetcher loader plugin previewer); + +yazi_macro::mod_flat!(entry preload runner spot); + +pub static RUNNER: yazi_shared::RoCell = yazi_shared::RoCell::new(); + +pub fn init(setter: fn(&mlua::Lua) -> mlua::Result<()>) { + crate::loader::init(); + + RUNNER.init(Runner { setter }); +} diff --git a/yazi-plugin/src/loader/chunk.rs b/yazi-runner/src/loader/chunk.rs similarity index 100% rename from yazi-plugin/src/loader/chunk.rs rename to yazi-runner/src/loader/chunk.rs diff --git a/yazi-plugin/src/loader/loader.rs b/yazi-runner/src/loader/loader.rs similarity index 100% rename from yazi-plugin/src/loader/loader.rs rename to yazi-runner/src/loader/loader.rs diff --git a/yazi-plugin/src/loader/mod.rs b/yazi-runner/src/loader/mod.rs similarity index 53% rename from yazi-plugin/src/loader/mod.rs rename to yazi-runner/src/loader/mod.rs index 187b8085..46ac2c14 100644 --- a/yazi-plugin/src/loader/mod.rs +++ b/yazi-runner/src/loader/mod.rs @@ -2,4 +2,4 @@ yazi_macro::mod_flat!(chunk loader require); pub(super) fn init() { LOADER.with(<_>::default); } -pub(super) fn install(lua: &mlua::Lua) -> mlua::Result<()> { Require::install(lua) } +pub fn install(lua: &mlua::Lua) -> mlua::Result<()> { Require::install(lua) } diff --git a/yazi-plugin/src/loader/require.rs b/yazi-runner/src/loader/require.rs similarity index 100% rename from yazi-plugin/src/loader/require.rs rename to yazi-runner/src/loader/require.rs diff --git a/yazi-runner/src/plugin/mod.rs b/yazi-runner/src/plugin/mod.rs new file mode 100644 index 00000000..bfb71d8d --- /dev/null +++ b/yazi-runner/src/plugin/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(option); diff --git a/yazi-runner/src/plugin/option.rs b/yazi-runner/src/plugin/option.rs new file mode 100644 index 00000000..45fb210c --- /dev/null +++ b/yazi-runner/src/plugin/option.rs @@ -0,0 +1,105 @@ +use std::{borrow::Cow, fmt, fmt::Debug, str::FromStr}; + +use anyhow::bail; +use dyn_clone::DynClone; +use hashbrown::HashMap; +use mlua::{Lua, Table}; +use serde::Deserialize; +use yazi_shared::{SStr, data::{Data, DataKey}, event::{Action, ActionCow}}; + +#[derive(Clone, Debug, Default)] +pub struct PluginOpt { + pub id: SStr, + pub args: HashMap, + pub mode: PluginMode, + pub callback: Option>, +} + +impl TryFrom for PluginOpt { + type Error = anyhow::Error; + + fn try_from(mut a: ActionCow) -> Result { + let Some(id) = a.take_first::().ok().filter(|s| !s.is_empty()) else { + bail!("plugin id cannot be empty"); + }; + + let args = if let Ok(s) = a.second() { + let (words, last) = yazi_shared::shell::unix::split(s, true)?; + Action::parse_args(words, last)? + } else { + Default::default() + }; + + let mode = a.str("mode").parse().unwrap_or_default(); + Ok(Self { id: Self::normalize_id(id), args, mode, callback: a.take_any("callback") }) + } +} + +impl PluginOpt { + pub fn new_callback(id: impl Into, f: impl PluginCallback) -> Self { + Self { + id: Self::normalize_id(id.into()), + mode: PluginMode::Sync, + callback: Some(Box::new(f)), + ..Default::default() + } + } + + fn normalize_id(s: SStr) -> SStr { + match s { + Cow::Borrowed(s) => s.trim_end_matches(".main").into(), + Cow::Owned(mut s) => { + s.truncate(s.trim_end_matches(".main").len()); + s.into() + } + } + } +} + +// --- Mode +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "kebab-case")] +pub enum PluginMode { + #[default] + Auto, + Sync, + Async, +} + +impl FromStr for PluginMode { + type Err = serde::de::value::Error; + + fn from_str(s: &str) -> Result { + Self::deserialize(serde::de::value::StrDeserializer::new(s)) + } +} + +impl PluginMode { + pub fn auto_then(self, sync: bool) -> Self { + if self != Self::Auto { + return self; + } + if sync { Self::Sync } else { Self::Async } + } +} + +// --- Callback +pub trait PluginCallback: + FnOnce(&Lua, Table) -> mlua::Result<()> + Send + Sync + DynClone + 'static +{ +} + +impl PluginCallback for T where + T: FnOnce(&Lua, Table) -> mlua::Result<()> + Send + Sync + DynClone + 'static +{ +} + +impl Clone for Box { + fn clone(&self) -> Self { dyn_clone::clone_box(&**self) } +} + +impl fmt::Debug for dyn PluginCallback { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PluginCallback").finish_non_exhaustive() + } +} diff --git a/yazi-runner/src/preload.rs b/yazi-runner/src/preload.rs new file mode 100644 index 00000000..5f107d14 --- /dev/null +++ b/yazi-runner/src/preload.rs @@ -0,0 +1,63 @@ +use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, VmState}; +use tokio::{runtime::Handle, select}; +use tokio_util::sync::CancellationToken; +use yazi_binding::{Error, File, elements::Rect}; +use yazi_config::LAYOUT; +use yazi_dds::Sendable; +use yazi_shared::event::Action; + +use crate::{Runner, loader::LOADER}; + +impl Runner { + pub async fn preload( + &'static self, + action: &'static Action, + file: yazi_fs::File, + ct: CancellationToken, + ) -> mlua::Result<(bool, Option)> { + let ct_ = ct.clone(); + tokio::task::spawn_blocking(move || { + let future = async { + LOADER.ensure(&action.name, |_| ()).await.into_lua_err()?; + + let lua = self.spawn(&action.name)?; + lua.set_hook( + HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000), + move |_, dbg| { + if ct.is_cancelled() && dbg.source().what != "C" { + Err("Preload task cancelled".into_lua_err()) + } else { + Ok(VmState::Continue) + } + }, + )?; + + let plugin = LOADER.load(&lua, &action.name).await?; + let job = lua.create_table_from([ + ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), + ("args", Sendable::args_to_table_ref(&lua, &action.args)?.into_lua(&lua)?), + ("file", File::new(file).into_lua(&lua)?), + ("skip", 0.into_lua(&lua)?), + ])?; + + if ct_.is_cancelled() { + Ok((false, None)) + } else { + plugin.call_async_method("preload", job).await + } + }; + + Handle::current().block_on(async { + select! { + _ = ct_.cancelled() => Ok((false, None)), + r = future => match r { + Err(e) if e.to_string().contains("Preload task cancelled") => Ok((false, None)), + Ok(_) | Err(_) => r, + }, + } + }) + }) + .await + .into_lua_err()? + } +} diff --git a/yazi-runner/src/previewer/error.rs b/yazi-runner/src/previewer/error.rs new file mode 100644 index 00000000..2ec7961a --- /dev/null +++ b/yazi-runner/src/previewer/error.rs @@ -0,0 +1,30 @@ +use std::sync::Arc; + +use mlua::{IntoLua, Lua, Value}; +use thiserror::Error; + +#[derive(Clone, Debug, Error)] +pub enum PeekError { + #[error("Sync previewer")] + ShouldSync, + #[error("Peek task cancelled")] + Cancelled, + #[error("Peek exceeded upper bound of {0}")] + Exceeded(usize), + #[error("Lua error during peek: {0}")] + Lua(#[from] mlua::Error), + #[error("Unexpected error during peek: {0}")] + Unexpected(Arc), +} + +impl From for PeekError { + fn from(e: anyhow::Error) -> Self { Self::Unexpected(e.into()) } +} + +impl From for PeekError { + fn from(e: tokio::task::JoinError) -> Self { Self::Unexpected(Arc::new(e.into())) } +} + +impl IntoLua for PeekError { + fn into_lua(self, lua: &Lua) -> mlua::Result { self.to_string().into_lua(lua) } +} diff --git a/yazi-runner/src/previewer/job.rs b/yazi-runner/src/previewer/job.rs new file mode 100644 index 00000000..a428d019 --- /dev/null +++ b/yazi-runner/src/previewer/job.rs @@ -0,0 +1,46 @@ +use mlua::{IntoLua, Lua, Value}; +use yazi_binding::{File, elements::Rect}; +use yazi_config::LAYOUT; +use yazi_dds::Sendable; +use yazi_shared::{event::Action, pool::Symbol}; + +#[derive(Clone, Debug)] +pub struct PeekJob { + pub action: &'static Action, + pub file: yazi_fs::File, + pub mime: Symbol, + pub skip: usize, +} + +impl IntoLua for PeekJob { + fn into_lua(self, lua: &Lua) -> mlua::Result { + lua + .create_table_from([ + ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), + ("args", Sendable::args_to_table_ref(&lua, &self.action.args)?.into_lua(&lua)?), + ("file", File::new(self.file).into_lua(&lua)?), + ("mime", self.mime.into_lua(&lua)?), + ("skip", self.skip.into_lua(&lua)?), + ])? + .into_lua(lua) + } +} + +// --- Seek +#[derive(Clone, Debug)] +pub struct SeekJob { + pub file: yazi_fs::File, + pub units: i16, +} + +impl IntoLua for SeekJob { + fn into_lua(self, lua: &Lua) -> mlua::Result { + lua + .create_table_from([ + ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), + ("file", File::new(self.file).into_lua(&lua)?), + ("units", self.units.into_lua(&lua)?), + ])? + .into_lua(lua) + } +} diff --git a/yazi-runner/src/previewer/mod.rs b/yazi-runner/src/previewer/mod.rs new file mode 100644 index 00000000..0b7ece62 --- /dev/null +++ b/yazi-runner/src/previewer/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(error job previewer); diff --git a/yazi-runner/src/previewer/previewer.rs b/yazi-runner/src/previewer/previewer.rs new file mode 100644 index 00000000..86901ee2 --- /dev/null +++ b/yazi-runner/src/previewer/previewer.rs @@ -0,0 +1,65 @@ +use mlua::{ExternalError, HookTriggers, ObjectLike, VmState}; +use tokio::{runtime::Handle, select, sync::mpsc}; + +use crate::{Runner, loader::LOADER, previewer::{PeekError, PeekJob}}; + +impl Runner { + pub async fn peek(&'static self, job: &PeekJob) -> mpsc::Receiver> { + let (tx, rx) = mpsc::channel(1); + + select! { + _ = tx.closed() => {}, + r = LOADER.ensure(&job.action.name, |c| c.sync_peek) => { + match r { + Ok(true) => _ = tx.try_send(Err(PeekError::ShouldSync)), + Ok(false) => self.peek_do(job, tx), + Err(e) => _ = tx.try_send(Err(e.into())), + } + }, + } + + rx + } + + fn peek_do(&'static self, job: &PeekJob, tx: mpsc::Sender>) { + let (tx_, job) = (tx.clone(), job.clone()); + tokio::task::spawn_blocking(move || { + let future = async { + let lua = self.spawn(&job.action.name)?; + lua.set_hook( + HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000), + move |_, dbg| { + if tx.is_closed() && dbg.source().what != "C" { + Err(PeekError::Cancelled.into_lua_err()) + } else { + Ok(VmState::Continue) + } + }, + )?; + + let plugin = LOADER.load(&lua, &job.action.name).await?; + if tx_.is_closed() { + Err(PeekError::Cancelled.into_lua_err()) + } else { + plugin.call_async_method("peek", job).await + } + }; + + Handle::current().block_on(async { + select! { + _ = tx_.closed() => {}, + r = future => match r { + Ok(()) => _ = tx_.send(Ok(())).await, + Err(err) => { + if let Some(e) = err.downcast_ref::() { + tx_.send(Err(e.clone())).await.ok(); + } else { + tx_.send(Err(err.into())).await.ok(); + } + }, + }, + } + }); + }); + } +} diff --git a/yazi-runner/src/runner.rs b/yazi-runner/src/runner.rs new file mode 100644 index 00000000..58a02359 --- /dev/null +++ b/yazi-runner/src/runner.rs @@ -0,0 +1,15 @@ +use mlua::Lua; + +pub struct Runner { + pub(super) setter: fn(&Lua) -> mlua::Result<()>, +} + +impl Runner { + pub fn spawn(&self, name: &str) -> mlua::Result { + let lua = Lua::new(); + lua.set_app_data(yazi_binding::Runtime::new_isolate(name)); + + (self.setter)(&lua)?; + Ok(lua) + } +} diff --git a/yazi-runner/src/spot.rs b/yazi-runner/src/spot.rs new file mode 100644 index 00000000..7d868be3 --- /dev/null +++ b/yazi-runner/src/spot.rs @@ -0,0 +1,65 @@ +use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, VmState}; +use tokio::{runtime::Handle, select}; +use tokio_util::sync::CancellationToken; +use tracing::error; +use yazi_binding::{File, Id}; +use yazi_dds::Sendable; +use yazi_shared::{Ids, event::Action, pool::Symbol}; + +use crate::{Runner, loader::LOADER}; + +static IDS: Ids = Ids::new(); + +impl Runner { + pub fn spot( + &'static self, + action: &'static Action, + file: yazi_fs::File, + mime: Symbol, + skip: usize, + ) -> CancellationToken { + let ct = CancellationToken::new(); + let (ct1, ct2) = (ct.clone(), ct.clone()); + + tokio::task::spawn_blocking(move || { + let future = async { + LOADER.ensure(&action.name, |_| ()).await.into_lua_err()?; + + let lua = self.spawn(&action.name)?; + lua.set_hook( + HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000), + move |_, dbg| { + if ct1.is_cancelled() && dbg.source().what != "C" { + Err("Spot task cancelled".into_lua_err()) + } else { + Ok(VmState::Continue) + } + }, + )?; + + let plugin = LOADER.load(&lua, &action.name).await?; + let job = lua.create_table_from([ + ("id", Id(IDS.next()).into_lua(&lua)?), + ("args", Sendable::args_to_table_ref(&lua, &action.args)?.into_lua(&lua)?), + ("file", File::new(file).into_lua(&lua)?), + ("mime", mime.into_lua(&lua)?), + ("skip", skip.into_lua(&lua)?), + ])?; + + if ct2.is_cancelled() { Ok(()) } else { plugin.call_async_method("spot", job).await } + }; + + Handle::current().block_on(async { + select! { + _ = ct2.cancelled() => {}, + Err(e) = future => if !e.to_string().contains("Spot task cancelled") { + error!("{e}"); + }, + else => {} + } + }); + }); + + ct + } +} diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index edb68f76..6b44d23b 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -18,9 +18,7 @@ yazi-config = { path = "../yazi-config", version = "26.2.2" } yazi-dds = { path = "../yazi-dds", version = "26.2.2" } yazi-fs = { path = "../yazi-fs", version = "26.2.2" } yazi-macro = { path = "../yazi-macro", version = "26.2.2" } -yazi-parser = { path = "../yazi-parser", version = "26.2.2" } -yazi-plugin = { path = "../yazi-plugin", version = "26.2.2" } -yazi-proxy = { path = "../yazi-proxy", version = "26.2.2" } +yazi-runner = { path = "../yazi-runner", version = "26.2.2" } yazi-shared = { path = "../yazi-shared", version = "26.2.2" } yazi-term = { path = "../yazi-term", version = "26.2.2" } yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } diff --git a/yazi-scheduler/src/fetch/fetch.rs b/yazi-scheduler/src/fetch/fetch.rs index 87fe6e59..2c1c2a31 100644 --- a/yazi-scheduler/src/fetch/fetch.rs +++ b/yazi-scheduler/src/fetch/fetch.rs @@ -7,8 +7,7 @@ use tokio::sync::mpsc; use tracing::error; use yazi_config::Priority; use yazi_fs::FsHash64; -use yazi_plugin::isolate; -use yazi_shared::event::ActionCow; +use yazi_runner::RUNNER; use crate::{HIGH, LOW, TaskOp, TaskOps, fetch::{FetchIn, FetchOutFetch}}; @@ -31,18 +30,19 @@ impl Fetch { } pub(crate) async fn fetch(&self, task: FetchIn) -> Result<(), FetchOutFetch> { + let (id, plugin) = (task.id, task.plugin); let hashes: Vec<_> = task.targets.iter().map(|f| f.hash_u64()).collect(); - let (state, err) = isolate::fetch(ActionCow::from(&task.plugin.run), task.targets).await?; + let (state, err) = RUNNER.fetch(task.into()).await?; let mut loaded = self.loaded.lock(); for (_, h) in hashes.into_iter().enumerate().filter(|&(i, _)| !state.get(i)) { - loaded.get_mut(&h).map(|x| *x &= !(1 << task.plugin.idx)); + loaded.get_mut(&h).map(|x| *x &= !(1 << plugin.idx)); } if let Some(e) = err { - error!("Error when running fetcher `{}`:\n{e}", task.plugin.run.name); + error!("Error when running fetcher `{}`:\n{e}", plugin.run.name); } - Ok(self.ops.out(task.id, FetchOutFetch::Succ)) + Ok(self.ops.out(id, FetchOutFetch::Succ)) } } diff --git a/yazi-scheduler/src/fetch/in.rs b/yazi-scheduler/src/fetch/in.rs index 50478048..f7411d9d 100644 --- a/yazi-scheduler/src/fetch/in.rs +++ b/yazi-scheduler/src/fetch/in.rs @@ -1,4 +1,5 @@ use yazi_config::plugin::Fetcher; +use yazi_runner::fetcher::FetchJob; use yazi_shared::Id; #[derive(Debug)] @@ -8,6 +9,10 @@ pub(crate) struct FetchIn { pub(crate) targets: Vec, } +impl From for FetchJob { + fn from(value: FetchIn) -> Self { FetchJob { action: &value.plugin.run, files: value.targets } } +} + impl FetchIn { pub(crate) fn id(&self) -> Id { self.id } } diff --git a/yazi-scheduler/src/fetch/progress.rs b/yazi-scheduler/src/fetch/progress.rs index 8bf5969b..33c6e346 100644 --- a/yazi-scheduler/src/fetch/progress.rs +++ b/yazi-scheduler/src/fetch/progress.rs @@ -1,5 +1,6 @@ use serde::Serialize; -use yazi_parser::app::TaskSummary; + +use crate::TaskSummary; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] pub struct FetchProg { diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index 6a529ec7..d5e3539e 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -9,7 +9,7 @@ use yazi_shared::{path::PathCow, url::{AsUrl, UrlCow, UrlLike}}; use yazi_vfs::{VfsCha, maybe_exists, provider::{self, DirEntry}, unique_file}; use super::{FileInCopy, FileInDelete, FileInHardlink, FileInLink, FileInTrash}; -use crate::{LOW, NORMAL, TaskOp, TaskOps, ctx, file::{FileIn, FileInCut, FileInDownload, FileInUpload, FileOutCopy, FileOutCopyDo, FileOutCut, FileOutCutDo, FileOutDelete, FileOutDeleteDo, FileOutDownload, FileOutDownloadDo, FileOutHardlink, FileOutHardlinkDo, FileOutLink, FileOutTrash, FileOutUpload, FileOutUploadDo, Transaction, Traverse}, hook::{HookInOutCopy, HookInOutCut}, ok_or_not_found, progress_or_break}; +use crate::{LOW, NORMAL, TaskOp, TaskOps, TasksProxy, ctx, file::{FileIn, FileInCut, FileInDownload, FileInUpload, FileOutCopy, FileOutCopyDo, FileOutCut, FileOutCutDo, FileOutDelete, FileOutDeleteDo, FileOutDownload, FileOutDownloadDo, FileOutHardlink, FileOutHardlinkDo, FileOutLink, FileOutTrash, FileOutUpload, FileOutUploadDo, Transaction, Traverse}, hook::{HookInOutCopy, HookInOutCut, HookInOutHardlink, HookInOutLink}, ok_or_not_found, progress_or_break}; pub(crate) struct File { ops: TaskOps, @@ -33,7 +33,9 @@ impl File { .context("Cannot determine unique destination name")?; } - self.ops.out(id, HookInOutCopy::from(&task)); + self.ops.out(id, HookInOutCopy::new(&task.from, &task.to)); + TasksProxy::update_succeed(id, [&task.to], true); + super::traverse::( task, async |dir| match provider::create_dir(dir).await { @@ -96,7 +98,9 @@ impl File { .context("Cannot determine unique destination name")?; } - self.ops.out(id, HookInOutCut::from(&task)); + self.ops.out(id, HookInOutCut::new(&task.from, &task.to)); + TasksProxy::update_succeed(id, [&task.to], true); + if !task.follow && ok_or_not_found(provider::rename(&task.from, &task.to).await).is_ok() { return Ok(self.ops.out(id, FileOutCut::Succ)); } @@ -186,7 +190,9 @@ impl File { unique_file(task.to, false).await.context("Cannot determine unique destination name")?; } - Ok(self.requeue(task, NORMAL)) + self.ops.out(task.id, HookInOutLink::new(&task.from, &task.to)); + self.requeue(task, NORMAL); + Ok(()) } pub(crate) async fn link_do(&self, task: FileInLink) -> Result<(), FileOutLink> { @@ -232,6 +238,7 @@ impl File { unique_file(task.to, false).await.context("Cannot determine unique destination name")?; } + self.ops.out(task.id, HookInOutHardlink::new(&task.from, &task.to)); super::traverse::( task, async |dir| match provider::create_dir(dir).await { diff --git a/yazi-scheduler/src/file/out.rs b/yazi-scheduler/src/file/out.rs index 2f4ec89a..fb452b3c 100644 --- a/yazi-scheduler/src/file/out.rs +++ b/yazi-scheduler/src/file/out.rs @@ -169,6 +169,7 @@ impl FileOutCutDo { pub(crate) enum FileOutLink { Succ, Fail(String), + Clean, } impl From for FileOutLink { @@ -186,6 +187,9 @@ impl FileOutLink { prog.state = Some(false); task.log(reason); } + Self::Clean => { + prog.cleaned = Some(true); + } } } else if let TaskProg::FileCopy(prog) = &mut task.prog { match self { @@ -196,6 +200,7 @@ impl FileOutLink { prog.failed_files += 1; task.log(reason); } + Self::Clean => {} } } else if let TaskProg::FileCut(prog) = &mut task.prog { match self { @@ -206,6 +211,7 @@ impl FileOutLink { prog.failed_files += 1; task.log(reason); } + Self::Clean => {} } } } @@ -218,6 +224,7 @@ pub(crate) enum FileOutHardlink { Deform(String), Succ, Fail(String), + Clean, } impl From for FileOutHardlink { @@ -247,6 +254,9 @@ impl FileOutHardlink { prog.collected = Some(false); task.log(reason); } + Self::Clean => { + prog.cleaned = Some(true); + } } } } diff --git a/yazi-scheduler/src/file/progress.rs b/yazi-scheduler/src/file/progress.rs index 7368a046..d69bbaa1 100644 --- a/yazi-scheduler/src/file/progress.rs +++ b/yazi-scheduler/src/file/progress.rs @@ -1,5 +1,6 @@ use serde::Serialize; -use yazi_parser::app::TaskSummary; + +use crate::TaskSummary; // --- Copy #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] @@ -110,7 +111,8 @@ impl FileProgCut { // --- Link #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] pub struct FileProgLink { - pub state: Option, + pub state: Option, + pub cleaned: Option, } impl From for TaskSummary { @@ -127,13 +129,13 @@ impl From for TaskSummary { impl FileProgLink { pub fn cooked(self) -> bool { self.state == Some(true) } - pub fn running(self) -> bool { self.state.is_none() } + pub fn running(self) -> bool { self.state.is_none() || (self.cleaned.is_none() && self.cooked()) } - pub fn success(self) -> bool { self.cooked() } + pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() } - pub fn failed(self) -> bool { self.state == Some(false) } + pub fn failed(self) -> bool { self.cleaned == Some(false) || self.state == Some(false) } - pub fn cleaned(self) -> Option { None } + pub fn cleaned(self) -> Option { self.cleaned } pub fn percent(self) -> Option { None } } @@ -145,6 +147,7 @@ pub struct FileProgHardlink { pub success: u32, pub failed: u32, pub collected: Option, + pub cleaned: Option, } impl From for TaskSummary { @@ -162,14 +165,16 @@ impl FileProgHardlink { pub fn cooked(self) -> bool { self.collected == Some(true) && self.success == self.total } pub fn running(self) -> bool { - self.collected.is_none() || self.success + self.failed != self.total + self.collected.is_none() + || self.success + self.failed != self.total + || (self.cleaned.is_none() && self.cooked()) } - pub fn success(self) -> bool { self.cooked() } + pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() } - pub fn failed(self) -> bool { self.collected == Some(false) } + pub fn failed(self) -> bool { self.cleaned == Some(false) || self.collected == Some(false) } - pub fn cleaned(self) -> Option { None } + pub fn cleaned(self) -> Option { self.cleaned } pub fn percent(self) -> Option { None } } diff --git a/yazi-scheduler/src/hook/hook.rs b/yazi-scheduler/src/hook/hook.rs index cfa5a600..be8e9460 100644 --- a/yazi-scheduler/src/hook/hook.rs +++ b/yazi-scheduler/src/hook/hook.rs @@ -4,10 +4,9 @@ use parking_lot::Mutex; use tokio::sync::mpsc; use yazi_dds::Pump; use yazi_fs::ok_or_not_found; -use yazi_proxy::TasksProxy; use yazi_vfs::provider; -use crate::{Ongoing, TaskOp, TaskOps, file::{FileOutCopy, FileOutCut, FileOutDelete, FileOutDownload, FileOutTrash, FileOutUpload}, hook::{HookIn, HookInDelete, HookInDownload, HookInOutCopy, HookInOutCut, HookInTrash, HookInUpload}}; +use crate::{Ongoing, TaskOp, TaskOps, TasksProxy, file::{FileOutCopy, FileOutCut, FileOutDelete, FileOutDownload, FileOutHardlink, FileOutLink, FileOutTrash, FileOutUpload}, hook::{HookIn, HookInDelete, HookInDownload, HookInOutCopy, HookInOutCut, HookInOutHardlink, HookInOutLink, HookInTrash, HookInUpload}}; pub(crate) struct Hook { ops: TaskOps, @@ -31,7 +30,7 @@ impl Hook { } let result = ok_or_not_found(provider::remove_dir_clean(&task.from).await); - TasksProxy::update_succeed([&task.to, &task.from]); + TasksProxy::update_succeed(task.id, [&task.to, &task.from], false); Pump::push_move(task.from, task.to); self.ops.out(task.id, FileOutCut::Clean(result)); @@ -39,7 +38,7 @@ impl Hook { pub(crate) async fn copy(&self, task: HookInOutCopy) { if self.ongoing.lock().intact(task.id) { - TasksProxy::update_succeed([&task.to]); + TasksProxy::update_succeed(task.id, [&task.to], false); Pump::push_duplicate(task.from, task.to); } @@ -52,7 +51,7 @@ impl Hook { } let result = ok_or_not_found(provider::remove_dir_all(&task.target).await); - TasksProxy::update_succeed([&task.target]); + TasksProxy::update_succeed(task.id, [&task.target], false); Pump::push_delete(task.target); self.ops.out(task.id, FileOutDelete::Clean(result)); @@ -61,16 +60,32 @@ impl Hook { pub(crate) async fn trash(&self, task: HookInTrash) { let intact = self.ongoing.lock().intact(task.id); if intact { - TasksProxy::update_succeed([&task.target]); + TasksProxy::update_succeed(task.id, [&task.target], false); Pump::push_trash(task.target); } self.ops.out(task.id, FileOutTrash::Clean); } + pub(crate) async fn link(&self, task: HookInOutLink) { + if self.ongoing.lock().intact(task.id) { + TasksProxy::update_succeed(task.id, [&task.to], true); + } + + self.ops.out(task.id, FileOutLink::Clean); + } + + pub(crate) async fn hardlink(&self, task: HookInOutHardlink) { + if self.ongoing.lock().intact(task.id) { + TasksProxy::update_succeed(task.id, [&task.to], true); + } + + self.ops.out(task.id, FileOutHardlink::Clean); + } + pub(crate) async fn download(&self, task: HookInDownload) { let intact = self.ongoing.lock().intact(task.id); if intact { - TasksProxy::update_succeed([&task.target]); + TasksProxy::update_succeed(task.id, [&task.target], false); Pump::push_download(task.target); } self.ops.out(task.id, FileOutDownload::Clean); @@ -79,7 +94,7 @@ impl Hook { pub(crate) async fn upload(&self, task: HookInUpload) { let intact = self.ongoing.lock().intact(task.id); if intact { - TasksProxy::update_succeed([task.target]); + TasksProxy::update_succeed(task.id, [task.target], false); } self.ops.out(task.id, FileOutUpload::Clean); } diff --git a/yazi-scheduler/src/hook/in.rs b/yazi-scheduler/src/hook/in.rs index 5b630994..117875f4 100644 --- a/yazi-scheduler/src/hook/in.rs +++ b/yazi-scheduler/src/hook/in.rs @@ -1,6 +1,6 @@ use yazi_shared::{Id, url::UrlBuf}; -use crate::{Task, TaskProg, file::{FileInCopy, FileInCut}}; +use crate::{Task, TaskProg, file::FileInCopy}; #[derive(Debug)] pub(crate) enum HookIn { @@ -8,6 +8,8 @@ pub(crate) enum HookIn { Cut(HookInOutCut), Delete(HookInDelete), Trash(HookInTrash), + Link(HookInOutLink), + Hardlink(HookInOutHardlink), Download(HookInDownload), Upload(HookInUpload), } @@ -17,6 +19,8 @@ impl_from_in!( Cut(HookInOutCut), Delete(HookInDelete), Trash(HookInTrash), + Link(HookInOutLink), + Hardlink(HookInOutHardlink), Download(HookInDownload), Upload(HookInUpload), ); @@ -28,10 +32,25 @@ impl HookIn { Self::Cut(r#in) => r#in.id, Self::Delete(r#in) => r#in.id, Self::Trash(r#in) => r#in.id, + Self::Link(r#in) => r#in.id, + Self::Hardlink(r#in) => r#in.id, Self::Download(r#in) => r#in.id, Self::Upload(r#in) => r#in.id, } } + + pub(crate) fn with_id(self, id: Id) -> Self { + match self { + Self::Copy(r#in) => Self::Copy(HookInOutCopy { id, ..r#in }), + Self::Cut(r#in) => Self::Cut(HookInOutCut { id, ..r#in }), + Self::Delete(r#in) => Self::Delete(HookInDelete { id, ..r#in }), + Self::Trash(r#in) => Self::Trash(HookInTrash { id, ..r#in }), + Self::Link(r#in) => Self::Link(HookInOutLink { id, ..r#in }), + Self::Hardlink(r#in) => Self::Hardlink(HookInOutHardlink { id, ..r#in }), + Self::Download(r#in) => Self::Download(HookInDownload { id, ..r#in }), + Self::Upload(r#in) => Self::Upload(HookInUpload { id, ..r#in }), + } + } } // --- Copy @@ -42,16 +61,17 @@ pub(crate) struct HookInOutCopy { pub(crate) to: UrlBuf, } -impl From<&FileInCopy> for HookInOutCopy { - fn from(value: &FileInCopy) -> Self { - Self { id: value.id, from: value.from.clone(), to: value.to.clone() } - } -} - impl HookInOutCopy { + pub(crate) fn new(from: U, to: U) -> Self + where + U: Into, + { + Self { id: Id::ZERO, from: from.into(), to: to.into() } + } + pub(crate) fn reduce(self, task: &mut Task) { if let TaskProg::FileCopy(_) = &task.prog { - task.hook = Some(self.into()); + task.hook = Some(HookIn::from(self).with_id(task.id)); } } } @@ -64,16 +84,17 @@ pub(crate) struct HookInOutCut { pub(crate) to: UrlBuf, } -impl From<&FileInCut> for HookInOutCut { - fn from(value: &FileInCut) -> Self { - Self { id: value.id, from: value.from.clone(), to: value.to.clone() } - } -} - impl HookInOutCut { + pub(crate) fn new(from: U, to: U) -> Self + where + U: Into, + { + Self { id: Id::ZERO, from: from.into(), to: to.into() } + } + pub(crate) fn reduce(self, task: &mut Task) { if let TaskProg::FileCut(_) = &task.prog { - task.hook = Some(self.into()); + task.hook = Some(HookIn::from(self).with_id(task.id)); } } } @@ -85,6 +106,15 @@ pub(crate) struct HookInDelete { pub(crate) target: UrlBuf, } +impl HookInDelete { + pub(crate) fn new(target: U) -> Self + where + U: Into, + { + Self { id: Id::ZERO, target: target.into() } + } +} + // --- Trash #[derive(Debug)] pub(crate) struct HookInTrash { @@ -92,6 +122,63 @@ pub(crate) struct HookInTrash { pub(crate) target: UrlBuf, } +impl HookInTrash { + pub(crate) fn new(target: U) -> Self + where + U: Into, + { + Self { id: Id::ZERO, target: target.into() } + } +} + +// --- Link +#[derive(Debug)] +pub(crate) struct HookInOutLink { + pub(crate) id: Id, + #[allow(dead_code)] + pub(crate) from: UrlBuf, + pub(crate) to: UrlBuf, +} + +impl HookInOutLink { + pub(crate) fn new(from: U, to: U) -> Self + where + U: Into, + { + Self { id: Id::ZERO, from: from.into(), to: to.into() } + } + + pub(crate) fn reduce(self, task: &mut Task) { + if let TaskProg::FileLink(_) = &task.prog { + task.hook = Some(HookIn::from(self).with_id(task.id)); + } + } +} + +// --- Hardlink +#[derive(Debug)] +pub(crate) struct HookInOutHardlink { + pub(crate) id: Id, + #[allow(dead_code)] + pub(crate) from: UrlBuf, + pub(crate) to: UrlBuf, +} + +impl HookInOutHardlink { + pub(crate) fn new(from: U, to: U) -> Self + where + U: Into, + { + Self { id: Id::ZERO, from: from.into(), to: to.into() } + } + + pub(crate) fn reduce(self, task: &mut Task) { + if let TaskProg::FileHardlink(_) = &task.prog { + task.hook = Some(HookIn::from(self).with_id(task.id)); + } + } +} + // --- Download #[derive(Debug)] pub(crate) struct HookInDownload { @@ -99,9 +186,27 @@ pub(crate) struct HookInDownload { pub(crate) target: UrlBuf, } +impl HookInDownload { + pub(crate) fn new(target: U) -> Self + where + U: Into, + { + Self { id: Id::ZERO, target: target.into() } + } +} + // --- Upload #[derive(Debug)] pub(crate) struct HookInUpload { pub(crate) id: Id, pub(crate) target: UrlBuf, } + +impl HookInUpload { + pub(crate) fn new(target: U) -> Self + where + U: Into, + { + Self { id: Id::ZERO, target: target.into() } + } +} diff --git a/yazi-scheduler/src/lib.rs b/yazi-scheduler/src/lib.rs index fd3404c9..66ce8de2 100644 --- a/yazi-scheduler/src/lib.rs +++ b/yazi-scheduler/src/lib.rs @@ -2,7 +2,7 @@ mod macros; yazi_macro::mod_pub!(fetch file hook plugin preload process size); -yazi_macro::mod_flat!(ongoing op out progress runner scheduler snap task); +yazi_macro::mod_flat!(ongoing op out progress proxy runner scheduler snap summary task); const LOW: u8 = yazi_config::Priority::Low as u8; const NORMAL: u8 = yazi_config::Priority::Normal as u8; diff --git a/yazi-scheduler/src/ongoing.rs b/yazi-scheduler/src/ongoing.rs index 0d17203a..fd8e1622 100644 --- a/yazi-scheduler/src/ongoing.rs +++ b/yazi-scheduler/src/ongoing.rs @@ -1,7 +1,5 @@ use hashbrown::{HashMap, hash_map::Entry}; -use ordered_float::OrderedFloat; use yazi_config::YAZI; -use yazi_parser::app::TaskSummary; use yazi_shared::{CompletionToken, Id, Ids}; use super::Task; @@ -13,14 +11,11 @@ pub struct Ongoing { } impl Ongoing { - pub(super) fn add(&mut self, name: String) -> &mut Task - where - T: Into + Default, - { + pub(super) fn add(&mut self, name: String, prog: TaskProg) -> &mut Task { static IDS: Ids = Ids::new(); let id = IDS.next(); - self.inner.entry(id).insert(Task::new::(id, name)).into_mut() + self.inner.entry(id).insert(Task::new(id, name, prog)).into_mut() } pub(super) fn cancel(&mut self, id: Id) -> Option { @@ -82,38 +77,4 @@ impl Ongoing { #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } - - pub fn summary(&self) -> TaskSummary { - let mut summary = TaskSummary::default(); - let mut percent_sum = 0.0f64; - let mut percent_count = 0; - - for task in self.values() { - let s: TaskSummary = task.prog.into(); - if s.total == 0 && !task.prog.failed() { - continue; - } - - summary.total += 1; - if let Some(p) = s.percent { - percent_sum += p.0 as f64; - percent_count += 1; - } - - if task.prog.running() { - continue; - } else if task.prog.success() { - summary.success += 1; - } else { - summary.failed += 1; - } - } - - summary.percent = if percent_count == 0 { - None - } else { - Some(OrderedFloat((percent_sum / percent_count as f64) as f32)) - }; - summary - } } diff --git a/yazi-scheduler/src/out.rs b/yazi-scheduler/src/out.rs index 2f64bdcc..5bafd645 100644 --- a/yazi-scheduler/src/out.rs +++ b/yazi-scheduler/src/out.rs @@ -1,4 +1,4 @@ -use crate::{Task, fetch::FetchOutFetch, file::{FileOutCopy, FileOutCopyDo, FileOutCut, FileOutCutDo, FileOutDelete, FileOutDeleteDo, FileOutDownload, FileOutDownloadDo, FileOutHardlink, FileOutHardlinkDo, FileOutLink, FileOutTrash, FileOutUpload, FileOutUploadDo}, hook::{HookInOutCopy, HookInOutCut}, impl_from_out, plugin::PluginOutEntry, preload::PreloadOut, process::{ProcessOutBg, ProcessOutBlock, ProcessOutOrphan}, size::SizeOut}; +use crate::{Task, fetch::FetchOutFetch, file::{FileOutCopy, FileOutCopyDo, FileOutCut, FileOutCutDo, FileOutDelete, FileOutDeleteDo, FileOutDownload, FileOutDownloadDo, FileOutHardlink, FileOutHardlinkDo, FileOutLink, FileOutTrash, FileOutUpload, FileOutUploadDo}, hook::{HookInOutCopy, HookInOutCut, HookInOutHardlink, HookInOutLink}, impl_from_out, plugin::PluginOutEntry, preload::PreloadOut, process::{ProcessOutBg, ProcessOutBlock, ProcessOutOrphan}, size::SizeOut}; #[derive(Debug)] pub(super) enum TaskOut { @@ -32,6 +32,8 @@ pub(super) enum TaskOut { // Hook HookCopy(HookInOutCopy), HookCut(HookInOutCut), + HookLink(HookInOutLink), + HookHardlink(HookInOutHardlink), } impl_from_out! { @@ -48,7 +50,7 @@ impl_from_out! { // Process ProcessBlock(ProcessOutBlock), ProcessOrphan(ProcessOutOrphan), ProcessBg(ProcessOutBg), // Hook - HookCopy(HookInOutCopy), HookCut(HookInOutCut), + HookCopy(HookInOutCopy), HookCut(HookInOutCut), HookLink(HookInOutLink), HookHardlink(HookInOutHardlink), } impl TaskOut { @@ -82,6 +84,8 @@ impl TaskOut { // Hook Self::HookCopy(out) => out.reduce(task), Self::HookCut(out) => out.reduce(task), + Self::HookLink(out) => out.reduce(task), + Self::HookHardlink(out) => out.reduce(task), } } } diff --git a/yazi-scheduler/src/plugin/in.rs b/yazi-scheduler/src/plugin/in.rs index 68ea3a8b..d82fafb6 100644 --- a/yazi-scheduler/src/plugin/in.rs +++ b/yazi-scheduler/src/plugin/in.rs @@ -1,4 +1,4 @@ -use yazi_parser::app::PluginOpt; +use yazi_runner::plugin::PluginOpt; use yazi_shared::Id; #[derive(Debug)] diff --git a/yazi-scheduler/src/plugin/plugin.rs b/yazi-scheduler/src/plugin/plugin.rs index 79ce41f6..76a10a14 100644 --- a/yazi-scheduler/src/plugin/plugin.rs +++ b/yazi-scheduler/src/plugin/plugin.rs @@ -1,9 +1,8 @@ use anyhow::Result; use tokio::sync::mpsc; -use yazi_plugin::isolate; +use yazi_runner::RUNNER; -use super::PluginInEntry; -use crate::{TaskOp, TaskOps, plugin::{PluginIn, PluginOutEntry}}; +use crate::{TaskOp, TaskOps, plugin::{PluginIn, PluginInEntry, PluginOutEntry}}; pub(crate) struct Plugin { ops: TaskOps, @@ -19,7 +18,7 @@ impl Plugin { } pub(crate) async fn entry(&self, task: PluginInEntry) -> Result<(), PluginOutEntry> { - isolate::entry(task.opt).await?; + RUNNER.entry(task.opt).await?; Ok(self.ops.out(task.id, PluginOutEntry::Succ)) } } diff --git a/yazi-scheduler/src/plugin/progress.rs b/yazi-scheduler/src/plugin/progress.rs index b629836a..87fb45a8 100644 --- a/yazi-scheduler/src/plugin/progress.rs +++ b/yazi-scheduler/src/plugin/progress.rs @@ -1,5 +1,6 @@ use serde::Serialize; -use yazi_parser::app::TaskSummary; + +use crate::TaskSummary; // --- Entry #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] diff --git a/yazi-scheduler/src/preload/preload.rs b/yazi-scheduler/src/preload/preload.rs index bf074b39..c40455aa 100644 --- a/yazi-scheduler/src/preload/preload.rs +++ b/yazi-scheduler/src/preload/preload.rs @@ -8,7 +8,7 @@ use tokio_util::sync::CancellationToken; use tracing::error; use yazi_config::Priority; use yazi_fs::FsHash64; -use yazi_plugin::isolate; +use yazi_runner::RUNNER; use crate::{HIGH, LOW, NORMAL, TaskOp, TaskOps, preload::{PreloadIn, PreloadOut}}; @@ -41,7 +41,7 @@ impl Preload { } let hash = task.target.hash_u64(); - let (ok, err) = isolate::preload(&task.plugin.run, task.target, ct).await?; + let (ok, err) = RUNNER.preload(&task.plugin.run, task.target, ct).await?; if !ok { self.loaded.lock().get_mut(&hash).map(|x| *x &= !(1 << task.plugin.idx)); diff --git a/yazi-scheduler/src/preload/progress.rs b/yazi-scheduler/src/preload/progress.rs index 961279a7..101b8d8b 100644 --- a/yazi-scheduler/src/preload/progress.rs +++ b/yazi-scheduler/src/preload/progress.rs @@ -1,5 +1,6 @@ use serde::Serialize; -use yazi_parser::app::TaskSummary; + +use crate::TaskSummary; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] pub struct PreloadProg { diff --git a/yazi-scheduler/src/process/mod.rs b/yazi-scheduler/src/process/mod.rs index 7db19e8f..434c5dc5 100644 --- a/yazi-scheduler/src/process/mod.rs +++ b/yazi-scheduler/src/process/mod.rs @@ -1,4 +1,4 @@ #[macro_use] mod macros; -yazi_macro::mod_flat!(out process progress r#in shell); +yazi_macro::mod_flat!(opt out process progress r#in shell); diff --git a/yazi-scheduler/src/process/opt.rs b/yazi-scheduler/src/process/opt.rs new file mode 100644 index 00000000..f7949ca1 --- /dev/null +++ b/yazi-scheduler/src/process/opt.rs @@ -0,0 +1,15 @@ +use std::ffi::OsString; + +use yazi_shared::url::UrlCow; + +// TODO: remove in favor of ShellOpt +#[derive(Clone, Debug)] +pub struct ProcessOpt { + pub cwd: UrlCow<'static>, + pub cmd: OsString, + pub args: Vec>, + pub block: bool, + pub orphan: bool, + + pub spread: bool, // TODO: remove +} diff --git a/yazi-scheduler/src/process/process.rs b/yazi-scheduler/src/process/process.rs index 57e71ae2..b85594bd 100644 --- a/yazi-scheduler/src/process/process.rs +++ b/yazi-scheduler/src/process/process.rs @@ -1,11 +1,10 @@ use anyhow::{Result, anyhow}; use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::mpsc}; use yazi_binding::Permit; -use yazi_proxy::{AppProxy, NotifyProxy}; use yazi_term::YIELD_TO_SUBPROCESS; use super::{ProcessInBg, ProcessInBlock, ProcessInOrphan, ShellOpt}; -use crate::{TaskOp, TaskOps, process::{ProcessIn, ProcessOutBg, ProcessOutBlock, ProcessOutOrphan}}; +use crate::{AppProxy, NotifyProxy, TaskOp, TaskOps, process::{ProcessIn, ProcessOutBg, ProcessOutBlock, ProcessOutOrphan}}; pub(crate) struct Process { ops: TaskOps, @@ -27,7 +26,10 @@ impl Process { let (id, cmd) = (task.id, task.cmd.clone()); let result = super::shell(task.into()).await; if let Err(e) = result { - NotifyProxy::push_warn(cmd.to_string_lossy(), format!("Failed to start process: {e}")); + NotifyProxy::push_warn( + cmd.to_string_lossy().into_owned(), + format!("Failed to start process: {e}"), + ); return Ok(self.ops.out(id, ProcessOutBlock::Succ)); } @@ -38,7 +40,7 @@ impl Process { Some(code) => format!("Process exited with status code: {code}"), None => "Process terminated by signal".to_string(), }; - NotifyProxy::push_warn(cmd.to_string_lossy(), content); + NotifyProxy::push_warn(cmd.to_string_lossy().into_owned(), content); } Ok(self.ops.out(id, ProcessOutBlock::Succ)) diff --git a/yazi-scheduler/src/process/progress.rs b/yazi-scheduler/src/process/progress.rs index 9dd27dd4..0128e1b6 100644 --- a/yazi-scheduler/src/process/progress.rs +++ b/yazi-scheduler/src/process/progress.rs @@ -1,5 +1,6 @@ use serde::Serialize; -use yazi_parser::app::TaskSummary; + +use crate::TaskSummary; // --- Block #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] diff --git a/yazi-scheduler/src/progress.rs b/yazi-scheduler/src/progress.rs index a06fbb40..3546e18b 100644 --- a/yazi-scheduler/src/progress.rs +++ b/yazi-scheduler/src/progress.rs @@ -1,7 +1,6 @@ use serde::Serialize; -use yazi_parser::app::TaskSummary; -use crate::{fetch::FetchProg, file::{FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, impl_from_prog, plugin::PluginProgEntry, preload::PreloadProg, process::{ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::SizeProg}; +use crate::{TaskSummary, fetch::FetchProg, file::{FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, impl_from_prog, plugin::PluginProgEntry, preload::PreloadProg, process::{ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::SizeProg}; #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] #[serde(tag = "kind")] diff --git a/yazi-scheduler/src/proxy.rs b/yazi-scheduler/src/proxy.rs new file mode 100644 index 00000000..0ffd3429 --- /dev/null +++ b/yazi-scheduler/src/proxy.rs @@ -0,0 +1,55 @@ +use yazi_macro::{emit, relay}; +use yazi_shared::{CompletionToken, Id, SStr, url::UrlBuf}; + +pub struct AppProxy; + +impl AppProxy { + // FIXME: use replier instead of token + pub async fn stop() { + let token = CompletionToken::default(); + emit!(Call(relay!(app:stop).with_any("token", token.clone()))); + token.future().await; + } + + // FIXME: use replier instead of token + pub async fn resume() { + let token = CompletionToken::default(); + emit!(Call(relay!(app:resume).with_any("token", token.clone()))); + token.future().await; + } +} + +// --- Tasks +pub struct TasksProxy; + +impl TasksProxy { + pub fn update_succeed(id: Id, urls: I, track: bool) + where + I: IntoIterator, + I::Item: Into, + { + let urls: Vec<_> = urls.into_iter().map(Into::into).collect(); + emit!(Call(relay!(tasks:update_succeed, [id]).with_any("urls", urls).with("track", track))); + } +} + +// --- Notify +pub struct NotifyProxy; + +impl NotifyProxy { + pub fn push_warn(title: impl Into, content: impl Into) { + emit!(Call( + relay!(notify:push, [content.into(), title.into()]) + .with("level", SStr::Borrowed("warn")) + .with("timeout", 5f64) + )); + } + + pub fn push_error(title: impl Into, content: impl Into) { + emit!(Call( + relay!(notify:push, [content.into(), title.into()]) + .with("level", SStr::Borrowed("error")) + .with("timeout", 10f64) + )); + } +} diff --git a/yazi-scheduler/src/runner.rs b/yazi-scheduler/src/runner.rs index 499b9061..03f9d9fe 100644 --- a/yazi-scheduler/src/runner.rs +++ b/yazi-scheduler/src/runner.rs @@ -266,6 +266,8 @@ impl Runner { HookIn::Cut(r#in) => self.hook.cut(r#in).await, HookIn::Delete(r#in) => self.hook.delete(r#in).await, HookIn::Trash(r#in) => self.hook.trash(r#in).await, + HookIn::Link(r#in) => self.hook.link(r#in).await, + HookIn::Hardlink(r#in) => self.hook.hardlink(r#in).await, HookIn::Download(r#in) => self.hook.download(r#in).await, HookIn::Upload(r#in) => self.hook.upload(r#in).await, } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 83a1aa38..9b066fa5 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -1,15 +1,16 @@ -use std::{ops::Deref, sync::Arc, time::Duration}; +use std::{ops::Deref, sync::{Arc, atomic::{AtomicU64, Ordering}}, time::Duration}; use tokio::task::JoinHandle; use yazi_config::{YAZI, plugin::{Fetcher, Preloader}}; -use yazi_parser::{app::PluginOpt, tasks::ProcessOpenOpt}; +use yazi_runner::plugin::PluginOpt; use yazi_shared::{CompletionToken, Id, Throttle, url::{UrlBuf, UrlLike}}; -use crate::{HIGH, LOW, NORMAL, Runner, fetch::{FetchIn, FetchProg}, file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload, FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, hook::{HookInDelete, HookInDownload, HookInTrash, HookInUpload}, plugin::{PluginInEntry, PluginProgEntry}, preload::{PreloadIn, PreloadProg}, process::{ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::{SizeIn, SizeProg}}; +use crate::{HIGH, LOW, NORMAL, Runner, Task, TaskProg, fetch::{FetchIn, FetchProg}, file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload, FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, hook::{HookIn, HookInDelete, HookInDownload, HookInTrash, HookInUpload}, plugin::{PluginInEntry, PluginProgEntry}, preload::{PreloadIn, PreloadProg}, process::{ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessOpt, ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::{SizeIn, SizeProg}}; pub struct Scheduler { - pub runner: Runner, - handles: Vec>, + pub runner: Runner, + pub user_action: AtomicU64, + handles: Vec>, } impl Deref for Scheduler { @@ -21,7 +22,35 @@ impl Deref for Scheduler { impl Scheduler { pub fn serve() -> Self { let (runner, handles) = Runner::make(); - Self { runner, handles } + Self { runner, user_action: AtomicU64::new(0), handles } + } + + fn add(&self, name: String, map: impl FnOnce(&mut Task) -> R) -> R + where + T: Into + Default, + { + let prog = T::default().into(); + let is_user = prog.is_user(); + + let mut ongoing = self.ongoing.lock(); + let task = ongoing.add(name, prog); + + if is_user { + self.user_action.store(task.id.get(), Ordering::Relaxed); + } + map(task) + } + + fn add_hooked( + &self, + name: String, + hook: impl Into, + map: impl FnOnce(&mut Task) -> R, + ) -> R + where + T: Into + Default, + { + self.add::(name, |t| map(t.with_hook(hook))) } pub fn cancel(&self, id: Id) -> bool { @@ -40,152 +69,112 @@ impl Scheduler { } pub fn file_cut(&self, from: UrlBuf, to: UrlBuf, force: bool) { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!("Cut {} to {}", from.display(), to.display())); + let name = format!("Cut {} to {}", from.display(), to.display()); + let (id, done) = self.add::(name, |t| (t.id, t.done.clone())); if to.try_starts_with(&from).unwrap_or(false) && !to.covariant(&from) { - return self - .ops - .out(task.id, FileOutCut::Fail("Cannot cut directory into itself".to_owned())); + return self.ops.out(id, FileOutCut::Fail("Cannot cut directory into itself".to_owned())); } let follow = !from.scheme().covariant(to.scheme()); self.file.submit( - FileInCut { - id: task.id, - from, - to, - force, - cha: None, - follow, - retry: 0, - drop: None, - done: task.done.clone(), - }, + FileInCut { id, from, to, force, cha: None, follow, retry: 0, drop: None, done }, LOW, ); } pub fn file_copy(&self, from: UrlBuf, to: UrlBuf, force: bool, follow: bool) { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!("Copy {} to {}", from.display(), to.display())); + let name = format!("Copy {} to {}", from.display(), to.display()); + let (id, done) = self.add::(name, |t| (t.id, t.done.clone())); if to.try_starts_with(&from).unwrap_or(false) && !to.covariant(&from) { - return self - .ops - .out(task.id, FileOutCopy::Fail("Cannot copy directory into itself".to_owned())); + return self.ops.out(id, FileOutCopy::Fail("Cannot copy directory into itself".to_owned())); } let follow = follow || !from.scheme().covariant(to.scheme()); - self.file.submit( - FileInCopy { - id: task.id, - from, - to, - force, - cha: None, - follow, - retry: 0, - done: task.done.clone(), - }, - LOW, - ); + self.file.submit(FileInCopy { id, from, to, force, cha: None, follow, retry: 0, done }, LOW); } pub fn file_link(&self, from: UrlBuf, to: UrlBuf, relative: bool, force: bool) { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!("Link {} to {}", from.display(), to.display())); + let name = format!("Link {} to {}", from.display(), to.display()); + let id = self.add::(name, |t| t.id); self.file.submit( - FileInLink { - id: task.id, - from, - to, - force, - cha: None, - resolve: false, - relative, - delete: false, - }, + FileInLink { id, from, to, force, cha: None, resolve: false, relative, delete: false }, LOW, ); } pub fn file_hardlink(&self, from: UrlBuf, to: UrlBuf, force: bool, follow: bool) { - let mut ongoing = self.ongoing.lock(); - let task = - ongoing.add::(format!("Hardlink {} to {}", from.display(), to.display())); + let name = format!("Hardlink {} to {}", from.display(), to.display()); + let id = self.add::(name, |t| t.id); if !from.scheme().covariant(to.scheme()) { return self .ops - .out(task.id, FileOutHardlink::Fail("Cannot hardlink cross filesystem".to_owned())); + .out(id, FileOutHardlink::Fail("Cannot hardlink cross filesystem".to_owned())); } if to.try_starts_with(&from).unwrap_or(false) && !to.covariant(&from) { return self .ops - .out(task.id, FileOutHardlink::Fail("Cannot hardlink directory into itself".to_owned())); + .out(id, FileOutHardlink::Fail("Cannot hardlink directory into itself".to_owned())); } - self.file.submit(FileInHardlink { id: task.id, from, to, force, cha: None, follow }, LOW); + self.file.submit(FileInHardlink { id, from, to, force, cha: None, follow }, LOW); } pub fn file_delete(&self, target: UrlBuf) { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!("Delete {}", target.display())); + let name = format!("Delete {}", target.display()); - task.set_hook(HookInDelete { id: task.id, target: target.clone() }); - self.file.submit(FileInDelete { id: task.id, target, cha: None }, LOW); + let hook = HookInDelete::new(&target); + let id = self.add_hooked::(name, hook, |t| t.id); + + self.file.submit(FileInDelete { id, target, cha: None }, LOW); } pub fn file_trash(&self, target: UrlBuf) { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!("Trash {}", target.display())); + let name = format!("Trash {}", target.display()); - task.set_hook(HookInTrash { id: task.id, target: target.clone() }); - self.file.submit(FileInTrash { id: task.id, target }, LOW); + let hook = HookInTrash::new(&target); + let id = self.add_hooked::(name, hook, |t| t.id); + + self.file.submit(FileInTrash { id, target }, LOW); } pub fn file_download(&self, target: UrlBuf) -> CompletionToken { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!("Download {}", target.display())); + let name = format!("Download {}", target.display()); - if target.kind().is_remote() { - task.set_hook(HookInDownload { id: task.id, target: target.clone() }); - self.file.submit( - FileInDownload { id: task.id, target, cha: None, retry: 0, done: task.done.clone() }, - LOW, - ); - } else { - self.ops.out(task.id, FileOutDownload::Fail("Cannot download non-remote file".to_owned())); + let hook = HookInDownload::new(&target); + let (id, done) = self.add_hooked::(name, hook, |t| (t.id, t.done.clone())); + + if !target.kind().is_remote() { + self.ops.out(id, FileOutDownload::Fail("Cannot download non-remote file".to_owned())); + return done; } - task.done.clone() + self.file.submit(FileInDownload { id, target, cha: None, retry: 0, done: done.clone() }, LOW); + done.clone() } pub fn file_upload(&self, target: UrlBuf) { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!("Upload {}", target.display())); + let name = format!("Upload {}", target.display()); + + let hook = HookInUpload::new(&target); + let (id, done) = self.add_hooked::(name, hook, |t| (t.id, t.done.clone())); if !target.kind().is_remote() { - return self - .ops - .out(task.id, FileOutUpload::Fail("Cannot upload non-remote file".to_owned())); - }; + return self.ops.out(id, FileOutUpload::Fail("Cannot upload non-remote file".to_owned())); + } - task.set_hook(HookInUpload { id: task.id, target: target.clone() }); - self.file.submit( - FileInUpload { id: task.id, target, cha: None, cache: None, done: task.done.clone() }, - LOW, - ); + self.file.submit(FileInUpload { id, target, cha: None, cache: None, done }, LOW); } pub fn plugin_entry(&self, opt: PluginOpt) { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!("Run micro plugin `{}`", opt.id)); + let name = format!("Run micro plugin `{}`", opt.id); + let id = self.add::(name, |t| t.id); - self.plugin.submit(PluginInEntry { id: task.id, opt }, NORMAL); + self.plugin.submit(PluginInEntry { id, opt }, NORMAL); } pub fn fetch_paged( @@ -193,15 +182,11 @@ impl Scheduler { fetcher: &'static Fetcher, targets: Vec, ) -> CompletionToken { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!( - "Run fetcher `{}` with {} target(s)", - fetcher.run.name, - targets.len() - )); + let name = format!("Run fetcher `{}` with {} target(s)", fetcher.run.name, targets.len()); + let (id, done) = self.add::(name, |t| (t.id, t.done.clone())); - self.fetch.submit(FetchIn { id: task.id, plugin: fetcher, targets }); - task.done.clone() + self.fetch.submit(FetchIn { id, plugin: fetcher, targets }); + done } pub async fn fetch_mimetype(&self, targets: Vec) -> bool { @@ -219,27 +204,24 @@ impl Scheduler { } pub fn preload_paged(&self, preloader: &'static Preloader, target: &yazi_fs::File) { - let mut ongoing = self.ongoing.lock(); - let task = ongoing.add::(format!("Run preloader `{}`", preloader.run.name)); + let name = format!("Run preloader `{}`", preloader.run.name); + let id = self.add::(name, |t| t.id); - let target = target.clone(); - self.preload.submit(PreloadIn { id: task.id, plugin: preloader, target }); + self.preload.submit(PreloadIn { id, plugin: preloader, target: target.clone() }); } pub fn prework_size(&self, targets: Vec<&UrlBuf>) { let throttle = Arc::new(Throttle::new(targets.len(), Duration::from_millis(300))); - let mut ongoing = self.ongoing.lock(); for target in targets { - let task = ongoing.add::(format!("Calculate the size of {}", target.display())); - let target = target.clone(); - let throttle = throttle.clone(); + let name = format!("Calculate the size of {}", target.display()); + let id = self.add::(name, |t| t.id); - self.size.submit(SizeIn { id: task.id, target, throttle }, NORMAL); + self.size.submit(SizeIn { id, target: target.clone(), throttle: throttle.clone() }, NORMAL); } } - pub fn process_open(&self, opt: ProcessOpenOpt) { + pub fn process_open(&self, opt: ProcessOpt) -> CompletionToken { let name = { let args = opt.args.iter().map(|a| a.display().to_string()).collect::>().join(" "); if args.is_empty() { @@ -249,39 +231,29 @@ impl Scheduler { } }; - let mut ongoing = self.ongoing.lock(); - let task = if opt.block { - ongoing.add::(name) + let (id, done) = if opt.block { + self.add::(name, |t| (t.id, t.done.clone())) } else if opt.orphan { - ongoing.add::(name) + self.add::(name, |t| (t.id, t.done.clone())) } else { - ongoing.add::(name) + self.add::(name, |t| (t.id, t.done.clone())) }; - if let Some(done) = opt.done { - task.done = done; - } - if opt.block { self .process - .submit(ProcessInBlock { id: task.id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }, NORMAL); + .submit(ProcessInBlock { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }, NORMAL); } else if opt.orphan { - self.process.submit( - ProcessInOrphan { id: task.id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }, - NORMAL, - ); + self + .process + .submit(ProcessInOrphan { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args }, NORMAL); } else { self.process.submit( - ProcessInBg { - id: task.id, - cwd: opt.cwd, - cmd: opt.cmd, - args: opt.args, - done: task.done.clone(), - }, + ProcessInBg { id, cwd: opt.cwd, cmd: opt.cmd, args: opt.args, done: done.clone() }, NORMAL, ); }; + + done } } diff --git a/yazi-scheduler/src/size/progress.rs b/yazi-scheduler/src/size/progress.rs index 0c8f8856..a4ba42f0 100644 --- a/yazi-scheduler/src/size/progress.rs +++ b/yazi-scheduler/src/size/progress.rs @@ -1,5 +1,6 @@ use serde::Serialize; -use yazi_parser::app::TaskSummary; + +use crate::TaskSummary; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] pub struct SizeProg { diff --git a/yazi-scheduler/src/summary.rs b/yazi-scheduler/src/summary.rs new file mode 100644 index 00000000..78f26b05 --- /dev/null +++ b/yazi-scheduler/src/summary.rs @@ -0,0 +1,48 @@ +use ordered_float::OrderedFloat; +use serde::Serialize; + +use crate::Ongoing; + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)] +pub struct TaskSummary { + pub total: u32, + pub success: u32, + pub failed: u32, + pub percent: Option>, +} + +impl From<&Ongoing> for TaskSummary { + fn from(value: &Ongoing) -> Self { + let mut summary = Self::default(); + let mut percent_sum = 0.0f64; + let mut percent_count = 0; + + for task in value.values() { + let s: TaskSummary = task.prog.into(); + if s.total == 0 && !task.prog.failed() { + continue; + } + + summary.total += 1; + if let Some(p) = s.percent { + percent_sum += p.0 as f64; + percent_count += 1; + } + + if task.prog.running() { + continue; + } else if task.prog.success() { + summary.success += 1; + } else { + summary.failed += 1; + } + } + + summary.percent = if percent_count == 0 { + None + } else { + Some(OrderedFloat((percent_sum / percent_count as f64) as f32)) + }; + summary + } +} diff --git a/yazi-scheduler/src/task.rs b/yazi-scheduler/src/task.rs index 02c5fc0d..a4037d62 100644 --- a/yazi-scheduler/src/task.rs +++ b/yazi-scheduler/src/task.rs @@ -16,14 +16,11 @@ pub struct Task { } impl Task { - pub(super) fn new(id: Id, name: String) -> Self - where - T: Into + Default, - { + pub(super) fn new(id: Id, name: String, prog: TaskProg) -> Self { Self { id, name, - prog: T::default().into(), + prog, hook: None, done: Default::default(), @@ -41,5 +38,8 @@ impl Task { } } - pub(super) fn set_hook(&mut self, hook: impl Into) { self.hook = Some(hook.into()); } + pub(super) fn with_hook(&mut self, hook: impl Into) -> &mut Self { + self.hook = Some(hook.into().with_id(self.id)); + self + } } diff --git a/yazi-shared/src/data/data.rs b/yazi-shared/src/data/data.rs index 6db6fb84..583dd91c 100644 --- a/yazi-shared/src/data/data.rs +++ b/yazi-shared/src/data/data.rs @@ -1,10 +1,10 @@ use std::borrow::Cow; -use anyhow::{Result, bail}; +use anyhow::{Result, anyhow, bail}; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; -use crate::{Id, SStr, data::{DataAny, DataKey}, path::PathBufDyn, strand::{IntoStrand, StrandBuf}, url::{UrlBuf, UrlCow}}; +use crate::{Id, SStr, data::{DataAny, DataKey}, path::PathBufDyn, strand::{IntoStrand, StrandBuf}, url::{Url, UrlBuf, UrlCow}}; // --- Data #[derive(Clone, Debug, Default, Deserialize, Serialize)] @@ -65,6 +65,10 @@ impl From for Data { fn from(value: Id) -> Self { Self::Id(value) } } +impl From> for Data { + fn from(value: Url) -> Self { Self::Url(value.into()) } +} + impl From for Data { fn from(value: UrlBuf) -> Self { Self::Url(value) } } @@ -105,10 +109,7 @@ impl TryFrom for SStr { type Error = anyhow::Error; fn try_from(value: Data) -> Result { - match value { - Data::String(s) => Ok(s), - _ => bail!("not a string"), - } + value.into_sstr().ok_or_else(|| anyhow!("not a string")) } } @@ -123,14 +124,19 @@ impl<'a> TryFrom<&'a Data> for Cow<'a, str> { } } -impl TryFrom for HashMap { +impl TryFrom<&Data> for String { + type Error = anyhow::Error; + + fn try_from(value: &Data) -> Result { + value.try_into().map(|s: &str| s.to_owned()) + } +} + +impl TryFrom for String { type Error = anyhow::Error; fn try_from(value: Data) -> Result { - match value { - Data::Dict(d) => Ok(d), - _ => bail!("not a dict"), - } + SStr::try_from(value).map(|s| s.into_owned()) } } @@ -142,6 +148,17 @@ impl TryFrom<&Data> for HashMap { } } +impl TryFrom for HashMap { + type Error = anyhow::Error; + + fn try_from(value: Data) -> Result { + match value { + Data::Dict(d) => Ok(d), + _ => bail!("not a dict"), + } + } +} + impl TryFrom for UrlCow<'static> { type Error = anyhow::Error; @@ -224,7 +241,7 @@ impl Data { } } - pub fn into_string(self) -> Option { + pub fn into_sstr(self) -> Option { match self { Self::String(s) => Some(s), _ => None, @@ -278,7 +295,7 @@ macro_rules! impl_into_number { Data::Integer(i) if *i == (*i as $t as _) => *i as $t, Data::Number(n) if *n == (*n as $t as _) => *n as $t, Data::String(s) => s.parse()?, - Data::Id(i) if i.0 == (i.0 as $t as _) => i.0 as $t, + Data::Id(i) if i.get() == (i.get() as $t as _) => i.get() as $t, _ => bail!("not a number"), }) } diff --git a/yazi-shared/src/errors/mod.rs b/yazi-shared/src/errors/mod.rs deleted file mode 100644 index 2e274470..00000000 --- a/yazi-shared/src/errors/mod.rs +++ /dev/null @@ -1 +0,0 @@ -yazi_macro::mod_flat!(peek); diff --git a/yazi-shared/src/errors/peek.rs b/yazi-shared/src/errors/peek.rs deleted file mode 100644 index 264f5954..00000000 --- a/yazi-shared/src/errors/peek.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::{error::Error, fmt::{self, Display}}; - -#[derive(Debug)] -pub enum PeekError { - Exceed(usize), - Unexpected(String), -} - -impl Display for PeekError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Exceed(lines) => write!(f, "Exceed maximum lines {lines}"), - Self::Unexpected(msg) => write!(f, "{msg}"), - } - } -} - -impl Error for PeekError {} - -impl From for PeekError { - fn from(error: String) -> Self { Self::Unexpected(error) } -} -impl From<&str> for PeekError { - fn from(error: &str) -> Self { Self::from(error.to_owned()) } -} -impl From for PeekError { - fn from(error: anyhow::Error) -> Self { Self::from(error.to_string()) } -} -impl From for PeekError { - fn from(error: std::io::Error) -> Self { Self::from(error.to_string()) } -} -impl From for PeekError { - fn from(error: tokio::task::JoinError) -> Self { Self::from(error.to_string()) } -} diff --git a/yazi-shared/src/event/action.rs b/yazi-shared/src/event/action.rs index cb6e9c6d..d405b27a 100644 --- a/yazi-shared/src/event/action.rs +++ b/yazi-shared/src/event/action.rs @@ -3,6 +3,7 @@ use std::{borrow::Cow, fmt::{self, Display}, mem, str::FromStr}; use anyhow::{Result, anyhow, bail}; use hashbrown::HashMap; use serde::{Deserialize, de}; +use tokio::sync::mpsc; use crate::{Layer, SStr, Source, data::{Data, DataAny, DataKey}}; @@ -88,6 +89,11 @@ impl Action { self } + pub fn with_replier(mut self, tx: mpsc::UnboundedSender>) -> Self { + self.args.insert("replier".into(), Data::Any(Box::new(tx))); + self + } + // --- Get pub fn get<'a, T>(&'a self, name: impl Into) -> Result where @@ -109,6 +115,10 @@ impl Action { self.args.get(&name.into())?.as_any() } + pub fn replier(&self) -> Option<&mpsc::UnboundedSender>> { + self.any("replier") + } + pub fn first<'a, T>(&'a self) -> Result where T: TryFrom<&'a Data>, @@ -200,6 +210,10 @@ impl Action { (0..self.len()).filter_map(|i| self.args.remove(&DataKey::from(i))?.into_any()) } + pub fn take_replier(&mut self) -> Option>> { + self.take_any("replier") + } + // Parse pub fn parse_args(words: I, last: Option) -> Result> where diff --git a/yazi-shared/src/event/cow.rs b/yazi-shared/src/event/cow.rs index 2f6c7314..1ae32cbe 100644 --- a/yazi-shared/src/event/cow.rs +++ b/yazi-shared/src/event/cow.rs @@ -1,6 +1,7 @@ use std::{iter, ops::Deref}; use anyhow::Result; +use tokio::sync::mpsc; use super::Action; use crate::data::{Data, DataKey}; @@ -103,4 +104,11 @@ impl ActionCow { Self::Borrowed(_) => Box::new(iter::empty()), } } + + pub fn take_replier(&mut self) -> Option>> { + match self { + Self::Owned(c) => c.take_replier(), + Self::Borrowed(_) => None, + } + } } diff --git a/yazi-shared/src/id.rs b/yazi-shared/src/id.rs index 0abaa021..a51da997 100644 --- a/yazi-shared/src/id.rs +++ b/yazi-shared/src/id.rs @@ -5,9 +5,11 @@ use serde::{Deserialize, Serialize}; #[derive( Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, )] -pub struct Id(pub u64); +pub struct Id(u64); impl Id { + pub const ZERO: Self = Self(0); + #[inline] pub const fn get(&self) -> u64 { self.0 } diff --git a/yazi-shared/src/lib.rs b/yazi-shared/src/lib.rs index eeaed195..0733bd98 100644 --- a/yazi-shared/src/lib.rs +++ b/yazi-shared/src/lib.rs @@ -1,4 +1,4 @@ -yazi_macro::mod_pub!(data errors event loc path pool scheme shell strand translit url wtf8); +yazi_macro::mod_pub!(data event loc path pool scheme shell strand translit url wtf8); yazi_macro::mod_flat!(alias bytes chars completion_token condition debounce env id last_value layer localset natsort os predictor ro_cell source sync_cell terminal tests throttle time utf8); diff --git a/yazi-shared/src/pool/symbol.rs b/yazi-shared/src/pool/symbol.rs index 917099d9..1d83b091 100644 --- a/yazi-shared/src/pool/symbol.rs +++ b/yazi-shared/src/pool/symbol.rs @@ -82,6 +82,10 @@ impl PartialEq for Symbol { fn eq(&self, other: &str) -> bool { self.as_ref() == other } } +impl PartialEq<&str> for Symbol { + fn eq(&self, other: &&str) -> bool { self.as_ref() == *other } +} + impl PartialEq<[u8]> for Symbol<[u8]> { fn eq(&self, other: &[u8]) -> bool { self.as_ref() == other } } diff --git a/yazi-shared/src/url/cov.rs b/yazi-shared/src/url/cov.rs index 42520ae9..38b3f504 100644 --- a/yazi-shared/src/url/cov.rs +++ b/yazi-shared/src/url/cov.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::url::{AsUrl, Url, UrlBuf, UrlCow, UrlLike}; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Eq)] pub struct UrlCov<'a>(Url<'a>); impl<'a> Deref for UrlCov<'a> { @@ -14,12 +14,20 @@ impl<'a> Deref for UrlCov<'a> { fn deref(&self) -> &Self::Target { &self.0 } } +impl<'a> From<&'a UrlBuf> for UrlCov<'a> { + fn from(value: &'a UrlBuf) -> Self { Self(value.as_url()) } +} + impl<'a> From<&'a UrlBufCov> for UrlCov<'a> { - fn from(value: &'a UrlBufCov) -> Self { Self(value.0.as_url()) } + fn from(value: &'a UrlBufCov) -> Self { Self(value.as_url()) } +} + +impl PartialEq for UrlCov<'_> { + fn eq(&self, other: &UrlCov) -> bool { self.covariant(other.0) } } impl PartialEq for UrlCov<'_> { - fn eq(&self, other: &UrlBufCov) -> bool { self.0.covariant(&other.0) } + fn eq(&self, other: &UrlBufCov) -> bool { self.covariant(&other.0) } } impl Hash for UrlCov<'_> { diff --git a/yazi-vfs/src/provider/sftp/conn.rs b/yazi-vfs/src/provider/sftp/conn.rs index 9c13b69d..9069ae97 100644 --- a/yazi-vfs/src/provider/sftp/conn.rs +++ b/yazi-vfs/src/provider/sftp/conn.rs @@ -1,6 +1,6 @@ use std::{io, sync::Arc, time::{Duration, SystemTime}}; -use russh::keys::PrivateKeyWithHashAlg; +use russh::keys::{PrivateKeyWithHashAlg, agent::AgentIdentity}; use yazi_config::vfs::ServiceSftp; use yazi_fs::provider::local::Local; @@ -221,24 +221,33 @@ impl Conn { let mut agent = russh::keys::agent::client::AgentClient::connect_named_pipe(identity_agent).await?; - let keys = agent.request_identities().await?; - if keys.is_empty() { + let identities = agent.request_identities().await?; + if identities.is_empty() { return Err(cfg_err!("No keys found in SSH agent")); } let mut session = russh::client::connect(pref, (self.config.host.as_str(), self.config.port), self).await?; - for key in keys { + for identity in identities { let hash_alg = session.best_supported_rsa_hash().await?.flatten(); - match session.authenticate_publickey_with(&self.config.user, key, hash_alg, &mut agent).await - { + let result = match identity { + AgentIdentity::PublicKey { key, .. } => { + session.authenticate_publickey_with(&self.config.user, key, hash_alg, &mut agent).await + } + AgentIdentity::Certificate { certificate, .. } => { + session + .authenticate_certificate_with(&self.config.user, certificate, hash_alg, &mut agent) + .await + } + }; + match result { Ok(result) if result.success() => return Ok(session), Ok(result) => tracing::debug!("Identity agent authentication failed: {result:?}"), Err(e) => tracing::error!("Identity agent authentication error: {e}"), } } - Err(cfg_err!("Public key authentication via agent failed")) + Err(cfg_err!("Identity agent authentication failed")) } } diff --git a/yazi-watcher/Cargo.toml b/yazi-watcher/Cargo.toml index 1f0e0bfa..aaeef903 100644 --- a/yazi-watcher/Cargo.toml +++ b/yazi-watcher/Cargo.toml @@ -17,7 +17,6 @@ yazi-adapter = { path = "../yazi-adapter", version = "26.2.2" } yazi-dds = { path = "../yazi-dds", version = "26.2.2" } yazi-fs = { path = "../yazi-fs", version = "26.2.2" } yazi-macro = { path = "../yazi-macro", version = "26.2.2" } -yazi-proxy = { path = "../yazi-proxy", version = "26.2.2" } yazi-shared = { path = "../yazi-shared", version = "26.2.2" } yazi-vfs = { path = "../yazi-vfs", version = "26.2.2" } diff --git a/yazi-watcher/src/backend.rs b/yazi-watcher/src/backend.rs index eb11fb7b..ec83fdf7 100644 --- a/yazi-watcher/src/backend.rs +++ b/yazi-watcher/src/backend.rs @@ -13,8 +13,8 @@ impl Backend { pub(crate) fn serve() -> Self { #[cfg(any(target_os = "linux", target_os = "macos"))] yazi_fs::mounts::Partitions::monitor(&yazi_fs::mounts::PARTITIONS, || { - yazi_proxy::MgrProxy::watch(); - yazi_proxy::MgrProxy::refresh(); + crate::MgrProxy::watch(); + crate::MgrProxy::refresh(); yazi_macro::err!(yazi_dds::Pubsub::pub_after_mount()) }); diff --git a/yazi-watcher/src/lib.rs b/yazi-watcher/src/lib.rs index c82a22fc..400d1b1b 100644 --- a/yazi-watcher/src/lib.rs +++ b/yazi-watcher/src/lib.rs @@ -1,6 +1,6 @@ yazi_macro::mod_pub!(local remote); -yazi_macro::mod_flat!(backend reporter watched watchee watcher); +yazi_macro::mod_flat!(backend proxy reporter watched watchee watcher); pub static WATCHED: yazi_shared::RoCell> = yazi_shared::RoCell::new(); pub static WATCHER: yazi_shared::RoCell = yazi_shared::RoCell::new(); diff --git a/yazi-watcher/src/proxy.rs b/yazi-watcher/src/proxy.rs new file mode 100644 index 00000000..db34c9a4 --- /dev/null +++ b/yazi-watcher/src/proxy.rs @@ -0,0 +1,21 @@ +use yazi_macro::{emit, relay}; +use yazi_shared::url::UrlBuf; + +pub struct MgrProxy; + +impl MgrProxy { + pub fn refresh() { + emit!(Call(relay!(mgr:refresh))); + } + + pub fn upload(urls: I) + where + I: IntoIterator, + { + emit!(Call(relay!(mgr:upload).with_seq(urls))); + } + + pub fn watch() { + emit!(Call(relay!(mgr:watch))); + } +} diff --git a/yazi-watcher/src/remote/remote.rs b/yazi-watcher/src/remote/remote.rs index fa0f8270..88584795 100644 --- a/yazi-watcher/src/remote/remote.rs +++ b/yazi-watcher/src/remote/remote.rs @@ -5,11 +5,10 @@ use notify::Result; use tokio::{pin, sync::mpsc::UnboundedReceiver}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_fs::{File, FilesOp}; -use yazi_proxy::MgrProxy; use yazi_shared::url::{UrlBuf, UrlLike}; use yazi_vfs::VfsFile; -use crate::{Reporter, WATCHER, Watchee}; +use crate::{MgrProxy, Reporter, WATCHER, Watchee}; pub(crate) struct Remote;