mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: new @since plugin annotation to specify the minimum supported Yazi version (#2290)
This commit is contained in:
parent
4e96341f51
commit
b236f34d31
15 changed files with 81 additions and 46 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::process;
|
||||
|
||||
pub(crate) struct Actions;
|
||||
pub struct Actions;
|
||||
|
||||
impl Actions {
|
||||
pub(crate) fn act(args: &crate::Args) {
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
" (",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,15 +39,16 @@ impl From<NotifyOpt> 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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::<RtRefMut>("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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<Cow<'static, [u8]>> 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Table> {
|
||||
let loaded: Table = lua.globals().raw_get::<Table>("package")?.raw_get("loaded")?;
|
||||
if let Ok(t) = loaded.raw_get::<Table>(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<Table> {
|
||||
let loaded: Table = lua.globals().raw_get::<Table>("package")?.raw_get("loaded")?;
|
||||
loaded.raw_get(id)
|
||||
lua.globals().raw_get::<Table>("package")?.raw_get::<Table>("loaded")?.raw_get(id)
|
||||
}
|
||||
|
||||
pub fn load_with(&self, lua: &Lua, id: &str, chunk: &Chunk) -> mlua::Result<Table> {
|
||||
let loaded: Table = lua.globals().raw_get::<Table>("package")?.raw_get("loaded")?;
|
||||
if let Ok(t) = loaded.raw_get::<Table>(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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ impl TryFrom<mlua::Table> for NotifyOpt {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq)]
|
||||
pub enum NotifyLevel {
|
||||
#[default]
|
||||
Info,
|
||||
|
|
|
|||
|
|
@ -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(());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue