diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e959026..fd9268fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): ### Added +- New `fs.access()` API to access the filesystem ([#3668]) - Allow using `ps.sub()` in `init.lua` directly without a plugin ([#3638]) - New `relay-notify-push` DDS event to allow custom notification handlers ([#3642]) - Custom tab name ([#3666]) @@ -1645,3 +1646,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3648]: https://github.com/sxyazi/yazi/pull/3648 [#3661]: https://github.com/sxyazi/yazi/pull/3661 [#3666]: https://github.com/sxyazi/yazi/pull/3666 +[#3668]: https://github.com/sxyazi/yazi/pull/3668 diff --git a/README.md b/README.md index 2d6dfff5..1dd2105c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@
Warp sponsorship
- Warp, built for coding with multiple AI agents + Warp, built for coding with multiple AI agents
Available for macOS, Linux and Windows diff --git a/yazi-binding/src/access.rs b/yazi-binding/src/access.rs new file mode 100644 index 00000000..bb8a1d85 --- /dev/null +++ b/yazi-binding/src/access.rs @@ -0,0 +1,42 @@ +use mlua::{AnyUserData, IntoLuaMulti, UserData, UserDataMethods, Value}; +use yazi_fs::provider::FileBuilder; + +use crate::{Error, Fd, UrlRef}; + +#[derive(Default)] +pub struct Access(yazi_vfs::provider::Gate); + +impl UserData for Access { + fn add_methods>(methods: &mut M) { + methods.add_function_mut("append", |_, (ud, append): (AnyUserData, bool)| { + ud.borrow_mut::()?.0.append(append); + Ok(ud) + }); + methods.add_function_mut("create", |_, (ud, create): (AnyUserData, bool)| { + ud.borrow_mut::()?.0.create(create); + Ok(ud) + }); + methods.add_function_mut("create_new", |_, (ud, create_new): (AnyUserData, bool)| { + ud.borrow_mut::()?.0.create_new(create_new); + Ok(ud) + }); + methods.add_async_method("open", |lua, me, url: UrlRef| async move { + match me.0.open(&*url).await { + Ok(fd) => Fd(fd).into_lua_multi(&lua), + Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), + } + }); + methods.add_function_mut("read", |_, (ud, read): (AnyUserData, bool)| { + ud.borrow_mut::()?.0.read(read); + Ok(ud) + }); + methods.add_function_mut("truncate", |_, (ud, truncate): (AnyUserData, bool)| { + ud.borrow_mut::()?.0.truncate(truncate); + Ok(ud) + }); + methods.add_function_mut("write", |_, (ud, write): (AnyUserData, bool)| { + ud.borrow_mut::()?.0.write(write); + Ok(ud) + }); + } +} diff --git a/yazi-binding/src/fd.rs b/yazi-binding/src/fd.rs new file mode 100644 index 00000000..62185505 --- /dev/null +++ b/yazi-binding/src/fd.rs @@ -0,0 +1,23 @@ +use mlua::{IntoLuaMulti, UserData, UserDataMethods}; +use tokio::io::AsyncWriteExt; + +use crate::Error; + +pub struct Fd(pub yazi_vfs::provider::RwFile); + +impl UserData for Fd { + fn add_methods>(methods: &mut M) { + methods.add_async_method_mut("write_all", |lua, mut me, src: mlua::String| async move { + match me.0.write_all(&*src.as_bytes()).await { + Ok(()) => true.into_lua_multi(&lua), + Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), + } + }); + methods.add_async_method_mut("flush", |lua, mut me, ()| async move { + match me.0.flush().await { + Ok(()) => true.into_lua_multi(&lua), + Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), + } + }); + } +} diff --git a/yazi-binding/src/lib.rs b/yazi-binding/src/lib.rs index 91516489..29e2a050 100644 --- a/yazi-binding/src/lib.rs +++ b/yazi-binding/src/lib.rs @@ -2,4 +2,4 @@ mod macros; yazi_macro::mod_pub!(elements); -yazi_macro::mod_flat!(calculator cha chan chord_cow color composer error file handle icon id image input iter layer mouse path permit range runtime scheme stage style url utils); +yazi_macro::mod_flat!(access calculator cha chan chord_cow color composer error fd file handle icon id image input iter layer mouse path permit range runtime scheme stage style url utils); diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index fc8c9d60..494d06a4 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -50,28 +50,21 @@ function Header:flags() end function Header:count() - local yanked = #cx.yanked + local selected = #self._tab.selected + local yanked = selected > 0 and 0 or #cx.yanked - local count, style - if yanked == 0 then - count = #self._tab.selected - style = th.mgr.count_selected - elseif cx.yanked.is_cut then - count = yanked - style = th.mgr.count_cut - else - count = yanked - style = th.mgr.count_copied - end - - if count == 0 then + local span + if selected > 0 then + span = ui.Span(" " .. selected .. " "):style(th.mgr.count_selected) + elseif yanked <= 0 then return "" + elseif cx.yanked.is_cut then + span = ui.Span(" " .. yanked .. " "):style(th.mgr.count_cut) + else + span = ui.Span(" " .. yanked .. " "):style(th.mgr.count_copied) end - return ui.Line { - ui.Span(string.format(" %d ", count)):style(style), - " ", - } + return ui.Line { span, " " } end function Header:reflow() return { self } end diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index 12fdb041..330e0fea 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -10,19 +10,20 @@ use yazi_vfs::{VfsFile, provider}; pub fn compose() -> Composer { fn get(lua: &Lua, key: &[u8]) -> mlua::Result { match key { - b"op" => op(lua)?, - b"cwd" => cwd(lua)?, + b"access" => access(lua)?, + b"calc_size" => calc_size(lua)?, b"cha" => cha(lua)?, b"copy" => copy(lua)?, - b"write" => write(lua)?, b"create" => create(lua)?, + b"cwd" => cwd(lua)?, + b"expand_url" => expand_url(lua)?, + b"op" => op(lua)?, + b"partitions" => partitions(lua)?, + b"read_dir" => read_dir(lua)?, b"remove" => remove(lua)?, b"rename" => rename(lua)?, - b"read_dir" => read_dir(lua)?, - b"calc_size" => calc_size(lua)?, - b"expand_url" => expand_url(lua)?, b"unique_name" => unique_name(lua)?, - b"partitions" => partitions(lua)?, + b"write" => write(lua)?, _ => return Ok(Value::Nil), } .into_lua(lua) @@ -33,19 +34,22 @@ pub fn compose() -> Composer { Composer::new(get, set) } -fn op(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, (name, t): (mlua::String, Table)| match &*name.as_bytes() { - b"part" => super::FilesOp::part(lua, t), - b"done" => super::FilesOp::done(lua, t), - b"size" => super::FilesOp::size(lua, t), - _ => Err("Unknown operation".into_lua_err())?, - }) +fn access(lua: &Lua) -> mlua::Result { + lua.create_function(|_, ()| Ok(yazi_binding::Access::default())) } -fn cwd(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, ()| match std::env::current_dir() { - Ok(p) => Url::new(p).into_lua_multi(lua), - Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(lua), +fn calc_size(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, url: UrlRef| async move { + let it = if let Some(path) = url.as_local() { + yazi_fs::provider::local::SizeCalculator::new(path).await.map(SizeCalculator::Local) + } else { + yazi_vfs::provider::SizeCalculator::new(&*url).await.map(SizeCalculator::Remote) + }; + + match it { + Ok(it) => it.into_lua_multi(&lua), + Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), + } }) } @@ -73,15 +77,6 @@ fn copy(lua: &Lua) -> mlua::Result { }) } -fn write(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move { - match provider::write(&*url, data.as_bytes()).await { - Ok(()) => true.into_lua_multi(&lua), - Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), - } - }) -} - fn create(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, (r#type, url): (mlua::String, UrlRef)| async move { let result = match &*r#type.as_bytes() { @@ -97,29 +92,59 @@ fn create(lua: &Lua) -> mlua::Result { }) } -fn remove(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, (r#type, url): (mlua::String, UrlRef)| async move { - let result = match &*r#type.as_bytes() { - b"file" => provider::remove_file(&*url).await, - b"dir" => provider::remove_dir(&*url).await, - b"dir_all" => provider::remove_dir_all(&*url).await, - b"dir_clean" => provider::remove_dir_clean(&*url).await, - _ => Err("Removal type must be 'file', 'dir', 'dir_all', or 'dir_clean'".into_lua_err())?, - }; +fn cwd(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, ()| match std::env::current_dir() { + Ok(p) => Url::new(p).into_lua_multi(lua), + Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(lua), + }) +} - match result { - Ok(()) => true.into_lua_multi(&lua), - Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), +#[allow(irrefutable_let_patterns)] +fn expand_url(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, value: Value| { + use yazi_fs::path::expand_url; + match &value { + Value::String(s) => Url::new(expand_url(UrlCow::try_from(&*s.as_bytes())?)).into_lua(lua), + Value::UserData(ud) => { + if let u = expand_url(&*ud.borrow::()?) + && u.is_owned() + { + Url::new(u.into_owned()).into_lua(lua) + } else { + Ok(value) + } + } + _ => Err("must be a string or a Url".into_lua_err())?, } }) } -fn rename(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, (from, to): (UrlRef, UrlRef)| async move { - match provider::rename(&*from, &*to).await { - Ok(()) => true.into_lua_multi(&lua), - Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), - } +fn op(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, (name, t): (mlua::String, Table)| match &*name.as_bytes() { + b"part" => super::FilesOp::part(lua, t), + b"done" => super::FilesOp::done(lua, t), + b"size" => super::FilesOp::size(lua, t), + _ => Err("Unknown operation".into_lua_err())?, + }) +} + +fn partitions(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, ()| async move { + PARTITIONS + .read() + .iter() + .filter(|&p| !p.systemic()) + .map(|p| { + lua.create_table_from([ + ("src", p.src.clone().into_lua(&lua)?), + ("dist", p.dist.clone().into_lua(&lua)?), + ("label", p.label.clone().into_lua(&lua)?), + ("fstype", p.fstype.clone().into_lua(&lua)?), + ("external", p.external.into_lua(&lua)?), + ("removable", p.removable.into_lua(&lua)?), + ]) + }) + .collect::>>() }) } @@ -164,37 +189,28 @@ fn read_dir(lua: &Lua) -> mlua::Result { }) } -fn calc_size(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, url: UrlRef| async move { - let it = if let Some(path) = url.as_local() { - yazi_fs::provider::local::SizeCalculator::new(path).await.map(SizeCalculator::Local) - } else { - yazi_vfs::provider::SizeCalculator::new(&*url).await.map(SizeCalculator::Remote) +fn remove(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, (r#type, url): (mlua::String, UrlRef)| async move { + let result = match &*r#type.as_bytes() { + b"file" => provider::remove_file(&*url).await, + b"dir" => provider::remove_dir(&*url).await, + b"dir_all" => provider::remove_dir_all(&*url).await, + b"dir_clean" => provider::remove_dir_clean(&*url).await, + _ => Err("Removal type must be 'file', 'dir', 'dir_all', or 'dir_clean'".into_lua_err())?, }; - match it { - Ok(it) => it.into_lua_multi(&lua), - Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua), + match result { + Ok(()) => true.into_lua_multi(&lua), + Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), } }) } -#[allow(irrefutable_let_patterns)] -fn expand_url(lua: &Lua) -> mlua::Result { - lua.create_function(|lua, value: Value| { - use yazi_fs::path::expand_url; - match &value { - Value::String(s) => Url::new(expand_url(UrlCow::try_from(&*s.as_bytes())?)).into_lua(lua), - Value::UserData(ud) => { - if let u = expand_url(&*ud.borrow::()?) - && u.is_owned() - { - Url::new(u.into_owned()).into_lua(lua) - } else { - Ok(value) - } - } - _ => Err("must be a string or a Url".into_lua_err())?, +fn rename(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, (from, to): (UrlRef, UrlRef)| async move { + match provider::rename(&*from, &*to).await { + Ok(()) => true.into_lua_multi(&lua), + Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), } }) } @@ -208,22 +224,11 @@ fn unique_name(lua: &Lua) -> mlua::Result { }) } -fn partitions(lua: &Lua) -> mlua::Result { - lua.create_async_function(|lua, ()| async move { - PARTITIONS - .read() - .iter() - .filter(|&p| !p.systemic()) - .map(|p| { - lua.create_table_from([ - ("src", p.src.clone().into_lua(&lua)?), - ("dist", p.dist.clone().into_lua(&lua)?), - ("label", p.label.clone().into_lua(&lua)?), - ("fstype", p.fstype.clone().into_lua(&lua)?), - ("external", p.external.into_lua(&lua)?), - ("removable", p.removable.into_lua(&lua)?), - ]) - }) - .collect::>>() +fn write(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move { + match provider::write(&*url, data.as_bytes()).await { + Ok(()) => true.into_lua_multi(&lua), + Err(e) => (false, Error::Io(e)).into_lua_multi(&lua), + } }) } diff --git a/yazi-plugin/src/utils/app.rs b/yazi-plugin/src/utils/app.rs index 8481871b..9dd56649 100644 --- a/yazi-plugin/src/utils/app.rs +++ b/yazi-plugin/src/utils/app.rs @@ -2,7 +2,7 @@ use std::any::TypeId; use mlua::{AnyUserData, ExternalError, Function, Lua}; use tokio::process::{ChildStderr, ChildStdin, ChildStdout}; -use yazi_binding::Id; +use yazi_binding::{Fd, Id}; use super::Utils; @@ -20,6 +20,7 @@ impl Utils { pub(super) fn drop(lua: &Lua) -> mlua::Result { lua.create_function(|_, ud: AnyUserData| { match ud.type_id() { + Some(t) if t == TypeId::of::() => {} Some(t) if t == TypeId::of::() => {} Some(t) if t == TypeId::of::() => {} Some(t) if t == TypeId::of::() => {}