feat: experimental module-level async support (#3594)

This commit is contained in:
三咲雅 misaki masa 2026-01-21 19:19:39 +08:00 committed by GitHub
parent e6d995710f
commit d17e7e14cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 41 additions and 39 deletions

View file

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

View file

@ -5,9 +5,9 @@ use mlua::Function;
#[derive(Debug)]
pub struct Runtime {
frames: VecDeque<RuntimeFrame>,
blocks: HashMap<String, Vec<Function>>,
pub initing: bool,
frames: VecDeque<RuntimeFrame>,
blocks: HashMap<String, Vec<Function>>,
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
}

View file

@ -44,8 +44,8 @@ pub(super) fn area(lua: &Lua) -> mlua::Result<Value> {
pub(super) fn hide(lua: &Lua) -> mlua::Result<Value> {
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::<PermitRef>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) {

View file

@ -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()?

View file

@ -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()?

View file

@ -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)?),

View file

@ -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)?),

View file

@ -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)?),

View file

@ -79,7 +79,7 @@ impl Loader {
result.map(|_| inspect)
}
pub fn load(&self, lua: &Lua, id: &str) -> mlua::Result<Table> {
pub async fn load(&self, lua: &Lua, id: &str) -> mlua::Result<Table> {
let (id, ..) = Self::normalize_id(id)?;
let loaded: Table = lua.globals().raw_get::<Table>("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<Table> {
pub async fn load_once(&self, lua: &Lua, id: &str) -> mlua::Result<Table> {
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<Table> {

View file

@ -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_?)

View file

@ -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(())
}

View file

@ -16,8 +16,8 @@ use crate::bindings::InputRx;
impl Utils {
pub(super) fn which(lua: &Lua) -> mlua::Result<Function> {
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::<usize>(1);
@ -45,8 +45,8 @@ impl Utils {
pub(super) fn input(lua: &Lua) -> mlua::Result<Function> {
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::<Value>("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 {

View file

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