mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-24 16:21:04 +00:00
feat: new experimental ya.async() API (#3422)
This commit is contained in:
parent
85a99937a1
commit
ac7718c226
19 changed files with 153 additions and 89 deletions
|
|
@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
|||
- Zoom in or out of the preview image ([#2864])
|
||||
- Improve the UX of the pick and input components ([#2906], [#2935])
|
||||
- Show progress of each task in task manager ([#3121], [#3131], [#3134])
|
||||
- New experimental `ya.async()` API ([#3422])
|
||||
- New `overall` option to set the overall background color ([#3317])
|
||||
- Rounded corners for indicator bar ([#3419])
|
||||
- New `bulk_rename` command always renames files with the editor ([#2984])
|
||||
|
|
@ -1554,3 +1555,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
|
|||
[#3393]: https://github.com/sxyazi/yazi/pull/3393
|
||||
[#3396]: https://github.com/sxyazi/yazi/pull/3396
|
||||
[#3419]: https://github.com/sxyazi/yazi/pull/3419
|
||||
[#3422]: https://github.com/sxyazi/yazi/pull/3422
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods, Value};
|
||||
use yazi_binding::Style;
|
||||
use yazi_binding::{Style, cached_field};
|
||||
use yazi_config::THEME;
|
||||
use yazi_plugin::bindings::Range;
|
||||
use yazi_shared::{path::AsPath, url::UrlLike};
|
||||
|
|
@ -20,6 +20,8 @@ pub(super) struct File {
|
|||
|
||||
v_name: Option<Value>,
|
||||
v_cache: Option<Value>,
|
||||
|
||||
v_bare: Option<Value>,
|
||||
}
|
||||
|
||||
impl Deref for File {
|
||||
|
|
@ -54,6 +56,8 @@ impl File {
|
|||
|
||||
v_name: None,
|
||||
v_cache: None,
|
||||
|
||||
v_bare: None,
|
||||
})?;
|
||||
ve.insert(ud.clone());
|
||||
ud
|
||||
|
|
@ -65,6 +69,7 @@ impl File {
|
|||
impl UserData for File {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
yazi_binding::impl_file_fields!(fields);
|
||||
cached_field!(fields, bare, |_, me| Ok(yazi_binding::File::new(&**me)));
|
||||
|
||||
fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1));
|
||||
fields.add_field_method_get("is_hovered", |_, me| Ok(me.idx == me.folder.cursor));
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{ExternalError, FromLua, Lua, Table, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
use mlua::{AnyUserData, ExternalError, FromLua, Lua, ObjectLike, Table, UserData, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
|
||||
use crate::{Cha, Url, impl_file_fields, impl_file_methods};
|
||||
|
||||
pub type FileRef = UserDataRef<File>;
|
||||
|
||||
const EXPECTED: &str = "expected a table, File, or fs::File";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct File {
|
||||
inner: yazi_fs::File,
|
||||
|
||||
|
|
@ -28,23 +31,51 @@ impl From<File> for yazi_fs::File {
|
|||
}
|
||||
|
||||
impl File {
|
||||
pub fn new(inner: yazi_fs::File) -> Self {
|
||||
Self { inner, v_cha: None, v_url: None, v_link_to: None, v_name: None, v_cache: None }
|
||||
pub fn new(inner: impl Into<yazi_fs::File>) -> Self {
|
||||
Self {
|
||||
inner: inner.into(),
|
||||
v_cha: None,
|
||||
v_url: None,
|
||||
v_link_to: None,
|
||||
v_name: None,
|
||||
v_cache: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.globals().raw_set("File", lua.create_function(|_, value: Value| Self::try_from(value))?)
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.globals().raw_set(
|
||||
"File",
|
||||
lua.create_function(|_, t: Table| {
|
||||
Ok(Self::new(yazi_fs::File {
|
||||
url: t.raw_get::<Url>("url")?.into(),
|
||||
cha: *t.raw_get::<Cha>("cha")?,
|
||||
..Default::default()
|
||||
}))
|
||||
})?,
|
||||
)
|
||||
impl TryFrom<Value> for File {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Value::Table(tbl) => Self::try_from(tbl),
|
||||
Value::UserData(ud) => Self::try_from(ud),
|
||||
_ => Err(EXPECTED.into_lua_err())?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Table> for File {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(value: Table) -> Result<Self, Self::Error> {
|
||||
Ok(Self::new(yazi_fs::File {
|
||||
url: value.raw_get::<Url>("url")?.into(),
|
||||
cha: *value.raw_get::<Cha>("cha")?,
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<AnyUserData> for File {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(value: AnyUserData) -> Result<Self, Self::Error> {
|
||||
Ok(if let Ok(me) = value.borrow::<Self>() { me.clone() } else { value.get("bare")? })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +83,7 @@ impl FromLua for File {
|
|||
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
|
||||
Ok(match value {
|
||||
Value::UserData(ud) => Self::new(ud.take::<Self>()?.inner),
|
||||
_ => Err("Expected a File".into_lua_err())?,
|
||||
_ => Err("expected a File".into_lua_err())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
yazi-binding/src/handle.rs
Normal file
16
yazi-binding/src/handle.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use mlua::{MultiValue, UserData, UserDataMethods};
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
pub enum Handle {
|
||||
AsyncFn(JoinHandle<mlua::Result<MultiValue>>),
|
||||
}
|
||||
|
||||
impl UserData for Handle {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("abort", |_, me, ()| {
|
||||
Ok(match me {
|
||||
Self::AsyncFn(h) => h.abort(),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -2,4 +2,4 @@ mod macros;
|
|||
|
||||
yazi_macro::mod_pub!(elements);
|
||||
|
||||
yazi_macro::mod_flat!(cha color composer error file icon id iter path permit runtime scheme stage style url utils);
|
||||
yazi_macro::mod_flat!(cha color composer error file handle icon id iter path permit runtime scheme stage style url utils);
|
||||
|
|
|
|||
|
|
@ -6,10 +6,14 @@ use std::process::ExitCode;
|
|||
|
||||
use clap::Parser;
|
||||
use yazi_macro::{errln, outln};
|
||||
use yazi_shared::LOCAL_SET;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> ExitCode {
|
||||
match run().await {
|
||||
yazi_shared::init();
|
||||
yazi_fs::init();
|
||||
|
||||
match LOCAL_SET.run_until(run()).await {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(e) => {
|
||||
for cause in e.chain() {
|
||||
|
|
@ -26,9 +30,6 @@ async fn main() -> ExitCode {
|
|||
}
|
||||
|
||||
async fn run() -> anyhow::Result<()> {
|
||||
yazi_shared::init();
|
||||
yazi_fs::init();
|
||||
|
||||
if std::env::args_os().nth(1).is_some_and(|s| s == "-V" || s == "--version") {
|
||||
outln!(
|
||||
"Ya {} ({} {})",
|
||||
|
|
|
|||
|
|
@ -37,5 +37,6 @@ async fn main() -> anyhow::Result<()> {
|
|||
yazi_plugin::init()?;
|
||||
|
||||
yazi_dds::serve();
|
||||
app::App::serve().await
|
||||
|
||||
yazi_shared::LOCAL_SET.run_until(app::App::serve()).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ impl Deref for File {
|
|||
fn deref(&self) -> &Self::Target { &self.cha }
|
||||
}
|
||||
|
||||
impl From<&File> for File {
|
||||
fn from(value: &File) -> Self { value.clone() }
|
||||
}
|
||||
|
||||
impl File {
|
||||
#[inline]
|
||||
pub fn from_dummy(url: impl Into<UrlBuf>, r#type: Option<ChaType>) -> Self {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Entity = {
|
|||
function Entity:new(file) return setmetatable({ _file = file }, { __index = self }) end
|
||||
|
||||
function Entity:padding()
|
||||
if not self._file.is_hovered or self._file.in_preview then
|
||||
if not self._file.is_hovered then
|
||||
return " "
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,19 +8,6 @@ Linemode = {
|
|||
|
||||
function Linemode:new(file) return setmetatable({ _file = file }, { __index = self }) end
|
||||
|
||||
function Linemode:padding()
|
||||
if not self._file.is_hovered then
|
||||
return " "
|
||||
end
|
||||
|
||||
local style = Entity:new(self._file):style_rev()
|
||||
if style then
|
||||
return ui.Span(th.indicator.padding.close):style(style)
|
||||
else
|
||||
return " "
|
||||
end
|
||||
end
|
||||
|
||||
function Linemode:solo()
|
||||
if not self._file.in_current then
|
||||
return ""
|
||||
|
|
@ -77,6 +64,19 @@ function Linemode:owner()
|
|||
return string.format("%s:%s", user, group)
|
||||
end
|
||||
|
||||
function Linemode:padding()
|
||||
if not self._file.is_hovered then
|
||||
return " "
|
||||
end
|
||||
|
||||
local style = Entity:new(self._file):style_rev()
|
||||
if style then
|
||||
return ui.Span(th.indicator.padding.close):style(style)
|
||||
else
|
||||
return " "
|
||||
end
|
||||
end
|
||||
|
||||
function Linemode:redraw()
|
||||
local lines = {}
|
||||
for _, c in ipairs(self._children) do
|
||||
|
|
|
|||
|
|
@ -21,17 +21,18 @@ function M:peek(job)
|
|||
return ya.preview_widget(job, ui.Text(s):area(job.area):align(ui.Align.CENTER):wrap(ui.Wrap.YES))
|
||||
end
|
||||
|
||||
local items = {}
|
||||
local left, right = {}, {}
|
||||
for _, f in ipairs(folder.window) do
|
||||
local entity = Entity:new(f)
|
||||
items[#items + 1] = entity:redraw():truncate {
|
||||
max = job.area.w,
|
||||
ellipsis = entity:ellipsis(job.area.w),
|
||||
}
|
||||
left[#left + 1], right[#right + 1] = entity:redraw(), Linemode:new(f):redraw()
|
||||
|
||||
local max = math.max(0, job.area.w - right[#right]:width())
|
||||
left[#left]:truncate { max = max, ellipsis = entity:ellipsis(max) }
|
||||
end
|
||||
|
||||
ya.preview_widget(job, {
|
||||
ui.List(items):area(job.area),
|
||||
ui.List(left):area(job.area),
|
||||
ui.Text(right):area(job.area):align(ui.Align.RIGHT),
|
||||
table.unpack(Marker:new(job.area, folder):redraw()),
|
||||
})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub fn slim_lua(name: &str) -> mlua::Result<Lua> {
|
|||
yazi_binding::Url::install(&lua)?;
|
||||
|
||||
yazi_binding::Error::install(&lua)?;
|
||||
crate::loader::install_isolate(&lua)?;
|
||||
crate::loader::install(&lua)?;
|
||||
crate::process::install(&lua)?;
|
||||
|
||||
// Addons
|
||||
|
|
|
|||
|
|
@ -3,5 +3,3 @@ yazi_macro::mod_flat!(chunk loader require);
|
|||
pub(super) fn init() { LOADER.with(<_>::default); }
|
||||
|
||||
pub(super) fn install(lua: &mlua::Lua) -> mlua::Result<()> { Require::install(lua) }
|
||||
|
||||
pub(super) fn install_isolate(lua: &mlua::Lua) -> mlua::Result<()> { Require::install_isolate(lua) }
|
||||
|
|
|
|||
|
|
@ -9,23 +9,6 @@ pub(super) struct Require;
|
|||
|
||||
impl Require {
|
||||
pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.globals().raw_set(
|
||||
"require",
|
||||
lua.create_function(|lua, id: mlua::String| {
|
||||
let id = id.to_str()?;
|
||||
let id = Self::absolute_id(lua, &id)?;
|
||||
futures::executor::block_on(LOADER.ensure(&id, |_| ())).into_lua_err()?;
|
||||
|
||||
runtime_mut!(lua)?.push(&id);
|
||||
let mod_ = LOADER.load(lua, &id);
|
||||
runtime_mut!(lua)?.pop();
|
||||
|
||||
Self::create_mt(lua, id.into_owned(), mod_?, true)
|
||||
})?,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn install_isolate(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.globals().raw_set(
|
||||
"require",
|
||||
lua.create_async_function(|lua, id: mlua::String| async move {
|
||||
|
|
@ -37,12 +20,12 @@ impl Require {
|
|||
let mod_ = LOADER.load(&lua, &id);
|
||||
runtime_mut!(lua)?.pop();
|
||||
|
||||
Self::create_mt(&lua, id.into_owned(), mod_?, false)
|
||||
Self::create_mt(&lua, id.into_owned(), mod_?)
|
||||
})?,
|
||||
)
|
||||
}
|
||||
|
||||
fn create_mt(lua: &Lua, id: String, r#mod: Table, sync: bool) -> mlua::Result<Table> {
|
||||
fn create_mt(lua: &Lua, id: String, r#mod: Table) -> mlua::Result<Table> {
|
||||
let id: Arc<str> = Arc::from(id);
|
||||
let mt = lua.create_table_from([
|
||||
(
|
||||
|
|
@ -50,7 +33,7 @@ impl Require {
|
|||
lua.create_function(move |lua, (ts, key): (Table, mlua::String)| {
|
||||
match ts.raw_get::<Table>("__mod")?.raw_get::<Value>(&key)? {
|
||||
Value::Function(_) => {
|
||||
Self::create_wrapper(lua, id.clone(), &key.to_str()?, sync)?.into_lua(lua)
|
||||
Self::create_wrapper(lua, id.clone(), &key.to_str()?)?.into_lua(lua)
|
||||
}
|
||||
v => Ok(v),
|
||||
}
|
||||
|
|
@ -69,29 +52,19 @@ impl Require {
|
|||
Ok(ts)
|
||||
}
|
||||
|
||||
fn create_wrapper(lua: &Lua, id: Arc<str>, f: &str, sync: bool) -> mlua::Result<Function> {
|
||||
fn create_wrapper(lua: &Lua, id: Arc<str>, f: &str) -> mlua::Result<Function> {
|
||||
let f: Arc<str> = Arc::from(f);
|
||||
|
||||
if sync {
|
||||
lua.create_function(move |lua, args: MultiValue| {
|
||||
let (r#mod, args) = Self::split_mod_and_args(lua, &id, args)?;
|
||||
lua.create_async_function(move |lua, args: MultiValue| {
|
||||
let (id, f) = (id.clone(), f.clone());
|
||||
async move {
|
||||
let (r#mod, args) = Self::split_mod_and_args(&lua, &id, args)?;
|
||||
runtime_mut!(lua)?.push(&id);
|
||||
let result = r#mod.call_function::<MultiValue>(&f, args);
|
||||
let result = r#mod.call_async_function::<MultiValue>(&f, args).await;
|
||||
runtime_mut!(lua)?.pop();
|
||||
result
|
||||
})
|
||||
} else {
|
||||
lua.create_async_function(move |lua, args: MultiValue| {
|
||||
let (id, f) = (id.clone(), f.clone());
|
||||
async move {
|
||||
let (r#mod, args) = Self::split_mod_and_args(&lua, &id, args)?;
|
||||
runtime_mut!(lua)?.push(&id);
|
||||
let result = r#mod.call_async_function::<MultiValue>(&f, args).await;
|
||||
runtime_mut!(lua)?.pop();
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn split_mod_and_args(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use anyhow::{Context, Result};
|
||||
use futures::executor::block_on;
|
||||
use mlua::Lua;
|
||||
use yazi_binding::Runtime;
|
||||
use yazi_boot::BOOT;
|
||||
|
|
@ -30,6 +31,7 @@ fn stage_1(lua: &'static Lua) -> Result<()> {
|
|||
yazi_binding::Error::install(lua)?;
|
||||
yazi_binding::Cha::install(lua)?;
|
||||
crate::loader::install(lua)?;
|
||||
crate::process::install(lua)?;
|
||||
yazi_binding::File::install(lua)?;
|
||||
yazi_binding::Url::install(lua)?;
|
||||
|
||||
|
|
@ -62,7 +64,7 @@ fn stage_2(lua: &'static Lua) -> mlua::Result<()> {
|
|||
lua.load(preset!("compat")).set_name("compat.lua").exec()?;
|
||||
|
||||
if let Ok(b) = std::fs::read(BOOT.config_dir.join("init.lua")) {
|
||||
lua.load(b).set_name("init.lua").exec()?;
|
||||
block_on(lua.load(b).set_name("init.lua").exec_async())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ use anyhow::Context;
|
|||
use futures::future::join_all;
|
||||
use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, MultiValue, Value, Variadic};
|
||||
use tokio::sync::oneshot;
|
||||
use yazi_binding::{runtime, runtime_mut};
|
||||
use yazi_binding::{Handle, runtime, runtime_mut};
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_parser::app::{PluginCallback, PluginOpt};
|
||||
use yazi_proxy::AppProxy;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_shared::{LOCAL_SET, data::Data};
|
||||
|
||||
use super::Utils;
|
||||
use crate::{bindings::{MpscRx, MpscTx, MpscUnboundedRx, MpscUnboundedTx, OneshotRx, OneshotTx}, loader::LOADER};
|
||||
|
|
@ -47,6 +47,29 @@ impl Utils {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn r#async(lua: &Lua, isolate: bool) -> mlua::Result<Function> {
|
||||
if isolate {
|
||||
lua.create_function(|_, _: Function| {
|
||||
Err::<(), _>("`ya.async()` can only be used in sync context at the moment".into_lua_err())
|
||||
})
|
||||
} else {
|
||||
lua.create_function(|lua, (f, args): (Function, MultiValue)| {
|
||||
let name = runtime!(lua)?.current_owned();
|
||||
|
||||
Ok(Handle::AsyncFn(LOCAL_SET.spawn_local(async move {
|
||||
let result = f.call_async::<MultiValue>(args).await;
|
||||
if let Err(ref e) = result {
|
||||
match name {
|
||||
Some(s) => tracing::error!("Failed to execute async block in `{s}` plugin: {e}"),
|
||||
None => tracing::error!("Failed to execute async block in `init.lua`: {e}"),
|
||||
}
|
||||
}
|
||||
result
|
||||
})))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn chan(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|lua, (r#type, buffer): (mlua::String, Option<usize>)| {
|
||||
match (&*r#type.as_bytes(), buffer) {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ pub fn compose(
|
|||
|
||||
// Sync
|
||||
b"sync" => Utils::sync(lua, isolate)?,
|
||||
b"async" => Utils::r#async(lua, isolate)?,
|
||||
b"chan" => Utils::chan(lua)?,
|
||||
b"join" => Utils::join(lua)?,
|
||||
b"select" => Utils::select(lua)?,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
yazi_macro::mod_pub!(data errors event loc path pool scheme shell strand translit url wtf8);
|
||||
|
||||
yazi_macro::mod_flat!(alias bytes chars condition debounce either env id layer natsort os predictor ro_cell source sync_cell terminal tests throttle time utf8);
|
||||
yazi_macro::mod_flat!(alias bytes chars condition debounce either env id layer localset natsort os predictor ro_cell source sync_cell terminal tests throttle time utf8);
|
||||
|
||||
pub fn init() {
|
||||
pool::init();
|
||||
LOCAL_SET.with(tokio::task::LocalSet::new);
|
||||
|
||||
LOG_LEVEL.replace(<_>::from(std::env::var("YAZI_LOG").unwrap_or_default()));
|
||||
|
||||
#[cfg(unix)]
|
||||
USERS_CACHE.with(<_>::default);
|
||||
|
||||
pool::init();
|
||||
event::Event::init();
|
||||
}
|
||||
|
|
|
|||
5
yazi-shared/src/localset.rs
Normal file
5
yazi-shared/src/localset.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
use tokio::task::LocalSet;
|
||||
|
||||
use crate::RoCell;
|
||||
|
||||
pub static LOCAL_SET: RoCell<LocalSet> = RoCell::new();
|
||||
Loading…
Add table
Reference in a new issue