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, 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::() => {}