feat: new fs.access() API to access the filesystem (#3668)

This commit is contained in:
三咲雅 misaki masa 2026-02-09 08:50:01 +08:00 committed by GitHub
parent 0aeeb3b0f7
commit 1a121bbfb0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 174 additions and 108 deletions

View file

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

View file

@ -4,7 +4,7 @@
<br>
<img alt="Warp sponsorship" width="400" src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-02.png">
<br>
<h>Warp, built for coding with multiple AI agents</b>
<b>Warp, built for coding with multiple AI agents</b>
<br>
<sup>Available for macOS, Linux and Windows</sup>
</a>

View file

@ -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<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_function_mut("append", |_, (ud, append): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.append(append);
Ok(ud)
});
methods.add_function_mut("create", |_, (ud, create): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.create(create);
Ok(ud)
});
methods.add_function_mut("create_new", |_, (ud, create_new): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.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::<Self>()?.0.read(read);
Ok(ud)
});
methods.add_function_mut("truncate", |_, (ud, truncate): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.truncate(truncate);
Ok(ud)
});
methods.add_function_mut("write", |_, (ud, write): (AnyUserData, bool)| {
ud.borrow_mut::<Self>()?.0.write(write);
Ok(ud)
});
}
}

23
yazi-binding/src/fd.rs Normal file
View file

@ -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<M: UserDataMethods<Self>>(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),
}
});
}
}

View file

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

View file

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

View file

@ -10,19 +10,20 @@ use yazi_vfs::{VfsFile, provider};
pub fn compose() -> Composer<ComposerGet, ComposerSet> {
fn get(lua: &Lua, key: &[u8]) -> mlua::Result<Value> {
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<ComposerGet, ComposerSet> {
Composer::new(get, set)
}
fn op(lua: &Lua) -> mlua::Result<Function> {
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<Function> {
lua.create_function(|_, ()| Ok(yazi_binding::Access::default()))
}
fn cwd(lua: &Lua) -> mlua::Result<Function> {
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<Function> {
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<Function> {
})
}
fn write(lua: &Lua) -> mlua::Result<Function> {
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<Function> {
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<Function> {
})
}
fn remove(lua: &Lua) -> mlua::Result<Function> {
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<Function> {
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<Function> {
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::<yazi_binding::Url>()?)
&& 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<Function> {
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<Function> {
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<Function> {
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::<mlua::Result<Vec<Table>>>()
})
}
@ -164,37 +189,28 @@ fn read_dir(lua: &Lua) -> mlua::Result<Function> {
})
}
fn calc_size(lua: &Lua) -> mlua::Result<Function> {
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<Function> {
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<Function> {
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::<yazi_binding::Url>()?)
&& 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<Function> {
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<Function> {
})
}
fn partitions(lua: &Lua) -> mlua::Result<Function> {
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::<mlua::Result<Vec<Table>>>()
fn write(lua: &Lua) -> mlua::Result<Function> {
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),
}
})
}

View file

@ -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<Function> {
lua.create_function(|_, ud: AnyUserData| {
match ud.type_id() {
Some(t) if t == TypeId::of::<Fd>() => {}
Some(t) if t == TypeId::of::<ChildStdin>() => {}
Some(t) if t == TypeId::of::<ChildStdout>() => {}
Some(t) if t == TypeId::of::<ChildStderr>() => {}