diff --git a/Cargo.lock b/Cargo.lock index 5da44b6d..f736b4ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,9 +72,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" [[package]] name = "bytemuck" @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", @@ -312,9 +312,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299353be8209bd133b049bf1c63582d184a8b39fd9c04f15fe65f50f88bdfe6c" +checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c" dependencies = [ "clap", ] @@ -331,9 +331,9 @@ dependencies = [ [[package]] name = "clap_complete_nushell" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92cd8ac7c321605310e4b3bdc047daf27c6ca1b50f6ae59b22aa0661ab45e295" +checksum = "80d0e48e026ce7df2040239117d25e4e79714907420c70294a5ce4b6bbe6a7b6" dependencies = [ "clap", "clap_complete", @@ -348,7 +348,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -749,7 +749,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1221,7 +1221,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1295,9 +1295,9 @@ dependencies = [ [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] @@ -1424,9 +1424,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plist" @@ -1444,9 +1444,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.11" +version = "0.17.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a" +checksum = "78c2378060fb13acff3ba0325b83442c1d2c44fbb76df481160ddc1687cce160" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -1695,7 +1695,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1868,7 +1868,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -1884,9 +1884,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" dependencies = [ "proc-macro2", "quote", @@ -1931,7 +1931,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2050,7 +2050,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2101,9 +2101,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.5" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e68c159e8f5ba8a28c4eb7b0c0c190d77bb479047ca713270048145a9ad28a" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" dependencies = [ "indexmap", "serde", @@ -2143,7 +2143,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] @@ -2366,7 +2366,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", "wasm-bindgen-shared", ] @@ -2388,7 +2388,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2658,9 +2658,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1dbce9e90e5404c5a52ed82b1d13fc8cfbdad85033b6f57546ffd1265f8451" +checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401" dependencies = [ "memchr", ] @@ -2857,7 +2857,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.49", ] [[package]] diff --git a/yazi-config/src/pattern.rs b/yazi-config/src/pattern.rs index 3a428dbf..652cafee 100644 --- a/yazi-config/src/pattern.rs +++ b/yazi-config/src/pattern.rs @@ -37,7 +37,10 @@ impl Pattern { } #[inline] - pub fn is_wildcard(&self) -> bool { self.inner.as_str() == "*" || self.inner.as_str() == "*/" } + pub fn any_file(&self) -> bool { self.inner.as_str() == "*" } + + #[inline] + pub fn any_dir(&self) -> bool { self.inner.as_str() == "*/" } } impl TryFrom<&str> for Pattern { diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 91ae8d73..00d93d00 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -11,24 +11,6 @@ pub struct Plugin { pub previewers: Vec, } -#[derive(Deserialize)] -pub struct PluginRule { - #[serde(default)] - pub id: u8, - pub cond: Option, - pub name: Option, - pub mime: Option, - #[serde(rename = "exec")] - #[serde(deserialize_with = "super::exec_deserialize")] - pub cmd: Cmd, - #[serde(default)] - pub sync: bool, - #[serde(default)] - pub multi: bool, - #[serde(default)] - pub prio: Priority, -} - impl Default for Plugin { fn default() -> Self { #[derive(Deserialize)] @@ -52,11 +34,17 @@ impl Default for Plugin { } let mut shadow = toml::from_str::(&MERGED_YAZI).unwrap().plugin; - if shadow.append_preloaders.iter().any(|r| r.name.as_ref().is_some_and(|p| p.is_wildcard())) { - shadow.preloaders.retain(|r| !r.name.as_ref().is_some_and(|p| p.is_wildcard())); + if shadow.append_preloaders.iter().any(|r| r.any_file()) { + shadow.preloaders.retain(|r| !r.any_file()); } - if shadow.append_previewers.iter().any(|r| r.name.as_ref().is_some_and(|p| p.is_wildcard())) { - shadow.previewers.retain(|r| !r.name.as_ref().is_some_and(|p| p.is_wildcard())); + if shadow.append_preloaders.iter().any(|r| r.any_dir()) { + shadow.preloaders.retain(|r| !r.any_dir()); + } + if shadow.append_previewers.iter().any(|r| r.any_file()) { + shadow.previewers.retain(|r| !r.any_file()); + } + if shadow.append_previewers.iter().any(|r| r.any_dir()) { + shadow.previewers.retain(|r| !r.any_dir()); } Preset::mix(&mut shadow.preloaders, shadow.prepend_preloaders, shadow.append_preloaders); @@ -104,3 +92,29 @@ impl Plugin { }) } } + +#[derive(Deserialize)] +pub struct PluginRule { + #[serde(default)] + pub id: u8, + pub cond: Option, + pub name: Option, + pub mime: Option, + #[serde(rename = "exec")] + #[serde(deserialize_with = "super::exec_deserialize")] + pub cmd: Cmd, + #[serde(default)] + pub sync: bool, + #[serde(default)] + pub multi: bool, + #[serde(default)] + pub prio: Priority, +} + +impl PluginRule { + #[inline] + fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) } + + #[inline] + fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) } +} diff --git a/yazi-config/src/theme/icon.rs b/yazi-config/src/theme/icon.rs index 3b2bd3f2..c0f78430 100644 --- a/yazi-config/src/theme/icon.rs +++ b/yazi-config/src/theme/icon.rs @@ -31,6 +31,13 @@ impl Icon { } let mut outer = IconOuter::deserialize(deserializer)?; + if outer.append_rules.iter().any(|r| r.name.any_file()) { + outer.rules.retain(|r| !r.name.any_file()); + } + if outer.append_rules.iter().any(|r| r.name.any_dir()) { + outer.rules.retain(|r| !r.name.any_dir()); + } + Preset::mix(&mut outer.rules, outer.prepend_rules, outer.append_rules); Ok( diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index 9e697774..f7602e36 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -71,12 +71,12 @@ impl Manager { error!("preload in watcher failed: {e}"); } - Self::_open_do(opt.interactive, done); + Self::_open_do(done, opt.interactive); }); } #[inline] - pub fn _open_do(interactive: bool, targets: Vec<(Url, String)>) { + pub fn _open_do(targets: Vec<(Url, String)>, interactive: bool) { emit!(Call( Cmd::new("open_do").with_bool("interactive", interactive).with_data(targets), Layer::Manager diff --git a/yazi-core/src/manager/commands/remove.rs b/yazi-core/src/manager/commands/remove.rs index 9352b996..00703383 100644 --- a/yazi-core/src/manager/commands/remove.rs +++ b/yazi-core/src/manager/commands/remove.rs @@ -1,17 +1,20 @@ -use yazi_shared::event::Cmd; +use yazi_config::popup::InputCfg; +use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; -use crate::{manager::Manager, tasks::Tasks}; +use crate::{input::Input, manager::Manager, tasks::Tasks}; pub struct Opt { force: bool, permanently: bool, + targets: Vec, } impl From for Opt { - fn from(c: Cmd) -> Self { + fn from(mut c: Cmd) -> Self { Self { force: c.named.contains_key("force"), permanently: c.named.contains_key("permanently"), + targets: c.take_data().unwrap_or_default(), } } } @@ -22,8 +25,44 @@ impl Manager { return; } + let mut opt = opt.into() as Opt; + opt.targets = self.selected_or_hovered().into_iter().cloned().collect(); + + if opt.force { + return self.remove_do(opt, tasks); + } + + tokio::spawn(async move { + let mut result = Input::_show(if opt.permanently { + InputCfg::delete(opt.targets.len()) + } else { + InputCfg::trash(opt.targets.len()) + }); + + if let Some(Ok(choice)) = result.recv().await { + if choice != "y" && choice != "Y" { + return; + } + + Self::_remove_do(opt.targets, opt.permanently); + } + }); + } + + #[inline] + pub fn _remove_do(targets: Vec, permanently: bool) { + emit!(Call( + Cmd::new("remove_do").with_bool("permanently", permanently).with_data(targets), + Layer::Manager + )); + } + + pub fn remove_do(&mut self, opt: impl Into, tasks: &Tasks) { let opt = opt.into() as Opt; - let targets = self.selected_or_hovered().into_iter().cloned().collect(); - tasks.file_remove(targets, opt.force, opt.permanently); + for u in &opt.targets { + self.active_mut().selected.remove(u); + } + + tasks.file_remove(opt.targets, opt.permanently); } } diff --git a/yazi-core/src/notify/commands/push.rs b/yazi-core/src/notify/commands/push.rs index f3f2572b..778241ea 100644 --- a/yazi-core/src/notify/commands/push.rs +++ b/yazi-core/src/notify/commands/push.rs @@ -5,6 +5,18 @@ use yazi_shared::{emit, event::Cmd, Layer}; use crate::notify::{Message, Notify}; impl Notify { + #[inline] + pub fn _push_warn(title: &str, content: &str) { + emit!(Call( + Cmd::new("notify") + .with("title", title) + .with("content", content) + .with("level", "warn") + .with("timeout", 5), + Layer::App + )); + } + pub fn push(&mut self, msg: impl TryInto) { let Ok(mut msg) = msg.try_into() else { return; diff --git a/yazi-core/src/notify/commands/tick.rs b/yazi-core/src/notify/commands/tick.rs index 7b78d814..ba8536cf 100644 --- a/yazi-core/src/notify/commands/tick.rs +++ b/yazi-core/src/notify/commands/tick.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use ratatui::layout::Rect; use yazi_shared::{emit, event::Cmd, Layer}; use crate::notify::Notify; @@ -22,13 +23,13 @@ impl TryFrom for Opt { } impl Notify { - pub fn tick(&mut self, opt: impl TryInto) { + pub fn tick(&mut self, opt: impl TryInto, area: Rect) { self.tick_handle.take().map(|h| h.abort()); let Ok(opt) = opt.try_into() else { return; }; - let limit = self.limit(); + let limit = self.limit(area); if limit == 0 { return; } @@ -37,14 +38,14 @@ impl Notify { if m.timeout.is_zero() { m.percent = m.percent.saturating_sub(20); } else if m.percent < 100 { - m.percent = m.percent.saturating_add(20); + m.percent += 20; } else { m.timeout = m.timeout.saturating_sub(opt.interval); } } self.messages.retain(|m| m.percent > 0 || !m.timeout.is_zero()); - let limit = self.limit(); + let limit = self.limit(area); let timeouts: Vec<_> = self.messages[..limit] .iter() .filter(|&m| m.percent == 100 && !m.timeout.is_zero()) diff --git a/yazi-core/src/notify/message.rs b/yazi-core/src/notify/message.rs index 13bb39d2..0f1d0f3e 100644 --- a/yazi-core/src/notify/message.rs +++ b/yazi-core/src/notify/message.rs @@ -1,5 +1,6 @@ use std::time::{Duration, Instant}; +use unicode_width::UnicodeWidthStr; use yazi_shared::event::Cmd; use super::{Level, NOTIFY_BORDER}; @@ -12,7 +13,6 @@ pub struct Message { pub instant: Instant, pub timeout: Duration, - pub lines: usize, pub percent: u8, } @@ -26,7 +26,6 @@ impl TryFrom for Message { } let content = c.take_name("content").ok_or(())?; - let lines = content.lines().count(); Ok(Self { title: c.take_name("title").ok_or(())?, content, @@ -35,7 +34,6 @@ impl TryFrom for Message { instant: Instant::now(), timeout: Duration::from_secs_f64(timeout), - lines, percent: 0, }) } @@ -43,5 +41,8 @@ impl TryFrom for Message { impl Message { #[inline] - pub fn height(&self) -> usize { self.lines + NOTIFY_BORDER as usize } + pub fn height(&self, width: u16) -> usize { + let lines = (self.content.width() as f64 / width as f64).ceil(); + lines as usize + NOTIFY_BORDER as usize + } } diff --git a/yazi-core/src/notify/notify.rs b/yazi-core/src/notify/notify.rs index a0adea4c..27b7a227 100644 --- a/yazi-core/src/notify/notify.rs +++ b/yazi-core/src/notify/notify.rs @@ -1,7 +1,7 @@ use std::ops::ControlFlow; +use ratatui::layout::Rect; use tokio::task::JoinHandle; -use yazi_shared::term::Term; use super::{Message, NOTIFY_SPACING}; @@ -12,14 +12,14 @@ pub struct Notify { } impl Notify { - pub fn limit(&self) -> usize { + pub fn limit(&self, area: Rect) -> usize { if self.messages.is_empty() { return 0; } - let mut height = Term::size().height as usize; + let mut height = area.height as usize; let flow = (0..self.messages.len().min(3)).try_fold(0, |acc, i| { - match height.checked_sub(self.messages[i].height() + NOTIFY_SPACING as usize) { + match height.checked_sub(self.messages[i].height(area.width) + NOTIFY_SPACING as usize) { Some(h) => { height = h; ControlFlow::Continue(acc + 1) diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index fb7d139d..ac7b4361 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -1,7 +1,7 @@ use bitflags::bitflags; use yazi_shared::{event::Cmd, render, render_and}; -use crate::{manager::Manager, tab::{Mode, Tab}}; +use crate::{manager::Manager, notify::Notify, tab::Tab}; bitflags! { pub struct Opt: u8 { @@ -56,29 +56,17 @@ impl Tab { } } - #[inline] pub fn escape_find(&mut self) -> bool { render_and!(self.finder.take().is_some()) } - #[inline] pub fn escape_visual(&mut self) -> bool { - let Some((_, indices)) = self.mode.visual() else { + if !self.mode.is_visual() { return false; - }; - - let state = self.mode.is_select(); - for f in indices.iter().filter_map(|i| self.current.files.get(*i)) { - if state { - self.selected.add(&f.url); - } else { - self.selected.remove(&f.url); - } } - self.mode = Mode::Normal; - render_and!(true) + self.try_escape_visual(); + true } - #[inline] pub fn escape_select(&mut self) -> bool { if self.selected.is_empty() { return false; @@ -91,7 +79,6 @@ impl Tab { render_and!(true) } - #[inline] pub fn escape_filter(&mut self) -> bool { if self.current.files.filter().is_none() { return false; @@ -101,7 +88,6 @@ impl Tab { render_and!(true) } - #[inline] pub fn escape_search(&mut self) -> bool { if !self.current.cwd.is_search() { return false; @@ -111,9 +97,29 @@ impl Tab { render_and!(true) } - #[inline] pub fn try_escape_visual(&mut self) -> bool { - self.escape_visual(); - true + let state = self.mode.is_select(); + let Some((_, indices)) = self.mode.take_visual() else { + return true; + }; + + let mut success = true; + for f in indices.iter().filter_map(|i| self.current.files.get(*i)) { + if state { + success &= self.selected.add(&f.url); + } else { + self.selected.remove(&f.url); + } + } + + if !success { + Notify::_push_warn( + "Escape visual mode", + "Some files cannot be selected, due to path nesting conflict.", + ); + } + + render!(); + success } } diff --git a/yazi-core/src/tab/mode.rs b/yazi-core/src/tab/mode.rs index af882893..389a5daa 100644 --- a/yazi-core/src/tab/mode.rs +++ b/yazi-core/src/tab/mode.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::{collections::BTreeSet, mem}; #[derive(Clone, Debug, Default, Eq, PartialEq)] pub enum Mode { @@ -9,8 +9,7 @@ pub enum Mode { } impl Mode { - #[inline] - pub fn visual(&self) -> Option<(usize, &BTreeSet)> { + pub fn visual_mut(&mut self) -> Option<(usize, &mut BTreeSet)> { match self { Mode::Normal => None, Mode::Select(start, indices) => Some((*start, indices)), @@ -18,12 +17,11 @@ impl Mode { } } - #[inline] - pub fn visual_mut(&mut self) -> Option<(usize, &mut BTreeSet)> { - match self { + pub fn take_visual(&mut self) -> Option<(usize, BTreeSet)> { + match mem::take(self) { Mode::Normal => None, - Mode::Select(start, indices) => Some((*start, indices)), - Mode::Unset(start, indices) => Some((*start, indices)), + Mode::Select(start, indices) => Some((start, indices)), + Mode::Unset(start, indices) => Some((start, indices)), } } } diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index eef9ab47..d354ed0b 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -5,7 +5,7 @@ use tokio::task::JoinHandle; use yazi_shared::{fs::Url, render}; use super::{Backstack, Config, Finder, Mode, Preview}; -use crate::{folder::{Folder, FolderStage}, tab::selected::Selected}; +use crate::{folder::{Folder, FolderStage}, tab::Selected}; pub struct Tab { pub mode: Mode, diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 7ae426d3..e5c72dee 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -2,13 +2,13 @@ use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, mem, path::Path use tokio::time::sleep; use tracing::debug; -use yazi_config::{manager::SortBy, open::Opener, plugin::{PluginRule, MAX_PRELOADERS}, popup::InputCfg, OPEN, PLUGIN}; +use yazi_config::{manager::SortBy, open::Opener, plugin::{PluginRule, MAX_PRELOADERS}, OPEN, PLUGIN}; use yazi_plugin::ValueSendable; use yazi_scheduler::{Scheduler, TaskSummary}; use yazi_shared::{emit, event::Cmd, fs::{File, Url}, term::Term, Layer, MIME_DIR}; use super::{TasksProgress, TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT}; -use crate::{folder::Files, input::Input}; +use crate::folder::Files; pub struct Tasks { pub(super) scheduler: Arc, @@ -113,39 +113,14 @@ impl Tasks { } } - pub fn file_remove(&self, targets: Vec, force: bool, permanently: bool) { - if force { - for u in targets { - if permanently { - self.scheduler.file_delete(u); - } else { - self.scheduler.file_trash(u); - } - } - return; - } - - let scheduler = self.scheduler.clone(); - tokio::spawn(async move { - let mut result = Input::_show(if permanently { - InputCfg::delete(targets.len()) + pub fn file_remove(&self, targets: Vec, permanently: bool) { + for u in targets { + if permanently { + self.scheduler.file_delete(u); } else { - InputCfg::trash(targets.len()) - }); - - if let Some(Ok(choice)) = result.recv().await { - if choice != "y" && choice != "Y" { - return; - } - for u in targets { - if permanently { - scheduler.file_delete(u); - } else { - scheduler.file_trash(u); - } - } + self.scheduler.file_trash(u); } - }); + } } #[inline] diff --git a/yazi-fm/src/app/commands/render.rs b/yazi-fm/src/app/commands/render.rs index c77e1242..e9106f4c 100644 --- a/yazi-fm/src/app/commands/render.rs +++ b/yazi-fm/src/app/commands/render.rs @@ -2,7 +2,7 @@ use std::sync::atomic::Ordering; use ratatui::{backend::{Backend, CrosstermBackend}, CompletedFrame}; -use crate::{app::App, lives::Lives, notify::Notify, root::{Root, COLLISION}}; +use crate::{app::App, lives::Lives, root::{Root, COLLISION}}; impl App { pub(crate) fn render(&mut self) { @@ -45,7 +45,7 @@ impl App { let frame = term .draw_partial(|f| { - f.render_widget(Notify::new(&self.cx), f.size()); + f.render_widget(crate::notify::Layout::new(&self.cx), f.size()); if let Some((x, y)) = self.cx.cursor() { f.set_cursor(x, y); diff --git a/yazi-fm/src/app/commands/update_notify.rs b/yazi-fm/src/app/commands/update_notify.rs index a9bb76a6..a408e1f3 100644 --- a/yazi-fm/src/app/commands/update_notify.rs +++ b/yazi-fm/src/app/commands/update_notify.rs @@ -1,10 +1,15 @@ -use yazi_shared::event::Cmd; +use crossterm::terminal::WindowSize; +use ratatui::layout::Rect; +use yazi_shared::{event::Cmd, term::Term}; use crate::app::App; impl App { pub(crate) fn update_notify(&mut self, cmd: Cmd) { - self.cx.notify.tick(cmd); + let WindowSize { width, height, .. } = Term::size(); + let area = crate::notify::Layout::available(Rect { x: 0, y: 0, width, height }); + + self.cx.notify.tick(cmd, area); if self.cx.notify.messages.is_empty() { self.render(); diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 1fb13d07..127d8123 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -98,6 +98,7 @@ impl<'a> Executor<'a> { on!(MANAGER, paste, &self.app.cx.tasks); on!(MANAGER, link, &self.app.cx.tasks); on!(MANAGER, remove, &self.app.cx.tasks); + on!(MANAGER, remove_do, &self.app.cx.tasks); on!(MANAGER, create); on!(MANAGER, rename); on!(ACTIVE, copy); diff --git a/yazi-fm/src/notify/layout.rs b/yazi-fm/src/notify/layout.rs new file mode 100644 index 00000000..b2b8acd9 --- /dev/null +++ b/yazi-fm/src/notify/layout.rs @@ -0,0 +1,67 @@ +use std::rc::Rc; + +use ratatui::{buffer::Buffer, layout::{self, Constraint, Offset, Rect}, style::{Style, Stylize}, widgets::{Block, BorderType, Paragraph, Widget, Wrap}}; +use yazi_core::notify::{Level, Message}; + +use crate::{widgets::Clear, Ctx}; + +pub(crate) struct Layout<'a> { + cx: &'a Ctx, +} + +impl<'a> Layout<'a> { + pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } + + pub(crate) fn available(area: Rect) -> Rect { + let chunks = + layout::Layout::horizontal([Constraint::Fill(1), Constraint::Length(40), Constraint::Max(1)]) + .split(area); + + let chunks = + layout::Layout::vertical([Constraint::Max(1), Constraint::Min(1)]).split(chunks[1]); + + chunks[1] + } + + fn tile(area: Rect, messages: &[Message]) -> Rc<[Rect]> { + layout::Layout::vertical( + messages.iter().map(|m| Constraint::Length(m.height(area.width) as u16)), + ) + .spacing(1) + .split(area) + } +} + +impl<'a> Widget for Layout<'a> { + fn render(self, area: Rect, buf: &mut Buffer) { + let notify = &self.cx.notify; + + let available = Self::available(area); + let limit = notify.limit(available); + let tile = Self::tile(available, ¬ify.messages[..limit]); + + for (i, m) in notify.messages.iter().enumerate().take(limit) { + let (icon, style) = match m.level { + Level::Info => ("", Style::default().green()), + Level::Warn => ("", Style::default().yellow()), + Level::Error => ("", Style::default().red()), + }; + + let mut rect = + tile[i].offset(Offset { x: (100 - m.percent) as i32 * tile[i].width as i32 / 100, y: 0 }); + rect.width = area.width.saturating_sub(rect.x); + + Clear.render(rect, buf); + Paragraph::new(m.content.as_str()) + .wrap(Wrap { trim: false }) + .block( + Block::bordered() + .border_type(BorderType::Rounded) + .title(format!("{} {}", icon, m.title)) + .title_style(style) + .border_style(style), + ) + .render(rect, buf); + } + } +} diff --git a/yazi-fm/src/notify/mod.rs b/yazi-fm/src/notify/mod.rs index 2e5703d1..03520470 100644 --- a/yazi-fm/src/notify/mod.rs +++ b/yazi-fm/src/notify/mod.rs @@ -1,3 +1,3 @@ -mod notify; +mod layout; -pub(super) use notify::*; +pub(super) use layout::*; diff --git a/yazi-fm/src/notify/notify.rs b/yazi-fm/src/notify/notify.rs deleted file mode 100644 index 97e040f5..00000000 --- a/yazi-fm/src/notify/notify.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::rc::Rc; - -use ratatui::{buffer::Buffer, layout::{Constraint, Layout, Offset, Rect}, style::{Style, Stylize}, widgets::{Block, BorderType, Paragraph, Widget}}; -use yazi_core::notify::{Level, Message}; - -use crate::{widgets::Clear, Ctx}; - -pub(crate) struct Notify<'a> { - cx: &'a Ctx, -} - -impl<'a> Notify<'a> { - pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } - - fn chunks(area: Rect, messages: &[Message]) -> Rc<[Rect]> { - let chunks = - Layout::horizontal([Constraint::Fill(1), Constraint::Length(40), Constraint::Max(1)]) - .split(area); - - let chunks = Layout::vertical([Constraint::Max(1), Constraint::Min(1)]).split(chunks[1]); - - Layout::vertical(messages.iter().map(|m| Constraint::Length(m.height() as u16))) - .spacing(1) - .split(chunks[1]) - } -} - -impl<'a> Widget for Notify<'a> { - fn render(self, area: Rect, buf: &mut Buffer) { - let notify = &self.cx.notify; - - let limit = notify.limit(); - let chunks = Self::chunks(area, ¬ify.messages[..limit]); - - for (i, m) in notify.messages.iter().enumerate().take(limit) { - let (icon, style) = match m.level { - Level::Info => ("", Style::default().green()), - Level::Warn => ("", Style::default().yellow()), - Level::Error => ("", Style::default().red()), - }; - - let mut rect = chunks[i] - .offset(Offset { x: (100 - m.percent) as i32 * chunks[i].width as i32 / 100, y: 0 }); - rect.width = area.width.saturating_sub(rect.x); - - Clear.render(rect, buf); - Paragraph::new(m.content.as_str()) - .block( - Block::bordered() - .border_type(BorderType::Rounded) - .title(format!("{} {}", icon, m.title)) - .title_style(style) - .border_style(style), - ) - .render(rect, buf); - } - } -} diff --git a/yazi-plugin/src/bindings/mod.rs b/yazi-plugin/src/bindings/mod.rs index 0ff28e9e..cba217e6 100644 --- a/yazi-plugin/src/bindings/mod.rs +++ b/yazi-plugin/src/bindings/mod.rs @@ -7,14 +7,9 @@ mod icon; mod range; mod window; -#[allow(unused_imports)] pub use bindings::*; pub use cha::*; pub use file::*; pub use icon::*; pub use range::*; pub use window::*; - -pub trait Cast { - fn cast(lua: &mlua::Lua, data: T) -> mlua::Result; -}