mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
134 lines
3.5 KiB
Rust
134 lines
3.5 KiB
Rust
use mlua::{ExternalError, HookTriggers, IntoLua, Lua, ObjectLike, Table, VmState};
|
|
use tokio::{runtime::Handle, select};
|
|
use tokio_util::sync::CancellationToken;
|
|
use tracing::error;
|
|
use yazi_binding::{Error, File, elements::{Rect, Renderable}};
|
|
use yazi_config::LAYOUT;
|
|
use yazi_dds::Sendable;
|
|
use yazi_parser::{app::PluginOpt, mgr::{PreviewLock, UpdatePeekedOpt}};
|
|
use yazi_proxy::{AppProxy, MgrProxy};
|
|
use yazi_shared::{event::Cmd, pool::Symbol};
|
|
|
|
use super::slim_lua;
|
|
use crate::loader::{LOADER, Loader};
|
|
|
|
pub fn peek(
|
|
cmd: &'static Cmd,
|
|
file: yazi_fs::File,
|
|
mime: Symbol<str>,
|
|
skip: usize,
|
|
) -> Option<CancellationToken> {
|
|
let (id, ..) = Loader::normalize_id(&cmd.name).ok()?;
|
|
|
|
let ct = CancellationToken::new();
|
|
if let Some(c) = LOADER.read().get(id) {
|
|
if let Err(e) = Loader::compatible_or_error(id, c) {
|
|
peek_error(file, mime, skip, e);
|
|
return None;
|
|
} else if c.sync_peek {
|
|
peek_sync(cmd, file, mime, skip);
|
|
} else {
|
|
peek_async(cmd, file, mime, skip, ct.clone());
|
|
}
|
|
return Some(ct).filter(|_| !c.sync_peek);
|
|
}
|
|
|
|
let ct_ = ct.clone();
|
|
tokio::spawn(async move {
|
|
select! {
|
|
_ = ct_.cancelled() => {},
|
|
Ok(b) = LOADER.ensure(id, |c| c.sync_peek) => {
|
|
if b {
|
|
peek_sync(cmd, file, mime, skip);
|
|
} else {
|
|
peek_async(cmd, file, mime, skip, ct_);
|
|
}
|
|
},
|
|
else => {}
|
|
}
|
|
});
|
|
|
|
Some(ct)
|
|
}
|
|
|
|
fn peek_sync(cmd: &'static Cmd, file: yazi_fs::File, mime: Symbol<str>, skip: usize) {
|
|
let cb = move |lua: &Lua, plugin: Table| {
|
|
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)?),
|
|
("file", File::new(file).into_lua(lua)?),
|
|
("mime", mime.into_lua(lua)?),
|
|
("skip", skip.into_lua(lua)?),
|
|
])?;
|
|
|
|
plugin.call_method("peek", job)
|
|
};
|
|
|
|
AppProxy::plugin(PluginOpt::new_callback(&*cmd.name, cb));
|
|
}
|
|
|
|
fn peek_async(
|
|
cmd: &'static Cmd,
|
|
file: yazi_fs::File,
|
|
mime: Symbol<str>,
|
|
skip: usize,
|
|
ct: CancellationToken,
|
|
) {
|
|
let ct_ = ct.clone();
|
|
tokio::task::spawn_blocking(move || {
|
|
let future = async {
|
|
let lua = slim_lua(&cmd.name)?;
|
|
lua.set_hook(
|
|
HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000),
|
|
move |_, dbg| {
|
|
if ct.is_cancelled() && dbg.source().what != "C" {
|
|
Err("Peek task cancelled".into_lua_err())
|
|
} else {
|
|
Ok(VmState::Continue)
|
|
}
|
|
},
|
|
)?;
|
|
|
|
let plugin = LOADER.load(&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)?),
|
|
("file", File::new(file).into_lua(&lua)?),
|
|
("mime", mime.into_lua(&lua)?),
|
|
("skip", skip.into_lua(&lua)?),
|
|
])?;
|
|
|
|
if ct_.is_cancelled() { Ok(()) } else { plugin.call_async_method("peek", job).await }
|
|
};
|
|
|
|
let result = Handle::current().block_on(async {
|
|
select! {
|
|
_ = ct_.cancelled() => Ok(()),
|
|
r = future => r,
|
|
}
|
|
});
|
|
|
|
if let Err(e) = result
|
|
&& !e.to_string().contains("Peek task cancelled")
|
|
{
|
|
error!("{e}");
|
|
}
|
|
});
|
|
}
|
|
|
|
fn peek_error(file: yazi_fs::File, mime: Symbol<str>, skip: usize, error: anyhow::Error) {
|
|
let area = LAYOUT.get().preview;
|
|
MgrProxy::update_peeked(UpdatePeekedOpt {
|
|
lock: PreviewLock {
|
|
url: file.url,
|
|
cha: file.cha,
|
|
mime: mime.to_string(),
|
|
skip,
|
|
area: area.into(),
|
|
data: vec![
|
|
Renderable::Clear(yazi_binding::elements::Clear { area: area.into() }),
|
|
Renderable::from(Error::custom(error.to_string())).with_area(area),
|
|
],
|
|
},
|
|
});
|
|
}
|