diff --git a/Cargo.lock b/Cargo.lock index a67214f2..57975e93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2554,7 +2554,6 @@ name = "yazi-core" version = "0.1.5" dependencies = [ "anyhow", - "async-channel", "bitflags 2.4.1", "clipboard-win", "crossterm", @@ -2566,12 +2565,10 @@ dependencies = [ "ratatui", "regex", "serde", - "serde_json", "syntect", "tokio", "tokio-stream", "tracing", - "trash", "unicode-width", "yazi-adaptor", "yazi-config", @@ -2632,27 +2629,17 @@ version = "0.1.5" dependencies = [ "anyhow", "async-channel", - "bitflags 2.4.1", - "clipboard-win", - "crossterm", "futures", - "indexmap", "libc", - "notify", "parking_lot", - "ratatui", "regex", "serde", "serde_json", - "syntect", "tokio", - "tokio-stream", "tracing", "trash", - "unicode-width", "yazi-adaptor", "yazi-config", - "yazi-prebuild", "yazi-shared", ] diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index b7512d01..2451b1f1 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -15,25 +15,22 @@ yazi-scheduler = { path = "../yazi-scheduler", version = "0.1.5" } yazi-shared = { path = "../yazi-shared", version = "0.1.5" } # External dependencies -anyhow = "^1" -async-channel = "^1" -bitflags = "^2" -crossterm = "^0" -futures = "^0" -indexmap = "^2" -libc = "^0" -notify = { version = "^6", default-features = false, features = [ "macos_fsevent" ] } -parking_lot = "^0" -ratatui = "^0" -regex = "^1" -serde = "^1" -serde_json = "^1" -syntect = { version = "^5", default-features = false, features = [ "parsing", "default-themes", "plist-load", "regex-onig" ] } -tokio = { version = "^1", features = [ "parking_lot", "macros", "rt-multi-thread", "sync", "time", "fs", "process", "io-std", "io-util" ] } -tokio-stream = "^0" -trash = "^3" -unicode-width = "^0" -yazi-prebuild = "^0" +anyhow = "^1" +bitflags = "^2" +crossterm = "^0" +futures = "^0" +indexmap = "^2" +libc = "^0" +notify = { version = "^6", default-features = false, features = [ "macos_fsevent" ] } +parking_lot = "^0" +ratatui = "^0" +regex = "^1" +serde = "^1" +syntect = { version = "^5", default-features = false, features = [ "parsing", "default-themes", "plist-load", "regex-onig" ] } +tokio = { version = "^1", features = [ "parking_lot", "macros", "rt-multi-thread", "sync", "time", "fs", "process", "io-std", "io-util" ] } +tokio-stream = "^0" +unicode-width = "^0" +yazi-prebuild = "^0" # Logging tracing = { version = "^0", features = [ "max_level_debug", "release_max_level_warn" ] } diff --git a/yazi-core/src/context.rs b/yazi-core/src/context.rs index b1b6d82c..373666f6 100644 --- a/yazi-core/src/context.rs +++ b/yazi-core/src/context.rs @@ -1,7 +1,5 @@ use ratatui::prelude::Rect; -use tokio::sync::oneshot; use yazi_config::popup::{Origin, Position}; -use yazi_shared::{emit, event::Exec, Layer}; use crate::{completion::Completion, help::Help, input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which}; @@ -29,19 +27,10 @@ impl Ctx { } #[inline] - pub async fn stop() { - let (tx, rx) = oneshot::channel::<()>(); - emit!(Call(Exec::call("stop", vec!["true".to_string()]).with_data(Some(tx)).vec(), Layer::App)); - rx.await.ok(); - } + pub async fn stop() { yazi_scheduler::Scheduler::app_stop().await } #[inline] - pub fn resume() { - emit!(Call( - Exec::call("stop", vec!["false".to_string()]).with_data(None::>).vec(), - Layer::App - )); - } + pub fn resume() { yazi_scheduler::Scheduler::app_resume() } pub fn area(&self, position: &Position) -> Rect { if position.origin != Origin::Hovered { diff --git a/yazi-core/src/lib.rs b/yazi-core/src/lib.rs index 2cad03bc..08b17e4d 100644 --- a/yazi-core/src/lib.rs +++ b/yazi-core/src/lib.rs @@ -6,7 +6,6 @@ clippy::unit_arg )] -mod blocker; pub mod completion; mod context; pub mod files; @@ -21,9 +20,8 @@ pub mod tab; pub mod tasks; pub mod which; -pub use blocker::*; pub use context::*; pub use highlighter::*; pub use step::*; -pub fn init() { init_blocker(); } +pub fn init() { yazi_scheduler::init(); } diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 89765522..b820754f 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -3,10 +3,10 @@ use std::{collections::BTreeMap, ffi::OsStr, io::{stdout, BufWriter, Write}, pat use anyhow::{anyhow, bail, Result}; use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{popup::InputCfg, OPEN, PREVIEW}; -use yazi_scheduler::external::{self, ShellOpt}; +use yazi_scheduler::{external::{self, ShellOpt}, BLOCKER}; use yazi_shared::{emit, event::Exec, fs::{max_common_root, File, FilesOp, Url}, term::Term, Defer}; -use crate::{input::Input, manager::Manager, Ctx, BLOCKER}; +use crate::{input::Input, manager::Manager, Ctx}; pub struct Opt { force: bool, diff --git a/yazi-core/src/tab/commands/jump.rs b/yazi-core/src/tab/commands/jump.rs index 9427c636..0700daf0 100644 --- a/yazi-core/src/tab/commands/jump.rs +++ b/yazi-core/src/tab/commands/jump.rs @@ -1,7 +1,7 @@ -use yazi_scheduler::external::{self, FzfOpt, ZoxideOpt}; +use yazi_scheduler::{external::{self, FzfOpt, ZoxideOpt}, BLOCKER}; use yazi_shared::{event::Exec, fs::ends_with_slash, Defer}; -use crate::{tab::Tab, Ctx, BLOCKER}; +use crate::{tab::Tab, Ctx}; pub struct Opt { type_: OptType, diff --git a/yazi-core/src/tasks/commands/inspect.rs b/yazi-core/src/tasks/commands/inspect.rs index 349dabff..e93f5479 100644 --- a/yazi-core/src/tasks/commands/inspect.rs +++ b/yazi-core/src/tasks/commands/inspect.rs @@ -2,9 +2,10 @@ use std::io::{stdout, Write}; use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time}; +use yazi_scheduler::BLOCKER; use yazi_shared::{event::Exec, term::Term, Defer}; -use crate::{tasks::Tasks, Ctx, BLOCKER}; +use crate::{tasks::Tasks, Ctx}; pub struct Opt; diff --git a/yazi-core/src/tasks/mod.rs b/yazi-core/src/tasks/mod.rs index 79983611..32ea75be 100644 --- a/yazi-core/src/tasks/mod.rs +++ b/yazi-core/src/tasks/mod.rs @@ -1,13 +1,8 @@ mod commands; -mod running; -mod scheduler; -mod task; +mod progress; mod tasks; -mod workers; -use running::*; -use scheduler::*; -use task::*; +pub use progress::*; pub use tasks::*; pub const TASKS_PADDING: u16 = 2; diff --git a/yazi-core/src/tasks/progress.rs b/yazi-core/src/tasks/progress.rs new file mode 100644 index 00000000..f14eabd6 --- /dev/null +++ b/yazi-core/src/tasks/progress.rs @@ -0,0 +1,31 @@ +use serde::Serialize; +use yazi_scheduler::Running; + +#[derive(Clone, Copy, Default, Eq, PartialEq, Serialize)] +pub struct TasksProgress { + pub total: u32, + pub succ: u32, + pub fail: u32, + + pub found: u64, + pub processed: u64, +} + +impl From<&Running> for TasksProgress { + fn from(running: &Running) -> Self { + let mut progress = Self::default(); + if running.is_empty() { + return progress; + } + + for task in running.values() { + progress.total += task.total; + progress.succ += task.succ; + progress.fail += task.fail; + + progress.found += task.found; + progress.processed += task.processed; + } + progress + } +} diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index ea6d4b68..d993254c 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -1,11 +1,12 @@ -use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, path::Path, sync::Arc}; +use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, path::Path, sync::Arc, time::Duration}; -use serde::Serialize; +use tokio::time::sleep; use tracing::debug; use yazi_config::{manager::SortBy, open::Opener, popup::InputCfg, OPEN}; -use yazi_shared::{fs::File, fs::Url, term::Term, MimeKind}; +use yazi_scheduler::{Scheduler, TaskSummary}; +use yazi_shared::{fs::{File, Url}, term::Term, MimeKind}; -use super::{running::Running, task::TaskSummary, Scheduler, TASKS_PADDING, TASKS_PERCENT}; +use super::{TasksProgress, TASKS_PADDING, TASKS_PERCENT}; use crate::{files::Files, input::Input}; pub struct Tasks { @@ -18,12 +19,28 @@ pub struct Tasks { impl Tasks { pub fn start() -> Self { - Self { + let tasks = Self { scheduler: Arc::new(Scheduler::start()), visible: false, cursor: 0, progress: Default::default(), - } + }; + + let running = tasks.scheduler.running.clone(); + tokio::spawn(async move { + let mut last = TasksProgress::default(); + loop { + sleep(Duration::from_millis(500)).await; + + let new = TasksProgress::from(&*running.read()); + if last != new { + last = new; + Tasks::_update(new); + } + } + }); + + tasks } #[inline] @@ -209,32 +226,3 @@ impl Tasks { #[inline] pub fn len(&self) -> usize { self.scheduler.running.read().len() } } - -#[derive(Clone, Copy, Default, Eq, PartialEq, Serialize)] -pub struct TasksProgress { - pub total: u32, - pub succ: u32, - pub fail: u32, - - pub found: u64, - pub processed: u64, -} - -impl From<&Running> for TasksProgress { - fn from(running: &Running) -> Self { - let mut progress = Self::default(); - if running.is_empty() { - return progress; - } - - for task in running.values() { - progress.total += task.total; - progress.succ += task.succ; - progress.fail += task.fail; - - progress.found += task.found; - progress.processed += task.processed; - } - progress - } -} diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index b3fe42ca..78db4f9d 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -16,26 +16,14 @@ yazi-shared = { path = "../yazi-shared", version = "0.1.5" } # External dependencies anyhow = "^1" async-channel = "^1" -bitflags = "^2" -crossterm = "^0" futures = "^0" -indexmap = "^2" libc = "^0" -notify = { version = "^6", default-features = false, features = [ "macos_fsevent" ] } parking_lot = "^0" -ratatui = "^0" regex = "^1" serde = "^1" serde_json = "^1" -syntect = { version = "^5", default-features = false, features = [ "parsing", "default-themes", "plist-load", "regex-onig" ] } -tokio = { version = "^1", features = [ "parking_lot", "macros", "rt-multi-thread", "sync", "time", "fs", "process", "io-std", "io-util" ] } -tokio-stream = "^0" +tokio = { version = "^1", features = [ "parking_lot", "rt-multi-thread" ] } trash = "^3" -unicode-width = "^0" -yazi-prebuild = "^0" # Logging tracing = { version = "^0", features = [ "max_level_debug", "release_max_level_warn" ] } - -[target."cfg(windows)".dependencies] -clipboard-win = "^4" diff --git a/yazi-core/src/blocker.rs b/yazi-scheduler/src/blocker.rs similarity index 100% rename from yazi-core/src/blocker.rs rename to yazi-scheduler/src/blocker.rs diff --git a/yazi-scheduler/src/lib.rs b/yazi-scheduler/src/lib.rs index 2e9488b8..ed2f4695 100644 --- a/yazi-scheduler/src/lib.rs +++ b/yazi-scheduler/src/lib.rs @@ -1 +1,15 @@ +#![allow(clippy::unit_arg)] + +mod blocker; pub mod external; +mod running; +mod scheduler; +mod task; +pub mod workers; + +pub use blocker::*; +pub use running::*; +pub use scheduler::*; +pub use task::*; + +pub fn init() { init_blocker(); } diff --git a/yazi-core/src/tasks/running.rs b/yazi-scheduler/src/running.rs similarity index 69% rename from yazi-core/src/tasks/running.rs rename to yazi-scheduler/src/running.rs index 882aa31d..d353a970 100644 --- a/yazi-core/src/tasks/running.rs +++ b/yazi-scheduler/src/running.rs @@ -5,7 +5,7 @@ use futures::future::BoxFuture; use super::{Task, TaskStage}; #[derive(Default)] -pub(super) struct Running { +pub struct Running { incr: usize, pub(super) hooks: @@ -21,25 +21,25 @@ impl Running { } #[inline] - pub(super) fn get(&self, id: usize) -> Option<&Task> { self.all.get(&id) } + pub fn get(&self, id: usize) -> Option<&Task> { self.all.get(&id) } #[inline] - pub(super) fn get_mut(&mut self, id: usize) -> Option<&mut Task> { self.all.get_mut(&id) } + pub fn get_mut(&mut self, id: usize) -> Option<&mut Task> { self.all.get_mut(&id) } #[inline] - pub(super) fn get_id(&self, idx: usize) -> Option { self.values().nth(idx).map(|t| t.id) } + pub fn get_id(&self, idx: usize) -> Option { self.values().nth(idx).map(|t| t.id) } #[inline] - pub(super) fn len(&self) -> usize { self.all.len() } + pub fn len(&self) -> usize { self.all.len() } #[inline] pub(super) fn exists(&self, id: usize) -> bool { self.all.contains_key(&id) } #[inline] - pub(super) fn values(&self) -> impl Iterator { self.all.values() } + pub fn values(&self) -> impl Iterator { self.all.values() } #[inline] - pub(super) fn is_empty(&self) -> bool { self.all.is_empty() } + pub fn is_empty(&self) -> bool { self.all.is_empty() } pub(super) fn try_remove( &mut self, diff --git a/yazi-core/src/tasks/scheduler.rs b/yazi-scheduler/src/scheduler.rs similarity index 84% rename from yazi-core/src/tasks/scheduler.rs rename to yazi-scheduler/src/scheduler.rs index 0309892f..55faa5a5 100644 --- a/yazi-core/src/tasks/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -2,25 +2,25 @@ use std::{ffi::OsStr, sync::Arc, time::Duration}; use futures::{future::BoxFuture, FutureExt}; use parking_lot::RwLock; -use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, time::sleep}; +use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}}; use yazi_config::{open::Opener, TASKS}; -use yazi_shared::{fs::{unique_path, Url}, Throttle}; +use yazi_shared::{emit, event::Exec, fs::{unique_path, Url}, Layer, Throttle}; -use super::{workers::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash, Precache, PrecacheOpMime, PrecacheOpSize, Process, ProcessOpOpen}, Running, TaskOp, TaskStage, TasksProgress}; -use crate::tasks::Tasks; +use super::{Running, TaskOp, TaskStage}; +use crate::workers::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash, Precache, PrecacheOpMime, PrecacheOpSize, Process, ProcessOpOpen}; pub struct Scheduler { file: Arc, precache: Arc, process: Arc, - todo: async_channel::Sender>, - prog: mpsc::UnboundedSender, - pub(super) running: Arc>, + todo: async_channel::Sender>, + prog: mpsc::UnboundedSender, + pub running: Arc>, } impl Scheduler { - pub(super) fn start() -> Self { + pub fn start() -> Self { let (todo_tx, todo_rx) = async_channel::unbounded(); let (prog_tx, prog_rx) = mpsc::unbounded_channel(); @@ -147,23 +147,9 @@ impl Scheduler { } } }); - - let running = self.running.clone(); - tokio::spawn(async move { - let mut last = TasksProgress::default(); - loop { - sleep(Duration::from_millis(500)).await; - - let new = TasksProgress::from(&*running.read()); - if last != new { - last = new; - Tasks::_update(new); - } - } - }); } - pub(super) fn cancel(&self, id: usize) -> bool { + pub fn cancel(&self, id: usize) -> bool { let mut running = self.running.write(); let b = running.all.remove(&id).is_some(); @@ -173,7 +159,20 @@ impl Scheduler { b } - pub(super) fn file_cut(&self, from: Url, mut to: Url, force: bool) { + pub async fn app_stop() { + let (tx, rx) = oneshot::channel::<()>(); + emit!(Call(Exec::call("stop", vec!["true".to_string()]).with_data(Some(tx)).vec(), Layer::App)); + rx.await.ok(); + } + + pub fn app_resume() { + emit!(Call( + Exec::call("stop", vec!["false".to_string()]).with_data(None::>).vec(), + Layer::App + )); + } + + pub fn file_cut(&self, from: Url, mut to: Url, force: bool) { let mut running = self.running.write(); let id = running.add(format!("Cut {:?} to {:?}", from, to)); @@ -204,7 +203,7 @@ impl Scheduler { }); } - pub(super) fn file_copy(&self, from: Url, mut to: Url, force: bool) { + pub fn file_copy(&self, from: Url, mut to: Url, force: bool) { let name = format!("Copy {:?} to {:?}", from, to); let id = self.running.write().add(name); @@ -220,7 +219,7 @@ impl Scheduler { }); } - pub(super) fn file_link(&self, from: Url, mut to: Url, relative: bool, force: bool) { + pub fn file_link(&self, from: Url, mut to: Url, relative: bool, force: bool) { let name = format!("Link {from:?} to {to:?}"); let id = self.running.write().add(name); @@ -239,7 +238,7 @@ impl Scheduler { }); } - pub(super) fn file_delete(&self, target: Url) { + pub fn file_delete(&self, target: Url) { let mut running = self.running.write(); let id = running.add(format!("Delete {:?}", target)); @@ -267,7 +266,7 @@ impl Scheduler { }); } - pub(super) fn file_trash(&self, target: Url) { + pub fn file_trash(&self, target: Url) { let name = format!("Trash {:?}", target); let id = self.running.write().add(name); @@ -280,7 +279,7 @@ impl Scheduler { }); } - pub(super) fn process_open(&self, opener: &Opener, args: &[impl AsRef]) { + pub fn process_open(&self, opener: &Opener, args: &[impl AsRef]) { let name = { let s = format!("Execute `{}`", opener.exec); let args = args.iter().map(|a| a.as_ref().to_string_lossy()).collect::>().join(" "); @@ -324,7 +323,7 @@ impl Scheduler { }); } - pub(super) fn precache_size(&self, targets: Vec<&Url>) { + pub fn precache_size(&self, targets: Vec<&Url>) { let throttle = Arc::new(Throttle::new(targets.len(), Duration::from_millis(300))); let mut handing = self.precache.size_handing.lock(); let mut running = self.running.write(); @@ -349,7 +348,7 @@ impl Scheduler { } } - pub(super) fn precache_mime(&self, targets: Vec) { + pub fn precache_mime(&self, targets: Vec) { let name = format!("Preload mimetype for {} files", targets.len()); let id = self.running.write().add(name); @@ -362,21 +361,21 @@ impl Scheduler { }); } - pub(super) fn precache_image(&self, targets: Vec) { + pub fn precache_image(&self, targets: Vec) { let name = format!("Precache of {} image files", targets.len()); let id = self.running.write().add(name); self.precache.image(id, targets).ok(); } - pub(super) fn precache_video(&self, targets: Vec) { + pub fn precache_video(&self, targets: Vec) { let name = format!("Precache of {} video files", targets.len()); let id = self.running.write().add(name); self.precache.video(id, targets).ok(); } - pub(super) fn precache_pdf(&self, targets: Vec) { + pub fn precache_pdf(&self, targets: Vec) { let name = format!("Precache of {} PDF files", targets.len()); let id = self.running.write().add(name); diff --git a/yazi-core/src/tasks/task.rs b/yazi-scheduler/src/task.rs similarity index 100% rename from yazi-core/src/tasks/task.rs rename to yazi-scheduler/src/task.rs diff --git a/yazi-core/src/tasks/workers/file.rs b/yazi-scheduler/src/workers/file.rs similarity index 99% rename from yazi-core/src/tasks/workers/file.rs rename to yazi-scheduler/src/workers/file.rs index 56ae4d58..3a50e262 100644 --- a/yazi-core/src/tasks/workers/file.rs +++ b/yazi-scheduler/src/workers/file.rs @@ -7,7 +7,7 @@ use tracing::warn; use yazi_config::TASKS; use yazi_shared::fs::{calculate_size, copy_with_progress, path_relative_to, Url}; -use crate::tasks::TaskOp; +use crate::TaskOp; pub(crate) struct File { tx: async_channel::Sender, diff --git a/yazi-core/src/tasks/workers/mod.rs b/yazi-scheduler/src/workers/mod.rs similarity index 100% rename from yazi-core/src/tasks/workers/mod.rs rename to yazi-scheduler/src/workers/mod.rs diff --git a/yazi-core/src/tasks/workers/precache.rs b/yazi-scheduler/src/workers/precache.rs similarity index 98% rename from yazi-core/src/tasks/workers/precache.rs rename to yazi-scheduler/src/workers/precache.rs index 78d366a4..9aa3ba60 100644 --- a/yazi-core/src/tasks/workers/precache.rs +++ b/yazi-scheduler/src/workers/precache.rs @@ -5,10 +5,9 @@ use parking_lot::Mutex; use tokio::{fs, sync::mpsc}; use yazi_adaptor::Image; use yazi_config::PREVIEW; -use yazi_scheduler::external; use yazi_shared::{emit, fs::{calculate_size, FilesOp, Url}, Throttle}; -use crate::tasks::TaskOp; +use crate::{external, TaskOp}; pub(crate) struct Precache { tx: async_channel::Sender, diff --git a/yazi-core/src/tasks/workers/process.rs b/yazi-scheduler/src/workers/process.rs similarity index 95% rename from yazi-core/src/tasks/workers/process.rs rename to yazi-scheduler/src/workers/process.rs index 1a3a95d8..2b1df4f8 100644 --- a/yazi-core/src/tasks/workers/process.rs +++ b/yazi-scheduler/src/workers/process.rs @@ -2,9 +2,8 @@ use std::{ffi::OsString, mem}; use anyhow::Result; use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::{mpsc, oneshot}}; -use yazi_scheduler::external::{self, ShellOpt}; -use crate::{tasks::TaskOp, Ctx, BLOCKER}; +use crate::{external::{self, ShellOpt}, Scheduler, TaskOp, BLOCKER}; pub(crate) struct Process { sch: mpsc::UnboundedSender, @@ -38,7 +37,7 @@ impl Process { let opt = ShellOpt::from(&mut task); if task.block { let _guard = BLOCKER.acquire().await.unwrap(); - Ctx::stop().await; + Scheduler::app_stop().await; match external::shell(opt) { Ok(mut child) => { @@ -50,7 +49,7 @@ impl Process { self.fail(task.id, format!("Failed to spawn process: {e}"))?; } } - return Ok(Ctx::resume()); + return Ok(Scheduler::app_resume()); } if task.orphan {