diff --git a/Cargo.lock b/Cargo.lock index d0aed2e7..1deba84e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -600,7 +600,7 @@ dependencies = [ "libc", "mio", "parking_lot", - "rustix 1.0.7", + "rustix 1.0.8", "signal-hook", "signal-hook-mio", "winapi", @@ -2256,15 +2256,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3135,7 +3135,7 @@ checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix 1.0.7", + "rustix 1.0.8", "winsafe", ] @@ -3545,7 +3545,6 @@ dependencies = [ "yazi-config", "yazi-fs", "yazi-macro", - "yazi-proxy", "yazi-shared", ] @@ -3776,7 +3775,6 @@ dependencies = [ "yazi-dds", "yazi-fs", "yazi-macro", - "yazi-proxy", "yazi-shared", ] @@ -3833,6 +3831,7 @@ dependencies = [ "tokio", "yazi-config", "yazi-macro", + "yazi-parser", "yazi-shared", ] @@ -3866,6 +3865,7 @@ name = "yazi-shared" version = "25.6.11" dependencies = [ "anyhow", + "bitflags 2.9.1", "crossterm 0.29.0", "foldhash", "futures", diff --git a/flake.lock b/flake.lock index 00ad72c1..2f328745 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1747312588, - "narHash": "sha256-MmJvj6mlWzeRwKGLcwmZpKaOPZ5nJb/6al5CXqJsgjo=", + "lastModified": 1752596105, + "narHash": "sha256-lFNVsu/mHLq3q11MuGkMhUUoSXEdQjCHvpReaGP1S2k=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b1bebd0fe266bbd1820019612ead889e96a8fa2d", + "rev": "dab3a6e781554f965bde3def0aa2fda4eb8f1708", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1747363019, - "narHash": "sha256-N4dwkRBmpOosa4gfFkFf/LTD8oOcNkAyvZ07JvRDEf0=", + "lastModified": 1752633862, + "narHash": "sha256-Bj7ozT1+5P7NmvDcuAXJvj56txcXuAhk3Vd9FdWFQzk=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "0e624f2b1972a34be1a9b35290ed18ea4b419b6f", + "rev": "8668ca94858206ac3db0860a9dec471de0d995f8", "type": "github" }, "original": { diff --git a/yazi-actor/src/cmp/show.rs b/yazi-actor/src/cmp/show.rs index e209eeda..45b1197f 100644 --- a/yazi-actor/src/cmp/show.rs +++ b/yazi-actor/src/cmp/show.rs @@ -2,8 +2,7 @@ use std::{mem, ops::ControlFlow}; use anyhow::Result; use yazi_macro::{render, succ}; -use yazi_parser::cmp::ShowOpt; -use yazi_proxy::options::CmpItem; +use yazi_parser::cmp::{CmpItem, ShowOpt}; use yazi_shared::{event::Data, osstr_contains, osstr_starts_with}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/cmp/trigger.rs b/yazi-actor/src/cmp/trigger.rs index 5f066b62..dc94b8a8 100644 --- a/yazi-actor/src/cmp/trigger.rs +++ b/yazi-actor/src/cmp/trigger.rs @@ -4,8 +4,7 @@ use anyhow::Result; use tokio::fs; use yazi_fs::{CWD, expand_path}; use yazi_macro::{act, emit, render, succ}; -use yazi_parser::cmp::{ShowOpt, TriggerOpt}; -use yazi_proxy::options::CmpItem; +use yazi_parser::cmp::{CmpItem, ShowOpt, TriggerOpt}; use yazi_shared::{event::{Cmd, Data}, natsort}; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/context.rs b/yazi-actor/src/context.rs index 47fe887e..31da66dd 100644 --- a/yazi-actor/src/context.rs +++ b/yazi-actor/src/context.rs @@ -3,7 +3,7 @@ use std::ops::{Deref, DerefMut}; use anyhow::{Result, anyhow}; use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}, tasks::Tasks}; use yazi_fs::File; -use yazi_shared::{Id, url::Url}; +use yazi_shared::{event::Cmd, url::Url}; pub struct Ctx<'a> { pub core: &'a mut Core, @@ -24,8 +24,8 @@ impl DerefMut for Ctx<'_> { impl<'a> Ctx<'a> { #[inline] - pub fn new(core: &'a mut Core, tab: Option) -> Result { - let tab = if let Some(id) = tab { + pub fn new(core: &'a mut Core, cmd: &Cmd) -> Result { + let tab = if let Some(id) = cmd.id("tab") { core .mgr .tabs diff --git a/yazi-actor/src/lib.rs b/yazi-actor/src/lib.rs index 40feb38e..236b4550 100644 --- a/yazi-actor/src/lib.rs +++ b/yazi-actor/src/lib.rs @@ -1,3 +1,5 @@ +#![allow(clippy::if_same_then_else, clippy::option_map_unit_fn)] + extern crate self as yazi_actor; yazi_macro::mod_pub!(cmp confirm help input mgr pick spot tasks which); diff --git a/yazi-actor/src/mgr/open.rs b/yazi-actor/src/mgr/open.rs index 61faab2e..62f72de5 100644 --- a/yazi-actor/src/mgr/open.rs +++ b/yazi-actor/src/mgr/open.rs @@ -6,9 +6,9 @@ use yazi_config::{YAZI, popup::PickCfg}; use yazi_core::tab::Folder; use yazi_fs::File; use yazi_macro::{act, succ}; -use yazi_parser::mgr::OpenOpt; +use yazi_parser::mgr::{OpenDoOpt, OpenOpt}; use yazi_plugin::isolate; -use yazi_proxy::{MgrProxy, PickProxy, TasksProxy, options::OpenDoOpt}; +use yazi_proxy::{MgrProxy, PickProxy, TasksProxy}; use yazi_shared::{MIME_DIR, event::{CmdCow, Data}, url::Url}; use crate::{Actor, Ctx, mgr::Quit}; diff --git a/yazi-actor/src/mgr/search.rs b/yazi-actor/src/mgr/search.rs index d1d33252..0324541c 100644 --- a/yazi-actor/src/mgr/search.rs +++ b/yazi-actor/src/mgr/search.rs @@ -6,8 +6,9 @@ use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_config::popup::InputCfg; use yazi_fs::{FilesOp, cha::Cha}; use yazi_macro::{act, succ}; +use yazi_parser::mgr::{SearchOpt, SearchOptVia}; use yazi_plugin::external; -use yazi_proxy::{InputProxy, MgrProxy, options::{SearchOpt, SearchOptVia}}; +use yazi_proxy::{InputProxy, MgrProxy}; use yazi_shared::event::Data; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/tasks/open_with.rs b/yazi-actor/src/tasks/open_with.rs index e90170e1..9540f2c1 100644 --- a/yazi-actor/src/tasks/open_with.rs +++ b/yazi-actor/src/tasks/open_with.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_macro::succ; -use yazi_proxy::options::OpenWithOpt; +use yazi_parser::mgr::OpenWithOpt; use yazi_shared::event::Data; use crate::{Actor, Ctx}; diff --git a/yazi-actor/src/tasks/process_exec.rs b/yazi-actor/src/tasks/process_exec.rs index ea5aab79..d1339d09 100644 --- a/yazi-actor/src/tasks/process_exec.rs +++ b/yazi-actor/src/tasks/process_exec.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_macro::succ; -use yazi_proxy::options::ProcessExecOpt; +use yazi_parser::tasks::ProcessExecOpt; use yazi_shared::event::Data; use crate::{Actor, Ctx}; diff --git a/yazi-adapter/src/dimension.rs b/yazi-adapter/src/dimension.rs index 3a551a4d..9a81e320 100644 --- a/yazi-adapter/src/dimension.rs +++ b/yazi-adapter/src/dimension.rs @@ -31,11 +31,11 @@ impl Dimension { _ = mem::replace(&mut size, s); } - if size.columns == 0 || size.rows == 0 { - if let Ok((cols, rows)) = crossterm::terminal::size() { - size.columns = cols; - size.rows = rows; - } + if (size.columns == 0 || size.rows == 0) + && let Ok((cols, rows)) = crossterm::terminal::size() + { + size.columns = cols; + size.rows = rows; } size.into() diff --git a/yazi-binding/Cargo.toml b/yazi-binding/Cargo.toml index 34814470..5adbbd99 100644 --- a/yazi-binding/Cargo.toml +++ b/yazi-binding/Cargo.toml @@ -13,7 +13,6 @@ yazi-adapter = { path = "../yazi-adapter", version = "25.6.11" } yazi-config = { path = "../yazi-config", version = "25.6.11" } yazi-fs = { path = "../yazi-fs", version = "25.6.11" } yazi-macro = { path = "../yazi-macro", version = "25.6.11" } -yazi-proxy = { path = "../yazi-proxy", version = "25.6.11" } yazi-shared = { path = "../yazi-shared", version = "25.6.11" } # External dependencies diff --git a/yazi-binding/src/composer.rs b/yazi-binding/src/composer.rs index 456cf2e8..95c7f68b 100644 --- a/yazi-binding/src/composer.rs +++ b/yazi-binding/src/composer.rs @@ -1,10 +1,14 @@ use foldhash::HashMap; -use mlua::{IntoLua, Lua, MetaMethod, UserData, UserDataMethods, Value}; +use mlua::{Lua, MetaMethod, UserData, UserDataMethods, Value}; + +pub type ComposerGet = fn(&Lua, &[u8]) -> mlua::Result; +pub type ComposerSet = fn(&Lua, &[u8], Value) -> mlua::Result; pub struct Composer { - get: G, - set: S, - cache: HashMap, Value>, + get: G, + set: S, + parent: Option<(G, S)>, + cache: HashMap, Value>, } impl Composer @@ -12,8 +16,12 @@ where G: Fn(&Lua, &[u8]) -> mlua::Result + 'static, S: Fn(&Lua, &[u8], Value) -> mlua::Result + 'static, { - pub fn make(lua: &Lua, get: G, set: S) -> mlua::Result { - Self { get, set, cache: Default::default() }.into_lua(lua) + #[inline] + pub fn new(get: G, set: S) -> Self { Self { get, set, parent: None, cache: Default::default() } } + + #[inline] + pub fn with_parent(get: G, set: S, p_get: G, p_set: S) -> Self { + Self { get, set, parent: Some((p_get, p_set)), cache: Default::default() } } } @@ -29,19 +37,30 @@ where return Ok(v.clone()); } - let v = (me.get)(lua, &key)?; - me.cache.insert(key.to_owned(), v.clone()); - Ok(v) + let mut value = (me.get)(lua, &key)?; + if value.is_nil() + && let Some((p_get, _)) = &me.parent + { + value = p_get(lua, &key)?; + } + + me.cache.insert(key.to_owned(), value.clone()); + Ok(value) }); methods.add_meta_method_mut( MetaMethod::NewIndex, |lua, me, (key, value): (mlua::String, Value)| { let key = key.as_bytes(); - let value = (me.set)(lua, key.as_ref(), value)?; + if value.is_nil() { me.cache.remove(key.as_ref()); + } else if let Some((_, p_set)) = &me.parent { + match p_set(lua, key.as_ref(), value)? { + Value::Nil => me.cache.remove(key.as_ref()), + v => me.cache.insert(key.to_owned(), v), + }; } else { me.cache.insert(key.to_owned(), value); } diff --git a/yazi-binding/src/elements/elements.rs b/yazi-binding/src/elements/elements.rs index 814dcb94..7414c145 100644 --- a/yazi-binding/src/elements/elements.rs +++ b/yazi-binding/src/elements/elements.rs @@ -2,9 +2,9 @@ use mlua::{AnyUserData, IntoLua, Lua, Value}; use tracing::error; use super::Renderable; -use crate::Composer; +use crate::{Composer, ComposerGet, ComposerSet}; -pub fn compose(lua: &Lua) -> mlua::Result { +pub fn compose(p_get: ComposerGet, p_set: ComposerSet) -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { b"Align" => super::Align::compose(lua)?, @@ -26,13 +26,6 @@ pub fn compose(lua: &Lua) -> mlua::Result { b"Table" => super::Table::compose(lua)?, b"Text" => super::Text::compose(lua)?, b"Wrap" => super::Wrap::compose(lua)?, - - b"area" => super::Utils::area(lua)?, - b"hide" => super::Utils::hide(lua)?, - b"width" => super::Utils::width(lua)?, - b"redraw" => super::Utils::redraw(lua)?, - b"render" => super::Utils::render(lua)?, - b"truncate" => super::Utils::truncate(lua)?, _ => return Ok(Value::Nil), } .into_lua(lua) @@ -40,7 +33,7 @@ pub fn compose(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::with_parent(get, set, p_get, p_set) } pub fn render_once(value: Value, buf: &mut ratatui::buffer::Buffer, trans: F) diff --git a/yazi-binding/src/elements/line.rs b/yazi-binding/src/elements/line.rs index 1a48d9c8..042c23a2 100644 --- a/yazi-binding/src/elements/line.rs +++ b/yazi-binding/src/elements/line.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, mem}; +use std::{borrow::Cow, mem, ops::{Deref, DerefMut}}; use ansi_to_tui::IntoText; use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; @@ -17,6 +17,16 @@ pub struct Line { pub(super) inner: ratatui::text::Line<'static>, } +impl Deref for Line { + type Target = ratatui::text::Line<'static>; + + fn deref(&self) -> &Self::Target { &self.inner } +} + +impl DerefMut for Line { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } +} + impl Line { pub fn compose(lua: &Lua) -> mlua::Result { let new = lua.create_function(|_, (_, value): (Table, Value)| Line::try_from(value))?; @@ -101,23 +111,23 @@ impl From for ratatui::text::Line<'static> { impl UserData for Line { fn add_methods>(methods: &mut M) { crate::impl_area_method!(methods); - crate::impl_style_method!(methods, inner.style); - crate::impl_style_shorthands!(methods, inner.style); + crate::impl_style_method!(methods, style); + crate::impl_style_shorthands!(methods, style); - methods.add_method("width", |_, me, ()| Ok(me.inner.width())); + methods.add_method("width", |_, me, ()| Ok(me.width())); methods.add_function_mut("align", |_, (ud, align): (AnyUserData, Align)| { - ud.borrow_mut::()?.inner.alignment = Some(align.0); + ud.borrow_mut::()?.alignment = Some(align.0); Ok(ud) }); methods.add_method("visible", |_, me, ()| { - Ok(me.inner.iter().flat_map(|s| s.content.chars()).any(|c| c.width().unwrap_or(0) > 0)) + Ok(me.iter().flat_map(|s| s.content.chars()).any(|c| c.width().unwrap_or(0) > 0)) }); methods.add_function_mut("truncate", |_, (ud, t): (AnyUserData, Table)| { let mut me = ud.borrow_mut::()?; let max = t.raw_get("max")?; if max < 1 { - me.inner.spans.clear(); + me.spans.clear(); return Ok(ud); } @@ -151,8 +161,7 @@ impl UserData for Line { traverse( max, max - ellipsis.0, - me.inner - .iter() + me.iter() .enumerate() .rev() .flat_map(|(x, s)| s.content.char_indices().rev().map(move |(y, c)| (x, y, c))), @@ -161,23 +170,22 @@ impl UserData for Line { traverse( max, max - ellipsis.0, - me.inner - .iter() + me.iter() .enumerate() .flat_map(|(x, s)| s.content.char_indices().map(move |(y, c)| (x, y, c))), ) }; let Some((x, y, width)) = cut else { - me.inner.spans.clear(); - me.inner.spans.push(ellipsis.1); + me.spans.clear(); + me.spans.push(ellipsis.1); return Ok(ud); }; if !remain { return Ok(ud); } - let spans = &mut me.inner.spans; + let spans = &mut me.spans; let len = match (rtl, width == max) { (a, b) if a == b => spans[x].content[y..].chars().next().map_or(0, |c| c.len_utf8()), _ => 0, @@ -219,7 +227,7 @@ mod tests { .call(()) .unwrap(); - line.inner.spans.iter().map(|s| s.content.as_ref()).collect() + line.spans.iter().map(|s| s.content.as_ref()).collect() } #[test] diff --git a/yazi-binding/src/elements/mod.rs b/yazi-binding/src/elements/mod.rs index a8696c68..92e9e139 100644 --- a/yazi-binding/src/elements/mod.rs +++ b/yazi-binding/src/elements/mod.rs @@ -1,3 +1,3 @@ #![allow(clippy::module_inception)] -yazi_macro::mod_flat!(align area bar border cell clear constraint edge elements gauge layout line list pad pos rect renderable row span table text utils wrap); +yazi_macro::mod_flat!(align area bar border cell clear constraint edge elements gauge layout line list pad pos rect renderable row span table text wrap); diff --git a/yazi-binding/src/elements/rect.rs b/yazi-binding/src/elements/rect.rs index c265ff66..13685949 100644 --- a/yazi-binding/src/elements/rect.rs +++ b/yazi-binding/src/elements/rect.rs @@ -6,7 +6,7 @@ use super::Pad; use crate::deprecate; #[derive(Clone, Copy, Debug, Default, FromLua)] -pub struct Rect(pub(super) ratatui::layout::Rect); +pub struct Rect(pub ratatui::layout::Rect); impl Deref for Rect { type Target = ratatui::layout::Rect; diff --git a/yazi-binding/src/elements/span.rs b/yazi-binding/src/elements/span.rs index c7bde820..39e85b45 100644 --- a/yazi-binding/src/elements/span.rs +++ b/yazi-binding/src/elements/span.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, ops::{Deref, DerefMut}}; use mlua::{AnyUserData, ExternalError, IntoLua, Lua, MetaMethod, Table, UserData, UserDataMethods, Value}; use unicode_width::UnicodeWidthChar; @@ -7,6 +7,16 @@ const EXPECTED: &str = "expected a string or Span"; pub struct Span(pub(super) ratatui::text::Span<'static>); +impl Deref for Span { + type Target = ratatui::text::Span<'static>; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for Span { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + impl Span { pub fn compose(lua: &Lua) -> mlua::Result { let new = lua.create_function(|_, (_, value): (Table, Value)| Span::try_from(value))?; diff --git a/yazi-binding/src/elements/utils.rs b/yazi-binding/src/elements/utils.rs deleted file mode 100644 index 739015ac..00000000 --- a/yazi-binding/src/elements/utils.rs +++ /dev/null @@ -1,244 +0,0 @@ -use mlua::{AnyUserData, ExternalError, IntoLua, Lua, ObjectLike, Table, Value}; -use tracing::error; -use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; -use yazi_config::LAYOUT; -use yazi_macro::render; -use yazi_proxy::{AppProxy, HIDER}; - -use super::{Line, Rect, Span}; -use crate::{Permit, PermitRef}; - -pub(super) struct Utils; - -impl Utils { - pub(super) fn area(lua: &Lua) -> mlua::Result { - let f = lua.create_function(|_, s: mlua::String| { - let layout = LAYOUT.get(); - Ok(match s.as_bytes().as_ref() { - b"current" => Rect(layout.current), - b"preview" => Rect(layout.preview), - b"progress" => Rect(layout.progress), - _ => Err(format!("unknown area: {}", s.display()).into_lua_err())?, - }) - })?; - - f.into_lua(lua) - } - - pub(super) fn hide(lua: &Lua) -> mlua::Result { - let f = lua.create_async_function(|lua, ()| async move { - if lua.named_registry_value::>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) { - return Err("Cannot hide while already hidden".into_lua_err()); - } - - let permit = HIDER.acquire().await.unwrap(); - AppProxy::stop().await; - - lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume as fn()))?; - lua.named_registry_value::("HIDE_PERMIT") - })?; - - f.into_lua(lua) - } - - pub(super) fn width(lua: &Lua) -> mlua::Result { - let f = lua.create_function(|_, v: Value| match v { - Value::String(s) => { - let (mut acc, b) = (0, s.as_bytes()); - for c in b.utf8_chunks() { - acc += c.valid().width(); - if !c.invalid().is_empty() { - acc += 1; - } - } - Ok(acc) - } - Value::UserData(ud) => { - if let Ok(line) = ud.borrow::() { - Ok(line.inner.width()) - } else if let Ok(span) = ud.borrow::() { - Ok(span.0.width()) - } else { - Err("expected a string, Line, or Span".into_lua_err())? - } - } - _ => Err("expected a string, Line, or Span".into_lua_err())?, - })?; - - f.into_lua(lua) - } - - pub(super) fn redraw(lua: &Lua) -> mlua::Result { - let f = lua.create_function(|lua, c: Table| { - let id: mlua::String = c.get("_id")?; - - let mut layout = LAYOUT.get(); - match id.as_bytes().as_ref() { - b"current" => layout.current = *c.raw_get::("_area")?, - b"preview" => layout.preview = *c.raw_get::("_area")?, - b"progress" => layout.progress = *c.raw_get::("_area")?, - _ => {} - } - - LAYOUT.set(layout); - match c.call_method::("redraw", ())? { - Value::Table(tbl) => Ok(tbl), - Value::UserData(ud) => lua.create_sequence_from([ud]), - _ => { - error!( - "Failed to `redraw()` the `{}` component: expected a table or UserData", - id.display(), - ); - lua.create_table() - } - } - })?; - - f.into_lua(lua) - } - - pub(super) fn render(lua: &Lua) -> mlua::Result { - let f = lua.create_function(|_, ()| { - render!(); - Ok(()) - })?; - - f.into_lua(lua) - } - - pub(super) fn truncate(lua: &Lua) -> mlua::Result { - fn traverse( - it: impl Iterator, - max: usize, - ) -> (Option, usize, bool) { - let (mut adv, mut last) = (0, 0); - let idx = it - .take_while(|&(_, c)| { - (last, adv) = (adv, adv + c.width().unwrap_or(0)); - adv <= max - }) - .map(|(i, _)| i) - .last(); - (idx, last, adv > max) - } - - let f = lua.create_function(|lua, (s, t): (mlua::String, Table)| { - let b = s.as_bytes(); - if b.is_empty() { - return Ok(s); - } - - let max = t.raw_get("max")?; - if b.len() <= max { - return Ok(s); - } else if max < 1 { - return lua.create_string(""); - } - - let lossy = String::from_utf8_lossy(&b); - let rtl = t.raw_get("rtl").unwrap_or(false); - let (idx, width, remain) = if rtl { - traverse(lossy.char_indices().rev(), max) - } else { - traverse(lossy.char_indices(), max) - }; - - let Some(idx) = idx else { return lua.create_string("…") }; - if !remain { - return Ok(s); - } - - let result: Vec<_> = match (rtl, width == max) { - (false, false) => { - let len = lossy[idx..].chars().next().map_or(0, |c| c.len_utf8()); - lossy[..idx + len].bytes().chain("…".bytes()).collect() - } - (false, true) => lossy[..idx].bytes().chain("…".bytes()).collect(), - (true, false) => "…".bytes().chain(lossy[idx..].bytes()).collect(), - (true, true) => { - let len = lossy[idx..].chars().next().map_or(0, |c| c.len_utf8()); - "…".bytes().chain(lossy[idx + len..].bytes()).collect() - } - }; - lua.create_string(result) - })?; - - f.into_lua(lua) - } -} - -#[cfg(test)] -mod tests { - use mlua::chunk; - - use super::*; - - fn truncate(s: &str, max: usize, rtl: bool) -> String { - let lua = Lua::new(); - let f = Utils::truncate(&lua).unwrap(); - - lua - .load(chunk! { - return $f($s, { max = $max, rtl = $rtl }) - }) - .call(()) - .unwrap() - } - - #[test] - fn test_truncate() { - assert_eq!(truncate("你好,world", 0, false), ""); - assert_eq!(truncate("你好,world", 1, false), "…"); - assert_eq!(truncate("你好,world", 2, false), "…"); - - assert_eq!(truncate("你好,世界", 3, false), "你…"); - assert_eq!(truncate("你好,世界", 4, false), "你…"); - assert_eq!(truncate("你好,世界", 5, false), "你好…"); - - assert_eq!(truncate("Hello, world", 5, false), "Hell…"); - assert_eq!(truncate("Ni好,世界", 3, false), "Ni…"); - } - - #[test] - fn test_truncate_rtl() { - assert_eq!(truncate("world,你好", 0, true), ""); - assert_eq!(truncate("world,你好", 1, true), "…"); - assert_eq!(truncate("world,你好", 2, true), "…"); - - assert_eq!(truncate("你好,世界", 3, true), "…界"); - assert_eq!(truncate("你好,世界", 4, true), "…界"); - assert_eq!(truncate("你好,世界", 5, true), "…世界"); - - assert_eq!(truncate("Hello, world", 5, true), "…orld"); - assert_eq!(truncate("你好,Shi界", 3, true), "…界"); - } - - #[test] - fn test_truncate_oboe() { - assert_eq!(truncate("Hello, world", 11, false), "Hello, wor…"); - assert_eq!(truncate("你好,世界", 9, false), "你好,世…"); - assert_eq!(truncate("你好,世Jie", 9, false), "你好,世…"); - - assert_eq!(truncate("Hello, world", 11, true), "…llo, world"); - assert_eq!(truncate("你好,世界", 9, true), "…好,世界"); - assert_eq!(truncate("Ni好,世界", 9, true), "…好,世界"); - } - - #[test] - fn test_truncate_exact() { - assert_eq!(truncate("Hello, world", 12, false), "Hello, world"); - assert_eq!(truncate("你好,世界", 10, false), "你好,世界"); - - assert_eq!(truncate("Hello, world", 12, true), "Hello, world"); - assert_eq!(truncate("你好,世界", 10, true), "你好,世界"); - } - - #[test] - fn test_truncate_overflow() { - assert_eq!(truncate("Hello, world", 13, false), "Hello, world"); - assert_eq!(truncate("你好,世界", 11, false), "你好,世界"); - - assert_eq!(truncate("Hello, world", 13, true), "Hello, world"); - assert_eq!(truncate("你好,世界", 11, true), "你好,世界"); - } -} diff --git a/yazi-binding/src/macros.rs b/yazi-binding/src/macros.rs index 08f00ed0..8e49b235 100644 --- a/yazi-binding/src/macros.rs +++ b/yazi-binding/src/macros.rs @@ -17,11 +17,16 @@ macro_rules! runtime_mut { #[macro_export] macro_rules! deprecate { ($lua:ident, $tt:tt) => {{ - let id = match $crate::runtime!($lua)?.current() { - Some(id) => &format!("`{id}.yazi` plugin"), - None => "`init.lua` config", - }; - yazi_proxy::deprecate!(format!($tt, id)); + static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { + let id = match $crate::runtime!($lua)?.current() { + Some(id) => &format!("`{id}.yazi` plugin"), + None => "`init.lua` config", + }; + yazi_macro::emit!(Call( + yazi_shared::event::Cmd::new("app:deprecate").with("content", format!($tt, id)) + )); + } }}; } diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index 48122b5b..4f1265ad 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -13,10 +13,10 @@ async fn main() -> ExitCode { Ok(()) => ExitCode::SUCCESS, Err(e) => { for cause in e.chain() { - if let Some(ioerr) = cause.downcast_ref::() { - if ioerr.kind() == std::io::ErrorKind::BrokenPipe { - return ExitCode::from(0); - } + if let Some(ioerr) = cause.downcast_ref::() + && ioerr.kind() == std::io::ErrorKind::BrokenPipe + { + return ExitCode::from(0); } } errln!("{e:#}").ok(); diff --git a/yazi-core/src/cmp/cmp.rs b/yazi-core/src/cmp/cmp.rs index 47b75a58..ac0d61a0 100644 --- a/yazi-core/src/cmp/cmp.rs +++ b/yazi-core/src/cmp/cmp.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, path::PathBuf}; -use yazi_proxy::options::CmpItem; +use yazi_parser::cmp::CmpItem; use yazi_shared::Id; use yazi_widgets::Scrollable; diff --git a/yazi-core/src/notify/commands/push.rs b/yazi-core/src/notify/commands/push.rs index ca409e0f..f52ac080 100644 --- a/yazi-core/src/notify/commands/push.rs +++ b/yazi-core/src/notify/commands/push.rs @@ -1,6 +1,7 @@ use std::time::{Duration, Instant}; -use yazi_proxy::{AppProxy, options::NotifyOpt}; +use yazi_parser::app::NotifyOpt; +use yazi_proxy::AppProxy; use crate::notify::{Message, Notify}; diff --git a/yazi-core/src/notify/message.rs b/yazi-core/src/notify/message.rs index 1c4dae62..0b3d5e96 100644 --- a/yazi-core/src/notify/message.rs +++ b/yazi-core/src/notify/message.rs @@ -1,7 +1,7 @@ use std::time::{Duration, Instant}; use unicode_width::UnicodeWidthStr; -use yazi_proxy::options::{NotifyLevel, NotifyOpt}; +use yazi_parser::app::{NotifyLevel, NotifyOpt}; use super::NOTIFY_BORDER; diff --git a/yazi-core/src/tasks/plugin.rs b/yazi-core/src/tasks/plugin.rs index 9432833f..cfc423bf 100644 --- a/yazi-core/src/tasks/plugin.rs +++ b/yazi-core/src/tasks/plugin.rs @@ -1,4 +1,4 @@ -use yazi_proxy::options::PluginOpt; +use yazi_parser::app::PluginOpt; use super::Tasks; diff --git a/yazi-core/src/tasks/process.rs b/yazi-core/src/tasks/process.rs index f91d3801..7eeb3cc8 100644 --- a/yazi-core/src/tasks/process.rs +++ b/yazi-core/src/tasks/process.rs @@ -1,7 +1,7 @@ use std::{borrow::Cow, collections::HashMap, ffi::OsString, mem}; use yazi_config::{YAZI, opener::OpenerRule}; -use yazi_proxy::options::ProcessExecOpt; +use yazi_parser::tasks::ProcessExecOpt; use yazi_shared::url::Url; use super::Tasks; diff --git a/yazi-dds/src/server.rs b/yazi-dds/src/server.rs index 3ee47fa2..ed337dfd 100644 --- a/yazi-dds/src/server.rs +++ b/yazi-dds/src/server.rs @@ -92,10 +92,10 @@ impl Server { let Ok(payload) = Payload::from_str(&s) else { return }; let Body::Hi(hi) = payload.body else { return }; - if id.is_none() { - if let Some(ref state) = *STATE.read() { - state.values().for_each(|s| _ = tx.send(s.clone())); - } + if id.is_none() + && let Some(ref state) = *STATE.read() + { + state.values().for_each(|s| _ = tx.send(s.clone())); } let mut clients = CLIENTS.write(); diff --git a/yazi-fm/src/app/commands/deprecate.rs b/yazi-fm/src/app/commands/deprecate.rs new file mode 100644 index 00000000..96a36d7c --- /dev/null +++ b/yazi-fm/src/app/commands/deprecate.rs @@ -0,0 +1,17 @@ +use anyhow::Result; +use yazi_macro::succ; +use yazi_parser::app::{DeprecateOpt, NotifyLevel, NotifyOpt}; +use yazi_shared::event::Data; + +use crate::app::App; + +impl App { + pub(crate) fn deprecate(&mut self, opt: DeprecateOpt) -> Result { + succ!(self.core.notify.push(NotifyOpt { + title: "Deprecated API".to_owned(), + content: opt.content, + level: NotifyLevel::Warn, + timeout: std::time::Duration::from_secs(20), + })); + } +} diff --git a/yazi-fm/src/app/commands/mod.rs b/yazi-fm/src/app/commands/mod.rs index 55ce2950..0357cf32 100644 --- a/yazi-fm/src/app/commands/mod.rs +++ b/yazi-fm/src/app/commands/mod.rs @@ -1,6 +1,7 @@ yazi_macro::mod_flat!( accept_payload bootstrap + deprecate mouse notify plugin diff --git a/yazi-fm/src/app/commands/notify.rs b/yazi-fm/src/app/commands/notify.rs index a0cb89c1..127a1876 100644 --- a/yazi-fm/src/app/commands/notify.rs +++ b/yazi-fm/src/app/commands/notify.rs @@ -1,6 +1,6 @@ use anyhow::Result; use yazi_macro::succ; -use yazi_proxy::options::NotifyOpt; +use yazi_parser::app::NotifyOpt; use yazi_shared::event::Data; use crate::app::App; diff --git a/yazi-fm/src/app/commands/plugin.rs b/yazi-fm/src/app/commands/plugin.rs index 7038be8f..ca9ed0d6 100644 --- a/yazi-fm/src/app/commands/plugin.rs +++ b/yazi-fm/src/app/commands/plugin.rs @@ -5,8 +5,9 @@ use tracing::{error, warn}; use yazi_binding::runtime_mut; use yazi_dds::Sendable; use yazi_macro::succ; +use yazi_parser::app::{PluginMode, PluginOpt}; use yazi_plugin::{LUA, loader::{LOADER, Loader}}; -use yazi_proxy::{AppProxy, options::{PluginMode, PluginOpt}}; +use yazi_proxy::AppProxy; use yazi_shared::event::Data; use crate::{app::App, lives::Lives}; diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 72bb343f..86f339c2 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -48,12 +48,13 @@ impl<'a> Executor<'a> { on!(resize); on!(stop); on!(resume); + on!(deprecate); succ!(); } fn mgr(&mut self, cmd: CmdCow) -> Result { - let cx = &mut Ctx::new(&mut self.app.core, cmd.id("tab"))?; + let cx = &mut Ctx::new(&mut self.app.core, &cmd)?; macro_rules! on { ($name:ident) => { @@ -143,7 +144,7 @@ impl<'a> Executor<'a> { } fn tasks(&mut self, cmd: CmdCow) -> Result { - let cx = &mut Ctx::new(&mut self.app.core, cmd.id("tab"))?; + let cx = &mut Ctx::new(&mut self.app.core, &cmd)?; macro_rules! on { ($name:ident) => { @@ -171,7 +172,7 @@ impl<'a> Executor<'a> { } fn spot(&mut self, cmd: CmdCow) -> Result { - let cx = &mut Ctx::new(&mut self.app.core, cmd.id("tab"))?; + let cx = &mut Ctx::new(&mut self.app.core, &cmd)?; macro_rules! on { ($name:ident) => { @@ -196,7 +197,7 @@ impl<'a> Executor<'a> { } fn pick(&mut self, cmd: CmdCow) -> Result { - let cx = &mut Ctx::new(&mut self.app.core, cmd.id("tab"))?; + let cx = &mut Ctx::new(&mut self.app.core, &cmd)?; macro_rules! on { ($name:ident) => { @@ -221,7 +222,7 @@ impl<'a> Executor<'a> { fn input(&mut self, cmd: CmdCow) -> Result { let mode = self.app.core.input.mode(); - let cx = &mut Ctx::new(&mut self.app.core, cmd.id("tab"))?; + let cx = &mut Ctx::new(&mut self.app.core, &cmd)?; macro_rules! on { ($name:ident) => { @@ -252,7 +253,7 @@ impl<'a> Executor<'a> { } fn confirm(&mut self, cmd: CmdCow) -> Result { - let cx = &mut Ctx::new(&mut self.app.core, cmd.id("tab"))?; + let cx = &mut Ctx::new(&mut self.app.core, &cmd)?; macro_rules! on { ($name:ident) => { @@ -270,7 +271,7 @@ impl<'a> Executor<'a> { } fn help(&mut self, cmd: CmdCow) -> Result { - let cx = &mut Ctx::new(&mut self.app.core, cmd.id("tab"))?; + let cx = &mut Ctx::new(&mut self.app.core, &cmd)?; macro_rules! on { ($name:ident) => { @@ -294,7 +295,7 @@ impl<'a> Executor<'a> { } fn cmp(&mut self, cmd: CmdCow) -> Result { - let cx = &mut Ctx::new(&mut self.app.core, cmd.id("tab"))?; + let cx = &mut Ctx::new(&mut self.app.core, &cmd)?; macro_rules! on { ($name:ident) => { @@ -319,7 +320,7 @@ impl<'a> Executor<'a> { } fn which(&mut self, cmd: CmdCow) -> Result { - let cx = &mut Ctx::new(&mut self.app.core, cmd.id("tab"))?; + let cx = &mut Ctx::new(&mut self.app.core, &cmd)?; macro_rules! on { ($name:ident) => { diff --git a/yazi-parser/Cargo.toml b/yazi-parser/Cargo.toml index 29025855..985c2c7c 100644 --- a/yazi-parser/Cargo.toml +++ b/yazi-parser/Cargo.toml @@ -15,7 +15,6 @@ yazi-config = { path = "../yazi-config", version = "25.6.11" } yazi-dds = { path = "../yazi-dds", version = "25.6.11" } yazi-fs = { path = "../yazi-fs", version = "25.6.11" } yazi-macro = { path = "../yazi-macro", version = "25.6.11" } -yazi-proxy = { path = "../yazi-proxy", version = "25.6.11" } yazi-shared = { path = "../yazi-shared", version = "25.6.11" } # External dependencies diff --git a/yazi-parser/src/app/deprecate.rs b/yazi-parser/src/app/deprecate.rs new file mode 100644 index 00000000..6bd47bb6 --- /dev/null +++ b/yazi-parser/src/app/deprecate.rs @@ -0,0 +1,18 @@ +use anyhow::bail; +use yazi_shared::event::CmdCow; + +pub struct DeprecateOpt { + pub content: String, +} + +impl TryFrom for DeprecateOpt { + type Error = anyhow::Error; + + fn try_from(mut c: CmdCow) -> Result { + let Some(content) = c.take_str("content") else { + bail!("Invalid 'content' in DeprecateOpt"); + }; + + Ok(Self { content: content.into_owned() }) + } +} diff --git a/yazi-parser/src/app/mod.rs b/yazi-parser/src/app/mod.rs index b021820b..d4c47930 100644 --- a/yazi-parser/src/app/mod.rs +++ b/yazi-parser/src/app/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(accept_payload mouse stop update_progress); +yazi_macro::mod_flat!(accept_payload deprecate mouse notify plugin stop update_progress); diff --git a/yazi-proxy/src/options/notify.rs b/yazi-parser/src/app/notify.rs similarity index 99% rename from yazi-proxy/src/options/notify.rs rename to yazi-parser/src/app/notify.rs index d5bf7c21..0ae0c885 100644 --- a/yazi-proxy/src/options/notify.rs +++ b/yazi-parser/src/app/notify.rs @@ -45,6 +45,7 @@ impl TryFrom for NotifyOpt { } } +// --- Level #[derive(Clone, Copy, Default, Deserialize, Eq, PartialEq)] #[serde(rename_all = "kebab-case")] pub enum NotifyLevel { diff --git a/yazi-proxy/src/options/plugin.rs b/yazi-parser/src/app/plugin.rs similarity index 100% rename from yazi-proxy/src/options/plugin.rs rename to yazi-parser/src/app/plugin.rs diff --git a/yazi-parser/src/cmp/show.rs b/yazi-parser/src/cmp/show.rs index c67d69f9..2dba7352 100644 --- a/yazi-parser/src/cmp/show.rs +++ b/yazi-parser/src/cmp/show.rs @@ -1,6 +1,5 @@ -use std::path::PathBuf; +use std::{ffi::OsString, path::{MAIN_SEPARATOR_STR, PathBuf}}; -use yazi_proxy::options::CmpItem; use yazi_shared::{Id, SStr, event::{Cmd, CmdCow}}; #[derive(Default)] @@ -25,3 +24,16 @@ impl From for ShowOpt { impl From for ShowOpt { fn from(c: Cmd) -> Self { Self::from(CmdCow::from(c)) } } + +// --- Item +#[derive(Debug, Clone)] +pub struct CmpItem { + pub name: OsString, + pub is_dir: bool, +} + +impl CmpItem { + pub fn completable(&self) -> String { + format!("{}{}", self.name.to_string_lossy(), if self.is_dir { MAIN_SEPARATOR_STR } else { "" }) + } +} diff --git a/yazi-parser/src/input/complete.rs b/yazi-parser/src/input/complete.rs index 6792a761..f8bf698e 100644 --- a/yazi-parser/src/input/complete.rs +++ b/yazi-parser/src/input/complete.rs @@ -1,7 +1,8 @@ use anyhow::bail; -use yazi_proxy::options::CmpItem; use yazi_shared::{Id, event::CmdCow}; +use crate::cmp::CmpItem; + pub struct CompleteOpt { pub item: CmpItem, pub _ticket: Id, // FIXME: not used diff --git a/yazi-parser/src/mgr/mod.rs b/yazi-parser/src/mgr/mod.rs index bf7d3bc9..21ee4539 100644 --- a/yazi-parser/src/mgr/mod.rs +++ b/yazi-parser/src/mgr/mod.rs @@ -11,6 +11,7 @@ yazi_macro::mod_flat!( quit remove rename + search seek spot tab_close diff --git a/yazi-parser/src/mgr/open.rs b/yazi-parser/src/mgr/open.rs index 9382d6db..3da47547 100644 --- a/yazi-parser/src/mgr/open.rs +++ b/yazi-parser/src/mgr/open.rs @@ -1,4 +1,8 @@ -use yazi_shared::event::CmdCow; +use std::borrow::Cow; + +use anyhow::anyhow; +use yazi_config::opener::OpenerRule; +use yazi_shared::{event::CmdCow, url::Url}; #[derive(Clone, Copy)] pub struct OpenOpt { @@ -11,3 +15,31 @@ impl From for OpenOpt { Self { interactive: c.bool("interactive"), hovered: c.bool("hovered") } } } + +// --- Do +#[derive(Default)] +pub struct OpenDoOpt { + pub cwd: Url, + pub hovered: Url, + pub targets: Vec<(Url, &'static str)>, + pub interactive: bool, +} + +impl From for OpenDoOpt { + fn from(mut c: CmdCow) -> Self { c.take_any("option").unwrap_or_default() } +} + +// --- Open with +pub struct OpenWithOpt { + pub opener: Cow<'static, OpenerRule>, + pub cwd: Url, + pub targets: Vec, +} + +impl TryFrom for OpenWithOpt { + type Error = anyhow::Error; + + fn try_from(mut c: CmdCow) -> Result { + c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in OpenWithOpt")) + } +} diff --git a/yazi-proxy/src/options/search.rs b/yazi-parser/src/mgr/search.rs similarity index 100% rename from yazi-proxy/src/options/search.rs rename to yazi-parser/src/mgr/search.rs diff --git a/yazi-parser/src/tasks/process_exec.rs b/yazi-parser/src/tasks/process_exec.rs index 8b137891..a8e39e09 100644 --- a/yazi-parser/src/tasks/process_exec.rs +++ b/yazi-parser/src/tasks/process_exec.rs @@ -1 +1,22 @@ +use std::{borrow::Cow, ffi::OsString}; +use anyhow::anyhow; +use tokio::sync::oneshot; +use yazi_config::opener::OpenerRule; +use yazi_shared::{event::CmdCow, url::Url}; + +// --- Exec +pub struct ProcessExecOpt { + pub cwd: Url, + pub opener: Cow<'static, OpenerRule>, + pub args: Vec, + pub done: Option>, +} + +impl TryFrom for ProcessExecOpt { + type Error = anyhow::Error; + + fn try_from(mut c: CmdCow) -> Result { + c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in ProcessExecOpt")) + } +} diff --git a/yazi-plugin/src/elements/elements.rs b/yazi-plugin/src/elements/elements.rs new file mode 100644 index 00000000..6196f4d0 --- /dev/null +++ b/yazi-plugin/src/elements/elements.rs @@ -0,0 +1,251 @@ +use mlua::{AnyUserData, ExternalError, IntoLua, Lua, ObjectLike, Table, Value}; +use tracing::error; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; +use yazi_binding::{Composer, ComposerGet, ComposerSet, Permit, PermitRef, elements::{Line, Rect, Span}}; +use yazi_config::LAYOUT; +use yazi_proxy::{AppProxy, HIDER}; + +pub fn compose() -> Composer { + fn get(lua: &Lua, key: &[u8]) -> mlua::Result { + match key { + b"area" => area(lua)?, + b"hide" => hide(lua)?, + b"width" => width(lua)?, + b"redraw" => redraw(lua)?, + b"render" => render(lua)?, + b"truncate" => truncate(lua)?, + _ => return Ok(Value::Nil), + } + .into_lua(lua) + } + + fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } + + yazi_binding::elements::compose(get, set) +} + +pub(super) fn area(lua: &Lua) -> mlua::Result { + let f = lua.create_function(|_, s: mlua::String| { + let layout = LAYOUT.get(); + Ok(match s.as_bytes().as_ref() { + b"current" => Rect(layout.current), + b"preview" => Rect(layout.preview), + b"progress" => Rect(layout.progress), + _ => Err(format!("unknown area: {}", s.display()).into_lua_err())?, + }) + })?; + + f.into_lua(lua) +} + +pub(super) fn hide(lua: &Lua) -> mlua::Result { + let f = lua.create_async_function(|lua, ()| async move { + if lua.named_registry_value::>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) { + return Err("Cannot hide while already hidden".into_lua_err()); + } + + let permit = HIDER.acquire().await.unwrap(); + AppProxy::stop().await; + + lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume as fn()))?; + lua.named_registry_value::("HIDE_PERMIT") + })?; + + f.into_lua(lua) +} + +pub(super) fn width(lua: &Lua) -> mlua::Result { + let f = lua.create_function(|_, v: Value| match v { + Value::String(s) => { + let (mut acc, b) = (0, s.as_bytes()); + for c in b.utf8_chunks() { + acc += c.valid().width(); + if !c.invalid().is_empty() { + acc += 1; + } + } + Ok(acc) + } + Value::UserData(ud) => { + if let Ok(line) = ud.borrow::() { + Ok(line.width()) + } else if let Ok(span) = ud.borrow::() { + Ok(span.width()) + } else { + Err("expected a string, Line, or Span".into_lua_err())? + } + } + _ => Err("expected a string, Line, or Span".into_lua_err())?, + })?; + + f.into_lua(lua) +} + +pub(super) fn redraw(lua: &Lua) -> mlua::Result { + let f = lua.create_function(|lua, c: Table| { + let id: mlua::String = c.get("_id")?; + + let mut layout = LAYOUT.get(); + match id.as_bytes().as_ref() { + b"current" => layout.current = *c.raw_get::("_area")?, + b"preview" => layout.preview = *c.raw_get::("_area")?, + b"progress" => layout.progress = *c.raw_get::("_area")?, + _ => {} + } + + LAYOUT.set(layout); + match c.call_method::("redraw", ())? { + Value::Table(tbl) => Ok(tbl), + Value::UserData(ud) => lua.create_sequence_from([ud]), + _ => { + error!( + "Failed to `redraw()` the `{}` component: expected a table or UserData", + id.display(), + ); + lua.create_table() + } + } + })?; + + f.into_lua(lua) +} + +pub(super) fn render(lua: &Lua) -> mlua::Result { + let f = lua.create_function(|_, ()| { + yazi_macro::render!(); + Ok(()) + })?; + + f.into_lua(lua) +} + +pub(super) fn truncate(lua: &Lua) -> mlua::Result { + fn traverse(it: impl Iterator, max: usize) -> (Option, usize, bool) { + let (mut adv, mut last) = (0, 0); + let idx = it + .take_while(|&(_, c)| { + (last, adv) = (adv, adv + c.width().unwrap_or(0)); + adv <= max + }) + .map(|(i, _)| i) + .last(); + (idx, last, adv > max) + } + + let f = lua.create_function(|lua, (s, t): (mlua::String, Table)| { + let b = s.as_bytes(); + if b.is_empty() { + return Ok(s); + } + + let max = t.raw_get("max")?; + if b.len() <= max { + return Ok(s); + } else if max < 1 { + return lua.create_string(""); + } + + let lossy = String::from_utf8_lossy(&b); + let rtl = t.raw_get("rtl").unwrap_or(false); + let (idx, width, remain) = if rtl { + traverse(lossy.char_indices().rev(), max) + } else { + traverse(lossy.char_indices(), max) + }; + + let Some(idx) = idx else { return lua.create_string("…") }; + if !remain { + return Ok(s); + } + + let result: Vec<_> = match (rtl, width == max) { + (false, false) => { + let len = lossy[idx..].chars().next().map_or(0, |c| c.len_utf8()); + lossy[..idx + len].bytes().chain("…".bytes()).collect() + } + (false, true) => lossy[..idx].bytes().chain("…".bytes()).collect(), + (true, false) => "…".bytes().chain(lossy[idx..].bytes()).collect(), + (true, true) => { + let len = lossy[idx..].chars().next().map_or(0, |c| c.len_utf8()); + "…".bytes().chain(lossy[idx + len..].bytes()).collect() + } + }; + lua.create_string(result) + })?; + + f.into_lua(lua) +} + +#[cfg(test)] +mod tests { + use mlua::{Lua, chunk}; + + fn truncate(s: &str, max: usize, rtl: bool) -> String { + let lua = Lua::new(); + let f = super::truncate(&lua).unwrap(); + + lua + .load(chunk! { + return $f($s, { max = $max, rtl = $rtl }) + }) + .call(()) + .unwrap() + } + + #[test] + fn test_truncate() { + assert_eq!(truncate("你好,world", 0, false), ""); + assert_eq!(truncate("你好,world", 1, false), "…"); + assert_eq!(truncate("你好,world", 2, false), "…"); + + assert_eq!(truncate("你好,世界", 3, false), "你…"); + assert_eq!(truncate("你好,世界", 4, false), "你…"); + assert_eq!(truncate("你好,世界", 5, false), "你好…"); + + assert_eq!(truncate("Hello, world", 5, false), "Hell…"); + assert_eq!(truncate("Ni好,世界", 3, false), "Ni…"); + } + + #[test] + fn test_truncate_rtl() { + assert_eq!(truncate("world,你好", 0, true), ""); + assert_eq!(truncate("world,你好", 1, true), "…"); + assert_eq!(truncate("world,你好", 2, true), "…"); + + assert_eq!(truncate("你好,世界", 3, true), "…界"); + assert_eq!(truncate("你好,世界", 4, true), "…界"); + assert_eq!(truncate("你好,世界", 5, true), "…世界"); + + assert_eq!(truncate("Hello, world", 5, true), "…orld"); + assert_eq!(truncate("你好,Shi界", 3, true), "…界"); + } + + #[test] + fn test_truncate_oboe() { + assert_eq!(truncate("Hello, world", 11, false), "Hello, wor…"); + assert_eq!(truncate("你好,世界", 9, false), "你好,世…"); + assert_eq!(truncate("你好,世Jie", 9, false), "你好,世…"); + + assert_eq!(truncate("Hello, world", 11, true), "…llo, world"); + assert_eq!(truncate("你好,世界", 9, true), "…好,世界"); + assert_eq!(truncate("Ni好,世界", 9, true), "…好,世界"); + } + + #[test] + fn test_truncate_exact() { + assert_eq!(truncate("Hello, world", 12, false), "Hello, world"); + assert_eq!(truncate("你好,世界", 10, false), "你好,世界"); + + assert_eq!(truncate("Hello, world", 12, true), "Hello, world"); + assert_eq!(truncate("你好,世界", 10, true), "你好,世界"); + } + + #[test] + fn test_truncate_overflow() { + assert_eq!(truncate("Hello, world", 13, false), "Hello, world"); + assert_eq!(truncate("你好,世界", 11, false), "你好,世界"); + + assert_eq!(truncate("Hello, world", 13, true), "Hello, world"); + assert_eq!(truncate("你好,世界", 11, true), "你好,世界"); + } +} diff --git a/yazi-plugin/src/elements/mod.rs b/yazi-plugin/src/elements/mod.rs new file mode 100644 index 00000000..06ab4314 --- /dev/null +++ b/yazi-plugin/src/elements/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(elements); diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index 4df4261e..c81c9e0c 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -1,12 +1,12 @@ use globset::GlobBuilder; use mlua::{ExternalError, ExternalResult, Function, IntoLua, IntoLuaMulti, Lua, Table, Value}; use tokio::fs; -use yazi_binding::{Cha, Composer, Error, File, Url, UrlRef}; +use yazi_binding::{Cha, Composer, ComposerGet, ComposerSet, Error, File, Url, UrlRef}; use yazi_fs::{mounts::PARTITIONS, remove_dir_clean}; use crate::bindings::SizeCalculator; -pub fn compose(lua: &Lua) -> mlua::Result { +pub fn compose() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { b"op" => op(lua)?, @@ -27,7 +27,7 @@ pub fn compose(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } fn op(lua: &Lua) -> mlua::Result { diff --git a/yazi-plugin/src/isolate/entry.rs b/yazi-plugin/src/isolate/entry.rs index 831cbb05..622626c4 100644 --- a/yazi-plugin/src/isolate/entry.rs +++ b/yazi-plugin/src/isolate/entry.rs @@ -1,7 +1,7 @@ use mlua::{ExternalResult, ObjectLike}; use tokio::runtime::Handle; use yazi_dds::Sendable; -use yazi_proxy::options::PluginOpt; +use yazi_parser::app::PluginOpt; use super::slim_lua; use crate::loader::LOADER; diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index 5b54989b..10e20f73 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -1,4 +1,4 @@ -use mlua::Lua; +use mlua::{IntoLua, Lua}; use yazi_binding::Runtime; use yazi_macro::plugin_preset as preset; @@ -8,11 +8,11 @@ pub fn slim_lua(name: &str) -> mlua::Result { // Base let globals = lua.globals(); - globals.raw_set("ui", yazi_binding::elements::compose(&lua)?)?; - globals.raw_set("ya", crate::utils::compose(&lua, true)?)?; - globals.raw_set("fs", crate::fs::compose(&lua)?)?; - globals.raw_set("rt", crate::runtime::compose(&lua)?)?; - globals.raw_set("th", crate::theme::compose(&lua)?)?; + globals.raw_set("ui", crate::elements::compose())?; + globals.raw_set("ya", crate::utils::compose(true))?; + globals.raw_set("fs", crate::fs::compose())?; + globals.raw_set("rt", crate::runtime::compose())?; + globals.raw_set("th", crate::theme::compose().into_lua(&lua)?)?; yazi_binding::Cha::install(&lua)?; yazi_binding::File::install(&lua)?; diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index 431c1b1b..2103576f 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -5,7 +5,8 @@ use tracing::error; use yazi_binding::{File, elements::Rect}; use yazi_config::LAYOUT; use yazi_dds::Sendable; -use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}}; +use yazi_parser::app::{PluginCallback, PluginOpt}; +use yazi_proxy::AppProxy; use yazi_shared::{SStr, event::Cmd}; use super::slim_lua; @@ -102,10 +103,10 @@ fn peek_async( } }); - if let Err(e) = result { - if !e.to_string().contains("Peek task cancelled") { - error!("{e}"); - } + if let Err(e) = result + && !e.to_string().contains("Peek task cancelled") + { + error!("{e}"); } }); } diff --git a/yazi-plugin/src/isolate/seek.rs b/yazi-plugin/src/isolate/seek.rs index 21f9f501..e51a4b5f 100644 --- a/yazi-plugin/src/isolate/seek.rs +++ b/yazi-plugin/src/isolate/seek.rs @@ -1,7 +1,8 @@ use mlua::{IntoLua, ObjectLike}; use yazi_binding::{File, elements::Rect}; use yazi_config::LAYOUT; -use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}}; +use yazi_parser::app::{PluginCallback, PluginOpt}; +use yazi_proxy::AppProxy; use yazi_shared::event::Cmd; pub fn seek_sync(cmd: &'static Cmd, file: yazi_fs::File, units: i16) { diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index 2216b907..03fbf6ab 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -1,6 +1,6 @@ #![allow(clippy::if_same_then_else, clippy::unit_arg)] -yazi_macro::mod_pub!(bindings external fs isolate loader process pubsub runtime theme utils); +yazi_macro::mod_pub!(bindings elements external fs isolate loader process pubsub runtime theme utils); yazi_macro::mod_flat!(lua twox); diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/lua.rs index 11db15e3..f63f860e 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/lua.rs @@ -20,12 +20,12 @@ fn stage_1(lua: &'static Lua) -> Result<()> { // Base let globals = lua.globals(); - globals.raw_set("ui", yazi_binding::elements::compose(lua)?)?; - globals.raw_set("ya", crate::utils::compose(lua, false)?)?; - globals.raw_set("fs", crate::fs::compose(lua)?)?; - globals.raw_set("ps", crate::pubsub::compose(lua)?)?; - globals.raw_set("rt", crate::runtime::compose(lua)?)?; - globals.raw_set("th", crate::theme::compose(lua)?)?; + globals.raw_set("ui", crate::elements::compose())?; + globals.raw_set("ya", crate::utils::compose(false))?; + globals.raw_set("fs", crate::fs::compose())?; + globals.raw_set("ps", crate::pubsub::compose())?; + globals.raw_set("rt", crate::runtime::compose())?; + globals.raw_set("th", crate::theme::compose())?; yazi_binding::Error::install(lua)?; yazi_binding::Cha::install(lua)?; diff --git a/yazi-plugin/src/pubsub/mod.rs b/yazi-plugin/src/pubsub/mod.rs index f9ccaa03..b020d7e4 100644 --- a/yazi-plugin/src/pubsub/mod.rs +++ b/yazi-plugin/src/pubsub/mod.rs @@ -1,11 +1,11 @@ #![allow(clippy::module_inception)] use mlua::{IntoLua, Lua, Value}; -use yazi_binding::Composer; +use yazi_binding::{Composer, ComposerGet, ComposerSet}; yazi_macro::mod_flat!(pubsub); -pub(super) fn compose(lua: &Lua) -> mlua::Result { +pub(super) fn compose() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { b"pub" => Pubsub::r#pub(lua)?, @@ -21,5 +21,5 @@ pub(super) fn compose(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } diff --git a/yazi-plugin/src/runtime/plugin.rs b/yazi-plugin/src/runtime/plugin.rs index 13a6d088..09203648 100644 --- a/yazi-plugin/src/runtime/plugin.rs +++ b/yazi-plugin/src/runtime/plugin.rs @@ -1,8 +1,8 @@ use mlua::{Function, IntoLua, Lua, UserData, Value}; -use yazi_binding::{Composer, FileRef, UrlRef, cached_field}; +use yazi_binding::{Composer, ComposerGet, ComposerSet, FileRef, UrlRef, cached_field}; use yazi_config::YAZI; -pub(super) fn plugin(lua: &Lua) -> mlua::Result { +pub(super) fn plugin() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { b"fetchers" => fetchers(lua)?, @@ -16,7 +16,7 @@ pub(super) fn plugin(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } fn fetchers(lua: &Lua) -> mlua::Result { diff --git a/yazi-plugin/src/runtime/runtime.rs b/yazi-plugin/src/runtime/runtime.rs index 07e4bcc0..4739e53f 100644 --- a/yazi-plugin/src/runtime/runtime.rs +++ b/yazi-plugin/src/runtime/runtime.rs @@ -1,20 +1,20 @@ use mlua::{IntoLua, Lua, LuaSerdeExt, SerializeOptions, Value}; -use yazi_binding::{Composer, Url}; +use yazi_binding::{Composer, ComposerGet, ComposerSet, Url}; use yazi_boot::ARGS; use yazi_config::YAZI; pub const OPTS: SerializeOptions = SerializeOptions::new().serialize_none_to_null(false).serialize_unit_to_null(false); -pub fn compose(lua: &Lua) -> mlua::Result { +pub fn compose() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { - b"args" => args(lua)?, - b"term" => super::term(lua)?, - b"mgr" => mgr(lua)?, - b"plugin" => super::plugin(lua)?, - b"preview" => preview(lua)?, - b"tasks" => tasks(lua)?, + b"args" => args().into_lua(lua)?, + b"term" => super::term().into_lua(lua)?, + b"mgr" => mgr().into_lua(lua)?, + b"plugin" => super::plugin().into_lua(lua)?, + b"preview" => preview().into_lua(lua)?, + b"tasks" => tasks().into_lua(lua)?, _ => return Ok(Value::Nil), } .into_lua(lua) @@ -22,10 +22,10 @@ pub fn compose(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn args(lua: &Lua) -> mlua::Result { +fn args() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { b"entries" => lua.create_sequence_from(ARGS.entries.iter().map(Url::new))?.into_lua(lua), @@ -37,10 +37,10 @@ fn args(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn mgr(lua: &Lua) -> mlua::Result { +fn mgr() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let m = &YAZI.mgr; match key { @@ -74,10 +74,10 @@ fn mgr(lua: &Lua) -> mlua::Result { }) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn preview(lua: &Lua) -> mlua::Result { +fn preview() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let p = &YAZI.preview; match key { @@ -101,10 +101,10 @@ fn preview(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn tasks(lua: &Lua) -> mlua::Result { +fn tasks() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &YAZI.tasks; match key { @@ -123,5 +123,5 @@ fn tasks(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } diff --git a/yazi-plugin/src/runtime/term.rs b/yazi-plugin/src/runtime/term.rs index bd0e3b63..6b8c9713 100644 --- a/yazi-plugin/src/runtime/term.rs +++ b/yazi-plugin/src/runtime/term.rs @@ -1,8 +1,8 @@ use mlua::{Function, IntoLua, IntoLuaMulti, Lua, Value}; use yazi_adapter::{Dimension, EMULATOR}; -use yazi_binding::Composer; +use yazi_binding::{Composer, ComposerGet, ComposerSet}; -pub(super) fn term(lua: &Lua) -> mlua::Result { +pub(super) fn term() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { b"light" => EMULATOR.get().light.into_lua(lua), @@ -13,7 +13,7 @@ pub(super) fn term(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } fn cell_size(lua: &Lua) -> mlua::Result { diff --git a/yazi-plugin/src/theme/theme.rs b/yazi-plugin/src/theme/theme.rs index f187e869..5b8f6d81 100644 --- a/yazi-plugin/src/theme/theme.rs +++ b/yazi-plugin/src/theme/theme.rs @@ -1,33 +1,34 @@ use mlua::{IntoLua, Lua, Value}; -use yazi_binding::{Composer, Style, Url}; +use yazi_binding::{Composer, ComposerGet, ComposerSet, Style, Url}; use yazi_config::THEME; -pub fn compose(lua: &Lua) -> mlua::Result { +pub fn compose() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { - b"mgr" => mgr(lua), - b"tabs" => tabs(lua), - b"mode" => mode(lua), - b"status" => status(lua), - b"which" => which(lua), - b"confirm" => confirm(lua), - b"spot" => spot(lua), - b"notify" => notify(lua), - b"pick" => pick(lua), - b"input" => input(lua), - b"cmp" => cmp(lua), - b"tasks" => tasks(lua), - b"help" => help(lua), - _ => Ok(Value::Nil), + b"mgr" => mgr(), + b"tabs" => tabs(), + b"mode" => mode(), + b"status" => status(), + b"which" => which(), + b"confirm" => confirm(), + b"spot" => spot(), + b"notify" => notify(), + b"pick" => pick(), + b"input" => input(), + b"cmp" => cmp(), + b"tasks" => tasks(), + b"help" => help(), + _ => return Ok(Value::Nil), } + .into_lua(lua) } fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn mgr(lua: &Lua) -> mlua::Result { +fn mgr() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let m = &THEME.mgr; match key { @@ -60,10 +61,10 @@ fn mgr(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn tabs(lua: &Lua) -> mlua::Result { +fn tabs() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.tabs; match key { @@ -89,10 +90,10 @@ fn tabs(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn mode(lua: &Lua) -> mlua::Result { +fn mode() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.mode; match key { @@ -111,10 +112,10 @@ fn mode(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn status(lua: &Lua) -> mlua::Result { +fn status() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.status; match key { @@ -148,10 +149,10 @@ fn status(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn which(lua: &Lua) -> mlua::Result { +fn which() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.which; match key { @@ -170,10 +171,10 @@ fn which(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn confirm(lua: &Lua) -> mlua::Result { +fn confirm() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.confirm; match key { @@ -197,10 +198,10 @@ fn confirm(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn spot(lua: &Lua) -> mlua::Result { +fn spot() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.spot; match key { @@ -216,10 +217,10 @@ fn spot(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn notify(lua: &Lua) -> mlua::Result { +fn notify() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.notify; match key { @@ -237,10 +238,10 @@ fn notify(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn pick(lua: &Lua) -> mlua::Result { +fn pick() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.pick; match key { @@ -254,10 +255,10 @@ fn pick(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn input(lua: &Lua) -> mlua::Result { +fn input() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.input; match key { @@ -272,10 +273,10 @@ fn input(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn cmp(lua: &Lua) -> mlua::Result { +fn cmp() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.cmp; match key { @@ -293,10 +294,10 @@ fn cmp(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn tasks(lua: &Lua) -> mlua::Result { +fn tasks() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.tasks; match key { @@ -310,10 +311,10 @@ fn tasks(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } -fn help(lua: &Lua) -> mlua::Result { +fn help() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { let t = &THEME.help; match key { @@ -330,5 +331,5 @@ fn help(lua: &Lua) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, get, set) + Composer::new(get, set) } diff --git a/yazi-plugin/src/utils/sync.rs b/yazi-plugin/src/utils/sync.rs index 5948dcfd..5f1ee3eb 100644 --- a/yazi-plugin/src/utils/sync.rs +++ b/yazi-plugin/src/utils/sync.rs @@ -3,7 +3,8 @@ use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValu use tokio::sync::oneshot; use yazi_binding::{runtime, runtime_mut}; use yazi_dds::Sendable; -use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}}; +use yazi_parser::app::{PluginCallback, PluginOpt}; +use yazi_proxy::AppProxy; use yazi_shared::event::Data; use super::Utils; diff --git a/yazi-plugin/src/utils/utils.rs b/yazi-plugin/src/utils/utils.rs index 45b2781f..02dc188e 100644 --- a/yazi-plugin/src/utils/utils.rs +++ b/yazi-plugin/src/utils/utils.rs @@ -1,9 +1,11 @@ use mlua::{IntoLua, Lua, Value}; -use yazi_binding::Composer; +use yazi_binding::{Composer, ComposerSet}; pub(super) struct Utils; -pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result { +pub fn compose( + isolate: bool, +) -> Composer mlua::Result, ComposerSet> { fn get(lua: &Lua, key: &[u8], isolate: bool) -> mlua::Result { match key { // App @@ -88,5 +90,5 @@ pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result { fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result { Ok(value) } - Composer::make(lua, move |lua, key| get(lua, key, isolate), set) + Composer::new(move |lua, key| get(lua, key, isolate), set) } diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index 840c6f2a..8137b3a5 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -15,6 +15,7 @@ vendored-lua = [ "mlua/vendored" ] [dependencies] yazi-config = { path = "../yazi-config", version = "25.6.11" } yazi-macro = { path = "../yazi-macro", version = "25.6.11" } +yazi-parser = { path = "../yazi-parser", version = "25.6.11" } yazi-shared = { path = "../yazi-shared", version = "25.6.11" } # External dependencies diff --git a/yazi-proxy/src/app.rs b/yazi-proxy/src/app.rs index 550023e7..f0a518eb 100644 --- a/yazi-proxy/src/app.rs +++ b/yazi-proxy/src/app.rs @@ -2,10 +2,9 @@ use std::time::Duration; use tokio::sync::oneshot; use yazi_macro::emit; +use yazi_parser::app::{NotifyLevel, NotifyOpt, PluginOpt}; use yazi_shared::event::Cmd; -use crate::options::{NotifyLevel, NotifyOpt, PluginOpt}; - pub struct AppProxy; impl AppProxy { diff --git a/yazi-proxy/src/lib.rs b/yazi-proxy/src/lib.rs index 2a987ff9..fc84b981 100644 --- a/yazi-proxy/src/lib.rs +++ b/yazi-proxy/src/lib.rs @@ -1,7 +1,5 @@ mod macros; -yazi_macro::mod_pub!(options); - yazi_macro::mod_flat!(app cmp confirm input mgr pick semaphore tasks); pub fn init() { crate::init_semaphore(); } diff --git a/yazi-proxy/src/macros.rs b/yazi-proxy/src/macros.rs index c94c5719..a2c1d4f4 100644 --- a/yazi-proxy/src/macros.rs +++ b/yazi-proxy/src/macros.rs @@ -3,12 +3,9 @@ macro_rules! deprecate { ($content:expr) => {{ static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { - $crate::AppProxy::notify($crate::options::NotifyOpt { - title: "Deprecated API".to_owned(), - content: $content.to_owned(), - level: $crate::options::NotifyLevel::Warn, - timeout: std::time::Duration::from_secs(20), - }); + $crate::emit!(Call( + yazi_shared::event::Cmd::new("app:deprecate").with("content", format!($tt, id)) + )); } }}; } diff --git a/yazi-proxy/src/mgr.rs b/yazi-proxy/src/mgr.rs index 41953082..36f1b18e 100644 --- a/yazi-proxy/src/mgr.rs +++ b/yazi-proxy/src/mgr.rs @@ -1,10 +1,9 @@ use std::borrow::Cow; use yazi_macro::emit; +use yazi_parser::mgr::{OpenDoOpt, SearchOpt}; use yazi_shared::{SStr, event::Cmd, url::Url}; -use crate::options::{OpenDoOpt, SearchOpt}; - pub struct MgrProxy; impl MgrProxy { diff --git a/yazi-proxy/src/options/cmp.rs b/yazi-proxy/src/options/cmp.rs deleted file mode 100644 index ce7401a4..00000000 --- a/yazi-proxy/src/options/cmp.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::{ffi::OsString, path::MAIN_SEPARATOR_STR}; - -#[derive(Debug, Clone)] -pub struct CmpItem { - pub name: OsString, - pub is_dir: bool, -} - -impl CmpItem { - pub fn completable(&self) -> String { - format!("{}{}", self.name.to_string_lossy(), if self.is_dir { MAIN_SEPARATOR_STR } else { "" }) - } -} diff --git a/yazi-proxy/src/options/mod.rs b/yazi-proxy/src/options/mod.rs deleted file mode 100644 index 4ba644b7..00000000 --- a/yazi-proxy/src/options/mod.rs +++ /dev/null @@ -1 +0,0 @@ -yazi_macro::mod_flat!(cmp notify open plugin process search); diff --git a/yazi-proxy/src/options/open.rs b/yazi-proxy/src/options/open.rs deleted file mode 100644 index f79ac832..00000000 --- a/yazi-proxy/src/options/open.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::borrow::Cow; - -use anyhow::anyhow; -use yazi_config::opener::OpenerRule; -use yazi_shared::{event::CmdCow, url::Url}; - -// --- Open -#[derive(Default)] -pub struct OpenDoOpt { - pub cwd: Url, - pub hovered: Url, - pub targets: Vec<(Url, &'static str)>, - pub interactive: bool, -} - -impl From for OpenDoOpt { - fn from(mut c: CmdCow) -> Self { c.take_any("option").unwrap_or_default() } -} - -// --- Open with -pub struct OpenWithOpt { - pub opener: Cow<'static, OpenerRule>, - pub cwd: Url, - pub targets: Vec, -} - -impl TryFrom for OpenWithOpt { - type Error = anyhow::Error; - - fn try_from(mut c: CmdCow) -> Result { - c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in OpenWithOpt")) - } -} diff --git a/yazi-proxy/src/options/process.rs b/yazi-proxy/src/options/process.rs deleted file mode 100644 index a8e39e09..00000000 --- a/yazi-proxy/src/options/process.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::{borrow::Cow, ffi::OsString}; - -use anyhow::anyhow; -use tokio::sync::oneshot; -use yazi_config::opener::OpenerRule; -use yazi_shared::{event::CmdCow, url::Url}; - -// --- Exec -pub struct ProcessExecOpt { - pub cwd: Url, - pub opener: Cow<'static, OpenerRule>, - pub args: Vec, - pub done: Option>, -} - -impl TryFrom for ProcessExecOpt { - type Error = anyhow::Error; - - fn try_from(mut c: CmdCow) -> Result { - c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in ProcessExecOpt")) - } -} diff --git a/yazi-proxy/src/tasks.rs b/yazi-proxy/src/tasks.rs index b1e642c3..5acfe6f2 100644 --- a/yazi-proxy/src/tasks.rs +++ b/yazi-proxy/src/tasks.rs @@ -3,10 +3,9 @@ use std::{borrow::Cow, ffi::OsString}; use tokio::sync::oneshot; use yazi_config::opener::OpenerRule; use yazi_macro::emit; +use yazi_parser::{mgr::OpenWithOpt, tasks::ProcessExecOpt}; use yazi_shared::{event::Cmd, url::Url}; -use crate::options::{OpenWithOpt, ProcessExecOpt}; - pub struct TasksProxy; impl TasksProxy { diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index b7bc4cd5..1806fe71 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -120,11 +120,12 @@ impl File { self.prog.send(TaskProg::Adv(task.id, 1, cha.len))?; } FileIn::Delete(task) => { - if let Err(e) = fs::remove_file(&task.target).await { - if e.kind() != NotFound && maybe_exists(&task.target).await { - self.fail(task.id, format!("Delete task failed: {task:?}, {e}"))?; - Err(e)? - } + if let Err(e) = fs::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))? } diff --git a/yazi-scheduler/src/plugin/in.rs b/yazi-scheduler/src/plugin/in.rs index 8a7e6c40..e8014646 100644 --- a/yazi-scheduler/src/plugin/in.rs +++ b/yazi-scheduler/src/plugin/in.rs @@ -1,4 +1,4 @@ -use yazi_proxy::options::PluginOpt; +use yazi_parser::app::PluginOpt; use yazi_shared::Id; #[derive(Debug)] diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 0a5b5fa6..c29b9156 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -7,7 +7,8 @@ use tokio::{fs, select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle} use yazi_config::{YAZI, plugin::{Fetcher, Preloader}}; use yazi_dds::Pump; use yazi_fs::{must_be_dir, remove_dir_clean, unique_name}; -use yazi_proxy::{MgrProxy, options::{PluginOpt, ProcessExecOpt}}; +use yazi_parser::{app::PluginOpt, tasks::ProcessExecOpt}; +use yazi_proxy::MgrProxy; use yazi_shared::{Id, Throttle, url::Url}; use super::{Ongoing, TaskProg, TaskStage}; @@ -376,10 +377,10 @@ impl Scheduler { task.succ += succ; task.processed += processed; } - if succ > 0 { - if let Some(fut) = ongoing.try_remove(id, TaskStage::Pending) { - micro.try_send(fut, LOW).ok(); - } + if succ > 0 + && let Some(fut) = ongoing.try_remove(id, TaskStage::Pending) + { + micro.try_send(fut, LOW).ok(); } } TaskProg::Succ(id) => { diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index caa62914..b09a73a6 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -7,13 +7,14 @@ authors = [ "sxyazi " ] description = "Yazi shared library" homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" -rust-version = "1.87.0" +rust-version = "1.88.0" [dependencies] yazi-macro = { path = "../yazi-macro", version = "25.6.11" } # External dependencies anyhow = { workspace = true } +bitflags = { workspace = true } crossterm = { workspace = true } foldhash = { workspace = true } futures = { workspace = true } diff --git a/yazi-shared/src/debounce.rs b/yazi-shared/src/debounce.rs index a4cb6d0f..31df8b25 100644 --- a/yazi-shared/src/debounce.rs +++ b/yazi-shared/src/debounce.rs @@ -35,10 +35,10 @@ where (Pin::new(&mut me.stream), me.interval, Pin::new_unchecked(&mut me.sleep), &mut me.last) }; - if sleep.poll_unpin(cx).is_ready() { - if let Some(last) = last.take() { - return Poll::Ready(Some(last)); - } + if sleep.poll_unpin(cx).is_ready() + && let Some(last) = last.take() + { + return Poll::Ready(Some(last)); } while let Poll::Ready(next) = stream.poll_next_unpin(cx) { diff --git a/yazi-shared/src/lib.rs b/yazi-shared/src/lib.rs index e1eecf6e..797400eb 100644 --- a/yazi-shared/src/lib.rs +++ b/yazi-shared/src/lib.rs @@ -2,7 +2,7 @@ yazi_macro::mod_pub!(errors event shell translit url); -yazi_macro::mod_flat!(alias bytes chars condition debounce either env id layer natsort os osstr rand ro_cell sync_cell terminal throttle time utf8); +yazi_macro::mod_flat!(alias bytes chars condition debounce either env id layer natsort os osstr rand ro_cell source sync_cell terminal throttle time utf8); pub fn init() { LOG_LEVEL.replace(<_>::from(std::env::var("YAZI_LOG").unwrap_or_default())); diff --git a/yazi-shared/src/source.rs b/yazi-shared/src/source.rs new file mode 100644 index 00000000..c6782f6e --- /dev/null +++ b/yazi-shared/src/source.rs @@ -0,0 +1,12 @@ +use bitflags::bitflags; + +bitflags! { + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] + pub struct Source: u8 { + const KEY = 0b00000001; + const EMIT = 0b00000010; + + const ACTOR = 0b00000100; + const PROXY = 0b00001000; + } +} diff --git a/yazi-widgets/src/lib.rs b/yazi-widgets/src/lib.rs index aab44f98..2b44b317 100644 --- a/yazi-widgets/src/lib.rs +++ b/yazi-widgets/src/lib.rs @@ -1,3 +1,5 @@ +#![allow(clippy::if_same_then_else)] + yazi_macro::mod_pub!(input); yazi_macro::mod_flat!(clipboard scrollable);