diff --git a/Cargo.lock b/Cargo.lock index db707bc1..c40205f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -313,9 +313,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cassowary" @@ -334,9 +334,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.10" +version = "1.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" dependencies = [ "jobserver", "libc", @@ -373,9 +373,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.27" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" dependencies = [ "clap_builder", "clap_derive", @@ -424,9 +424,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", @@ -2458,9 +2458,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2718,9 +2718,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" dependencies = [ "indexmap", "serde", @@ -3294,9 +3294,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.25" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" +checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 64ff965d..02003602 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ ansi-to-tui = "7.0.0" anyhow = "1.0.95" base64 = "0.22.1" bitflags = "2.8.0" -clap = { version = "4.5.27", features = [ "derive" ] } +clap = { version = "4.5.28", features = [ "derive" ] } core-foundation-sys = "0.8.7" crossterm = { version = "0.28.1", features = [ "event-stream" ] } dirs = "6.0.0" diff --git a/yazi-boot/src/actions/actions.rs b/yazi-boot/src/actions/actions.rs index 191b2f55..d4d120fa 100644 --- a/yazi-boot/src/actions/actions.rs +++ b/yazi-boot/src/actions/actions.rs @@ -1,6 +1,6 @@ use std::process; -pub(crate) struct Actions; +pub struct Actions; impl Actions { pub(crate) fn act(args: &crate::Args) { diff --git a/yazi-boot/src/actions/version.rs b/yazi-boot/src/actions/version.rs index 4af8a3b7..84906dac 100644 --- a/yazi-boot/src/actions/version.rs +++ b/yazi-boot/src/actions/version.rs @@ -1,7 +1,7 @@ use super::Actions; impl Actions { - pub(super) fn version() -> &'static str { + pub fn version() -> &'static str { concat!( env!("CARGO_PKG_VERSION"), " (", diff --git a/yazi-codegen/Cargo.toml b/yazi-codegen/Cargo.toml index 7884d0fb..25404b9d 100644 --- a/yazi-codegen/Cargo.toml +++ b/yazi-codegen/Cargo.toml @@ -13,5 +13,5 @@ proc-macro = true [dependencies] # External dependencies -syn = { version = "2.0.96", features = [ "full" ] } +syn = { version = "2.0.98", features = [ "full" ] } quote = "1.0.38" diff --git a/yazi-core/src/manager/commands/quit.rs b/yazi-core/src/manager/commands/quit.rs index dda95d90..caf3811b 100644 --- a/yazi-core/src/manager/commands/quit.rs +++ b/yazi-core/src/manager/commands/quit.rs @@ -40,7 +40,7 @@ impl Manager { _ = time::sleep(Duration::from_millis(50)) => { i += 1; if i > 40 { break } - else if ongoing.lock().len() == 0 { + else if ongoing.lock().is_empty() { emit!(Quit(opt)); return; } diff --git a/yazi-core/src/notify/commands/push.rs b/yazi-core/src/notify/commands/push.rs index 3befaed5..b217b20f 100644 --- a/yazi-core/src/notify/commands/push.rs +++ b/yazi-core/src/notify/commands/push.rs @@ -11,8 +11,10 @@ impl Notify { let instant = Instant::now(); msg.timeout += instant - self.messages.first().map_or(instant, |m| m.instant); - self.messages.push(msg); - emit!(Call(Cmd::args("update_notify", &[0]), Layer::App)); + if self.messages.iter().all(|m| m != &msg) { + self.messages.push(msg); + emit!(Call(Cmd::args("update_notify", &[0]), Layer::App)); + } } } diff --git a/yazi-core/src/notify/message.rs b/yazi-core/src/notify/message.rs index 6e611ca6..1c4dae62 100644 --- a/yazi-core/src/notify/message.rs +++ b/yazi-core/src/notify/message.rs @@ -39,15 +39,16 @@ impl From for Message { impl Message { #[inline] pub fn height(&self, width: u16) -> usize { - if width == 0 { - return 0; // In case we can't get the width of the terminal - } - - let mut lines = 0; - for line in self.content.lines() { - lines += (line.width() + 1).div_ceil(width as usize) - } + let lines = ratatui::widgets::Paragraph::new(self.content.as_str()) + .wrap(ratatui::widgets::Wrap { trim: false }) + .line_count(width); lines + NOTIFY_BORDER as usize } } + +impl PartialEq for Message { + fn eq(&self, other: &Self) -> bool { + self.level == other.level && self.content == other.content && self.title == other.title + } +} diff --git a/yazi-fm/src/app/commands/plugin.rs b/yazi-fm/src/app/commands/plugin.rs index 812ade65..dfbee9cc 100644 --- a/yazi-fm/src/app/commands/plugin.rs +++ b/yazi-fm/src/app/commands/plugin.rs @@ -4,7 +4,7 @@ use mlua::ObjectLike; use scopeguard::defer; use tracing::{error, warn}; use yazi_dds::Sendable; -use yazi_plugin::{LUA, RtRefMut, loader::LOADER}; +use yazi_plugin::{LUA, RtRefMut, loader::{LOADER, Loader}}; use yazi_proxy::{AppProxy, options::{PluginMode, PluginOpt}}; use crate::{app::App, lives::Lives}; @@ -29,8 +29,9 @@ impl App { } tokio::spawn(async move { - if LOADER.ensure(&opt.id).await.is_ok() { - AppProxy::plugin_do(opt); + match LOADER.ensure(&opt.id).await { + Ok(()) => AppProxy::plugin_do(opt), + Err(e) => AppProxy::notify_error("Plugin load failed", e), } }); } @@ -46,6 +47,10 @@ impl App { return warn!("plugin `{}` not found", opt.id); }; + if let Err(e) = Loader::compatible_or_error(&opt.id, chunk) { + return AppProxy::notify_error("Incompatible plugin", e); + } + if opt.mode.auto_then(chunk.sync_entry) != PluginMode::Sync { return self.cx.tasks.plugin_micro(opt); } @@ -57,7 +62,7 @@ impl App { defer! { _ = LUA.named_registry_value::("rt").map(|mut r| r.pop()) } let plugin = match LOADER.load_with(&LUA, &opt.id, chunk) { - Ok(plugin) => plugin, + Ok(t) => t, Err(e) => return warn!("{e}"), }; drop(loader); diff --git a/yazi-fm/src/term.rs b/yazi-fm/src/term.rs index 16d3f419..8c808415 100644 --- a/yazi-fm/src/term.rs +++ b/yazi-fm/src/term.rs @@ -240,7 +240,7 @@ mod cursor { fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result { let (shape, shape_blink) = match SHAPE.load(Ordering::Relaxed) { u8::MAX => (0, false), - n => ((n.max(1) + 1) / 2, n.max(1) & 1 == 1), + n => (n.max(1).div_ceil(2), n.max(1) & 1 == 1), }; let blink = BLINK.load(Ordering::Relaxed) ^ shape_blink; diff --git a/yazi-plugin/src/loader/chunk.rs b/yazi-plugin/src/loader/chunk.rs index 765b876b..c53c0705 100644 --- a/yazi-plugin/src/loader/chunk.rs +++ b/yazi-plugin/src/loader/chunk.rs @@ -1,7 +1,10 @@ use std::borrow::Cow; +use yazi_shared::natsort; + pub struct Chunk { pub bytes: Cow<'static, [u8]>, + pub since: String, pub sync_entry: bool, } @@ -9,6 +12,12 @@ impl Chunk { #[inline] pub fn as_bytes(&self) -> &[u8] { &self.bytes } + #[inline] + pub fn compatible(&self) -> bool { + let s = yazi_boot::actions::Actions::version(); + natsort(s.as_bytes(), self.since.as_bytes(), false) != std::cmp::Ordering::Less + } + fn analyze(&mut self) { for line in self.bytes.split(|&b| b == b'\n') { if line.trim_ascii().is_empty() { @@ -21,6 +30,10 @@ impl Chunk { let Some(i) = rest.iter().position(|&b| b == b' ' || b == b'\t') else { break }; match (rest[..i].trim_ascii(), rest[i..].trim_ascii()) { (b"@sync", b"entry") => self.sync_entry = true, + + (b"@since", b"") => continue, + (b"@since", b) => self.since = String::from_utf8_lossy(b).to_string(), + (_, []) => break, (b, _) if b.strip_prefix(b"@").unwrap_or(b"").is_empty() => break, _ => continue, @@ -31,7 +44,7 @@ impl Chunk { impl From> for Chunk { fn from(b: Cow<'static, [u8]>) -> Self { - let mut chunk = Self { bytes: b, sync_entry: false }; + let mut chunk = Self { bytes: b, since: String::new(), sync_entry: false }; chunk.analyze(); chunk } diff --git a/yazi-plugin/src/loader/loader.rs b/yazi-plugin/src/loader/loader.rs index 6d4803b6..07f8c6bd 100644 --- a/yazi-plugin/src/loader/loader.rs +++ b/yazi-plugin/src/loader/loader.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, ops::Deref}; -use anyhow::{Context, Result}; +use anyhow::{Context, Result, bail}; use mlua::{ExternalError, Lua, Table}; use parking_lot::RwLock; use tokio::fs; @@ -51,8 +51,8 @@ impl Default for Loader { impl Loader { pub async fn ensure(&self, name: &str) -> Result<()> { - if self.cache.read().contains_key(name) { - return Ok(()); + if let Some(chunk) = self.cache.read().get(name) { + return Self::compatible_or_error(name, chunk); } // TODO: remove this @@ -80,13 +80,14 @@ Please run `ya pack -m` to automatically migrate all plugins, or manually rename Err(e) => Err(e).with_context(|| format!("Failed to load plugin from {p:?}"))?, }; - self.cache.write().insert(name.to_owned(), chunk.into()); - Ok(()) + let mut cache = self.cache.write(); + cache.insert(name.to_owned(), chunk.into()); + Self::compatible_or_error(name, cache.get(name).unwrap()) } pub fn load(&self, lua: &Lua, id: &str) -> mlua::Result { let loaded: Table = lua.globals().raw_get::
("package")?.raw_get("loaded")?; - if let Ok(t) = loaded.raw_get::
(id) { + if let Ok(t) = loaded.raw_get(id) { return Ok(t); } @@ -101,13 +102,12 @@ Please run `ya pack -m` to automatically migrate all plugins, or manually rename } pub fn try_load(&self, lua: &Lua, id: &str) -> mlua::Result
{ - let loaded: Table = lua.globals().raw_get::
("package")?.raw_get("loaded")?; - loaded.raw_get(id) + lua.globals().raw_get::
("package")?.raw_get::
("loaded")?.raw_get(id) } pub fn load_with(&self, lua: &Lua, id: &str, chunk: &Chunk) -> mlua::Result
{ let loaded: Table = lua.globals().raw_get::
("package")?.raw_get("loaded")?; - if let Ok(t) = loaded.raw_get::
(id) { + if let Ok(t) = loaded.raw_get(id) { return Ok(t); } @@ -117,4 +117,16 @@ Please run `ya pack -m` to automatically migrate all plugins, or manually rename loaded.raw_set(id, t.clone())?; Ok(t) } + + pub fn compatible_or_error(name: &str, chunk: &Chunk) -> Result<()> { + if chunk.compatible() { + return Ok(()); + } + + bail!( + "Plugin `{name}` requires at least Yazi {}, but your current version is Yazi {}.", + chunk.since, + yazi_boot::actions::Actions::version() + ); + } } diff --git a/yazi-plugin/src/macros.rs b/yazi-plugin/src/macros.rs index c0ca1f83..7a3499da 100644 --- a/yazi-plugin/src/macros.rs +++ b/yazi-plugin/src/macros.rs @@ -102,6 +102,8 @@ macro_rules! impl_file_methods { ($methods:ident) => { use mlua::UserDataMethods; + $methods.add_method("hash", |_, me, ()| Ok(me.hash())); + $methods.add_method("icon", |_, me, ()| { use yazi_shared::theme::IconCache; use $crate::bindings::Icon; diff --git a/yazi-proxy/src/options/notify.rs b/yazi-proxy/src/options/notify.rs index f8901c7e..1c2669bc 100644 --- a/yazi-proxy/src/options/notify.rs +++ b/yazi-proxy/src/options/notify.rs @@ -42,7 +42,7 @@ impl TryFrom for NotifyOpt { } } -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy, Default, Eq, PartialEq)] pub enum NotifyLevel { #[default] Info, diff --git a/yazi-scheduler/src/plugin/plugin.rs b/yazi-scheduler/src/plugin/plugin.rs index 0af379cf..cac6d03e 100644 --- a/yazi-scheduler/src/plugin/plugin.rs +++ b/yazi-scheduler/src/plugin/plugin.rs @@ -31,7 +31,7 @@ impl Plugin { self.prog.send(TaskProg::New(task.id, 0))?; if let Err(e) = isolate::entry(task.opt).await { - self.fail(task.id, format!("Micro plugin failed:\n{e}"))?; + self.fail(task.id, format!("Failed to run the plugin:\n{e}"))?; return Ok(()); }