From d17e7e14cd09150e1c685568352d4d03ca3984e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Wed, 21 Jan 2026 19:19:39 +0800 Subject: [PATCH] feat: experimental module-level async support (#3594) --- CHANGELOG.md | 2 ++ yazi-binding/src/runtime.rs | 20 ++++++++++---------- yazi-plugin/src/elements/elements.rs | 4 ++-- yazi-plugin/src/isolate/entry.rs | 7 ++++--- yazi-plugin/src/isolate/fetch.rs | 15 +++++++-------- yazi-plugin/src/isolate/peek.rs | 2 +- yazi-plugin/src/isolate/preload.rs | 2 +- yazi-plugin/src/isolate/spot.rs | 2 +- yazi-plugin/src/loader/loader.rs | 8 ++++---- yazi-plugin/src/loader/require.rs | 2 +- yazi-plugin/src/lua.rs | 2 +- yazi-plugin/src/utils/layer.rs | 12 ++++++------ yazi-plugin/src/utils/sync.rs | 2 +- 13 files changed, 41 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be1849f3..32698ad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): - Tree view for the preset archive previewer ([#3525]) - Support compressed tarballs (`.tar.gz`, `.tar.bz2`, etc.) in the preset archive previewer ([#3518]) - Check and refresh the file list when the terminal gains focus ([#3561]) +- Experimental module-level async support ([#3594]) - Disable ANSI escape sequences in `ya pkg` when stdout is not a TTY ([#3566]) - New `Path.os()` API creates an OS-native `Path` ([#3541]) @@ -1607,3 +1608,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3561]: https://github.com/sxyazi/yazi/pull/3561 [#3566]: https://github.com/sxyazi/yazi/pull/3566 [#3582]: https://github.com/sxyazi/yazi/pull/3582 +[#3594]: https://github.com/sxyazi/yazi/pull/3594 diff --git a/yazi-binding/src/runtime.rs b/yazi-binding/src/runtime.rs index 24c0ae96..3d33ff18 100644 --- a/yazi-binding/src/runtime.rs +++ b/yazi-binding/src/runtime.rs @@ -5,9 +5,9 @@ use mlua::Function; #[derive(Debug)] pub struct Runtime { - frames: VecDeque, - blocks: HashMap>, - pub initing: bool, + frames: VecDeque, + blocks: HashMap>, + pub blocking: bool, } #[derive(Debug)] @@ -17,13 +17,13 @@ struct RuntimeFrame { } impl Runtime { - pub fn new() -> Self { Self { frames: <_>::default(), blocks: <_>::default(), initing: true } } + pub fn new() -> Self { Self { frames: <_>::default(), blocks: <_>::default(), blocking: true } } pub fn new_isolate(id: &str) -> Self { Self { - frames: VecDeque::from([RuntimeFrame { id: id.to_owned(), calls: 0 }]), - blocks: <_>::default(), - initing: false, + frames: VecDeque::from([RuntimeFrame { id: id.to_owned(), calls: 0 }]), + blocks: <_>::default(), + blocking: false, } } @@ -48,12 +48,12 @@ impl Runtime { self.blocks.get(id).and_then(|v| v.get(calls)).cloned() } - pub fn put_block(&mut self, f: Function) -> bool { + pub fn put_block(&mut self, f: &Function) -> bool { let Some(cur) = self.frames.back() else { return false }; if let Some(v) = self.blocks.get_mut(&cur.id) { - v.push(f); + v.push(f.clone()); } else { - self.blocks.insert(cur.id.clone(), vec![f]); + self.blocks.insert(cur.id.clone(), vec![f.clone()]); } true } diff --git a/yazi-plugin/src/elements/elements.rs b/yazi-plugin/src/elements/elements.rs index 08466c5a..af94fe47 100644 --- a/yazi-plugin/src/elements/elements.rs +++ b/yazi-plugin/src/elements/elements.rs @@ -44,8 +44,8 @@ pub(super) fn area(lua: &Lua) -> mlua::Result { pub(super) fn hide(lua: &Lua) -> mlua::Result { let f = lua.create_async_function(|lua, ()| async move { - if runtime!(lua)?.initing { - return Err("Cannot call `ui.hide()` during app initialization".into_lua_err()); + if runtime!(lua)?.blocking { + return Err("Cannot call `ui.hide()` while main thread is blocked".into_lua_err()); } if lua.named_registry_value::("HIDE_PERMIT").is_ok_and(|h| h.is_some()) { diff --git a/yazi-plugin/src/isolate/entry.rs b/yazi-plugin/src/isolate/entry.rs index 622626c4..f7527fb6 100644 --- a/yazi-plugin/src/isolate/entry.rs +++ b/yazi-plugin/src/isolate/entry.rs @@ -11,10 +11,11 @@ pub async fn entry(opt: PluginOpt) -> mlua::Result<()> { tokio::task::spawn_blocking(move || { let lua = slim_lua(&opt.id)?; - let plugin = LOADER.load_once(&lua, &opt.id)?; - let job = lua.create_table_from([("args", Sendable::args_to_table(&lua, opt.args)?)])?; - Handle::current().block_on(plugin.call_async_method("entry", job)) + + Handle::current().block_on(async { + LOADER.load_once(&lua, &opt.id).await?.call_async_method("entry", job).await + }) }) .await .into_lua_err()? diff --git a/yazi-plugin/src/isolate/fetch.rs b/yazi-plugin/src/isolate/fetch.rs index 6253b627..2d44bd06 100644 --- a/yazi-plugin/src/isolate/fetch.rs +++ b/yazi-plugin/src/isolate/fetch.rs @@ -18,15 +18,14 @@ pub async fn fetch( tokio::task::spawn_blocking(move || { let lua = slim_lua(&cmd.name)?; - let plugin = LOADER.load_once(&lua, &cmd.name)?; + let job = lua.create_table_from([ + ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), + ("files", lua.create_sequence_from(files.into_iter().map(File::new))?.into_lua(&lua)?), + ])?; - Handle::current().block_on(plugin.call_async_method( - "fetch", - lua.create_table_from([ - ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), - ("files", lua.create_sequence_from(files.into_iter().map(File::new))?.into_lua(&lua)?), - ])?, - )) + Handle::current().block_on(async { + LOADER.load_once(&lua, &cmd.name).await?.call_async_method("fetch", job).await + }) }) .await .into_lua_err()? diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index 146061a0..bc3b2cd6 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -89,7 +89,7 @@ fn peek_async( }, )?; - let plugin = LOADER.load_once(&lua, &cmd.name)?; + let plugin = LOADER.load_once(&lua, &cmd.name).await?; let job = lua.create_table_from([ ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), diff --git a/yazi-plugin/src/isolate/preload.rs b/yazi-plugin/src/isolate/preload.rs index a61b8d76..90ac21e8 100644 --- a/yazi-plugin/src/isolate/preload.rs +++ b/yazi-plugin/src/isolate/preload.rs @@ -31,7 +31,7 @@ pub async fn preload( }, )?; - let plugin = LOADER.load_once(&lua, &cmd.name)?; + let plugin = LOADER.load_once(&lua, &cmd.name).await?; let job = lua.create_table_from([ ("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?), ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), diff --git a/yazi-plugin/src/isolate/spot.rs b/yazi-plugin/src/isolate/spot.rs index 0b1fa111..0d9db6a3 100644 --- a/yazi-plugin/src/isolate/spot.rs +++ b/yazi-plugin/src/isolate/spot.rs @@ -36,7 +36,7 @@ pub fn spot( }, )?; - let plugin = LOADER.load_once(&lua, &cmd.name)?; + let plugin = LOADER.load_once(&lua, &cmd.name).await?; let job = lua.create_table_from([ ("id", Id(IDS.next()).into_lua(&lua)?), ("args", Sendable::args_to_table_ref(&lua, &cmd.args)?.into_lua(&lua)?), diff --git a/yazi-plugin/src/loader/loader.rs b/yazi-plugin/src/loader/loader.rs index 4185718f..5ef40fa9 100644 --- a/yazi-plugin/src/loader/loader.rs +++ b/yazi-plugin/src/loader/loader.rs @@ -79,7 +79,7 @@ impl Loader { result.map(|_| inspect) } - pub fn load(&self, lua: &Lua, id: &str) -> mlua::Result { + pub async fn load(&self, lua: &Lua, id: &str) -> mlua::Result
{ let (id, ..) = Self::normalize_id(id)?; let loaded: Table = lua.globals().raw_get::
("package")?.raw_get("loaded")?; @@ -87,14 +87,14 @@ impl Loader { return Ok(t); } - let t = self.load_once(lua, id)?; + let t = self.load_once(lua, id).await?; t.raw_set("_id", lua.create_string(id)?)?; loaded.raw_set(id, t.clone())?; Ok(t) } - pub fn load_once(&self, lua: &Lua, id: &str) -> mlua::Result
{ + pub async fn load_once(&self, lua: &Lua, id: &str) -> mlua::Result
{ let (id, ..) = Self::normalize_id(id)?; let mut mode = ChunkMode::Text; @@ -114,7 +114,7 @@ impl Loader { } } - f.call(()) + f.call_async(()).await } pub fn try_load(&self, lua: &Lua, id: &str) -> mlua::Result
{ diff --git a/yazi-plugin/src/loader/require.rs b/yazi-plugin/src/loader/require.rs index 9480f9d3..9b81d10e 100644 --- a/yazi-plugin/src/loader/require.rs +++ b/yazi-plugin/src/loader/require.rs @@ -17,7 +17,7 @@ impl Require { LOADER.ensure(&id, |_| ()).await.into_lua_err()?; runtime_mut!(lua)?.push(&id); - let mod_ = LOADER.load(&lua, &id); + let mod_ = LOADER.load(&lua, &id).await; runtime_mut!(lua)?.pop(); Self::create_mt(&lua, id.into_owned(), mod_?) diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/lua.rs index b1bed1a2..08ee42e1 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/lua.rs @@ -68,6 +68,6 @@ fn stage_2(lua: &'static Lua) -> mlua::Result<()> { block_on(lua.load(b).set_name("init.lua").exec_async())?; } - runtime_mut!(lua)?.initing = false; + runtime_mut!(lua)?.blocking = false; Ok(()) } diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 19a82b09..e688b716 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -16,8 +16,8 @@ use crate::bindings::InputRx; impl Utils { pub(super) fn which(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, t: Table| async move { - if runtime!(lua)?.initing { - return Err("Cannot call `ya.which()` during app initialization".into_lua_err()); + if runtime!(lua)?.blocking { + return Err("Cannot call `ya.which()` while main thread is blocked".into_lua_err()); } let (tx, mut rx) = mpsc::channel::(1); @@ -45,8 +45,8 @@ impl Utils { pub(super) fn input(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, t: Table| async move { - if runtime!(lua)?.initing { - return Err("Cannot call `ya.input()` during app initialization".into_lua_err()); + if runtime!(lua)?.blocking { + return Err("Cannot call `ya.input()` while main thread is blocked".into_lua_err()); } let mut pos = t.raw_get::("pos")?; @@ -92,8 +92,8 @@ impl Utils { } lua.create_async_function(|lua, t: Table| async move { - if runtime!(lua)?.initing { - return Err("Cannot call `ya.confirm()` during app initialization".into_lua_err()); + if runtime!(lua)?.blocking { + return Err("Cannot call `ya.confirm()` while main thread is blocked".into_lua_err()); } let result = ConfirmProxy::show(ConfirmCfg { diff --git a/yazi-plugin/src/utils/sync.rs b/yazi-plugin/src/utils/sync.rs index 36aec1ce..238aeabd 100644 --- a/yazi-plugin/src/utils/sync.rs +++ b/yazi-plugin/src/utils/sync.rs @@ -34,7 +34,7 @@ impl Utils { } else { lua.create_function(|lua, f: Function| { let mut rt = runtime_mut!(lua)?; - if !rt.put_block(f.clone()) { + if !rt.put_block(&f) { return Err("`ya.sync()` must be called in a plugin").into_lua_err(); }