feat: tasks without progress (#3131)

This commit is contained in:
三咲雅 misaki masa 2025-09-02 16:58:34 +08:00 committed by GitHub
parent eb537733a2
commit d7df581b67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
127 changed files with 1876 additions and 1157 deletions

192
Cargo.lock generated
View file

@ -343,10 +343,11 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.34"
version = "1.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc"
checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
@ -652,9 +653,9 @@ dependencies = [
[[package]]
name = "deranged"
version = "0.4.0"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc"
dependencies = [
"powerfmt",
]
@ -873,6 +874,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650"
[[package]]
name = "flate2"
version = "1.1.2"
@ -1025,7 +1032,7 @@ dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
"wasi 0.14.3+wasi-0.2.4",
]
[[package]]
@ -1053,8 +1060,8 @@ dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata 0.4.10",
"regex-syntax 0.8.6",
"regex-automata",
"regex-syntax",
]
[[package]]
@ -1076,6 +1083,17 @@ dependencies = [
"allocator-api2",
"equivalent",
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
dependencies = [
"allocator-api2",
"equivalent",
"foldhash 0.2.0",
"serde",
]
@ -1117,9 +1135,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "image"
version = "0.25.6"
version = "0.25.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a"
checksum = "1c6a3ce16143778e24df6f95365f12ed105425b22abefd289dd88a64bab59605"
dependencies = [
"bytemuck",
"byteorder-lite",
@ -1127,6 +1145,7 @@ dependencies = [
"exr",
"gif",
"image-webp",
"moxcms",
"num-traits",
"png",
"qoi",
@ -1159,7 +1178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.15.5",
"serde",
]
@ -1390,7 +1409,7 @@ version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
dependencies = [
"hashbrown",
"hashbrown 0.15.5",
]
[[package]]
@ -1399,7 +1418,7 @@ version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86ea4e65087ff52f3862caff188d489f1fab49a0cb09e01b2e3f1a617b10aaed"
dependencies = [
"hashbrown",
"hashbrown 0.15.5",
]
[[package]]
@ -1432,11 +1451,11 @@ dependencies = [
[[package]]
name = "matchers"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
"regex-automata 0.1.10",
"regex-automata",
]
[[package]]
@ -1484,9 +1503,9 @@ dependencies = [
[[package]]
name = "mlua"
version = "0.11.2"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2fea92b2adabd51808311b101551d6e3f8602b65e9fae51f7ad5b3d500f4cd"
checksum = "5b3dd94c3c4dea0049b22296397040840a8f6b5b5229f438434ba82df402b42d"
dependencies = [
"anyhow",
"bstr",
@ -1531,6 +1550,16 @@ dependencies = [
"syn",
]
[[package]]
name = "moxcms"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08"
dependencies = [
"num-traits",
"pxfm",
]
[[package]]
name = "new_debug_unreachable"
version = "1.0.6"
@ -1579,12 +1608,11 @@ checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
dependencies = [
"overload",
"winapi",
"windows-sys 0.52.0",
]
[[package]]
@ -1764,12 +1792,6 @@ dependencies = [
"serde",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "palette"
version = "0.7.6"
@ -1867,11 +1889,11 @@ dependencies = [
[[package]]
name = "png"
version = "0.17.16"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
dependencies = [
"bitflags 1.3.2",
"bitflags 2.9.3",
"crc32fast",
"fdeflate",
"flate2",
@ -1943,6 +1965,15 @@ dependencies = [
"syn",
]
[[package]]
name = "pxfm"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e790881194f6f6e86945f0a42a6981977323669aeb6c40e9c7ec253133b96f8"
dependencies = [
"num-traits",
]
[[package]]
name = "qoi"
version = "0.4.1"
@ -2170,17 +2201,8 @@ checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.10",
"regex-syntax 0.8.6",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
"regex-automata",
"regex-syntax",
]
[[package]]
@ -2191,15 +2213,9 @@ checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.6",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.6"
@ -2273,9 +2289,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "safe_arch"
version = "0.9.2"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb5032219cc30e5bb98749b19a18ceb2cf15e24ba8d517a7e64dff4f1f1eca5"
checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
dependencies = [
"bytemuck",
]
@ -2525,7 +2541,7 @@ dependencies = [
"once_cell",
"onig",
"plist",
"regex-syntax 0.8.6",
"regex-syntax",
"serde",
"serde_derive",
"serde_json",
@ -2640,12 +2656,11 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.41"
version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3"
dependencies = [
"deranged",
"itoa",
"libc",
"num-conv",
"num_threads",
@ -2657,15 +2672,15 @@ dependencies = [
[[package]]
name = "time-core"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68"
[[package]]
name = "time-macros"
version = "0.2.22"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c"
dependencies = [
"num-conv",
"time-core",
@ -2856,14 +2871,14 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex",
"regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
@ -3048,11 +3063,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
version = "0.14.3+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
dependencies = [
"wit-bindgen-rt",
"wit-bindgen",
]
[[package]]
@ -3133,9 +3148,9 @@ dependencies = [
[[package]]
name = "wide"
version = "0.7.34"
version = "0.7.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a0ab08c041f0cbb00a12fd091b2877dcec2311f90f87a88391d4b0961ffb4fe"
checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03"
dependencies = [
"bytemuck",
"safe_arch",
@ -3284,6 +3299,15 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
@ -3447,13 +3471,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
name = "wit-bindgen"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.3",
]
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
[[package]]
name = "wyz"
@ -3471,7 +3492,7 @@ dependencies = [
"anyhow",
"crossterm 0.29.0",
"futures",
"hashbrown",
"hashbrown 0.16.0",
"libc",
"mlua",
"paste",
@ -3522,7 +3543,7 @@ name = "yazi-binding"
version = "25.6.11"
dependencies = [
"ansi-to-tui",
"hashbrown",
"hashbrown 0.16.0",
"mlua",
"paste",
"ratatui",
@ -3546,7 +3567,7 @@ dependencies = [
"clap_complete_fig",
"clap_complete_nushell",
"futures",
"hashbrown",
"hashbrown 0.16.0",
"regex",
"serde",
"vergen-gitcl",
@ -3604,7 +3625,7 @@ dependencies = [
"crossterm 0.29.0",
"dirs",
"globset",
"hashbrown",
"hashbrown 0.16.0",
"indexmap",
"ratatui",
"regex",
@ -3624,7 +3645,7 @@ version = "25.6.11"
dependencies = [
"anyhow",
"crossterm 0.29.0",
"hashbrown",
"hashbrown 0.16.0",
"indexmap",
"parking_lot",
"ratatui",
@ -3652,7 +3673,7 @@ name = "yazi-dds"
version = "25.6.11"
dependencies = [
"anyhow",
"hashbrown",
"hashbrown 0.16.0",
"mlua",
"ordered-float 5.0.0",
"parking_lot",
@ -3735,7 +3756,7 @@ dependencies = [
"dirs",
"foldhash 0.2.0",
"futures",
"hashbrown",
"hashbrown 0.16.0",
"libc",
"objc",
"parking_lot",
@ -3767,8 +3788,9 @@ dependencies = [
"anyhow",
"bitflags 2.9.3",
"crossterm 0.29.0",
"hashbrown",
"hashbrown 0.16.0",
"mlua",
"ordered-float 5.0.0",
"serde",
"tokio",
"yazi-binding",
@ -3786,7 +3808,7 @@ dependencies = [
"anyhow",
"crossterm 0.29.0",
"futures",
"hashbrown",
"hashbrown 0.16.0",
"libc",
"mlua",
"parking_lot",
@ -3841,9 +3863,11 @@ dependencies = [
"anyhow",
"async-priority-channel",
"futures",
"hashbrown",
"hashbrown 0.16.0",
"libc",
"lru 0.16.0",
"mlua",
"ordered-float 5.0.0",
"parking_lot",
"scopeguard",
"tokio",
@ -3867,7 +3891,7 @@ dependencies = [
"crossterm 0.29.0",
"foldhash 0.2.0",
"futures",
"hashbrown",
"hashbrown 0.16.0",
"libc",
"memchr",
"ordered-float 5.0.0",
@ -3898,7 +3922,7 @@ name = "yazi-watcher"
version = "25.6.11"
dependencies = [
"anyhow",
"hashbrown",
"hashbrown 0.16.0",
"notify",
"parking_lot",
"tokio",

View file

@ -31,11 +31,11 @@ dirs = "6.0.0"
foldhash = "0.2.0"
futures = "0.3.31"
globset = "0.4.16"
hashbrown = { version = "0.15.5", features = [ "serde" ] }
hashbrown = { version = "0.16.0", features = [ "serde" ] }
indexmap = { version = "2.11.0", features = [ "serde" ] }
libc = "0.2.175"
lru = "0.16.0"
mlua = { version = "0.11.2", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] }
mlua = { version = "0.11.3", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serde" ] }
objc = "0.2.7"
ordered-float = { version = "5.0.0", features = [ "serde" ] }
parking_lot = "0.12.4"

View file

@ -15,12 +15,10 @@ pub struct Ctx<'a> {
impl Deref for Ctx<'_> {
type Target = Core;
#[inline]
fn deref(&self) -> &Self::Target { self.core }
}
impl DerefMut for Ctx<'_> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target { self.core }
}

View file

@ -22,7 +22,6 @@ impl Deref for Core {
}
impl Core {
#[inline]
pub(super) fn make(inner: &yazi_core::Core) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self {
inner: inner.into(),

View file

@ -30,7 +30,6 @@ impl AsRef<yazi_fs::File> for File {
}
impl File {
#[inline]
pub(super) fn make(
idx: usize,
folder: &yazi_core::tab::Folder,

View file

@ -20,7 +20,6 @@ impl Deref for Files {
}
impl Files {
#[inline]
pub(super) fn make(
window: Range<usize>,
folder: &yazi_core::tab::Folder,

View file

@ -15,7 +15,6 @@ impl Deref for Filter {
}
impl Filter {
#[inline]
pub(super) fn make(inner: &yazi_fs::Filter) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() })
}

View file

@ -15,7 +15,6 @@ impl Deref for Finder {
}
impl Finder {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Finder) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() })
}

View file

@ -25,7 +25,6 @@ impl Deref for Folder {
}
impl Folder {
#[inline]
pub(super) fn make(
window: Option<Range<usize>>,
inner: &yazi_core::tab::Folder,

View file

@ -38,7 +38,6 @@ impl Lives {
result
}
#[inline]
pub(crate) fn scoped_userdata<T>(data: T) -> mlua::Result<AnyUserData>
where
T: UserData + 'static,

View file

@ -15,7 +15,6 @@ impl Deref for Mode {
}
impl Mode {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Mode) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() })
}

View file

@ -19,7 +19,6 @@ impl Deref for Preference {
}
impl Preference {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Preference) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into(), v_sort_by: None, v_linemode: None })
}

View file

@ -19,7 +19,6 @@ impl Deref for Preview {
}
impl Preview {
#[inline]
pub(super) fn make(tab: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { tab: tab.into(), v_folder: None })
}

View file

@ -7,7 +7,6 @@ use crate::lives::PtrCell;
pub(super) struct Selected;
impl Selected {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Selected) -> mlua::Result<AnyUserData> {
let inner = PtrCell::from(inner);

View file

@ -25,7 +25,6 @@ impl Deref for Tab {
}
impl Tab {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self {
inner: inner.into(),

View file

@ -15,7 +15,6 @@ impl Deref for Tabs {
}
impl Tabs {
#[inline]
pub(super) fn make(inner: &yazi_core::mgr::Tabs) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() })
}

View file

@ -1,13 +1,15 @@
use std::ops::Deref;
use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value};
use yazi_binding::cached_field;
use yazi_binding::{cached_field, deprecate};
use yazi_plugin::runtime::SER_OPT;
use super::{Lives, PtrCell};
pub(super) struct Tasks {
inner: PtrCell<yazi_core::tasks::Tasks>,
v_summary: Option<Value>,
v_progress: Option<Value>,
}
@ -18,14 +20,27 @@ impl Deref for Tasks {
}
impl Tasks {
#[inline]
pub(super) fn make(inner: &yazi_core::tasks::Tasks) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into(), v_progress: None })
Lives::scoped_userdata(Self { inner: inner.into(), v_summary: None, v_progress: None })
}
}
impl UserData for Tasks {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, progress, |lua, me| lua.to_value(&me.progress));
cached_field!(fields, summary, |lua, me| lua.to_value_with(&me.summary, SER_OPT));
cached_field!(fields, progress, |lua, me| {
deprecate!(
lua,
"`cx.tasks.progress` is deprecated, use `cx.tasks.summary` instead, in your {}"
);
lua.create_table_from([
("total", me.summary.total),
("succ", me.summary.success),
("fail", me.summary.failed),
("found", 0),
("processed", 0),
])
});
}
}

View file

@ -17,7 +17,6 @@ impl Deref for Yanked {
}
impl Yanked {
#[inline]
pub(super) fn make(inner: &yazi_core::mgr::Yanked) -> mlua::Result<AnyUserData> {
let inner = PtrCell::from(inner);

View file

@ -17,7 +17,7 @@ impl Actor for Arrow {
let tasks = &mut cx.tasks;
let old = tasks.cursor;
tasks.cursor = opt.step.add(tasks.cursor, tasks.summaries.len(), Tasks::limit());
tasks.cursor = opt.step.add(tasks.cursor, tasks.snaps.len(), Tasks::limit());
succ!(render!(tasks.cursor != old));
}

View file

@ -20,7 +20,7 @@ impl Actor for Cancel {
succ!();
}
tasks.summaries = tasks.paginate();
tasks.snaps = tasks.paginate();
act!(tasks:arrow, cx)?;
succ!(render!());
}

View file

@ -19,7 +19,7 @@ impl Actor for Close {
}
tasks.visible = false;
tasks.summaries = Vec::new();
tasks.snaps = Vec::new();
act!(tasks:arrow, cx)?;
succ!(render!());

View file

@ -19,7 +19,7 @@ impl Actor for Show {
}
tasks.visible = true;
tasks.summaries = tasks.paginate();
tasks.snaps = tasks.paginate();
act!(tasks:arrow, cx)?;
succ!(render!());

View file

@ -20,7 +20,7 @@ ansi-to-tui = { workspace = true }
anyhow = { workspace = true }
base64 = { workspace = true }
crossterm = { workspace = true }
image = { version = "0.25.6", default-features = false, features = [ "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "webp" ] }
image = { version = "0.25.7", default-features = false, features = [ "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "webp" ] }
palette = { version = "0.7.6", default-features = false }
quantette = { version = "0.3.0", default-features = false }
ratatui = { workspace = true }

View file

@ -25,7 +25,6 @@ impl KgpOld {
})
}
#[inline]
pub(crate) fn image_erase(_: Rect) -> Result<()> {
let mut w = TTY.lockout();
write!(w, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?;

View file

@ -85,7 +85,6 @@ impl Image {
.unwrap_or(rect)
}
#[inline]
fn filter() -> FilterType {
match YAZI.preview.image_filter.as_str() {
"nearest" => FilterType::Nearest,

View file

@ -14,7 +14,6 @@ impl Deref for Cha {
impl Cha {
pub fn install(lua: &Lua) -> mlua::Result<()> {
#[inline]
fn parse_time(f: Option<f64>) -> mlua::Result<Option<SystemTime>> {
Ok(match f {
Some(n) if n >= 0.0 => Some(SystemTime::UNIX_EPOCH + Duration::from_secs_f64(n)),

View file

@ -17,7 +17,6 @@ impl Default for Area {
}
impl Area {
#[inline]
pub fn size(self) -> ratatui::layout::Size {
match self {
Self::Pos(pos) => {
@ -27,7 +26,6 @@ impl Area {
}
}
#[inline]
pub fn inner(self, padding: ratatui::widgets::Padding) -> Self {
match self {
Self::Pos(mut pos) => {
@ -38,7 +36,6 @@ impl Area {
}
}
#[inline]
pub fn transform(
self,
f: impl FnOnce(yazi_config::popup::Position) -> ratatui::layout::Rect,

View file

@ -54,7 +54,6 @@ impl UserData for Clear {
}
}
#[inline]
const fn is_overlapping(a: ratatui::layout::Rect, b: ratatui::layout::Rect) -> bool {
a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y
}

View file

@ -78,7 +78,6 @@ impl Table {
table.render(rect, buf, &mut self.state);
}
#[inline]
pub fn len(&self) -> usize { self.rows.len() }
pub fn select(&mut self, idx: Option<usize>) {

View file

@ -19,7 +19,6 @@ impl Error {
lua.globals().raw_set("Error", lua.create_table_from([("custom", new)])?)
}
#[inline]
pub fn custom(msg: impl Into<SStr>) -> Self { Self::Custom(msg.into()) }
pub fn into_string(self) -> SStr {

View file

@ -8,7 +8,6 @@ pub struct Id(pub yazi_shared::Id);
impl Deref for Id {
type Target = yazi_shared::Id;
#[inline]
fn deref(&self) -> &Self::Target { &self.0 }
}

View file

@ -13,7 +13,6 @@ where
I: Iterator<Item = T> + 'static,
T: IntoLua + 'static,
{
#[inline]
pub fn new(iter: I, len: Option<usize>) -> Self { Self { iter, len, count: 0, cache: vec![] } }
}
@ -22,7 +21,6 @@ where
I: Iterator<Item = T> + 'static,
T: FromLua + 'static,
{
#[inline]
pub fn into_iter(self, lua: &Lua) -> impl Iterator<Item = mlua::Result<T>> {
self
.cache

View file

@ -39,7 +39,6 @@ impl Dependency {
}
}
#[inline]
pub(super) fn identical(&self, other: &Self) -> bool {
self.parent == other.parent && self.child == other.child
}

View file

@ -138,10 +138,8 @@ impl Package {
Local::write(Self::toml(), s).await.context("Failed to write package.toml")
}
#[inline]
fn toml() -> PathBuf { Xdg::config_dir().join("package.toml") }
#[inline]
fn identical(&self, other: &Dependency) -> Option<&Dependency> {
self.plugins.iter().chain(&self.flavors).find(|d| d.identical(other))
}

View file

@ -19,7 +19,6 @@ pub struct Keymap {
}
impl Keymap {
#[inline]
pub fn get(&self, layer: Layer) -> &[Chord] {
match layer {
Layer::App => &[],

View file

@ -16,7 +16,6 @@ pub struct OpenerRule {
}
impl OpenerRule {
#[inline]
pub fn desc(&self) -> String {
if !self.desc.is_empty() {
self.desc.clone()

View file

@ -17,7 +17,6 @@ pub struct Pattern {
}
impl Pattern {
#[inline]
pub fn match_url(&self, url: impl AsRef<UrlBuf>, is_dir: bool) -> bool {
let url = url.as_ref();
@ -42,7 +41,6 @@ impl Pattern {
self.inner.is_match(path)
}
#[inline]
pub fn match_mime(&self, mime: impl AsRef<str>) -> bool {
self.is_star || (!mime.as_ref().is_empty() && self.inner.is_match(mime.as_ref()))
}

View file

@ -176,7 +176,6 @@ impl ConfirmCfg {
}
impl PickCfg {
#[inline]
fn max_height(len: usize) -> u16 {
YAZI.pick.open_offset.height.min(YAZI.pick.border().saturating_add(len as u16))
}

View file

@ -10,7 +10,6 @@ pub struct Position {
}
impl Position {
#[inline]
pub const fn new(origin: Origin, offset: Offset) -> Self { Self { origin, offset } }
pub fn rect(&self, WindowSize { columns, rows, .. }: WindowSize) -> Rect {

View file

@ -3,17 +3,14 @@ use crate::{Yazi, keymap::Keymap, theme::Theme};
pub(crate) struct Preset;
impl Preset {
#[inline]
pub(super) fn yazi() -> Result<Yazi, toml::de::Error> {
toml::from_str(&yazi_macro::config_preset!("yazi"))
}
#[inline]
pub(super) fn keymap() -> Result<Keymap, toml::de::Error> {
toml::from_str(&yazi_macro::config_preset!("keymap"))
}
#[inline]
pub(super) fn theme(light: bool) -> Result<Theme, toml::de::Error> {
toml::from_str(&if light {
yazi_macro::theme_preset!("light")

View file

@ -29,15 +29,12 @@ pub struct Preview {
}
impl Preview {
#[inline]
pub fn tmpfile(&self, prefix: &str) -> PathBuf {
self.cache_dir.join(format!("{prefix}-{}", timestamp_us()))
}
#[inline]
pub fn indent(&self) -> SStr { Self::indent_with(self.tab_size as usize) }
#[inline]
pub fn indent_with(n: usize) -> SStr {
if let Some(s) = TABS.get(n) { Cow::Borrowed(s) } else { Cow::Owned(" ".repeat(n)) }
}

View file

@ -76,7 +76,6 @@ impl From<Style> for ratatui::style::Modifier {
}
impl Style {
#[inline]
pub fn derive(self, other: ratatui::style::Style) -> ratatui::style::Style {
ratatui::style::Style::from(self).patch(other)
}

View file

@ -69,12 +69,10 @@ impl Icon {
self.conds.iter().find(|(c, _)| c.eval(f) == Some(true)).map(|(_, i)| i)
}
#[inline]
fn match_by_glob(&self, file: &File) -> Option<&I> {
self.globs.iter().find(|(p, _)| p.match_url(&file.url, file.is_dir())).map(|(_, i)| i)
}
#[inline]
fn match_by_name(&self, file: &File) -> Option<&I> {
let name = file.name()?.to_str()?;
if file.is_dir() {
@ -88,7 +86,6 @@ impl Icon {
}
}
#[inline]
fn match_by_ext(&self, file: &File) -> Option<&I> {
let ext = file.url.ext()?.to_str()?;
self.exts.get(ext).or_else(|| self.exts.get(&ext.to_ascii_lowercase()))

View file

@ -19,7 +19,6 @@ pub enum Is {
}
impl Is {
#[inline]
pub fn check(&self, cha: &Cha) -> bool {
match self {
Self::None => true,

View file

@ -16,30 +16,23 @@ pub struct Cmp {
impl Cmp {
// --- Cands
#[inline]
pub fn window(&self) -> &[CmpItem] {
let end = (self.offset + self.limit()).min(self.cands.len());
&self.cands[self.offset..end]
}
#[inline]
pub fn selected(&self) -> Option<&CmpItem> { self.cands.get(self.cursor) }
// --- Cursor
#[inline]
pub fn rel_cursor(&self) -> usize { self.cursor - self.offset }
}
impl Scrollable for Cmp {
#[inline]
fn total(&self) -> usize { self.cands.len() }
#[inline]
fn limit(&self) -> usize { self.cands.len().min(10) }
#[inline]
fn cursor_mut(&mut self) -> &mut usize { &mut self.cursor }
#[inline]
fn offset_mut(&mut self) -> &mut usize { &mut self.offset }
}

View file

@ -31,7 +31,6 @@ impl Core {
}
}
#[inline]
pub fn cursor(&self) -> Option<(Position, SetCursorStyle)> {
if self.input.visible {
let Rect { x, y, .. } = self.mgr.area(self.input.position);
@ -46,7 +45,6 @@ impl Core {
None
}
#[inline]
pub fn layer(&self) -> Layer {
if self.which.visible {
Layer::Which

View file

@ -64,7 +64,6 @@ impl Help {
impl Help {
// --- Keyword
#[inline]
pub fn keyword(&self) -> Option<String> {
self
.in_filter
@ -75,14 +74,12 @@ impl Help {
}
// --- Bindings
#[inline]
pub fn window(&self) -> &[&Chord] {
let end = (self.offset + self.limit()).min(self.bindings.len());
&self.bindings[self.offset..end]
}
// --- Cursor
#[inline]
pub fn cursor(&self) -> Option<(u16, u16)> {
if !self.visible || self.in_filter.is_none() {
return None;
@ -93,10 +90,8 @@ impl Help {
None
}
#[inline]
pub fn rel_cursor(&self) -> usize { self.cursor - self.offset }
#[inline]
pub fn cursor_shape(&self) -> SetCursorStyle {
if YAZI.input.cursor_blink {
SetCursorStyle::BlinkingBlock
@ -107,15 +102,11 @@ impl Help {
}
impl Scrollable for Help {
#[inline]
fn total(&self) -> usize { self.bindings.len() }
#[inline]
fn limit(&self) -> usize { Dimension::available().rows.saturating_sub(HELP_MARGIN) as usize }
#[inline]
fn cursor_mut(&mut self) -> &mut usize { &mut self.cursor }
#[inline]
fn offset_mut(&mut self) -> &mut usize { &mut self.offset }
}

View file

@ -6,32 +6,26 @@ use yazi_shared::{MIME_DIR, pool::{InternStr, Symbol}, url::{Url, UrlBufCov, Url
pub struct Mimetype(HashMap<UrlBufCov, Symbol<str>>);
impl Mimetype {
#[inline]
pub fn by_url<'a>(&self, url: impl Into<Url<'a>>) -> Option<&str> {
self.0.get(&UrlCov::new(url)).map(|s| s.as_ref())
}
#[inline]
pub fn by_url_owned<'a>(&self, url: impl Into<Url<'a>>) -> Option<Symbol<str>> {
self.0.get(&UrlCov::new(url)).cloned()
}
#[inline]
pub fn by_file(&self, file: &File) -> Option<&str> {
if file.is_dir() { Some(MIME_DIR) } else { self.by_url(&file.url) }
}
#[inline]
pub fn by_file_owned(&self, file: &File) -> Option<Symbol<str>> {
if file.is_dir() { Some(MIME_DIR.intern()) } else { self.by_url_owned(&file.url) }
}
#[inline]
pub fn contains<'a>(&self, url: impl Into<Url<'a>>) -> bool {
self.0.contains_key(&UrlCov::new(url))
}
#[inline]
pub fn extend(&mut self, iter: impl IntoIterator<Item = (UrlBufCov, Symbol<str>)>) {
self.0.extend(iter);
}

View file

@ -41,7 +41,6 @@ impl Yanked {
self.revision += 1;
}
#[inline]
pub fn contains<'a>(&self, url: impl Into<Url<'a>>) -> bool {
self.urls.contains(&UrlCov::new(url))
}

View file

@ -37,7 +37,6 @@ impl From<NotifyOpt> for Message {
}
impl Message {
#[inline]
pub fn height(&self, width: u16) -> usize {
let lines = ratatui::widgets::Paragraph::new(self.content.as_str())
.wrap(ratatui::widgets::Wrap { trim: false })

View file

@ -17,27 +17,21 @@ pub struct Pick {
}
impl Pick {
#[inline]
pub fn title(&self) -> &str { &self.title }
#[inline]
pub fn window(&self) -> impl Iterator<Item = (usize, &str)> {
self.items.iter().map(AsRef::as_ref).enumerate().skip(self.offset).take(self.limit())
}
}
impl Scrollable for Pick {
#[inline]
fn total(&self) -> usize { self.items.len() }
#[inline]
fn limit(&self) -> usize {
self.position.offset.height.saturating_sub(YAZI.pick.border()) as usize
}
#[inline]
fn cursor_mut(&mut self) -> &mut usize { &mut self.cursor }
#[inline]
fn offset_mut(&mut self) -> &mut usize { &mut self.offset }
}

View file

@ -30,28 +30,22 @@ impl Spot {
self.ct = Some(isolate::spot(&spotter.run, file, mime, self.skip));
}
#[inline]
pub fn visible(&self) -> bool { self.lock.is_some() }
#[inline]
pub fn abort(&mut self) { self.ct.take().map(|ct| ct.cancel()); }
#[inline]
pub fn reset(&mut self) {
self.abort();
render!(self.lock.take().is_some());
}
#[inline]
pub fn same_url(&self, url: &UrlBuf) -> bool { self.lock.as_ref().is_some_and(|l| *url == l.url) }
#[inline]
pub fn same_file(&self, file: &File, mime: &str) -> bool {
self.same_url(&file.url)
&& self.lock.as_ref().is_some_and(|l| file.cha.hits(l.cha) && mime == l.mime)
}
#[inline]
pub fn same_lock(&self, file: &File, mime: &str) -> bool {
self.same_file(file, mime) && self.lock.as_ref().is_some_and(|l| self.skip == l.skip)
}

View file

@ -76,7 +76,6 @@ impl Finder {
}
impl Finder {
#[inline]
pub fn matched_idx(&self, folder: &Folder, urn: &Urn) -> Option<u8> {
if self.lock == *folder { self.matched.get(urn).copied() } else { None }
}

View file

@ -119,7 +119,6 @@ impl Folder {
self.arrow(new - self.cursor as isize)
}
#[inline]
pub fn repos(&mut self, urn: Option<&Urn>) -> bool {
if let Some(u) = urn {
self.hover(u)
@ -177,18 +176,13 @@ impl Folder {
}
impl Scrollable for Folder {
#[inline]
fn total(&self) -> usize { self.files.len() }
#[inline]
fn limit(&self) -> usize { LAYOUT.get().limit() }
#[inline]
fn scrolloff(&self) -> usize { (self.limit() / 2).min(YAZI.mgr.scrolloff.get() as usize) }
#[inline]
fn cursor_mut(&mut self) -> &mut usize { &mut self.cursor }
#[inline]
fn offset_mut(&mut self) -> &mut usize { &mut self.offset }
}

View file

@ -11,17 +11,14 @@ pub struct History(HashMap<UrlBuf, Folder>);
impl Deref for History {
type Target = HashMap<UrlBuf, Folder>;
#[inline]
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for History {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl History {
#[inline]
pub fn remove_or<'a>(&mut self, url: impl Into<Url<'a>>) -> Folder {
let url = url.into();
self.0.remove(&url).unwrap_or_else(|| Folder::from(url))

View file

@ -27,13 +27,10 @@ impl Mode {
}
impl Mode {
#[inline]
pub fn is_select(&self) -> bool { matches!(self, Mode::Select(..)) }
#[inline]
pub fn is_unset(&self) -> bool { matches!(self, Mode::Unset(..)) }
#[inline]
pub fn is_visual(&self) -> bool { matches!(self, Mode::Select(..) | Mode::Unset(..)) }
}

View file

@ -68,35 +68,29 @@ impl Preview {
}));
}
#[inline]
pub fn abort(&mut self) {
self.previewer_ct.take().map(|ct| ct.cancel());
Highlighter::abort();
}
#[inline]
pub fn reset(&mut self) {
self.abort();
ADAPTOR.get().image_hide().ok();
render!(self.lock.take().is_some())
}
#[inline]
pub fn reset_image(&mut self) {
self.abort();
ADAPTOR.get().image_hide().ok();
}
#[inline]
pub fn same_url(&self, url: &UrlBuf) -> bool { matches!(&self.lock, Some(l) if l.url == *url) }
#[inline]
pub fn same_file(&self, file: &File, mime: &str) -> bool {
self.same_url(&file.url)
&& matches!(&self.lock , Some(l) if l.cha.hits(file.cha) && l.mime == mime && *l.area == LAYOUT.get().preview)
}
#[inline]
pub fn same_lock(&self, file: &File, mime: &str) -> bool {
self.same_file(file, mime) && matches!(&self.lock, Some(l) if l.skip == self.skip)
}

View file

@ -12,21 +12,16 @@ pub struct Selected {
}
impl Selected {
#[inline]
pub fn len(&self) -> usize { self.inner.len() }
#[inline]
pub fn is_empty(&self) -> bool { self.inner.is_empty() }
#[inline]
pub fn values(&self) -> impl Iterator<Item = &UrlBuf> { self.inner.keys().map(Deref::deref) }
#[inline]
pub fn contains<'a>(&self, url: impl Into<Url<'a>>) -> bool {
self.inner.contains_key(&UrlCov::new(url))
}
#[inline]
pub fn add<'a>(&mut self, url: impl Into<Url<'a>>) -> bool { self.add_same([url]) == 1 }
pub fn add_many<'a, I, T>(&mut self, urls: I) -> usize

View file

@ -3,9 +3,7 @@ use yazi_parser::app::PluginOpt;
use super::Tasks;
impl Tasks {
#[inline]
pub fn plugin_micro(&self, opt: PluginOpt) { self.scheduler.plugin_micro(opt); }
#[inline]
pub fn plugin_macro(&self, opt: PluginOpt) { self.scheduler.plugin_macro(opt); }
}

View file

@ -3,9 +3,9 @@ use std::{sync::Arc, time::Duration};
use parking_lot::Mutex;
use tokio::{task::JoinHandle, time::sleep};
use yazi_adapter::Dimension;
use yazi_parser::app::TasksProgress;
use yazi_parser::app::TaskSummary;
use yazi_proxy::AppProxy;
use yazi_scheduler::{Ongoing, Scheduler, TaskSummary};
use yazi_scheduler::{Ongoing, Scheduler, TaskSnap};
use super::{TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT};
@ -13,10 +13,10 @@ pub struct Tasks {
pub scheduler: Arc<Scheduler>,
handle: JoinHandle<()>,
pub visible: bool,
pub cursor: usize,
pub progress: TasksProgress,
pub summaries: Vec<TaskSummary>,
pub visible: bool,
pub cursor: usize,
pub snaps: Vec<TaskSnap>,
pub summary: TaskSummary,
}
impl Tasks {
@ -25,14 +25,14 @@ impl Tasks {
let ongoing = scheduler.ongoing.clone();
let handle = tokio::spawn(async move {
let mut last = TasksProgress::default();
let mut last = TaskSummary::default();
loop {
sleep(Duration::from_millis(500)).await;
let new = ongoing.lock().progress();
let new = ongoing.lock().summary();
if last != new {
last = new;
AppProxy::update_progress(new);
AppProxy::update_summary(new);
}
}
});
@ -43,8 +43,8 @@ impl Tasks {
visible: false,
cursor: 0,
progress: Default::default(),
summaries: Default::default(),
snaps: Default::default(),
summary: Default::default(),
}
}
@ -53,16 +53,14 @@ impl Tasks {
self.handle.abort();
}
#[inline]
pub fn limit() -> usize {
(Dimension::available().rows * TASKS_PERCENT / 100).saturating_sub(TASKS_BORDER + TASKS_PADDING)
as usize
}
pub fn paginate(&self) -> Vec<TaskSummary> {
pub fn paginate(&self) -> Vec<TaskSnap> {
self.ongoing().lock().values().take(Self::limit()).map(Into::into).collect()
}
#[inline]
pub fn ongoing(&self) -> &Arc<Mutex<Ongoing>> { &self.scheduler.ongoing }
}

View file

@ -156,12 +156,10 @@ impl Client {
}
}
#[inline]
pub(super) fn push<'a>(payload: impl Into<Payload<'a>>) -> Result<()> {
Ok(QUEUE_TX.send(try_format!("{}\n", payload.into())?)?)
}
#[inline]
pub(super) fn able(&self, ability: &str) -> bool { self.abilities.contains(ability) }
async fn connect(server: &mut Option<JoinHandle<()>>) -> (ClientReader, ClientWriter) {
@ -201,9 +199,7 @@ impl Client {
}
impl Peer {
#[inline]
pub(super) fn new(abilities: &HashSet<String>) -> Self { Self { abilities: abilities.clone() } }
#[inline]
pub(super) fn able(&self, ability: &str) -> bool { self.abilities.contains(ability) }
}

View file

@ -90,7 +90,6 @@ impl Ember<'static> {
}
impl<'a> Ember<'a> {
#[inline]
pub fn kind(&self) -> &str {
match self {
Self::Hi(_) => "hi",
@ -111,12 +110,10 @@ impl<'a> Ember<'a> {
}
}
#[inline]
pub fn with_receiver(self, receiver: Id) -> Payload<'a> {
Payload::new(self).with_receiver(receiver)
}
#[inline]
pub fn with_sender(self, sender: Id) -> Payload<'a> { Payload::new(self).with_sender(sender) }
}

View file

@ -137,7 +137,6 @@ impl Pubsub {
Ok(())
}
#[inline]
fn any_remote_own(kind: &str) -> bool {
REMOTE.read().contains_key(kind) // Own remote abilities
|| PEERS.read().values().any(|p| p.able(kind)) // Remote peers' abilities

View file

@ -17,21 +17,18 @@ static DELETE_TX: Mutex<Option<mpsc::UnboundedSender<UrlBuf>>> = Mutex::new(None
pub struct Pump;
impl Pump {
#[inline]
pub fn push_move(from: UrlBuf, to: UrlBuf) {
if let Some(tx) = &*MOVE_TX.lock() {
tx.send(BodyMoveItem { from, to }).ok();
}
}
#[inline]
pub fn push_trash(target: UrlBuf) {
if let Some(tx) = &*TRASH_TX.lock() {
tx.send(target).ok();
}
}
#[inline]
pub fn push_delete(target: UrlBuf) {
if let Some(tx) = &*DELETE_TX.lock() {
tx.send(target).ok();

View file

@ -227,7 +227,6 @@ impl Sendable {
})
}
#[inline]
fn key_to_value(lua: &Lua, key: DataKey) -> mlua::Result<Value> {
match key {
DataKey::Url(u) => yazi_binding::Url::new(u).into_lua(lua),

View file

@ -57,7 +57,7 @@ tokio-stream = { workspace = true }
# Logging
tracing = { workspace = true }
tracing-appender = "0.2.3"
tracing-subscriber = { version = "0.3.19", features = [ "env-filter" ] }
tracing-subscriber = { version = "0.3.20", features = [ "env-filter" ] }
[target."cfg(unix)".dependencies]
libc = { workspace = true }

View file

@ -12,5 +12,5 @@ yazi_macro::mod_flat!(
resume
stop
update_notify
update_progress
update_summary
);

View file

@ -1,23 +1,23 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_macro::{act, render, succ};
use yazi_parser::app::UpdateProgressOpt;
use yazi_parser::app::UpdateSummaryOpt;
use yazi_shared::event::Data;
use crate::app::App;
impl App {
pub(crate) fn update_progress(&mut self, opt: UpdateProgressOpt) -> Result<Data> {
pub(crate) fn update_summary(&mut self, opt: UpdateSummaryOpt) -> Result<Data> {
// Update the progress of all tasks.
let tasks = &mut self.core.tasks;
let progressed = tasks.progress != opt.progress;
tasks.progress = opt.progress;
let progressed = tasks.summary != opt.summary;
tasks.summary = opt.summary;
// If the task manager is visible, update the summaries with a complete render.
// If the task manager is visible, update the snaps with a full render.
if tasks.visible {
let new = tasks.paginate();
if tasks.summaries != new {
tasks.summaries = new;
if tasks.snaps != new {
tasks.snaps = new;
let cx = &mut Ctx::active(&mut self.core);
act!(tasks:arrow, cx)?;
succ!(render!());
@ -26,7 +26,7 @@ impl App {
if !progressed {
succ!()
} else if tasks.progress.total == 0 {
} else if tasks.summary.total == 0 {
succ!(render!())
} else {
act!(render_partially, self)

View file

@ -44,7 +44,7 @@ impl<'a> Executor<'a> {
on!(plugin);
on!(plugin_do);
on!(update_notify);
on!(update_progress);
on!(update_summary);
on!(resize);
on!(stop);
on!(resume);

View file

@ -10,10 +10,8 @@ pub(super) struct Router<'a> {
}
impl<'a> Router<'a> {
#[inline]
pub(super) fn new(app: &'a mut App) -> Self { Self { app } }
#[inline]
pub(super) fn route(&mut self, key: Key) -> Result<bool> {
let core = &mut self.app.core;
let layer = core.layer();
@ -36,7 +34,6 @@ impl<'a> Router<'a> {
})
}
#[inline]
fn matches(&mut self, layer: Layer, key: Key) -> bool {
for chord @ Chord { on, .. } in KEYMAP.get(layer) {
if on.is_empty() || on[0] != key {

View file

@ -24,7 +24,6 @@ impl Signals {
}
#[cfg(unix)]
#[inline]
fn handle_sys(n: libc::c_int) -> bool {
use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGSTOP, SIGTERM, SIGTSTP};
use tracing::error;
@ -55,7 +54,6 @@ impl Signals {
#[inline]
fn handle_sys(_: ()) -> bool { unreachable!() }
#[inline]
fn handle_term(event: CrosstermEvent) {
match event {
CrosstermEvent::Key(key @ KeyEvent { kind: KeyEventKind::Press, .. }) => {

View file

@ -42,7 +42,7 @@ impl Widget for Tasks<'_> {
block.render(area, buf);
let tasks = &self.core.tasks;
let items = tasks.summaries.iter().take(inner.height as usize).enumerate().map(|(i, v)| {
let items = tasks.snaps.iter().take(inner.height as usize).enumerate().map(|(i, v)| {
let mut item =
Text::from_iter(textwrap::wrap(&v.name, inner.width as usize).into_iter().map(Line::from));
if i == tasks.cursor {

View file

@ -136,7 +136,6 @@ impl Term {
})
}
#[inline]
pub(super) fn can_partial(&mut self) -> bool {
self.inner.autoresize().is_ok() && self.last_area == self.inner.get_frame().area()
}

View file

@ -1,7 +1,7 @@
use std::{fs::{FileType, Metadata}, time::SystemTime};
use yazi_macro::{unix_either, win_either};
use yazi_shared::url::UrlBuf;
use yazi_shared::url::{Url, UrlBuf};
use super::ChaKind;
use crate::provider;
@ -53,17 +53,20 @@ impl Default for Cha {
impl Cha {
#[inline]
pub fn new(url: &UrlBuf, meta: Metadata) -> Self {
pub fn new<'a>(url: impl Into<Url<'a>>, meta: Metadata) -> Self {
Self::from_just_meta(&meta).attach(ChaKind::hidden(url, &meta))
}
#[inline]
pub async fn from_url(url: &UrlBuf) -> std::io::Result<Self> {
pub async fn from_url<'a>(url: impl Into<Url<'a>>) -> std::io::Result<Self> {
let url = url.into();
Ok(Self::from_follow(url, provider::symlink_metadata(url).await?).await)
}
pub async fn from_follow(url: &UrlBuf, mut meta: Metadata) -> Self {
pub async fn from_follow<'a>(url: impl Into<Url<'a>>, mut meta: Metadata) -> Self {
let url = url.into();
let mut attached = ChaKind::hidden(url, &meta);
if meta.is_symlink() {
attached |= ChaKind::LINK;
meta = provider::metadata(url).await.unwrap_or(meta);

View file

@ -1,7 +1,7 @@
use std::fs::Metadata;
use bitflags::bitflags;
use yazi_shared::url::UrlBuf;
use yazi_shared::url::Url;
bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
@ -20,11 +20,11 @@ bitflags! {
impl ChaKind {
#[inline]
pub(super) fn hidden(_url: &UrlBuf, _meta: &Metadata) -> Self {
pub(super) fn hidden<'a>(_url: impl Into<Url<'a>>, _meta: &Metadata) -> Self {
let mut me = Self::empty();
#[cfg(unix)]
if _url.urn().is_hidden() {
if _url.into().urn().is_hidden() {
me |= Self::HIDDEN;
}
#[cfg(windows)]

View file

@ -15,7 +15,6 @@ pub struct File {
impl Deref for File {
type Target = Cha;
#[inline]
fn deref(&self) -> &Self::Target { &self.cha }
}

View file

@ -31,38 +31,38 @@ pub fn copy_with_progress(
to: &UrlBuf,
cha: Cha,
) -> mpsc::Receiver<Result<u64, io::Error>> {
let (tx, rx) = mpsc::channel(1);
let (tick_tx, mut tick_rx) = oneshot::channel();
let (prog_tx, prog_rx) = mpsc::channel(1);
let (done_tx, mut done_rx) = oneshot::channel();
tokio::spawn({
let (from, to) = (from.clone(), to.clone());
async move {
tick_tx.send(provider::copy(&from, &to, cha).await).ok();
done_tx.send(provider::copy(&from, &to, cha).await).ok();
}
});
tokio::spawn({
let (tx, to) = (tx.clone(), to.clone());
let (prog_tx, to) = (prog_tx.clone(), to.clone());
async move {
let mut last = 0;
let mut exit = None;
let mut done = None;
loop {
select! {
res = &mut tick_rx => exit = Some(res.unwrap()),
_ = tx.closed() => break,
_ = time::sleep(time::Duration::from_secs(3)) => (),
res = &mut done_rx => done = Some(res.unwrap()),
_ = prog_tx.closed() => break,
_ = time::sleep(time::Duration::from_secs(3)) => {},
}
match exit {
match done {
Some(Ok(len)) => {
if len > last {
tx.send(Ok(len - last)).await.ok();
prog_tx.send(Ok(len - last)).await.ok();
}
tx.send(Ok(0)).await.ok();
prog_tx.send(Ok(0)).await.ok();
break;
}
Some(Err(e)) => {
tx.send(Err(e)).await.ok();
prog_tx.send(Err(e)).await.ok();
break;
}
None => {}
@ -70,14 +70,14 @@ pub fn copy_with_progress(
let len = provider::symlink_metadata(&to).await.map(|m| m.len()).unwrap_or(0);
if len > last {
tx.send(Ok(len - last)).await.ok();
prog_tx.send(Ok(len - last)).await.ok();
last = len;
}
}
}
});
rx
prog_rx
}
pub async fn remove_dir_clean(dir: &UrlBuf) {

View file

@ -38,13 +38,11 @@ impl Partition {
}
impl Partition {
#[inline]
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(super) fn new(name: &std::ffi::OsStr) -> Self {
Self { src: std::path::Path::new("/dev/").join(name).into(), ..Default::default() }
}
#[inline]
#[cfg(target_os = "linux")]
pub(super) fn dev_name(&self, full: bool) -> Option<&std::ffi::OsStr> {
use std::os::unix::ffi::OsStrExt;

View file

@ -26,7 +26,6 @@ impl Deref for Partitions {
}
impl Partitions {
#[inline]
#[cfg(unix)]
pub fn by_dev(&self, dev: libc::dev_t) -> Option<&Partition> {
self.inner.iter().find(|p| p.rdev == Some(dev))

View file

@ -202,6 +202,25 @@ impl Local {
tokio::fs::rename(from, to).await
}
#[inline]
pub async fn symlink<P, Q, F>(original: P, link: Q, _is_dir: F) -> io::Result<()>
where
P: AsRef<Path>,
Q: AsRef<Path>,
F: AsyncFnOnce() -> io::Result<bool>,
{
#[cfg(unix)]
{
tokio::fs::symlink(original, link).await
}
#[cfg(windows)]
if _is_dir().await? {
Self::symlink_dir(original, link).await
} else {
Self::symlink_file(original, link).await
}
}
#[inline]
pub async fn symlink_dir<P, Q>(original: P, link: Q) -> io::Result<()>
where

View file

@ -229,6 +229,19 @@ where
}
}
#[inline]
pub async fn symlink<'a, U, F>(original: &Path, link: U, is_dir: F) -> io::Result<()>
where
U: Into<Url<'a>>,
F: AsyncFnOnce() -> io::Result<bool>,
{
if let Some(link) = link.into().as_path() {
Local::symlink(original, link, is_dir).await
} else {
Err(io::Error::new(io::ErrorKind::Unsupported, "Unsupported filesystem"))
}
}
#[inline]
pub async fn symlink_dir<'a, U>(original: &Path, link: U) -> io::Result<()>
where

View file

@ -5,7 +5,6 @@ pub enum RwFile {
}
impl RwFile {
#[inline]
pub fn reader(self) -> Box<dyn BufRead> {
match self {
RwFile::Local(local) => Box::new(local.reader()),

View file

@ -9,7 +9,6 @@ pub enum FolderStage {
}
impl FolderStage {
#[inline]
pub fn is_loading(self) -> bool { self == Self::Loading }
}

View file

@ -21,10 +21,11 @@ yazi-macro = { path = "../yazi-macro", version = "25.6.11" }
yazi-shared = { path = "../yazi-shared", version = "25.6.11" }
# External dependencies
anyhow = { workspace = true }
bitflags = { workspace = true }
crossterm = { workspace = true }
hashbrown = { workspace = true }
mlua = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
anyhow = { workspace = true }
bitflags = { workspace = true }
crossterm = { workspace = true }
hashbrown = { workspace = true }
mlua = { workspace = true }
ordered-float = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }

View file

@ -1 +1 @@
yazi_macro::mod_flat!(deprecate mouse notify plugin stop update_progress);
yazi_macro::mod_flat!(deprecate mouse notify plugin stop update_summary);

View file

@ -1,30 +0,0 @@
use anyhow::bail;
use serde::Serialize;
use yazi_shared::event::CmdCow;
pub struct UpdateProgressOpt {
pub progress: TasksProgress,
}
impl TryFrom<CmdCow> for UpdateProgressOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
let Some(progress) = c.take_any("progress") else {
bail!("Invalid 'progress' in UpdateProgressOpt");
};
Ok(Self { progress })
}
}
// --- Progress
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct TasksProgress {
pub total: u32,
pub succ: u32,
pub fail: u32,
pub found: u64,
pub processed: u64,
}

View file

@ -0,0 +1,29 @@
use anyhow::bail;
use ordered_float::OrderedFloat;
use serde::Serialize;
use yazi_shared::event::CmdCow;
pub struct UpdateSummaryOpt {
pub summary: TaskSummary,
}
impl TryFrom<CmdCow> for UpdateSummaryOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
let Some(summary) = c.take_any("summary") else {
bail!("Invalid 'summary' in UpdateProgressOpt");
};
Ok(Self { summary })
}
}
// --- Progress
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct TaskSummary {
pub total: u32,
pub success: u32,
pub failed: u32,
pub percent: Option<OrderedFloat<f32>>,
}

View file

@ -62,7 +62,6 @@ impl TryFrom<Table> for SpotLock {
}
impl SpotLock {
#[inline]
pub fn len(&self) -> Option<usize> { Some(self.table()?.len()) }
pub fn select(&mut self, idx: Option<usize>) {

View file

@ -22,25 +22,25 @@ end
function Progress:reflow() return { self } end
function Progress:redraw()
local progress = cx.tasks.progress
if progress.total == 0 then
local summary = cx.tasks.summary
if summary.total == 0 then
return {}
end
local gauge = ui.Gauge():area(self._area)
if progress.fail == 0 then
if summary.failed == 0 then
gauge = gauge:gauge_style(th.status.progress_normal)
else
gauge = gauge:gauge_style(th.status.progress_error)
end
local percent = 99
if progress.found ~= 0 then
percent = math.min(99, ya.round(progress.processed * 100 / progress.found))
local label, percent = "", summary.percent
if percent then
label = string.format("%3d%%, ", math.floor(percent))
else
percent = 99
end
local left = progress.total - progress.succ
return gauge
:percent(percent)
:label(ui.Span(string.format("%3d%%, %d left", percent, left)):style(th.status.progress_label))
label = label .. string.format("%d left", summary.total - summary.success)
return gauge:percent(percent):label(ui.Span(label):style(th.status.progress_label))
end

View file

@ -10,8 +10,6 @@ function ya.clamp(min, x, max)
end
end
function ya.round(x) return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) end
function ya.list_merge(a, b)
for _, v in ipairs(b) do
a[#a + 1] = v

View file

@ -48,7 +48,6 @@ impl UserData for ImageFormat {
F::Farbfeld => "Farbfeld",
F::Avif => "AVIF",
F::Qoi => "QOI",
F::Pcx => "PCX",
_ => "Unknown",
})
});

View file

@ -57,7 +57,7 @@ fn cha(lua: &Lua) -> mlua::Result<Function> {
};
match meta {
Ok(m) => Cha(yazi_fs::cha::Cha::new(&url, m)).into_lua_multi(&lua),
Ok(m) => Cha(yazi_fs::cha::Cha::new(&*url, m)).into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
}
})

View file

@ -3,7 +3,7 @@ use yazi_binding::{Composer, ComposerGet, ComposerSet, Url};
use yazi_boot::ARGS;
use yazi_config::YAZI;
pub const OPTS: SerializeOptions =
pub const SER_OPT: SerializeOptions =
SerializeOptions::new().serialize_none_to_null(false).serialize_unit_to_null(false);
pub fn compose() -> Composer<ComposerGet, ComposerSet> {
@ -44,20 +44,20 @@ fn mgr() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let m = &YAZI.mgr;
match key {
b"ratio" => lua.to_value_with(&m.ratio, OPTS)?,
b"ratio" => lua.to_value_with(&m.ratio, SER_OPT)?,
b"sort_by" => lua.to_value_with(&m.sort_by, OPTS)?,
b"sort_sensitive" => lua.to_value_with(&m.sort_sensitive, OPTS)?,
b"sort_reverse" => lua.to_value_with(&m.sort_reverse, OPTS)?,
b"sort_dir_first" => lua.to_value_with(&m.sort_dir_first, OPTS)?,
b"sort_translit" => lua.to_value_with(&m.sort_translit, OPTS)?,
b"sort_by" => lua.to_value_with(&m.sort_by, SER_OPT)?,
b"sort_sensitive" => lua.to_value_with(&m.sort_sensitive, SER_OPT)?,
b"sort_reverse" => lua.to_value_with(&m.sort_reverse, SER_OPT)?,
b"sort_dir_first" => lua.to_value_with(&m.sort_dir_first, SER_OPT)?,
b"sort_translit" => lua.to_value_with(&m.sort_translit, SER_OPT)?,
b"linemode" => lua.to_value_with(&m.linemode, OPTS)?,
b"show_hidden" => lua.to_value_with(&m.show_hidden, OPTS)?,
b"show_symlink" => lua.to_value_with(&m.show_symlink, OPTS)?,
b"scrolloff" => lua.to_value_with(&m.scrolloff, OPTS)?,
b"mouse_events" => lua.to_value_with(&m.mouse_events, OPTS)?,
b"title_format" => lua.to_value_with(&m.title_format, OPTS)?,
b"linemode" => lua.to_value_with(&m.linemode, SER_OPT)?,
b"show_hidden" => lua.to_value_with(&m.show_hidden, SER_OPT)?,
b"show_symlink" => lua.to_value_with(&m.show_symlink, SER_OPT)?,
b"scrolloff" => lua.to_value_with(&m.scrolloff, SER_OPT)?,
b"mouse_events" => lua.to_value_with(&m.mouse_events, SER_OPT)?,
b"title_format" => lua.to_value_with(&m.title_format, SER_OPT)?,
_ => return Ok(Value::Nil),
}
.into_lua(lua)
@ -81,19 +81,19 @@ fn preview() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let p = &YAZI.preview;
match key {
b"wrap" => lua.to_value_with(&p.wrap, OPTS)?,
b"tab_size" => lua.to_value_with(&p.tab_size, OPTS)?,
b"max_width" => lua.to_value_with(&p.max_width, OPTS)?,
b"max_height" => lua.to_value_with(&p.max_height, OPTS)?,
b"wrap" => lua.to_value_with(&p.wrap, SER_OPT)?,
b"tab_size" => lua.to_value_with(&p.tab_size, SER_OPT)?,
b"max_width" => lua.to_value_with(&p.max_width, SER_OPT)?,
b"max_height" => lua.to_value_with(&p.max_height, SER_OPT)?,
b"cache_dir" => lua.to_value_with(&p.cache_dir, OPTS)?,
b"cache_dir" => lua.to_value_with(&p.cache_dir, SER_OPT)?,
b"image_delay" => lua.to_value_with(&p.image_delay, OPTS)?,
b"image_filter" => lua.to_value_with(&p.image_filter, OPTS)?,
b"image_quality" => lua.to_value_with(&p.image_quality, OPTS)?,
b"image_delay" => lua.to_value_with(&p.image_delay, SER_OPT)?,
b"image_filter" => lua.to_value_with(&p.image_filter, SER_OPT)?,
b"image_quality" => lua.to_value_with(&p.image_quality, SER_OPT)?,
b"ueberzug_scale" => lua.to_value_with(&p.ueberzug_scale, OPTS)?,
b"ueberzug_offset" => lua.to_value_with(&p.ueberzug_offset, OPTS)?,
b"ueberzug_scale" => lua.to_value_with(&p.ueberzug_scale, SER_OPT)?,
b"ueberzug_offset" => lua.to_value_with(&p.ueberzug_offset, SER_OPT)?,
_ => return Ok(Value::Nil),
}
.into_lua(lua)
@ -108,14 +108,14 @@ fn tasks() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &YAZI.tasks;
match key {
b"micro_workers" => lua.to_value_with(&t.micro_workers, OPTS)?,
b"macro_workers" => lua.to_value_with(&t.macro_workers, OPTS)?,
b"bizarre_retry" => lua.to_value_with(&t.bizarre_retry, OPTS)?,
b"micro_workers" => lua.to_value_with(&t.micro_workers, SER_OPT)?,
b"macro_workers" => lua.to_value_with(&t.macro_workers, SER_OPT)?,
b"bizarre_retry" => lua.to_value_with(&t.bizarre_retry, SER_OPT)?,
b"image_alloc" => lua.to_value_with(&t.image_alloc, OPTS)?,
b"image_bound" => lua.to_value_with(&t.image_bound, OPTS)?,
b"image_alloc" => lua.to_value_with(&t.image_alloc, SER_OPT)?,
b"image_bound" => lua.to_value_with(&t.image_bound, SER_OPT)?,
b"suppress_preload" => lua.to_value_with(&t.suppress_preload, OPTS)?,
b"suppress_preload" => lua.to_value_with(&t.suppress_preload, SER_OPT)?,
_ => return Ok(Value::Nil),
}
.into_lua(lua)

View file

@ -2,7 +2,7 @@ use mlua::{Function, IntoLuaMulti, Lua, LuaSerdeExt, Value};
use yazi_binding::Error;
use super::Utils;
use crate::runtime::OPTS;
use crate::runtime::SER_OPT;
impl Utils {
pub(super) fn json_encode(lua: &Lua) -> mlua::Result<Function> {
@ -17,7 +17,7 @@ impl Utils {
pub(super) fn json_decode(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, s: mlua::String| async move {
match serde_json::from_slice::<serde_json::Value>(&s.as_bytes()) {
Ok(v) => lua.to_value_with(&v, OPTS)?.into_lua_multi(&lua),
Ok(v) => lua.to_value_with(&v, SER_OPT)?.into_lua_multi(&lua),
Err(e) => (Value::Nil, Error::Serde(e)).into_lua_multi(&lua),
}
})

View file

@ -2,7 +2,7 @@ use std::time::Duration;
use tokio::sync::oneshot;
use yazi_macro::{emit, relay};
use yazi_parser::app::{NotifyLevel, NotifyOpt, PluginOpt, TasksProgress};
use yazi_parser::app::{NotifyLevel, NotifyOpt, PluginOpt, TaskSummary};
pub struct AppProxy;
@ -51,7 +51,7 @@ impl AppProxy {
emit!(Call(relay!(app:plugin_do).with_any("opt", opt)));
}
pub fn update_progress(progress: TasksProgress) {
emit!(Call(relay!(app:update_progress).with_any("progress", progress)));
pub fn update_summary(summary: TaskSummary) {
emit!(Call(relay!(app:update_summary).with_any("summary", summary)));
}
}

View file

@ -24,6 +24,8 @@ async-priority-channel = "0.2.0"
futures = { workspace = true }
hashbrown = { workspace = true }
lru = { workspace = true }
mlua = { workspace = true }
ordered-float = { workspace = true }
parking_lot = { workspace = true }
scopeguard = { workspace = true }
tokio = { workspace = true }

View file

@ -1,140 +1,31 @@
use std::{borrow::Cow, collections::VecDeque};
use anyhow::{Result, anyhow, bail};
use anyhow::{Result, anyhow};
use tokio::{io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc};
use tracing::warn;
use yazi_config::YAZI;
use yazi_fs::{SizeCalculator, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path::{path_relative_to, skip_url}, provider::{self, DirEntry}};
use yazi_shared::{Id, url::{UrlBuf, UrlCow}};
use yazi_fs::{cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path::{path_relative_to, skip_url}, provider::{self, DirEntry}};
use yazi_shared::url::{Url, UrlBuf, UrlCow};
use super::{FileIn, FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash};
use crate::{LOW, NORMAL, TaskOp, TaskProg};
use super::{FileInDelete, FileInHardlink, FileInLink, FileInPaste, FileInTrash};
use crate::{LOW, NORMAL, TaskIn, TaskOp, TaskOps, file::{FileOutDelete, FileOutDeleteDo, FileOutHardlink, FileOutHardlinkDo, FileOutLink, FileOutPaste, FileOutPasteDo, FileOutTrash}};
pub struct File {
r#macro: async_priority_channel::Sender<TaskOp, u8>,
prog: mpsc::UnboundedSender<TaskProg>,
pub(crate) struct File {
ops: TaskOps,
r#macro: async_priority_channel::Sender<TaskIn, u8>,
}
impl File {
pub fn new(
r#macro: async_priority_channel::Sender<TaskOp, u8>,
prog: mpsc::UnboundedSender<TaskProg>,
pub(crate) fn new(
tx: &mpsc::UnboundedSender<TaskOp>,
r#macro: &async_priority_channel::Sender<TaskIn, u8>,
) -> Self {
Self { r#macro, prog }
Self { ops: tx.into(), r#macro: r#macro.clone() }
}
pub async fn work(&self, r#in: FileIn) -> Result<()> {
match r#in {
FileIn::Paste(mut task) => {
ok_or_not_found(provider::remove_file(&task.to).await)?;
let mut it = copy_with_progress(&task.from, &task.to, task.cha.unwrap());
while let Some(res) = it.recv().await {
match res {
Ok(0) => {
if task.cut {
provider::remove_file(&task.from).await.ok();
}
break;
}
Ok(n) => self.prog.send(TaskProg::Adv(task.id, 0, n))?,
Err(e) if e.kind() == NotFound => {
warn!("Paste task partially done: {task:?}");
break;
}
// Operation not permitted (os error 1)
// Attribute not found (os error 93)
Err(e)
if task.retry < YAZI.tasks.bizarre_retry
&& matches!(e.raw_os_error(), Some(1) | Some(93)) =>
{
task.retry += 1;
self.log(task.id, format!("Paste task retry: {task:?}"))?;
self.queue(FileIn::Paste(task), LOW).await?;
return Ok(());
}
Err(e) => Err(e)?,
}
}
self.prog.send(TaskProg::Adv(task.id, 1, 0))?;
}
FileIn::Link(task) => {
let cha = task.cha.unwrap();
let src: Cow<_> = if task.resolve {
match provider::read_link(&task.from).await {
Ok(p) => p.into(),
Err(e) if e.kind() == NotFound => {
warn!("Link task partially done: {task:?}");
return Ok(self.prog.send(TaskProg::Adv(task.id, 1, cha.len))?);
}
Err(e) => Err(e)?,
}
} else if task.from.scheme.covariant(&task.to.scheme) {
task.from.loc.as_path().into()
} else {
bail!("Source and target must be on the same filesystem: {task:?}")
};
let src = if task.relative {
path_relative_to(provider::canonicalize(task.to.parent().unwrap()).await?.loc, &src)?
} else {
src
};
ok_or_not_found(provider::remove_file(&task.to).await)?;
if cha.is_dir() {
provider::symlink_dir(&src, &task.to).await?;
} else {
provider::symlink_file(&src, &task.to).await?;
}
if task.delete {
provider::remove_file(&task.from).await.ok();
}
self.prog.send(TaskProg::Adv(task.id, 1, cha.len))?;
}
FileIn::Hardlink(task) => {
let cha = task.cha.unwrap();
let src = if !task.follow {
UrlCow::from(&task.from)
} else if let Ok(p) = provider::canonicalize(&task.from).await {
UrlCow::from(p)
} else {
UrlCow::from(&task.from)
};
ok_or_not_found(provider::remove_file(&task.to).await)?;
match provider::hard_link(&src, &task.to).await {
Err(e) if e.kind() == NotFound => {
warn!("Hardlink task partially done: {task:?}");
}
v => v?,
}
self.prog.send(TaskProg::Adv(task.id, 1, cha.len))?;
}
FileIn::Delete(task) => {
if let Err(e) = provider::remove_file(&task.target).await
&& e.kind() != NotFound
&& maybe_exists(&task.target).await
{
self.fail(task.id, format!("Delete task failed: {task:?}, {e}"))?;
Err(e)?
}
self.prog.send(TaskProg::Adv(task.id, 1, task.length))?
}
FileIn::Trash(task) => {
provider::trash(&task.target).await?;
self.prog.send(TaskProg::Adv(task.id, 1, task.length))?;
}
}
Ok(())
}
pub async fn paste(&self, mut task: FileInPaste) -> Result<()> {
pub(crate) async fn paste(&self, mut task: FileInPaste) -> Result<(), FileOutPaste> {
if task.cut && ok_or_not_found(provider::rename(&task.from, &task.to).await).is_ok() {
return self.succ(task.id);
return Ok(self.ops.out(task.id, FileOutPaste::Init));
}
if task.cha.is_none() {
@ -144,14 +35,14 @@ impl File {
let cha = task.cha.unwrap();
if !cha.is_dir() {
let id = task.id;
self.prog.send(TaskProg::New(id, cha.len))?;
if cha.is_orphan() || (cha.is_link() && !task.follow) {
self.queue(FileIn::Link(task.into()), NORMAL).await?;
self.ops.out(id, FileOutPaste::New(0));
self.queue(task.into_link(), NORMAL);
} else {
self.queue(FileIn::Paste(task), LOW).await?;
self.ops.out(id, FileOutPaste::New(cha.len));
self.queue(task, LOW);
}
return self.succ(id);
return Ok(self.ops.out(id, FileOutPaste::Init));
}
macro_rules! continue_unless_ok {
@ -159,8 +50,7 @@ impl File {
match $result {
Ok(v) => v,
Err(e) => {
self.prog.send(TaskProg::New(task.id, 0))?;
self.fail(task.id, format!("An error occurred while pasting: {e}"))?;
self.ops.out(task.id, FileOutPaste::Deform(e.to_string()));
continue;
}
}
@ -189,30 +79,93 @@ impl File {
}
let to = dest.join(from.name().unwrap());
self.prog.send(TaskProg::New(task.id, cha.len))?;
if cha.is_orphan() || (cha.is_link() && !task.follow) {
self.queue(FileIn::Link(task.spawn(from, to, cha).into()), NORMAL).await?;
self.ops.out(task.id, FileOutPaste::New(0));
self.queue(task.spawn(from, to, cha).into_link(), NORMAL);
} else {
self.queue(FileIn::Paste(task.spawn(from, to, cha)), LOW).await?;
self.ops.out(task.id, FileOutPaste::New(cha.len));
self.queue(task.spawn(from, to, cha), LOW);
}
}
}
self.succ(task.id)
Ok(self.ops.out(task.id, FileOutPaste::Init))
}
pub async fn link(&self, mut task: FileInLink) -> Result<()> {
let id = task.id;
if task.cha.is_none() {
task.cha = Some(Self::cha(&task.from, false).await?);
pub(crate) async fn paste_do(&self, mut task: FileInPaste) -> Result<(), FileOutPasteDo> {
ok_or_not_found(provider::remove_file(&task.to).await)?;
let mut it = copy_with_progress(&task.from, &task.to, task.cha.unwrap());
while let Some(res) = it.recv().await {
match res {
Ok(0) => {
if task.cut {
provider::remove_file(&task.from).await.ok();
}
break;
}
Ok(n) => self.ops.out(task.id, FileOutPasteDo::Adv(n)),
Err(e) if e.kind() == NotFound => {
warn!("Paste task partially done: {task:?}");
break;
}
// Operation not permitted (os error 1)
// Attribute not found (os error 93)
Err(e)
if task.retry < YAZI.tasks.bizarre_retry
&& matches!(e.raw_os_error(), Some(1) | Some(93)) =>
{
task.retry += 1;
self.ops.out(task.id, FileOutPasteDo::Log(format!("Retrying due to error: {e}")));
return Ok(self.queue(task, LOW));
}
Err(e) => Err(e)?,
}
}
self.prog.send(TaskProg::New(id, task.cha.unwrap().len))?;
self.queue(FileIn::Link(task), NORMAL).await?;
self.succ(id)
Ok(self.ops.out(task.id, FileOutPasteDo::Succ))
}
pub async fn hardlink(&self, mut task: FileInHardlink) -> Result<()> {
pub(crate) fn link(&self, task: FileInLink) -> Result<(), FileOutLink> {
Ok(self.queue(task, NORMAL))
}
pub(crate) async fn link_do(&self, task: FileInLink) -> Result<(), FileOutLink> {
let src: Cow<_> = if task.resolve {
match provider::read_link(&task.from).await {
Ok(p) => p.into(),
Err(e) if e.kind() == NotFound => {
return Ok(self.ops.out(task.id, FileOutLink::Succ));
}
Err(e) => Err(e)?,
}
} else if task.from.scheme.covariant(&task.to.scheme) {
task.from.loc.as_path().into()
} else {
Err(anyhow!("Source and target must be on the same filesystem: {task:?}"))?
};
let src = if task.relative {
path_relative_to(provider::canonicalize(task.to.parent().unwrap()).await?.loc, &src)?
} else {
src
};
ok_or_not_found(provider::remove_file(&task.to).await)?;
provider::symlink(&src, &task.to, async || {
Ok(match task.cha {
Some(cha) => cha.is_dir(),
None => Self::cha(&task.from, task.resolve).await?.is_dir(),
})
})
.await?;
if task.delete {
provider::remove_file(&task.from).await.ok();
}
Ok(self.ops.out(task.id, FileOutLink::Succ))
}
pub(crate) async fn hardlink(&self, mut task: FileInHardlink) -> Result<(), FileOutHardlink> {
if task.cha.is_none() {
task.cha = Some(Self::cha(&task.from, task.follow).await?);
}
@ -220,9 +173,10 @@ impl File {
let cha = task.cha.unwrap();
if !cha.is_dir() {
let id = task.id;
self.prog.send(TaskProg::New(id, cha.len))?;
self.queue(FileIn::Hardlink(task), NORMAL).await?;
return self.succ(id);
self.ops.out(id, FileOutHardlink::New);
self.queue(task, NORMAL);
self.ops.out(id, FileOutHardlink::Init);
return Ok(());
}
macro_rules! continue_unless_ok {
@ -230,8 +184,7 @@ impl File {
match $result {
Ok(v) => v,
Err(e) => {
self.prog.send(TaskProg::New(task.id, 0))?;
self.fail(task.id, format!("An error occurred while hardlinking: {e}"))?;
self.ops.out(task.id, FileOutHardlink::Deform(e.to_string()));
continue;
}
}
@ -260,21 +213,38 @@ impl File {
}
let to = dest.join(from.name().unwrap());
self.prog.send(TaskProg::New(task.id, cha.len))?;
self.queue(FileIn::Hardlink(task.spawn(from, to, cha)), NORMAL).await?;
self.ops.out(task.id, FileOutHardlink::New);
self.queue(task.spawn(from, to, cha), NORMAL);
}
}
self.succ(task.id)
Ok(self.ops.out(task.id, FileOutHardlink::Init))
}
pub async fn delete(&self, mut task: FileInDelete) -> Result<()> {
pub(crate) async fn hardlink_do(&self, task: FileInHardlink) -> Result<(), FileOutHardlinkDo> {
let src = if !task.follow {
UrlCow::from(&task.from)
} else if let Ok(p) = provider::canonicalize(&task.from).await {
UrlCow::from(p)
} else {
UrlCow::from(&task.from)
};
ok_or_not_found(provider::remove_file(&task.to).await)?;
ok_or_not_found(provider::hard_link(&src, &task.to).await)?;
Ok(self.ops.out(task.id, FileOutHardlinkDo::Succ))
}
pub(crate) async fn delete(&self, mut task: FileInDelete) -> Result<(), FileOutDelete> {
let meta = provider::symlink_metadata(&task.target).await?;
if !meta.is_dir() {
let id = task.id;
task.length = meta.len();
self.prog.send(TaskProg::New(id, meta.len()))?;
self.queue(FileIn::Delete(task), NORMAL).await?;
return self.succ(id);
self.ops.out(id, FileOutDelete::New(meta.len()));
self.queue(task, NORMAL);
self.ops.out(id, FileOutDelete::Init);
return Ok(());
}
let mut dirs = VecDeque::from([task.target]);
@ -291,24 +261,36 @@ impl File {
task.target = entry.url();
task.length = meta.len();
self.prog.send(TaskProg::New(task.id, meta.len()))?;
self.queue(FileIn::Delete(task.clone()), NORMAL).await?;
self.ops.out(task.id, FileOutDelete::New(meta.len()));
self.queue(task.clone(), NORMAL);
}
}
self.succ(task.id)
Ok(self.ops.out(task.id, FileOutDelete::Init))
}
pub async fn trash(&self, mut task: FileInTrash) -> Result<()> {
let id = task.id;
task.length = SizeCalculator::total(&task.target).await?;
pub(crate) async fn delete_do(&self, task: FileInDelete) -> Result<(), FileOutDeleteDo> {
match provider::remove_file(&task.target).await {
Ok(()) => {}
Err(e) if e.kind() == NotFound => {}
Err(_) if !maybe_exists(&task.target).await => {}
Err(e) => Err(e)?,
}
Ok(self.ops.out(task.id, FileOutDeleteDo::Succ(task.length)))
}
self.prog.send(TaskProg::New(id, task.length))?;
self.queue(FileIn::Trash(task), LOW).await?;
self.succ(id)
pub(crate) fn trash(&self, task: FileInTrash) -> Result<(), FileOutTrash> {
Ok(self.queue(task, LOW))
}
pub(crate) async fn trash_do(&self, task: FileInTrash) -> Result<(), FileOutTrash> {
provider::trash(&task.target).await?;
Ok(self.ops.out(task.id, FileOutTrash::Succ))
}
#[inline]
async fn cha(url: &UrlBuf, follow: bool) -> io::Result<Cha> {
async fn cha<'a>(url: impl Into<Url<'a>>, follow: bool) -> io::Result<Cha> {
let url = url.into();
let meta = provider::symlink_metadata(url).await?;
Ok(if follow { Cha::from_follow(url, meta).await } else { Cha::new(url, meta) })
}
@ -325,18 +307,7 @@ impl File {
impl File {
#[inline]
fn succ(&self, id: Id) -> Result<()> { Ok(self.prog.send(TaskProg::Succ(id))?) }
#[inline]
fn fail(&self, id: Id, reason: String) -> Result<()> {
Ok(self.prog.send(TaskProg::Fail(id, reason))?)
}
#[inline]
fn log(&self, id: Id, line: String) -> Result<()> { Ok(self.prog.send(TaskProg::Log(id, line))?) }
#[inline]
async fn queue(&self, r#in: impl Into<TaskOp>, priority: u8) -> Result<()> {
self.r#macro.send(r#in.into(), priority).await.map_err(|_| anyhow!("Failed to send task"))
fn queue(&self, r#in: impl Into<TaskIn>, priority: u8) {
_ = self.r#macro.try_send(r#in.into(), priority);
}
}

View file

@ -1,37 +1,16 @@
use yazi_fs::cha::Cha;
use yazi_shared::{Id, url::UrlBuf};
#[derive(Debug)]
pub enum FileIn {
Paste(FileInPaste),
Link(FileInLink),
Hardlink(FileInHardlink),
Delete(FileInDelete),
Trash(FileInTrash),
}
impl FileIn {
pub fn id(&self) -> Id {
match self {
Self::Paste(r#in) => r#in.id,
Self::Link(r#in) => r#in.id,
Self::Hardlink(r#in) => r#in.id,
Self::Delete(r#in) => r#in.id,
Self::Trash(r#in) => r#in.id,
}
}
}
// --- Paste
#[derive(Clone, Debug)]
pub struct FileInPaste {
pub id: Id,
pub from: UrlBuf,
pub to: UrlBuf,
pub cha: Option<Cha>,
pub cut: bool,
pub follow: bool,
pub retry: u8,
pub(crate) struct FileInPaste {
pub(crate) id: Id,
pub(crate) from: UrlBuf,
pub(crate) to: UrlBuf,
pub(crate) cha: Option<Cha>,
pub(crate) cut: bool,
pub(crate) follow: bool,
pub(crate) retry: u8,
}
impl FileInPaste {
@ -46,42 +25,40 @@ impl FileInPaste {
retry: self.retry,
}
}
}
// --- Link
#[derive(Clone, Debug)]
pub struct FileInLink {
pub id: Id,
pub from: UrlBuf,
pub to: UrlBuf,
pub cha: Option<Cha>,
pub resolve: bool,
pub relative: bool,
pub delete: bool,
}
impl From<FileInPaste> for FileInLink {
fn from(value: FileInPaste) -> Self {
Self {
id: value.id,
from: value.from,
to: value.to,
cha: value.cha,
pub(super) fn into_link(self) -> FileInLink {
FileInLink {
id: self.id,
from: self.from,
to: self.to,
cha: self.cha,
resolve: true,
relative: false,
delete: value.cut,
delete: self.cut,
}
}
}
// --- Link
#[derive(Clone, Debug)]
pub(crate) struct FileInLink {
pub(crate) id: Id,
pub(crate) from: UrlBuf,
pub(crate) to: UrlBuf,
pub(crate) cha: Option<Cha>,
pub(crate) resolve: bool,
pub(crate) relative: bool,
pub(crate) delete: bool,
}
// --- Hardlink
#[derive(Clone, Debug)]
pub struct FileInHardlink {
pub id: Id,
pub from: UrlBuf,
pub to: UrlBuf,
pub cha: Option<Cha>,
pub follow: bool,
pub(crate) struct FileInHardlink {
pub(crate) id: Id,
pub(crate) from: UrlBuf,
pub(crate) to: UrlBuf,
pub(crate) cha: Option<Cha>,
pub(crate) follow: bool,
}
impl FileInHardlink {
@ -92,16 +69,15 @@ impl FileInHardlink {
// --- Delete
#[derive(Clone, Debug)]
pub struct FileInDelete {
pub id: Id,
pub target: UrlBuf,
pub length: u64,
pub(crate) struct FileInDelete {
pub(crate) id: Id,
pub(crate) target: UrlBuf,
pub(crate) length: u64,
}
// --- Trash
#[derive(Clone, Debug)]
pub struct FileInTrash {
pub id: Id,
pub target: UrlBuf,
pub length: u64,
pub(crate) struct FileInTrash {
pub(crate) id: Id,
pub(crate) target: UrlBuf,
}

View file

@ -1,3 +1,3 @@
#![allow(clippy::module_inception)]
yazi_macro::mod_flat!(file out r#in);
yazi_macro::mod_flat!(file out progress r#in);

View file

@ -1,23 +1,250 @@
use crate::{Task, TaskProg};
// --- Paste
#[derive(Debug)]
pub enum FileOut {
Paste(FileOutPaste),
Link(FileOutLink),
Hardlink(FileOutHardlink),
Delete(FileOutDelete),
Trash(FileOutTrash),
pub(crate) enum FileOutPaste {
New(u64),
Init,
Deform(String),
}
#[derive(Debug)]
pub struct FileOutPaste;
impl From<std::io::Error> for FileOutPaste {
fn from(value: std::io::Error) -> Self { Self::Deform(value.to_string()) }
}
#[derive(Debug)]
pub struct FileOutLink;
impl FileOutPaste {
pub(crate) fn reduce(self, task: &mut Task) {
let TaskProg::FilePaste(prog) = &mut task.prog else { return };
match self {
Self::New(bytes) => {
prog.total_files += 1;
prog.total_bytes += bytes;
}
Self::Init => {
prog.collected = true;
}
Self::Deform(reason) => {
prog.total_files += 1;
prog.failed_files += 1;
task.log(reason);
}
}
}
}
// --- PasteDo
#[derive(Debug)]
pub struct FileOutHardlink;
pub(crate) enum FileOutPasteDo {
Adv(u64),
Log(String),
Succ,
Fail(String),
}
#[derive(Debug)]
pub struct FileOutDelete;
impl From<std::io::Error> for FileOutPasteDo {
fn from(value: std::io::Error) -> Self { Self::Fail(value.to_string()) }
}
impl FileOutPasteDo {
pub(crate) fn reduce(self, task: &mut Task) {
let TaskProg::FilePaste(prog) = &mut task.prog else { return };
match self {
Self::Adv(size) => {
prog.processed_bytes += size;
}
Self::Log(line) => {
task.log(line);
}
Self::Succ => {
prog.success_files += 1;
}
Self::Fail(reason) => {
prog.failed_files += 1;
task.log(reason);
}
}
}
}
// --- Link
#[derive(Debug)]
pub struct FileOutTrash;
pub(crate) enum FileOutLink {
Succ,
Fail(String),
}
impl From<anyhow::Error> for FileOutLink {
fn from(value: anyhow::Error) -> Self { Self::Fail(value.to_string()) }
}
impl From<std::io::Error> for FileOutLink {
fn from(value: std::io::Error) -> Self { Self::Fail(value.to_string()) }
}
impl FileOutLink {
pub(crate) fn reduce(self, task: &mut Task) {
if let TaskProg::FileLink(prog) = &mut task.prog {
match self {
Self::Succ => {
prog.state = Some(true);
}
Self::Fail(reason) => {
prog.state = Some(false);
task.log(reason);
}
}
} else if let TaskProg::FilePaste(prog) = &mut task.prog {
match self {
Self::Succ => {
prog.success_files += 1;
}
Self::Fail(reason) => {
prog.failed_files += 1;
task.log(reason);
}
}
}
}
}
// --- Hardlink
#[derive(Debug)]
pub(crate) enum FileOutHardlink {
New,
Init,
Deform(String),
}
impl From<std::io::Error> for FileOutHardlink {
fn from(value: std::io::Error) -> Self { Self::Deform(value.to_string()) }
}
impl FileOutHardlink {
pub(crate) fn reduce(self, task: &mut Task) {
let TaskProg::FileHardlink(prog) = &mut task.prog else { return };
match self {
Self::New => {
prog.total += 1;
}
Self::Init => {
prog.collected = true;
}
Self::Deform(reason) => {
prog.total += 1;
prog.failed += 1;
task.log(reason);
}
}
}
}
// --- HardlinkDo
#[derive(Debug)]
pub(crate) enum FileOutHardlinkDo {
Succ,
Fail(String),
}
impl From<std::io::Error> for FileOutHardlinkDo {
fn from(value: std::io::Error) -> Self { Self::Fail(value.to_string()) }
}
impl FileOutHardlinkDo {
pub(crate) fn reduce(self, task: &mut Task) {
let TaskProg::FileHardlink(prog) = &mut task.prog else { return };
match self {
Self::Succ => {
prog.success += 1;
}
Self::Fail(reason) => {
prog.failed += 1;
task.log(reason);
}
}
}
}
// --- Delete
#[derive(Debug)]
pub(crate) enum FileOutDelete {
New(u64),
Init,
Deform(String),
}
impl From<std::io::Error> for FileOutDelete {
fn from(value: std::io::Error) -> Self { Self::Deform(value.to_string()) }
}
impl FileOutDelete {
pub(crate) fn reduce(self, task: &mut Task) {
let TaskProg::FileDelete(prog) = &mut task.prog else { return };
match self {
Self::New(size) => {
prog.total_files += 1;
prog.total_bytes += size;
}
Self::Init => {
prog.collected = true;
}
Self::Deform(reason) => {
prog.total_files += 1;
prog.failed_files += 1;
task.log(reason);
}
}
}
}
// --- DeleteDo
#[derive(Debug)]
pub(crate) enum FileOutDeleteDo {
Succ(u64),
Fail(String),
}
impl From<std::io::Error> for FileOutDeleteDo {
fn from(value: std::io::Error) -> Self { Self::Fail(value.to_string()) }
}
impl FileOutDeleteDo {
pub(crate) fn reduce(self, task: &mut Task) {
let TaskProg::FileDelete(prog) = &mut task.prog else { return };
match self {
Self::Succ(size) => {
prog.success_files += 1;
prog.processed_bytes += size;
}
Self::Fail(reason) => {
prog.failed_files += 1;
task.log(reason);
}
}
}
}
// --- Trash
#[derive(Debug)]
pub(crate) enum FileOutTrash {
Succ,
Fail(String),
}
impl From<std::io::Error> for FileOutTrash {
fn from(value: std::io::Error) -> Self { Self::Fail(value.to_string()) }
}
impl FileOutTrash {
pub(crate) fn reduce(self, task: &mut Task) {
let TaskProg::FileTrash(prog) = &mut task.prog else { return };
match self {
Self::Succ => {
prog.state = Some(true);
}
Self::Fail(reason) => {
prog.state = Some(false);
task.log(reason);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show more