mirror of
https://github.com/sxyazi/yazi.git
synced 2026-05-13 08:16:40 +00:00
refactor: simplify the remote progressive file copier (#3852)
This commit is contained in:
parent
0cedbd9c7b
commit
4bb4f37555
47 changed files with 486 additions and 353 deletions
|
|
@ -1,21 +0,0 @@
|
|||
use mlua::{ExternalResult, ObjectLike};
|
||||
use tokio::runtime::Handle;
|
||||
use yazi_dds::Sendable;
|
||||
|
||||
use crate::{Runner, loader::LOADER, plugin::PluginOpt};
|
||||
|
||||
impl Runner {
|
||||
pub async fn entry(&'static self, opt: PluginOpt) -> mlua::Result<()> {
|
||||
LOADER.ensure(&opt.id, |_| ()).await.into_lua_err()?;
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let lua = self.spawn(&opt.id)?;
|
||||
let job = lua.create_table_from([("args", Sendable::args_to_table(&lua, opt.args)?)])?;
|
||||
|
||||
Handle::current()
|
||||
.block_on(async { LOADER.load(&lua, &opt.id).await?.call_async_method("entry", job).await })
|
||||
})
|
||||
.await
|
||||
.into_lua_err()?
|
||||
}
|
||||
}
|
||||
19
yazi-runner/src/entry/entry.rs
Normal file
19
yazi-runner/src/entry/entry.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use mlua::{ExternalResult, ObjectLike};
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
use crate::{Runner, entry::EntryJob, loader::LOADER};
|
||||
|
||||
impl Runner {
|
||||
pub async fn entry(&'static self, job: EntryJob) -> mlua::Result<()> {
|
||||
LOADER.ensure(&job.plugin, |_| ()).await.into_lua_err()?;
|
||||
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let lua = self.spawn(&job.plugin)?;
|
||||
Handle::current().block_on(async {
|
||||
LOADER.load(&lua, &job.plugin).await?.call_async_method("entry", job).await
|
||||
})
|
||||
})
|
||||
.await
|
||||
.into_lua_err()?
|
||||
}
|
||||
}
|
||||
22
yazi-runner/src/entry/job.rs
Normal file
22
yazi-runner/src/entry/job.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use hashbrown::HashMap;
|
||||
use mlua::{IntoLua, Lua, Value};
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_shared::{Id, SStr, data::{Data, DataKey}};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct EntryJob {
|
||||
pub id: Id,
|
||||
pub args: HashMap<DataKey, Data>,
|
||||
pub plugin: SStr,
|
||||
}
|
||||
|
||||
impl IntoLua for EntryJob {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
lua
|
||||
.create_table_from([
|
||||
("id", self.id.get().into_lua(lua)?),
|
||||
("args", Sendable::args_to_table(lua, self.args)?.into_lua(lua)?),
|
||||
])?
|
||||
.into_lua(lua)
|
||||
}
|
||||
}
|
||||
1
yazi-runner/src/entry/mod.rs
Normal file
1
yazi-runner/src/entry/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(entry job);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
yazi_macro::mod_pub!(fetcher loader plugin previewer);
|
||||
yazi_macro::mod_pub!(entry fetcher loader preloader previewer);
|
||||
|
||||
yazi_macro::mod_flat!(entry preload runner spot);
|
||||
yazi_macro::mod_flat!(runner spot);
|
||||
|
||||
pub static RUNNER: yazi_shared::RoCell<Runner> = yazi_shared::RoCell::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
yazi_macro::mod_flat!(option);
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
use std::{borrow::Cow, fmt, fmt::Debug};
|
||||
|
||||
use anyhow::bail;
|
||||
use dyn_clone::DynClone;
|
||||
use hashbrown::HashMap;
|
||||
use mlua::{Lua, Table};
|
||||
use serde::Deserialize;
|
||||
use strum::EnumString;
|
||||
use yazi_shared::{SStr, data::{Data, DataKey}, event::{Action, ActionCow}};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PluginOpt {
|
||||
pub id: SStr,
|
||||
pub args: HashMap<DataKey, Data>,
|
||||
pub mode: PluginMode,
|
||||
pub callback: Option<Box<dyn PluginCallback>>,
|
||||
}
|
||||
|
||||
impl TryFrom<ActionCow> for PluginOpt {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(mut a: ActionCow) -> Result<Self, Self::Error> {
|
||||
let Some(id) = a.take_first::<SStr>().ok().filter(|s| !s.is_empty()) else {
|
||||
bail!("plugin id cannot be empty");
|
||||
};
|
||||
|
||||
let args = if let Ok(s) = a.second() {
|
||||
let (words, last) = yazi_shared::shell::unix::split(s, true)?;
|
||||
Action::parse_args(words, last)?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let mode = a.str("mode").parse().unwrap_or_default();
|
||||
Ok(Self { id: Self::normalize_id(id), args, mode, callback: a.take_any("callback") })
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginOpt {
|
||||
pub fn new_callback(id: impl Into<SStr>, f: impl PluginCallback) -> Self {
|
||||
Self {
|
||||
id: Self::normalize_id(id.into()),
|
||||
mode: PluginMode::Sync,
|
||||
callback: Some(Box::new(f)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_id(s: SStr) -> SStr {
|
||||
match s {
|
||||
Cow::Borrowed(s) => s.trim_end_matches(".main").into(),
|
||||
Cow::Owned(mut s) => {
|
||||
s.truncate(s.trim_end_matches(".main").len());
|
||||
s.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Mode
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, EnumString, Eq, PartialEq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum PluginMode {
|
||||
#[default]
|
||||
Auto,
|
||||
Sync,
|
||||
Async,
|
||||
}
|
||||
|
||||
impl PluginMode {
|
||||
pub fn auto_then(self, sync: bool) -> Self {
|
||||
if self != Self::Auto {
|
||||
return self;
|
||||
}
|
||||
if sync { Self::Sync } else { Self::Async }
|
||||
}
|
||||
}
|
||||
|
||||
// --- Callback
|
||||
pub trait PluginCallback:
|
||||
FnOnce(&Lua, Table) -> mlua::Result<()> + Send + Sync + DynClone + 'static
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> PluginCallback for T where
|
||||
T: FnOnce(&Lua, Table) -> mlua::Result<()> + Send + Sync + DynClone + 'static
|
||||
{
|
||||
}
|
||||
|
||||
impl Clone for Box<dyn PluginCallback> {
|
||||
fn clone(&self) -> Self { dyn_clone::clone_box(&**self) }
|
||||
}
|
||||
|
||||
impl fmt::Debug for dyn PluginCallback {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PluginCallback").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
use mlua::{ExternalError, ExternalResult, HookTriggers, IntoLua, ObjectLike, VmState};
|
||||
use tokio::{runtime::Handle, select};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use yazi_binding::{Error, File, elements::Rect};
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_shared::event::Action;
|
||||
|
||||
use crate::{Runner, loader::LOADER};
|
||||
|
||||
impl Runner {
|
||||
pub async fn preload(
|
||||
&'static self,
|
||||
action: &'static Action,
|
||||
file: yazi_fs::File,
|
||||
ct: CancellationToken,
|
||||
) -> mlua::Result<(bool, Option<Error>)> {
|
||||
let ct_ = ct.clone();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let future = async {
|
||||
LOADER.ensure(&action.name, |_| ()).await.into_lua_err()?;
|
||||
|
||||
let lua = self.spawn(&action.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("Preload task cancelled".into_lua_err())
|
||||
} else {
|
||||
Ok(VmState::Continue)
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
let plugin = LOADER.load(&lua, &action.name).await?;
|
||||
let job = lua.create_table_from([
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(&lua)?),
|
||||
("args", Sendable::args_to_table_ref(&lua, &action.args)?.into_lua(&lua)?),
|
||||
("file", File::new(file).into_lua(&lua)?),
|
||||
("skip", 0.into_lua(&lua)?),
|
||||
])?;
|
||||
|
||||
if ct_.is_cancelled() {
|
||||
Ok((false, None))
|
||||
} else {
|
||||
plugin.call_async_method("preload", job).await
|
||||
}
|
||||
};
|
||||
|
||||
Handle::current().block_on(async {
|
||||
select! {
|
||||
_ = ct_.cancelled() => Ok((false, None)),
|
||||
r = future => match r {
|
||||
Err(e) if e.to_string().contains("Preload task cancelled") => Ok((false, None)),
|
||||
Ok(_) | Err(_) => r,
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
.await
|
||||
.into_lua_err()?
|
||||
}
|
||||
}
|
||||
17
yazi-runner/src/preloader/error.rs
Normal file
17
yazi-runner/src/preloader/error.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum PreloadError {
|
||||
#[error("Preload task cancelled")]
|
||||
Cancelled,
|
||||
#[error("Lua error during preload: {0}")]
|
||||
Lua(#[from] mlua::Error),
|
||||
#[error("Unexpected error during preload: {0}")]
|
||||
Unexpected(Arc<anyhow::Error>),
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for PreloadError {
|
||||
fn from(e: anyhow::Error) -> Self { Self::Unexpected(e.into()) }
|
||||
}
|
||||
23
yazi-runner/src/preloader/job.rs
Normal file
23
yazi-runner/src/preloader/job.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use mlua::{IntoLua, Lua, Value};
|
||||
use yazi_binding::{File, elements::Rect};
|
||||
use yazi_config::LAYOUT;
|
||||
use yazi_dds::Sendable;
|
||||
use yazi_shared::event::Action;
|
||||
|
||||
pub struct PreloadJob {
|
||||
pub action: &'static Action,
|
||||
pub file: yazi_fs::File,
|
||||
}
|
||||
|
||||
impl IntoLua for PreloadJob {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
lua
|
||||
.create_table_from([
|
||||
("area", Rect::from(LAYOUT.get().preview).into_lua(lua)?),
|
||||
("args", Sendable::args_to_table_ref(lua, &self.action.args)?.into_lua(lua)?),
|
||||
("file", File::new(self.file).into_lua(lua)?),
|
||||
("skip", 0.into_lua(lua)?),
|
||||
])?
|
||||
.into_lua(lua)
|
||||
}
|
||||
}
|
||||
1
yazi-runner/src/preloader/mod.rs
Normal file
1
yazi-runner/src/preloader/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(error job preloader state);
|
||||
64
yazi-runner/src/preloader/preloader.rs
Normal file
64
yazi-runner/src/preloader/preloader.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use mlua::{ExternalError, HookTriggers, ObjectLike, VmState};
|
||||
use tokio::{runtime::Handle, select, sync::mpsc};
|
||||
|
||||
use crate::{Runner, loader::LOADER, preloader::{PreloadError, PreloadJob, PreloadState}};
|
||||
|
||||
impl Runner {
|
||||
pub async fn preload(
|
||||
&'static self,
|
||||
job: PreloadJob,
|
||||
) -> mpsc::Receiver<Result<PreloadState, PreloadError>> {
|
||||
let (tx, rx) = mpsc::channel(1);
|
||||
match LOADER.ensure(&job.action.name, |_| ()).await {
|
||||
Ok(()) => self.preload_do(job, tx),
|
||||
Err(e) => _ = tx.try_send(Err(e.into())),
|
||||
};
|
||||
rx
|
||||
}
|
||||
|
||||
fn preload_do(
|
||||
&'static self,
|
||||
job: PreloadJob,
|
||||
tx: mpsc::Sender<Result<PreloadState, PreloadError>>,
|
||||
) {
|
||||
let tx_ = tx.clone();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let future = async {
|
||||
let lua = self.spawn(&job.action.name)?;
|
||||
lua.set_hook(
|
||||
HookTriggers::new().on_calls().on_returns().every_nth_instruction(2000),
|
||||
move |_, dbg| {
|
||||
if tx.is_closed() && dbg.source().what != "C" {
|
||||
Err(PreloadError::Cancelled.into_lua_err())
|
||||
} else {
|
||||
Ok(VmState::Continue)
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
let plugin = LOADER.load(&lua, &job.action.name).await?;
|
||||
if tx_.is_closed() {
|
||||
Err(PreloadError::Cancelled.into_lua_err())
|
||||
} else {
|
||||
plugin.call_async_method("preload", job).await
|
||||
}
|
||||
};
|
||||
|
||||
Handle::current().block_on(async {
|
||||
select! {
|
||||
_ = tx_.closed() => {},
|
||||
r = future => match r {
|
||||
Ok(state) => _ = tx_.send(Ok(state)).await,
|
||||
Err(err) => {
|
||||
if let Some(e) = err.downcast_ref::<PreloadError>() {
|
||||
tx_.send(Err(e.clone())).await.ok();
|
||||
} else {
|
||||
tx_.send(Err(err.into())).await.ok();
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
14
yazi-runner/src/preloader/state.rs
Normal file
14
yazi-runner/src/preloader/state.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
use mlua::{FromLuaMulti, Lua, MultiValue};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PreloadState {
|
||||
pub complete: bool,
|
||||
pub error: Option<yazi_binding::Error>,
|
||||
}
|
||||
|
||||
impl FromLuaMulti for PreloadState {
|
||||
fn from_lua_multi(values: MultiValue, lua: &Lua) -> mlua::Result<Self> {
|
||||
let (complete, error) = FromLuaMulti::from_lua_multi(values, lua)?;
|
||||
Ok(Self { complete, error })
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue