refactor: invert the dependency relationship between yazi-parser and yazi-proxy (#2992)

This commit is contained in:
三咲雅 misaki masa 2025-07-16 22:39:53 +08:00 committed by sxyazi
parent 548bb0d063
commit 5f8a1496fe
No known key found for this signature in database
77 changed files with 640 additions and 544 deletions

14
Cargo.lock generated
View file

@ -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",

12
flake.lock generated
View file

@ -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": {

View file

@ -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};

View file

@ -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};

View file

@ -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<Id>) -> Result<Self> {
let tab = if let Some(id) = tab {
pub fn new(core: &'a mut Core, cmd: &Cmd) -> Result<Self> {
let tab = if let Some(id) = cmd.id("tab") {
core
.mgr
.tabs

View file

@ -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);

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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};

View file

@ -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()

View file

@ -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

View file

@ -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<Value>;
pub type ComposerSet = fn(&Lua, &[u8], Value) -> mlua::Result<Value>;
pub struct Composer<G, S> {
get: G,
set: S,
cache: HashMap<Vec<u8>, Value>,
get: G,
set: S,
parent: Option<(G, S)>,
cache: HashMap<Vec<u8>, Value>,
}
impl<G, S> Composer<G, S>
@ -12,8 +16,12 @@ where
G: Fn(&Lua, &[u8]) -> mlua::Result<Value> + 'static,
S: Fn(&Lua, &[u8], Value) -> mlua::Result<Value> + 'static,
{
pub fn make(lua: &Lua, get: G, set: S) -> mlua::Result<Value> {
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);
}

View file

@ -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<Value> {
pub fn compose(p_get: ComposerGet, p_set: ComposerSet) -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
match key {
b"Align" => super::Align::compose(lua)?,
@ -26,13 +26,6 @@ pub fn compose(lua: &Lua) -> mlua::Result<Value> {
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<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::with_parent(get, set, p_get, p_set)
}
pub fn render_once<F>(value: Value, buf: &mut ratatui::buffer::Buffer, trans: F)

View file

@ -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<Value> {
let new = lua.create_function(|_, (_, value): (Table, Value)| Line::try_from(value))?;
@ -101,23 +111,23 @@ impl From<Line> for ratatui::text::Line<'static> {
impl UserData for Line {
fn add_methods<M: UserDataMethods<Self>>(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::<Self>()?.inner.alignment = Some(align.0);
ud.borrow_mut::<Self>()?.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::<Self>()?;
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]

View file

@ -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);

View file

@ -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;

View file

@ -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<Value> {
let new = lua.create_function(|_, (_, value): (Table, Value)| Span::try_from(value))?;

View file

@ -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<Value> {
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<Value> {
let f = lua.create_async_function(|lua, ()| async move {
if lua.named_registry_value::<PermitRef<fn()>>("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::<AnyUserData>("HIDE_PERMIT")
})?;
f.into_lua(lua)
}
pub(super) fn width(lua: &Lua) -> mlua::Result<Value> {
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::<Line>() {
Ok(line.inner.width())
} else if let Ok(span) = ud.borrow::<Span>() {
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<Value> {
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::<crate::elements::Rect>("_area")?,
b"preview" => layout.preview = *c.raw_get::<crate::elements::Rect>("_area")?,
b"progress" => layout.progress = *c.raw_get::<crate::elements::Rect>("_area")?,
_ => {}
}
LAYOUT.set(layout);
match c.call_method::<Value>("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<Value> {
let f = lua.create_function(|_, ()| {
render!();
Ok(())
})?;
f.into_lua(lua)
}
pub(super) fn truncate(lua: &Lua) -> mlua::Result<Value> {
fn traverse(
it: impl Iterator<Item = (usize, char)>,
max: usize,
) -> (Option<usize>, 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), "你好,世界");
}
}

View file

@ -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))
));
}
}};
}

View file

@ -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::<std::io::Error>() {
if ioerr.kind() == std::io::ErrorKind::BrokenPipe {
return ExitCode::from(0);
}
if let Some(ioerr) = cause.downcast_ref::<std::io::Error>()
&& ioerr.kind() == std::io::ErrorKind::BrokenPipe
{
return ExitCode::from(0);
}
}
errln!("{e:#}").ok();

View file

@ -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;

View file

@ -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};

View file

@ -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;

View file

@ -1,4 +1,4 @@
use yazi_proxy::options::PluginOpt;
use yazi_parser::app::PluginOpt;
use super::Tasks;

View file

@ -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;

View file

@ -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();

View file

@ -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<Data> {
succ!(self.core.notify.push(NotifyOpt {
title: "Deprecated API".to_owned(),
content: opt.content,
level: NotifyLevel::Warn,
timeout: std::time::Duration::from_secs(20),
}));
}
}

View file

@ -1,6 +1,7 @@
yazi_macro::mod_flat!(
accept_payload
bootstrap
deprecate
mouse
notify
plugin

View file

@ -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;

View file

@ -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};

View file

@ -48,12 +48,13 @@ impl<'a> Executor<'a> {
on!(resize);
on!(stop);
on!(resume);
on!(deprecate);
succ!();
}
fn mgr(&mut self, cmd: CmdCow) -> Result<Data> {
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<Data> {
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<Data> {
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<Data> {
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<Data> {
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<Data> {
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<Data> {
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<Data> {
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<Data> {
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) => {

View file

@ -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

View file

@ -0,0 +1,18 @@
use anyhow::bail;
use yazi_shared::event::CmdCow;
pub struct DeprecateOpt {
pub content: String,
}
impl TryFrom<CmdCow> for DeprecateOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
let Some(content) = c.take_str("content") else {
bail!("Invalid 'content' in DeprecateOpt");
};
Ok(Self { content: content.into_owned() })
}
}

View file

@ -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);

View file

@ -45,6 +45,7 @@ impl TryFrom<mlua::Table> for NotifyOpt {
}
}
// --- Level
#[derive(Clone, Copy, Default, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum NotifyLevel {

View file

@ -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<CmdCow> for ShowOpt {
impl From<Cmd> 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 { "" })
}
}

View file

@ -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

View file

@ -11,6 +11,7 @@ yazi_macro::mod_flat!(
quit
remove
rename
search
seek
spot
tab_close

View file

@ -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<CmdCow> 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<CmdCow> 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<Url>,
}
impl TryFrom<CmdCow> for OpenWithOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in OpenWithOpt"))
}
}

View file

@ -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<OsString>,
pub done: Option<oneshot::Sender<()>>,
}
impl TryFrom<CmdCow> for ProcessExecOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in ProcessExecOpt"))
}
}

View file

@ -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<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
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<Value> { Ok(value) }
yazi_binding::elements::compose(get, set)
}
pub(super) fn area(lua: &Lua) -> mlua::Result<Value> {
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<Value> {
let f = lua.create_async_function(|lua, ()| async move {
if lua.named_registry_value::<PermitRef<fn()>>("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::<AnyUserData>("HIDE_PERMIT")
})?;
f.into_lua(lua)
}
pub(super) fn width(lua: &Lua) -> mlua::Result<Value> {
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::<Line>() {
Ok(line.width())
} else if let Ok(span) = ud.borrow::<Span>() {
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<Value> {
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::<Rect>("_area")?,
b"preview" => layout.preview = *c.raw_get::<Rect>("_area")?,
b"progress" => layout.progress = *c.raw_get::<Rect>("_area")?,
_ => {}
}
LAYOUT.set(layout);
match c.call_method::<Value>("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<Value> {
let f = lua.create_function(|_, ()| {
yazi_macro::render!();
Ok(())
})?;
f.into_lua(lua)
}
pub(super) fn truncate(lua: &Lua) -> mlua::Result<Value> {
fn traverse(it: impl Iterator<Item = (usize, char)>, max: usize) -> (Option<usize>, 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), "你好,世界");
}
}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(elements);

View file

@ -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<Value> {
pub fn compose() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
match key {
b"op" => op(lua)?,
@ -27,7 +27,7 @@ pub fn compose(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn op(lua: &Lua) -> mlua::Result<Function> {

View file

@ -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;

View file

@ -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<Lua> {
// 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)?;

View file

@ -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}");
}
});
}

View file

@ -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) {

View file

@ -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);

View file

@ -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)?;

View file

@ -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<Value> {
pub(super) fn compose() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
match key {
b"pub" => Pubsub::r#pub(lua)?,
@ -21,5 +21,5 @@ pub(super) fn compose(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}

View file

@ -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<Value> {
pub(super) fn plugin() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
match key {
b"fetchers" => fetchers(lua)?,
@ -16,7 +16,7 @@ pub(super) fn plugin(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn fetchers(lua: &Lua) -> mlua::Result<Function> {

View file

@ -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<Value> {
pub fn compose() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
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<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn args(lua: &Lua) -> mlua::Result<Value> {
fn args() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
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<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn mgr(lua: &Lua) -> mlua::Result<Value> {
fn mgr() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let m = &YAZI.mgr;
match key {
@ -74,10 +74,10 @@ fn mgr(lua: &Lua) -> mlua::Result<Value> {
})
}
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn preview(lua: &Lua) -> mlua::Result<Value> {
fn preview() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let p = &YAZI.preview;
match key {
@ -101,10 +101,10 @@ fn preview(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn tasks(lua: &Lua) -> mlua::Result<Value> {
fn tasks() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &YAZI.tasks;
match key {
@ -123,5 +123,5 @@ fn tasks(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}

View file

@ -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<Value> {
pub(super) fn term() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
match key {
b"light" => EMULATOR.get().light.into_lua(lua),
@ -13,7 +13,7 @@ pub(super) fn term(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn cell_size(lua: &Lua) -> mlua::Result<Function> {

View file

@ -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<Value> {
pub fn compose() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
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<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn mgr(lua: &Lua) -> mlua::Result<Value> {
fn mgr() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let m = &THEME.mgr;
match key {
@ -60,10 +61,10 @@ fn mgr(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn tabs(lua: &Lua) -> mlua::Result<Value> {
fn tabs() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.tabs;
match key {
@ -89,10 +90,10 @@ fn tabs(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn mode(lua: &Lua) -> mlua::Result<Value> {
fn mode() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.mode;
match key {
@ -111,10 +112,10 @@ fn mode(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn status(lua: &Lua) -> mlua::Result<Value> {
fn status() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.status;
match key {
@ -148,10 +149,10 @@ fn status(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn which(lua: &Lua) -> mlua::Result<Value> {
fn which() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.which;
match key {
@ -170,10 +171,10 @@ fn which(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn confirm(lua: &Lua) -> mlua::Result<Value> {
fn confirm() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.confirm;
match key {
@ -197,10 +198,10 @@ fn confirm(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn spot(lua: &Lua) -> mlua::Result<Value> {
fn spot() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.spot;
match key {
@ -216,10 +217,10 @@ fn spot(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn notify(lua: &Lua) -> mlua::Result<Value> {
fn notify() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.notify;
match key {
@ -237,10 +238,10 @@ fn notify(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn pick(lua: &Lua) -> mlua::Result<Value> {
fn pick() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.pick;
match key {
@ -254,10 +255,10 @@ fn pick(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn input(lua: &Lua) -> mlua::Result<Value> {
fn input() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.input;
match key {
@ -272,10 +273,10 @@ fn input(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn cmp(lua: &Lua) -> mlua::Result<Value> {
fn cmp() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.cmp;
match key {
@ -293,10 +294,10 @@ fn cmp(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn tasks(lua: &Lua) -> mlua::Result<Value> {
fn tasks() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.tasks;
match key {
@ -310,10 +311,10 @@ fn tasks(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}
fn help(lua: &Lua) -> mlua::Result<Value> {
fn help() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
let t = &THEME.help;
match key {
@ -330,5 +331,5 @@ fn help(lua: &Lua) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, get, set)
Composer::new(get, set)
}

View file

@ -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;

View file

@ -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<Value> {
pub fn compose(
isolate: bool,
) -> Composer<impl Fn(&Lua, &[u8]) -> mlua::Result<Value>, ComposerSet> {
fn get(lua: &Lua, key: &[u8], isolate: bool) -> mlua::Result<Value> {
match key {
// App
@ -88,5 +90,5 @@ pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result<Value> {
fn set(_: &Lua, _: &[u8], value: Value) -> mlua::Result<Value> { Ok(value) }
Composer::make(lua, move |lua, key| get(lua, key, isolate), set)
Composer::new(move |lua, key| get(lua, key, isolate), set)
}

View file

@ -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

View file

@ -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 {

View file

@ -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(); }

View file

@ -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))
));
}
}};
}

View file

@ -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 {

View file

@ -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 { "" })
}
}

View file

@ -1 +0,0 @@
yazi_macro::mod_flat!(cmp notify open plugin process search);

View file

@ -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<CmdCow> 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<Url>,
}
impl TryFrom<CmdCow> for OpenWithOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in OpenWithOpt"))
}
}

View file

@ -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<OsString>,
pub done: Option<oneshot::Sender<()>>,
}
impl TryFrom<CmdCow> for ProcessExecOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
c.take_any("option").ok_or_else(|| anyhow!("Missing 'option' in ProcessExecOpt"))
}
}

View file

@ -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 {

View file

@ -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))?
}

View file

@ -1,4 +1,4 @@
use yazi_proxy::options::PluginOpt;
use yazi_parser::app::PluginOpt;
use yazi_shared::Id;
#[derive(Debug)]

View file

@ -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) => {

View file

@ -7,13 +7,14 @@ authors = [ "sxyazi <sxyazi@gmail.com>" ]
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 }

View file

@ -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) {

View file

@ -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()));

12
yazi-shared/src/source.rs Normal file
View file

@ -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;
}
}

View file

@ -1,3 +1,5 @@
#![allow(clippy::if_same_then_else)]
yazi_macro::mod_pub!(input);
yazi_macro::mod_flat!(clipboard scrollable);