feat: port task manager to Lua (#3134)

This commit is contained in:
三咲雅 misaki masa 2025-09-03 22:25:16 +08:00 committed by GitHub
parent d670da80d3
commit cfc7238638
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 283 additions and 174 deletions

2
Cargo.lock generated
View file

@ -3510,6 +3510,7 @@ dependencies = [
"yazi-parser",
"yazi-plugin",
"yazi-proxy",
"yazi-scheduler",
"yazi-shared",
"yazi-term",
"yazi-watcher",
@ -3870,6 +3871,7 @@ dependencies = [
"ordered-float 5.0.0",
"parking_lot",
"scopeguard",
"serde",
"tokio",
"tokio-util",
"tracing",

View file

@ -40,7 +40,7 @@ objc = "0.2.7"
ordered-float = { version = "5.0.0", features = [ "serde" ] }
parking_lot = "0.12.4"
paste = "1.0.15"
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] }
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info", "unstable-widget-ref" ] }
regex = "1.11.2"
scopeguard = "1.2.0"
serde = { version = "1.0.219", features = [ "derive" ] }

View file

@ -13,20 +13,21 @@ default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]
[dependencies]
yazi-binding = { path = "../yazi-binding", version = "25.6.11" }
yazi-boot = { path = "../yazi-boot", version = "25.6.11" }
yazi-config = { path = "../yazi-config", version = "25.6.11" }
yazi-core = { path = "../yazi-core", version = "25.6.11" }
yazi-dds = { path = "../yazi-dds", version = "25.6.11" }
yazi-fs = { path = "../yazi-fs", version = "25.6.11" }
yazi-macro = { path = "../yazi-macro", version = "25.6.11" }
yazi-parser = { path = "../yazi-parser", version = "25.6.11" }
yazi-plugin = { path = "../yazi-plugin", version = "25.6.11" }
yazi-proxy = { path = "../yazi-proxy", version = "25.6.11" }
yazi-shared = { path = "../yazi-shared", version = "25.6.11" }
yazi-term = { path = "../yazi-term", version = "25.6.11" }
yazi-watcher = { path = "../yazi-watcher", version = "25.6.11" }
yazi-widgets = { path = "../yazi-widgets", version = "25.6.11" }
yazi-binding = { path = "../yazi-binding", version = "25.6.11" }
yazi-boot = { path = "../yazi-boot", version = "25.6.11" }
yazi-config = { path = "../yazi-config", version = "25.6.11" }
yazi-core = { path = "../yazi-core", version = "25.6.11" }
yazi-dds = { path = "../yazi-dds", version = "25.6.11" }
yazi-fs = { path = "../yazi-fs", version = "25.6.11" }
yazi-macro = { path = "../yazi-macro", version = "25.6.11" }
yazi-parser = { path = "../yazi-parser", version = "25.6.11" }
yazi-plugin = { path = "../yazi-plugin", version = "25.6.11" }
yazi-proxy = { path = "../yazi-proxy", version = "25.6.11" }
yazi-scheduler = { path = "../yazi-scheduler", version = "25.6.11" }
yazi-shared = { path = "../yazi-shared", version = "25.6.11" }
yazi-term = { path = "../yazi-term", version = "25.6.11" }
yazi-watcher = { path = "../yazi-watcher", version = "25.6.11" }
yazi-widgets = { path = "../yazi-widgets", version = "25.6.11" }
# External dependencies
anyhow = { workspace = true }

View file

@ -1,3 +1,3 @@
#![allow(clippy::module_inception)]
yazi_macro::mod_flat!(core file files filter finder folder lives mode preference preview ptr selected tab tabs tasks yanked);
yazi_macro::mod_flat!(core file files filter finder folder lives mode preference preview ptr selected tab tabs task tasks yanked);

View file

@ -0,0 +1,37 @@
use std::ops::Deref;
use mlua::{AnyUserData, LuaSerdeExt, UserData, UserDataFields, Value};
use yazi_binding::cached_field;
use yazi_plugin::runtime::SER_OPT;
use super::{Lives, PtrCell};
pub(super) struct TaskSnap {
inner: PtrCell<yazi_scheduler::TaskSnap>,
v_name: Option<Value>,
v_prog: Option<Value>,
}
impl Deref for TaskSnap {
type Target = yazi_scheduler::TaskSnap;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl TaskSnap {
pub(super) fn make(inner: &yazi_scheduler::TaskSnap) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into(), v_name: None, v_prog: None })
}
}
impl UserData for TaskSnap {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, name, |lua, me| lua.create_string(&me.name));
cached_field!(fields, prog, |lua, me| lua.to_value_with(&me.prog, SER_OPT));
fields.add_field_method_get("running", |_, me| Ok(me.prog.running()));
fields.add_field_method_get("success", |_, me| Ok(me.prog.success()));
fields.add_field_method_get("percent", |_, me| Ok(me.prog.percent()));
}
}

View file

@ -5,10 +5,12 @@ use yazi_binding::{cached_field, deprecate};
use yazi_plugin::runtime::SER_OPT;
use super::{Lives, PtrCell};
use crate::lives::TaskSnap;
pub(super) struct Tasks {
inner: PtrCell<yazi_core::tasks::Tasks>,
v_snaps: Option<Value>,
v_summary: Option<Value>,
v_progress: Option<Value>,
}
@ -21,12 +23,28 @@ impl Deref for Tasks {
impl Tasks {
pub(super) fn make(inner: &yazi_core::tasks::Tasks) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into(), v_summary: None, v_progress: None })
Lives::scoped_userdata(Self {
inner: inner.into(),
v_snaps: None,
v_summary: None,
v_progress: None,
})
}
}
impl UserData for Tasks {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("cursor", |_, me| Ok(me.cursor));
cached_field!(fields, snaps, |lua, me| {
let tbl = lua.create_table_with_capacity(me.snaps.len(), 0)?;
for snap in &me.snaps {
tbl.raw_push(TaskSnap::make(snap)?)?;
}
Ok(tbl)
});
cached_field!(fields, summary, |lua, me| lua.to_value_with(&me.summary, SER_OPT));
cached_field!(fields, progress, |lua, me| {

View file

@ -47,7 +47,6 @@ yazi_macro::mod_flat!(
update_paged
update_peeked
update_spotted
update_tasks
update_yanked
visual_mode
watch

View file

@ -1 +1 @@
yazi_macro::mod_flat!(arrow cancel close inspect open_with process_exec show);
yazi_macro::mod_flat!(arrow cancel close inspect open_with process_exec show update_succeed);

View file

@ -1,16 +1,16 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::mgr::UpdateTasksOpt;
use yazi_parser::tasks::UpdateSucceedOpt;
use yazi_shared::event::Data;
use crate::{Actor, Ctx};
pub struct UpdateTasks;
pub struct UpdateSucceed;
impl Actor for UpdateTasks {
type Options = UpdateTasksOpt;
impl Actor for UpdateSucceed {
type Options = UpdateSucceedOpt;
const NAME: &str = "update_tasks";
const NAME: &str = "update_succeed";
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
cx.mgr.watcher.push_files(opt.urls);

View file

@ -25,8 +25,9 @@ pub struct Border {
impl Border {
pub fn compose(lua: &Lua) -> mlua::Result<Value> {
let new = lua
.create_function(|_, (_, edge): (Table, Edge)| Ok(Border { edge, ..Default::default() }))?;
let new = lua.create_function(|_, (_, edge): (Table, Edge)| {
Ok(Border { edge, r#type: ratatui::widgets::BorderType::Rounded, ..Default::default() })
})?;
let border = lua.create_table_from([
// Type

View file

@ -100,7 +100,7 @@ perm_exec = { fg = "cyan" }
# Progress
progress_label = { bold = true }
progress_normal = { fg = "blue", bg = "black" }
progress_error = { fg = "red", bg = "black" }
progress_error = { fg = "blue", bg = "red" }
# : }}}

View file

@ -100,7 +100,7 @@ perm_exec = { fg = "cyan" }
# Progress
progress_label = { bold = true }
progress_normal = { fg = "blue", bg = "black" }
progress_error = { fg = "red", bg = "black" }
progress_error = { fg = "blue", bg = "red" }
# : }}}

View file

@ -12,5 +12,5 @@ impl Layout {
Self { current: Rect::ZERO, preview: Rect::ZERO, progress: Rect::ZERO }
}
pub const fn limit(&self) -> usize { self.current.height as _ }
pub const fn folder_limit(self) -> usize { self.current.height as _ }
}

View file

@ -130,7 +130,7 @@ impl Folder {
}
pub fn sync_page(&mut self, force: bool) {
let limit = LAYOUT.get().limit();
let limit = LAYOUT.get().folder_limit();
if limit == 0 {
return;
}
@ -145,7 +145,7 @@ impl Folder {
let old = self.offset;
let len = self.files.len();
let limit = LAYOUT.get().limit();
let limit = LAYOUT.get().folder_limit();
let scrolloff = (limit / 2).min(YAZI.mgr.scrolloff.get() as usize);
self.offset = if self.cursor < (self.offset + limit).min(len).saturating_sub(scrolloff) {
@ -167,7 +167,7 @@ impl Folder {
pub fn paginate(&self, page: usize) -> &[File] {
let len = self.files.len();
let limit = LAYOUT.get().limit();
let limit = LAYOUT.get().folder_limit();
let start = (page.saturating_sub(1) * limit).min(len.saturating_sub(1));
let end = ((page + 2) * limit).min(len);
@ -178,7 +178,7 @@ impl Folder {
impl Scrollable for Folder {
fn total(&self) -> usize { self.files.len() }
fn limit(&self) -> usize { LAYOUT.get().limit() }
fn limit(&self) -> usize { LAYOUT.get().folder_limit() }
fn scrolloff(&self) -> usize { (self.limit() / 2).min(YAZI.mgr.scrolloff.get() as usize) }

View file

@ -32,7 +32,7 @@ impl Tasks {
let new = ongoing.lock().summary();
if last != new {
last = new;
AppProxy::update_summary(new);
AppProxy::update_progress(new);
}
}
});

View file

@ -66,7 +66,6 @@ pub enum Spark<'a> {
UpdatePaged(yazi_parser::mgr::UpdatePagedOpt),
UpdatePeeked(yazi_parser::mgr::UpdatePeekedOpt),
UpdateSpotted(yazi_parser::mgr::UpdateSpottedOpt),
UpdateTasks(yazi_parser::mgr::UpdateTasksOpt),
UpdateYanked(yazi_parser::mgr::UpdateYankedOpt<'a>),
VisualMode(yazi_parser::mgr::VisualModeOpt),
Watch(yazi_parser::VoidOpt),
@ -109,6 +108,7 @@ pub enum Spark<'a> {
// Tasks
TasksProcessExec(yazi_parser::tasks::ProcessExecOpt),
TasksUpdateSucceed(yazi_parser::tasks::UpdateSucceedOpt),
// Which
WhichCallback(yazi_parser::which::CallbackOpt),
@ -188,7 +188,6 @@ impl<'a> IntoLua for Spark<'a> {
Self::UpdatePaged(b) => b.into_lua(lua),
Self::UpdatePeeked(b) => b.into_lua(lua),
Self::UpdateSpotted(b) => b.into_lua(lua),
Self::UpdateTasks(b) => b.into_lua(lua),
Self::UpdateYanked(b) => b.into_lua(lua),
Self::VisualMode(b) => b.into_lua(lua),
Self::Watch(b) => b.into_lua(lua),
@ -231,6 +230,7 @@ impl<'a> IntoLua for Spark<'a> {
// Tasks
Self::TasksProcessExec(b) => b.into_lua(lua),
Self::TasksUpdateSucceed(b) => b.into_lua(lua),
// Which
Self::WhichCallback(b) => b.into_lua(lua),
@ -314,7 +314,6 @@ try_from_spark!(mgr::UpdateMimesOpt, mgr:update_mimes);
try_from_spark!(mgr::UpdatePagedOpt, mgr:update_paged);
try_from_spark!(mgr::UpdatePeekedOpt, mgr:update_peeked);
try_from_spark!(mgr::UpdateSpottedOpt, mgr:update_spotted);
try_from_spark!(mgr::UpdateTasksOpt, mgr:update_tasks);
try_from_spark!(mgr::UpdateYankedOpt<'a>, mgr:update_yanked);
try_from_spark!(mgr::VisualModeOpt, mgr:visual_mode);
try_from_spark!(mgr::YankOpt, mgr:yank);
@ -323,5 +322,6 @@ try_from_spark!(pick::CloseOpt, pick:close);
try_from_spark!(pick::ShowOpt, pick:show);
try_from_spark!(spot::CopyOpt, spot:copy);
try_from_spark!(tasks::ProcessExecOpt, tasks:process_exec);
try_from_spark!(tasks::UpdateSucceedOpt, tasks:update_succeed);
try_from_spark!(which::CallbackOpt, which:callback);
try_from_spark!(which::ShowOpt, which:show);

View file

@ -12,5 +12,5 @@ yazi_macro::mod_flat!(
resume
stop
update_notify
update_summary
update_progress
);

View file

@ -1,13 +1,13 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_macro::{act, render, succ};
use yazi_parser::app::UpdateSummaryOpt;
use yazi_parser::app::UpdateProgressOpt;
use yazi_shared::event::Data;
use crate::app::App;
impl App {
pub(crate) fn update_summary(&mut self, opt: UpdateSummaryOpt) -> Result<Data> {
pub(crate) fn update_progress(&mut self, opt: UpdateProgressOpt) -> Result<Data> {
// Update the progress of all tasks.
let tasks = &mut self.core.tasks;
let progressed = tasks.summary != opt.summary;

View file

@ -44,7 +44,7 @@ impl<'a> Executor<'a> {
on!(plugin);
on!(plugin_do);
on!(update_notify);
on!(update_summary);
on!(update_progress);
on!(resize);
on!(stop);
on!(resume);
@ -65,7 +65,6 @@ impl<'a> Executor<'a> {
}
on!(cd);
on!(update_tasks);
on!(update_yanked);
on!(update_files);
@ -155,6 +154,8 @@ impl<'a> Executor<'a> {
};
}
on!(update_succeed);
on!(show);
on!(close);
on!(arrow);

30
yazi-fm/src/tasks/list.rs Normal file
View file

@ -0,0 +1,30 @@
use mlua::{ObjectLike, Table};
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
use tracing::error;
use yazi_binding::elements::render_once;
use yazi_core::Core;
use yazi_plugin::LUA;
pub(crate) struct List<'a> {
core: &'a Core,
}
impl<'a> List<'a> {
#[inline]
pub(crate) fn new(core: &'a Core) -> Self { Self { core } }
}
impl Widget for List<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let mut f = || {
let area = yazi_binding::elements::Rect::from(area);
let root = LUA.globals().raw_get::<Table>("Tasks")?.call_method::<Table>("new", area)?;
render_once(root.call_method("redraw", ())?, buf, |p| self.core.mgr.area(p));
Ok::<_, mlua::Error>(())
};
if let Err(e) = f() {
error!("Failed to redraw the `Tasks` component:\n{e}");
}
}
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(progress tasks);
yazi_macro::mod_flat!(list progress tasks);

View file

@ -1,7 +1,9 @@
use ratatui::{buffer::Buffer, layout::{self, Alignment, Constraint, Rect}, text::{Line, Text}, widgets::{Block, BorderType, List, Padding, Widget}};
use ratatui::{buffer::Buffer, layout::{self, Alignment, Constraint, Rect}, text::Line, widgets::{Block, BorderType, Widget, WidgetRef}};
use yazi_config::THEME;
use yazi_core::{Core, tasks::TASKS_PERCENT};
use crate::tasks::List;
pub(crate) struct Tasks<'a> {
core: &'a Core,
}
@ -31,26 +33,14 @@ impl Widget for Tasks<'_> {
let area = Self::area(area);
yazi_binding::elements::Clear::default().render(area, buf);
let block = Block::bordered()
.title(Line::styled("Tasks", THEME.tasks.title))
.title_alignment(Alignment::Center)
.padding(Padding::symmetric(1, 1))
.border_type(BorderType::Rounded)
.border_style(THEME.tasks.border);
block.render_ref(area, buf);
let inner = block.inner(area);
block.render(area, buf);
let tasks = &self.core.tasks;
let items = tasks.snaps.iter().take(inner.height as usize).enumerate().map(|(i, v)| {
let mut item =
Text::from_iter(textwrap::wrap(&v.name, inner.width as usize).into_iter().map(Line::from));
if i == tasks.cursor {
item = item.style(THEME.tasks.hovered);
}
item
});
List::new(items).render(inner, buf);
List::new(self.core).render(block.inner(area), buf);
}
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(deprecate mouse notify plugin stop update_summary);
yazi_macro::mod_flat!(deprecate mouse notify plugin stop update_progress);

View file

@ -3,11 +3,11 @@ use ordered_float::OrderedFloat;
use serde::Serialize;
use yazi_shared::event::CmdCow;
pub struct UpdateSummaryOpt {
pub struct UpdateProgressOpt {
pub summary: TaskSummary,
}
impl TryFrom<CmdCow> for UpdateSummaryOpt {
impl TryFrom<CmdCow> for UpdateProgressOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {

View file

@ -37,7 +37,6 @@ yazi_macro::mod_flat!(
update_paged
update_peeked
update_spotted
update_tasks
update_yanked
visual_mode
yank

View file

@ -1 +1 @@
yazi_macro::mod_flat!(process_exec);
yazi_macro::mod_flat!(process_exec update_succeed);

View file

@ -3,26 +3,26 @@ use mlua::{ExternalError, FromLua, IntoLua, Lua, Value};
use yazi_shared::{event::CmdCow, url::UrlBuf};
#[derive(Debug)]
pub struct UpdateTasksOpt {
pub struct UpdateSucceedOpt {
pub urls: Vec<UrlBuf>,
}
impl TryFrom<CmdCow> for UpdateTasksOpt {
impl TryFrom<CmdCow> for UpdateSucceedOpt {
type Error = anyhow::Error;
fn try_from(mut c: CmdCow) -> Result<Self, Self::Error> {
let Some(urls) = c.take_any("urls") else {
bail!("Invalid 'urls' argument in UpdateTasksOpt");
bail!("Invalid 'urls' argument in UpdateSucceedOpt");
};
Ok(Self { urls })
}
}
impl FromLua for UpdateTasksOpt {
impl FromLua for UpdateSucceedOpt {
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
}
impl IntoLua for UpdateTasksOpt {
impl IntoLua for UpdateSucceedOpt {
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
}

View file

@ -38,7 +38,7 @@ function Progress:redraw()
if percent then
label = string.format("%3d%%, ", math.floor(percent))
else
percent = 99
percent = 0
end
label = label .. string.format("%d left", summary.total - summary.success)

View file

@ -0,0 +1,22 @@
Tasks = {
_id = "tasks",
}
function Tasks:new(area) return setmetatable({ _area = area }, { __index = self }) end
function Tasks:reflow() return { self } end
function Tasks:redraw()
local rows = {}
for _, snap in ipairs(cx.tasks.snaps) do
rows[#rows + 1] = ui.Row { snap.name }
end
local tbl = ui.Table(rows)
:area(self._area:pad(ui.Pad.x(1)))
:row(cx.tasks.cursor)
:row_style(th.tasks.hovered)
:widths { ui.Constraint.Fill(1) }
return { tbl }
end

View file

@ -52,6 +52,7 @@ fn stage_1(lua: &'static Lua) -> Result<()> {
lua.load(preset!("components/status")).set_name("status.lua").exec()?;
lua.load(preset!("components/tab")).set_name("tab.lua").exec()?;
lua.load(preset!("components/tabs")).set_name("tabs.lua").exec()?;
lua.load(preset!("components/tasks")).set_name("tasks.lua").exec()?;
Ok(())
}

View file

@ -51,7 +51,7 @@ impl AppProxy {
emit!(Call(relay!(app:plugin_do).with_any("opt", opt)));
}
pub fn update_summary(summary: TaskSummary) {
emit!(Call(relay!(app:update_summary).with_any("summary", summary)));
pub fn update_progress(summary: TaskSummary) {
emit!(Call(relay!(app:update_progress).with_any("summary", summary)));
}
}

View file

@ -54,10 +54,6 @@ impl MgrProxy {
emit!(Call(relay!(mgr:update_spotted).with_any("opt", opt)));
}
pub fn update_tasks(url: &UrlBuf) {
emit!(Call(relay!(mgr:update_tasks).with_any("urls", vec![url.clone()])));
}
pub fn update_paged_by(page: usize, only_if: &UrlBuf) {
emit!(Call(relay!(mgr:update_paged, [page]).with_any("only-if", only_if.clone())));
}

View file

@ -23,4 +23,8 @@ impl TasksProxy {
})));
rx.await.ok();
}
pub fn update_succeed(url: impl Into<UrlBuf>) {
emit!(Call(relay!(tasks:update_succeed).with_any("urls", vec![url.into()])));
}
}

View file

@ -28,6 +28,7 @@ mlua = { workspace = true }
ordered-float = { workspace = true }
parking_lot = { workspace = true }
scopeguard = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true }
tracing = { workspace = true }

View file

@ -1,14 +1,15 @@
use serde::Serialize;
use yazi_parser::app::TaskSummary;
// --- Paste
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct FileProgPaste {
pub(crate) total_files: u32,
pub(crate) success_files: u32,
pub(crate) failed_files: u32,
pub(crate) total_bytes: u64,
pub(crate) processed_bytes: u64,
pub(crate) collected: bool,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct FileProgPaste {
pub total_files: u32,
pub success_files: u32,
pub failed_files: u32,
pub total_bytes: u64,
pub processed_bytes: u64,
pub collected: bool,
}
impl From<FileProgPaste> for TaskSummary {
@ -23,13 +24,13 @@ impl From<FileProgPaste> for TaskSummary {
}
impl FileProgPaste {
pub(crate) fn running(self) -> bool {
pub fn running(self) -> bool {
!self.collected || self.success_files + self.failed_files != self.total_files
}
pub(crate) fn success(self) -> bool { self.collected && self.success_files == self.total_files }
pub fn success(self) -> bool { self.collected && self.success_files == self.total_files }
pub(crate) fn percent(self) -> Option<f32> {
pub fn percent(self) -> Option<f32> {
Some(if self.success() {
100.0
} else if self.total_bytes == 0 {
@ -41,9 +42,9 @@ impl FileProgPaste {
}
// --- Link
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct FileProgLink {
pub(crate) state: Option<bool>,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct FileProgLink {
pub state: Option<bool>,
}
impl From<FileProgLink> for TaskSummary {
@ -58,20 +59,20 @@ impl From<FileProgLink> for TaskSummary {
}
impl FileProgLink {
pub(crate) fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
pub(crate) fn success(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.state == Some(true) }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}
// --- Hardlink
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct FileProgHardlink {
pub(crate) total: u32,
pub(crate) success: u32,
pub(crate) failed: u32,
pub(crate) collected: bool,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct FileProgHardlink {
pub total: u32,
pub success: u32,
pub failed: u32,
pub collected: bool,
}
impl From<FileProgHardlink> for TaskSummary {
@ -86,24 +87,22 @@ impl From<FileProgHardlink> for TaskSummary {
}
impl FileProgHardlink {
pub(crate) fn running(self) -> bool {
!self.collected || self.success + self.failed != self.total
}
pub fn running(self) -> bool { !self.collected || self.success + self.failed != self.total }
pub(crate) fn success(self) -> bool { self.collected && self.success == self.total }
pub fn success(self) -> bool { self.collected && self.success == self.total }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}
// --- Delete
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct FileProgDelete {
pub(crate) total_files: u32,
pub(crate) success_files: u32,
pub(crate) failed_files: u32,
pub(crate) total_bytes: u64,
pub(crate) processed_bytes: u64,
pub(crate) collected: bool,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct FileProgDelete {
pub total_files: u32,
pub success_files: u32,
pub failed_files: u32,
pub total_bytes: u64,
pub processed_bytes: u64,
pub collected: bool,
}
impl From<FileProgDelete> for TaskSummary {
@ -118,13 +117,13 @@ impl From<FileProgDelete> for TaskSummary {
}
impl FileProgDelete {
pub(crate) fn running(self) -> bool {
pub fn running(self) -> bool {
!self.collected || self.success_files + self.failed_files != self.total_files
}
pub(crate) fn success(self) -> bool { self.collected && self.success_files == self.total_files }
pub fn success(self) -> bool { self.collected && self.success_files == self.total_files }
pub(crate) fn percent(self) -> Option<f32> {
pub fn percent(self) -> Option<f32> {
Some(if self.success() {
100.0
} else if self.total_bytes == 0 {
@ -136,9 +135,9 @@ impl FileProgDelete {
}
// --- Trash
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct FileProgTrash {
pub(crate) state: Option<bool>,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct FileProgTrash {
pub state: Option<bool>,
}
impl From<FileProgTrash> for TaskSummary {
@ -153,9 +152,9 @@ impl From<FileProgTrash> for TaskSummary {
}
impl FileProgTrash {
pub(crate) fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
pub(crate) fn success(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.state == Some(true) }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}

View file

@ -1,9 +1,10 @@
use serde::Serialize;
use yazi_parser::app::TaskSummary;
// --- Entry
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct PluginProgEntry {
pub(crate) state: Option<bool>,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct PluginProgEntry {
pub state: Option<bool>,
}
impl From<PluginProgEntry> for TaskSummary {
@ -18,9 +19,9 @@ impl From<PluginProgEntry> for TaskSummary {
}
impl PluginProgEntry {
pub(crate) fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
pub(crate) fn success(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.state == Some(true) }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}

View file

@ -1,9 +1,10 @@
use serde::Serialize;
use yazi_parser::app::TaskSummary;
// --- Fetch
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct PreworkProgFetch {
pub(crate) state: Option<bool>,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct PreworkProgFetch {
pub state: Option<bool>,
}
impl From<PreworkProgFetch> for TaskSummary {
@ -18,17 +19,17 @@ impl From<PreworkProgFetch> for TaskSummary {
}
impl PreworkProgFetch {
pub(crate) fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
pub(crate) fn success(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.state == Some(true) }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}
// --- Load
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct PreworkProgLoad {
pub(crate) state: Option<bool>,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct PreworkProgLoad {
pub state: Option<bool>,
}
impl From<PreworkProgLoad> for TaskSummary {
@ -43,17 +44,17 @@ impl From<PreworkProgLoad> for TaskSummary {
}
impl PreworkProgLoad {
pub(crate) fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
pub(crate) fn success(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.state == Some(true) }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}
// --- Size
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct PreworkProgSize {
pub(crate) done: bool,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct PreworkProgSize {
pub done: bool,
}
impl From<PreworkProgSize> for TaskSummary {
@ -68,9 +69,9 @@ impl From<PreworkProgSize> for TaskSummary {
}
impl PreworkProgSize {
pub(crate) fn running(self) -> bool { !self.done }
pub fn running(self) -> bool { !self.done }
pub(crate) fn success(self) -> bool { self.done }
pub fn success(self) -> bool { self.done }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}

View file

@ -1,9 +1,10 @@
use serde::Serialize;
use yazi_parser::app::TaskSummary;
// --- Block
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ProcessProgBlock {
pub(crate) state: Option<bool>,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct ProcessProgBlock {
pub state: Option<bool>,
}
impl From<ProcessProgBlock> for TaskSummary {
@ -18,17 +19,17 @@ impl From<ProcessProgBlock> for TaskSummary {
}
impl ProcessProgBlock {
pub(crate) fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
pub(crate) fn success(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.state == Some(true) }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}
// --- Orphan
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ProcessProgOrphan {
pub(crate) state: Option<bool>,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct ProcessProgOrphan {
pub state: Option<bool>,
}
impl From<ProcessProgOrphan> for TaskSummary {
@ -43,17 +44,17 @@ impl From<ProcessProgOrphan> for TaskSummary {
}
impl ProcessProgOrphan {
pub(crate) fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
pub(crate) fn success(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.state == Some(true) }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}
// --- Bg
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ProcessProgBg {
pub(crate) state: Option<bool>,
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct ProcessProgBg {
pub state: Option<bool>,
}
impl From<ProcessProgBg> for TaskSummary {
@ -68,9 +69,9 @@ impl From<ProcessProgBg> for TaskSummary {
}
impl ProcessProgBg {
pub(crate) fn running(self) -> bool { self.state.is_none() }
pub fn running(self) -> bool { self.state.is_none() }
pub(crate) fn success(self) -> bool { self.state == Some(true) }
pub fn success(self) -> bool { self.state == Some(true) }
pub(crate) fn percent(self) -> Option<f32> { None }
pub fn percent(self) -> Option<f32> { None }
}

View file

@ -1,9 +1,11 @@
use serde::Serialize;
use yazi_parser::app::TaskSummary;
use crate::{file::{FileProgDelete, FileProgHardlink, FileProgLink, FileProgPaste, FileProgTrash}, impl_from_prog, plugin::PluginProgEntry, prework::{PreworkProgFetch, PreworkProgLoad, PreworkProgSize}, process::{ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}};
#[derive(Clone, Copy, Debug)]
pub(crate) enum TaskProg {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(tag = "kind")]
pub enum TaskProg {
// File
FilePaste(FileProgPaste),
FileLink(FileProgLink),
@ -57,7 +59,7 @@ impl From<TaskProg> for TaskSummary {
}
impl TaskProg {
pub(crate) fn running(self) -> bool {
pub fn running(self) -> bool {
match self {
// File
Self::FilePaste(p) => p.running(),
@ -78,7 +80,7 @@ impl TaskProg {
}
}
pub(crate) fn success(self) -> bool {
pub fn success(self) -> bool {
match self {
// File
Self::FilePaste(p) => p.success(),
@ -99,7 +101,7 @@ impl TaskProg {
}
}
pub(crate) fn percent(self) -> Option<f32> {
pub fn percent(self) -> Option<f32> {
match self {
// File
Self::FilePaste(p) => p.percent(),

View file

@ -8,7 +8,7 @@ use yazi_config::{YAZI, plugin::{Fetcher, Preloader}};
use yazi_dds::Pump;
use yazi_fs::{must_be_dir, path::unique_name, provider, remove_dir_clean};
use yazi_parser::{app::PluginOpt, tasks::ProcessExecOpt};
use yazi_proxy::MgrProxy;
use yazi_proxy::TasksProxy;
use yazi_shared::{Id, Throttle, url::UrlBuf};
use super::{Ongoing, TaskOp};
@ -174,7 +174,7 @@ impl Scheduler {
move |canceled: bool| async move {
if !canceled {
provider::remove_dir_all(&target).await.ok();
MgrProxy::update_tasks(&target);
TasksProxy::update_succeed(&target);
Pump::push_delete(target);
}
ongoing.lock().remove(id);
@ -199,7 +199,7 @@ impl Scheduler {
move |canceled: bool| async move {
if !canceled {
MgrProxy::update_tasks(&target);
TasksProxy::update_succeed(&target);
Pump::push_trash(target);
}
ongoing.lock().remove(id);

View file

@ -1,10 +1,13 @@
use crate::Task;
use serde::Serialize;
#[derive(Debug, PartialEq, Eq)]
use crate::{Task, TaskProg};
#[derive(Debug, PartialEq, Eq, Serialize)]
pub struct TaskSnap {
pub name: String,
pub prog: TaskProg,
}
impl From<&Task> for TaskSnap {
fn from(task: &Task) -> Self { TaskSnap { name: task.name.clone() } }
fn from(task: &Task) -> Self { TaskSnap { name: task.name.clone(), prog: task.prog } }
}