From 0c8c973e6c766ff026665e487a33ad82f0c7e4f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Tue, 13 Jan 2026 16:53:00 +0800 Subject: [PATCH] feat: check and refresh the file list when the terminal gains focus (#3561) --- CHANGELOG.md | 2 ++ yazi-boot/src/actions/debug.rs | 13 ++++++------ yazi-fm/src/app/commands/focus.rs | 15 +++++++++++++ yazi-fm/src/app/commands/mod.rs | 1 + yazi-fm/src/dispatcher.rs | 1 + yazi-fm/src/signals.rs | 3 ++- yazi-fm/src/term.rs | 5 ++++- yazi-scheduler/src/file/out.rs | 4 ++++ yazi-scheduler/src/file/progress.rs | 11 ++++++---- yazi-scheduler/src/hook/hook.rs | 16 ++++++++++---- yazi-scheduler/src/hook/in.rs | 33 +++++++---------------------- yazi-scheduler/src/in.rs | 13 +++++++----- yazi-scheduler/src/out.rs | 10 ++------- yazi-scheduler/src/runner.rs | 2 ++ yazi-scheduler/src/scheduler.rs | 9 ++++---- yazi-shared/src/event/event.rs | 1 + yazi-watcher/src/remote/remote.rs | 15 +++++++------ yazi-watcher/src/reporter.rs | 10 ++++----- 18 files changed, 95 insertions(+), 69 deletions(-) create mode 100644 yazi-fm/src/app/commands/focus.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 195c2c30..fb7529ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,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]) - New `Path.os()` API creates an OS-native `Path` ([#3541]) ### Fixed @@ -1601,3 +1602,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3532]: https://github.com/sxyazi/yazi/pull/3532 [#3540]: https://github.com/sxyazi/yazi/pull/3540 [#3541]: https://github.com/sxyazi/yazi/pull/3541 +[#3561]: https://github.com/sxyazi/yazi/pull/3561 diff --git a/yazi-boot/src/actions/debug.rs b/yazi-boot/src/actions/debug.rs index b77ff130..59034370 100644 --- a/yazi-boot/src/actions/debug.rs +++ b/yazi-boot/src/actions/debug.rs @@ -21,11 +21,12 @@ impl Actions { writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?; writeln!(s, "\nConfig")?; - writeln!(s, " Yazi : {}", Self::config_state("yazi"))?; - writeln!(s, " Keymap : {}", Self::config_state("keymap"))?; - writeln!(s, " Theme : {}", Self::config_state("theme"))?; - writeln!(s, " VFS : {}", Self::config_state("vfs"))?; - writeln!(s, " Package : {}", Self::config_state("package"))?; + writeln!(s, " Init : {}", Self::config_state("init.lua"))?; + writeln!(s, " Yazi : {}", Self::config_state("yazi.toml"))?; + writeln!(s, " Keymap : {}", Self::config_state("keymap.toml"))?; + writeln!(s, " Theme : {}", Self::config_state("theme.toml"))?; + writeln!(s, " VFS : {}", Self::config_state("vfs.toml"))?; + writeln!(s, " Package : {}", Self::config_state("package.toml"))?; writeln!(s, " Dark/light flavor: {:?} / {:?}", THEME.flavor.dark, THEME.flavor.light)?; writeln!(s, "\nEmulator")?; @@ -125,7 +126,7 @@ impl Actions { } fn config_state(name: &str) -> String { - let p = Xdg::config_dir().join(format!("{name}.toml")); + let p = Xdg::config_dir().join(name); match std::fs::read_to_string(&p) { Ok(s) if s.is_empty() => format!("{} (empty)", p.display()), Ok(s) if s.trim().is_empty() => format!("{} (whitespaces)", p.display()), diff --git a/yazi-fm/src/app/commands/focus.rs b/yazi-fm/src/app/commands/focus.rs new file mode 100644 index 00000000..8bbff642 --- /dev/null +++ b/yazi-fm/src/app/commands/focus.rs @@ -0,0 +1,15 @@ +use anyhow::Result; +use yazi_actor::Ctx; +use yazi_macro::act; +use yazi_parser::VoidOpt; +use yazi_shared::data::Data; + +use crate::app::App; + +impl App { + pub fn focus(&mut self, _: VoidOpt) -> Result { + let cx = &mut Ctx::active(&mut self.core); + + act!(mgr:refresh, cx) + } +} diff --git a/yazi-fm/src/app/commands/mod.rs b/yazi-fm/src/app/commands/mod.rs index 0357cf32..aed61646 100644 --- a/yazi-fm/src/app/commands/mod.rs +++ b/yazi-fm/src/app/commands/mod.rs @@ -2,6 +2,7 @@ yazi_macro::mod_flat!( accept_payload bootstrap deprecate + focus mouse notify plugin diff --git a/yazi-fm/src/dispatcher.rs b/yazi-fm/src/dispatcher.rs index e75abc7d..4069e2aa 100644 --- a/yazi-fm/src/dispatcher.rs +++ b/yazi-fm/src/dispatcher.rs @@ -28,6 +28,7 @@ impl<'a> Dispatcher<'a> { Event::Key(key) => self.dispatch_key(key), Event::Mouse(mouse) => act!(mouse, self.app, mouse), Event::Resize => act!(resize, self.app), + Event::Focus => act!(focus, self.app), Event::Paste(str) => self.dispatch_paste(str), Event::Quit(opt) => act!(quit, self.app, opt), }; diff --git a/yazi-fm/src/signals.rs b/yazi-fm/src/signals.rs index 78a0f505..f30ccbe1 100644 --- a/yazi-fm/src/signals.rs +++ b/yazi-fm/src/signals.rs @@ -64,8 +64,9 @@ impl Signals { Event::Mouse(mouse).emit(); } } - CrosstermEvent::Paste(str) => Event::Paste(str).emit(), CrosstermEvent::Resize(..) => Event::Resize.emit(), + CrosstermEvent::FocusGained => Event::Focus.emit(), + CrosstermEvent::Paste(str) => Event::Paste(str).emit(), _ => {} } } diff --git a/yazi-fm/src/term.rs b/yazi-fm/src/term.rs index c3bf1c19..c906befc 100644 --- a/yazi-fm/src/term.rs +++ b/yazi-fm/src/term.rs @@ -1,7 +1,7 @@ use std::{io, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, Ordering}}; use anyhow::Result; -use crossterm::{event::{DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{EnterAlternateScreen, LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode}}; +use crossterm::{event::{DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, EnableFocusChange, EnableMouseCapture, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{EnterAlternateScreen, LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode}}; use ratatui::{CompletedFrame, Frame, Terminal, backend::CrosstermBackend, buffer::Buffer, layout::Rect}; use yazi_adapter::{Emulator, Mux, TMUX}; use yazi_config::{THEME, YAZI}; @@ -40,6 +40,7 @@ impl Term { yazi_term::If(TMUX.get(), EnterAlternateScreen), yazi_term::SetBackground(true, THEME.app.bg_color()), // Set app background EnableBracketedPaste, + EnableFocusChange, yazi_term::If(!YAZI.mgr.mouse_events.get().is_empty(), EnableMouseCapture), )?; @@ -81,6 +82,7 @@ impl Term { TTY.writer(), yazi_term::If(!YAZI.mgr.mouse_events.get().is_empty(), DisableMouseCapture), yazi_term::RestoreCursor, + DisableFocusChange, DisableBracketedPaste, LeaveAlternateScreen, )?; @@ -103,6 +105,7 @@ impl Term { yazi_term::If(!YAZI.mgr.mouse_events.get().is_empty(), DisableMouseCapture), yazi_term::SetBackground(false, THEME.app.bg_color()), yazi_term::RestoreCursor, + DisableFocusChange, DisableBracketedPaste, LeaveAlternateScreen, crossterm::cursor::Show diff --git a/yazi-scheduler/src/file/out.rs b/yazi-scheduler/src/file/out.rs index ab09d23a..2f4ec89a 100644 --- a/yazi-scheduler/src/file/out.rs +++ b/yazi-scheduler/src/file/out.rs @@ -455,6 +455,7 @@ pub(crate) enum FileOutUpload { Deform(String), Succ, Fail(String), + Clean, } impl From for FileOutUpload { @@ -481,6 +482,9 @@ impl FileOutUpload { prog.collected = Some(false); task.log(reason); } + Self::Clean => { + prog.cleaned = Some(true); + } } } } diff --git a/yazi-scheduler/src/file/progress.rs b/yazi-scheduler/src/file/progress.rs index 184bc82e..7368a046 100644 --- a/yazi-scheduler/src/file/progress.rs +++ b/yazi-scheduler/src/file/progress.rs @@ -321,6 +321,7 @@ pub struct FileProgUpload { pub total_bytes: u64, pub processed_bytes: u64, pub collected: Option, + pub cleaned: Option, } impl From for TaskSummary { @@ -340,14 +341,16 @@ impl FileProgUpload { } pub fn running(self) -> bool { - self.collected.is_none() || self.success_files + self.failed_files != self.total_files + self.collected.is_none() + || self.success_files + self.failed_files != self.total_files + || (self.cleaned.is_none() && self.cooked()) } - pub fn success(self) -> bool { self.cooked() } + pub fn success(self) -> bool { self.cleaned == Some(true) && self.cooked() } - pub fn failed(self) -> bool { self.collected == Some(false) } + pub fn failed(self) -> bool { self.cleaned == Some(false) || self.collected == Some(false) } - pub fn cleaned(self) -> Option { None } + pub fn cleaned(self) -> Option { self.cleaned } pub fn percent(self) -> Option { Some(if self.success() { diff --git a/yazi-scheduler/src/hook/hook.rs b/yazi-scheduler/src/hook/hook.rs index 14dbc9a9..54e8e84c 100644 --- a/yazi-scheduler/src/hook/hook.rs +++ b/yazi-scheduler/src/hook/hook.rs @@ -7,7 +7,7 @@ use yazi_fs::ok_or_not_found; use yazi_proxy::TasksProxy; use yazi_vfs::provider; -use crate::{Ongoing, TaskOp, TaskOps, file::{FileOutCopy, FileOutCut, FileOutDelete, FileOutDownload, FileOutTrash}, hook::{HookInOutCopy, HookInOutCut, HookInOutDelete, HookInOutDownload, HookInOutTrash}}; +use crate::{Ongoing, TaskOp, TaskOps, file::{FileOutCopy, FileOutCut, FileOutDelete, FileOutDownload, FileOutTrash, FileOutUpload}, hook::{HookInDelete, HookInDownload, HookInOutCopy, HookInOutCut, HookInTrash, HookInUpload}}; pub(crate) struct Hook { ops: TaskOps, @@ -41,7 +41,7 @@ impl Hook { self.ops.out(task.id, FileOutCopy::Clean); } - pub(crate) async fn delete(&self, task: HookInOutDelete) { + pub(crate) async fn delete(&self, task: HookInDelete) { if !self.ongoing.lock().intact(task.id) { return self.ops.out(task.id, FileOutDelete::Clean(Ok(()))); } @@ -53,7 +53,7 @@ impl Hook { self.ops.out(task.id, FileOutDelete::Clean(result)); } - pub(crate) async fn trash(&self, task: HookInOutTrash) { + pub(crate) async fn trash(&self, task: HookInTrash) { let intact = self.ongoing.lock().intact(task.id); if intact { TasksProxy::update_succeed([&task.target]); @@ -62,7 +62,15 @@ impl Hook { self.ops.out(task.id, FileOutTrash::Clean); } - pub(crate) async fn download(&self, task: HookInOutDownload) { + pub(crate) async fn download(&self, task: HookInDownload) { self.ops.out(task.id, FileOutDownload::Clean); } + + pub(crate) async fn upload(&self, task: HookInUpload) { + let intact = self.ongoing.lock().intact(task.id); + if intact { + TasksProxy::update_succeed([task.target]); + } + self.ops.out(task.id, FileOutUpload::Clean); + } } diff --git a/yazi-scheduler/src/hook/in.rs b/yazi-scheduler/src/hook/in.rs index bb0ec782..c5856978 100644 --- a/yazi-scheduler/src/hook/in.rs +++ b/yazi-scheduler/src/hook/in.rs @@ -48,44 +48,27 @@ impl HookInOutCut { // --- Delete #[derive(Debug)] -pub(crate) struct HookInOutDelete { +pub(crate) struct HookInDelete { pub(crate) id: Id, pub(crate) target: UrlBuf, } -impl HookInOutDelete { - pub(crate) fn reduce(self, task: &mut Task) { - if let TaskProg::FileDelete(_) = &task.prog { - task.hook = Some(self.into()); - } - } -} - // --- Trash #[derive(Debug)] -pub(crate) struct HookInOutTrash { +pub(crate) struct HookInTrash { pub(crate) id: Id, pub(crate) target: UrlBuf, } -impl HookInOutTrash { - pub(crate) fn reduce(self, task: &mut Task) { - if let TaskProg::FileTrash(_) = &task.prog { - task.hook = Some(self.into()); - } - } -} - // --- Download #[derive(Debug)] -pub(crate) struct HookInOutDownload { +pub(crate) struct HookInDownload { pub(crate) id: Id, } -impl HookInOutDownload { - pub(crate) fn reduce(self, task: &mut Task) { - if let TaskProg::FileDownload(_) = &task.prog { - task.hook = Some(self.into()); - } - } +// --- Upload +#[derive(Debug)] +pub(crate) struct HookInUpload { + pub(crate) id: Id, + pub(crate) target: UrlBuf, } diff --git a/yazi-scheduler/src/in.rs b/yazi-scheduler/src/in.rs index 09ff4d2a..c3aa1e51 100644 --- a/yazi-scheduler/src/in.rs +++ b/yazi-scheduler/src/in.rs @@ -1,6 +1,6 @@ use yazi_shared::Id; -use crate::{file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload}, hook::{HookInOutCopy, HookInOutCut, HookInOutDelete, HookInOutDownload, HookInOutTrash}, impl_from_in, plugin::PluginInEntry, prework::{PreworkInFetch, PreworkInLoad, PreworkInSize}, process::{ProcessInBg, ProcessInBlock, ProcessInOrphan}}; +use crate::{file::{FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload}, hook::{HookInDelete, HookInDownload, HookInOutCopy, HookInOutCut, HookInTrash, HookInUpload}, impl_from_in, plugin::PluginInEntry, prework::{PreworkInFetch, PreworkInLoad, PreworkInSize}, process::{ProcessInBg, ProcessInBlock, ProcessInOrphan}}; #[derive(Debug)] pub(crate) enum TaskIn { @@ -26,9 +26,10 @@ pub(crate) enum TaskIn { // Hook HookCopy(HookInOutCopy), HookCut(HookInOutCut), - HookDelete(HookInOutDelete), - HookTrash(HookInOutTrash), - HookDownload(HookInOutDownload), + HookDelete(HookInDelete), + HookTrash(HookInTrash), + HookDownload(HookInDownload), + HookUpload(HookInUpload), } impl_from_in! { @@ -41,7 +42,7 @@ impl_from_in! { // Process ProcessBlock(ProcessInBlock), ProcessOrphan(ProcessInOrphan), ProcessBg(ProcessInBg), // Hook - HookCopy(HookInOutCopy), HookCut(HookInOutCut), HookDelete(HookInOutDelete), HookTrash(HookInOutTrash), HookDownload(HookInOutDownload), + HookCopy(HookInOutCopy), HookCut(HookInOutCut), HookDelete(HookInDelete), HookTrash(HookInTrash), HookDownload(HookInDownload), HookUpload(HookInUpload), } impl TaskIn { @@ -72,6 +73,7 @@ impl TaskIn { Self::HookDelete(r#in) => r#in.id, Self::HookTrash(r#in) => r#in.id, Self::HookDownload(r#in) => r#in.id, + Self::HookUpload(r#in) => r#in.id, } } @@ -102,6 +104,7 @@ impl TaskIn { Self::HookDelete(_) => true, Self::HookTrash(_) => true, Self::HookDownload(_) => true, + Self::HookUpload(_) => true, } } } diff --git a/yazi-scheduler/src/out.rs b/yazi-scheduler/src/out.rs index ebcac919..4b421d61 100644 --- a/yazi-scheduler/src/out.rs +++ b/yazi-scheduler/src/out.rs @@ -1,4 +1,4 @@ -use crate::{Task, file::{FileOutCopy, FileOutCopyDo, FileOutCut, FileOutCutDo, FileOutDelete, FileOutDeleteDo, FileOutDownload, FileOutDownloadDo, FileOutHardlink, FileOutHardlinkDo, FileOutLink, FileOutTrash, FileOutUpload, FileOutUploadDo}, hook::{HookInOutCopy, HookInOutCut, HookInOutDelete, HookInOutDownload, HookInOutTrash}, impl_from_out, plugin::PluginOutEntry, prework::{PreworkOutFetch, PreworkOutLoad, PreworkOutSize}, process::{ProcessOutBg, ProcessOutBlock, ProcessOutOrphan}}; +use crate::{Task, file::{FileOutCopy, FileOutCopyDo, FileOutCut, FileOutCutDo, FileOutDelete, FileOutDeleteDo, FileOutDownload, FileOutDownloadDo, FileOutHardlink, FileOutHardlinkDo, FileOutLink, FileOutTrash, FileOutUpload, FileOutUploadDo}, hook::{HookInOutCopy, HookInOutCut}, impl_from_out, plugin::PluginOutEntry, prework::{PreworkOutFetch, PreworkOutLoad, PreworkOutSize}, process::{ProcessOutBg, ProcessOutBlock, ProcessOutOrphan}}; #[derive(Debug)] pub(super) enum TaskOut { @@ -30,9 +30,6 @@ pub(super) enum TaskOut { // Hook HookCopy(HookInOutCopy), HookCut(HookInOutCut), - HookDelete(HookInOutDelete), - HookTrash(HookInOutTrash), - HookDownload(HookInOutDownload), } impl_from_out! { @@ -45,7 +42,7 @@ impl_from_out! { // Process ProcessBlock(ProcessOutBlock), ProcessOrphan(ProcessOutOrphan), ProcessBg(ProcessOutBg), // Hook - HookCopy(HookInOutCopy), HookCut(HookInOutCut), HookDelete(HookInOutDelete), HookTrash(HookInOutTrash), HookDownload(HookInOutDownload), + HookCopy(HookInOutCopy), HookCut(HookInOutCut), } impl TaskOut { @@ -79,9 +76,6 @@ impl TaskOut { // Hook Self::HookCopy(out) => out.reduce(task), Self::HookCut(out) => out.reduce(task), - Self::HookDelete(out) => out.reduce(task), - Self::HookTrash(out) => out.reduce(task), - Self::HookDownload(out) => out.reduce(task), } } } diff --git a/yazi-scheduler/src/runner.rs b/yazi-scheduler/src/runner.rs index 179e9e23..71061099 100644 --- a/yazi-scheduler/src/runner.rs +++ b/yazi-scheduler/src/runner.rs @@ -39,6 +39,7 @@ impl Runner { TaskIn::HookDelete(r#in) => Ok(self.hook.delete(r#in).await), TaskIn::HookTrash(r#in) => Ok(self.hook.trash(r#in).await), TaskIn::HookDownload(r#in) => Ok(self.hook.download(r#in).await), + TaskIn::HookUpload(r#in) => Ok(self.hook.upload(r#in).await), } } @@ -69,6 +70,7 @@ impl Runner { TaskIn::HookDelete(_in) => unreachable!(), TaskIn::HookTrash(_in) => unreachable!(), TaskIn::HookDownload(_in) => unreachable!(), + TaskIn::HookUpload(_in) => unreachable!(), } } } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index ffcc83d5..2a745d96 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -7,7 +7,7 @@ use yazi_parser::{app::PluginOpt, tasks::ProcessOpenOpt}; use yazi_shared::{CompletionToken, Id, Throttle, url::{UrlBuf, UrlLike}}; use super::{Ongoing, TaskOp}; -use crate::{HIGH, LOW, NORMAL, Runner, TaskIn, TaskOps, file::{File, FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload, FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, hook::{Hook, HookInOutDelete, HookInOutDownload, HookInOutTrash}, plugin::{Plugin, PluginInEntry, PluginProgEntry}, prework::{Prework, PreworkInFetch, PreworkInLoad, PreworkInSize, PreworkProgFetch, PreworkProgLoad, PreworkProgSize}, process::{Process, ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}}; +use crate::{HIGH, LOW, NORMAL, Runner, TaskIn, TaskOps, file::{File, FileInCopy, FileInCut, FileInDelete, FileInDownload, FileInHardlink, FileInLink, FileInTrash, FileInUpload, FileOutCopy, FileOutCut, FileOutDownload, FileOutHardlink, FileOutUpload, FileProgCopy, FileProgCut, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgTrash, FileProgUpload}, hook::{Hook, HookInDelete, HookInDownload, HookInTrash, HookInUpload}, plugin::{Plugin, PluginInEntry, PluginProgEntry}, prework::{Prework, PreworkInFetch, PreworkInLoad, PreworkInSize, PreworkProgFetch, PreworkProgLoad, PreworkProgSize}, process::{Process, ProcessInBg, ProcessInBlock, ProcessInOrphan, ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}}; pub struct Scheduler { ops: TaskOps, @@ -163,7 +163,7 @@ impl Scheduler { let mut ongoing = self.ongoing.lock(); let task = ongoing.add::(format!("Delete {}", target.display())); - task.set_hook(HookInOutDelete { id: task.id, target: target.clone() }); + task.set_hook(HookInDelete { id: task.id, target: target.clone() }); self.queue(FileInDelete { id: task.id, target, cha: None }, LOW); } @@ -171,7 +171,7 @@ impl Scheduler { let mut ongoing = self.ongoing.lock(); let task = ongoing.add::(format!("Trash {}", target.display())); - task.set_hook(HookInOutTrash { id: task.id, target: target.clone() }); + task.set_hook(HookInTrash { id: task.id, target: target.clone() }); self.queue(FileInTrash { id: task.id, target }, LOW); } @@ -179,8 +179,8 @@ impl Scheduler { let mut ongoing = self.ongoing.lock(); let task = ongoing.add::(format!("Download {}", url.display())); - task.set_hook(HookInOutDownload { id: task.id }); if url.kind().is_remote() { + task.set_hook(HookInDownload { id: task.id }); self.queue( FileInDownload { id: task.id, url, cha: None, retry: 0, done: task.done.clone() }, LOW, @@ -202,6 +202,7 @@ impl Scheduler { .out(task.id, FileOutUpload::Fail("Cannot upload non-remote file".to_owned())); }; + task.set_hook(HookInUpload { id: task.id, target: url.clone() }); self.queue( FileInUpload { id: task.id, url, cha: None, cache: None, done: task.done.clone() }, LOW, diff --git a/yazi-shared/src/event/event.rs b/yazi-shared/src/event/event.rs index 5a6c3606..7dca880e 100644 --- a/yazi-shared/src/event/event.rs +++ b/yazi-shared/src/event/event.rs @@ -15,6 +15,7 @@ pub enum Event { Key(KeyEvent), Mouse(MouseEvent), Resize, + Focus, Paste(String), Quit(EventQuit), } diff --git a/yazi-watcher/src/remote/remote.rs b/yazi-watcher/src/remote/remote.rs index dde45220..1b804f9e 100644 --- a/yazi-watcher/src/remote/remote.rs +++ b/yazi-watcher/src/remote/remote.rs @@ -1,7 +1,7 @@ use std::time::{Duration, SystemTime}; use anyhow::Result; -use hashbrown::HashSet; +use hashbrown::HashMap; use tokio::{pin, sync::mpsc::UnboundedReceiver}; use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream}; use yazi_fs::{File, FilesOp}; @@ -14,7 +14,7 @@ use crate::{Reporter, WATCHER}; pub(crate) struct Remote; impl Remote { - pub(crate) fn serve(rx: UnboundedReceiver, _reporter: Reporter) -> Self { + pub(crate) fn serve(rx: UnboundedReceiver<(UrlBuf, bool)>, _reporter: Reporter) -> Self { tokio::spawn(Self::changed(rx)); Self @@ -24,19 +24,22 @@ impl Remote { pub(crate) fn unwatch(&mut self, _url: Url) -> Result<()> { Ok(()) } - async fn changed(rx: UnboundedReceiver) { + async fn changed(rx: UnboundedReceiver<(UrlBuf, bool)>) { let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(250)); pin!(rx); while let Some(chunk) = rx.next().await { - let urls: HashSet<_> = chunk.into_iter().collect(); + let mut urls = HashMap::with_capacity(chunk.len()); + for (u, upload) in chunk { + urls.entry(u).and_modify(|b| *b |= upload).or_insert(upload); + } let _permit = WATCHER.acquire().await.unwrap(); let mut ops = Vec::with_capacity(urls.len()); let mut ups = Vec::with_capacity(urls.len()); - for u in urls { + for (u, upload) in urls { let Some((parent, urn)) = u.pair() else { continue }; let Ok(mut file) = File::new(&u).await else { ops.push(FilesOp::Deleting(parent.into(), [urn.into()].into())); @@ -47,7 +50,7 @@ impl Remote { file.cha.ctime = Some(SystemTime::now()); ops.push(FilesOp::Upserting(parent.into(), [(urn.into(), file)].into())); - if is_file { + if upload && is_file { ups.push(u); } } diff --git a/yazi-watcher/src/reporter.rs b/yazi-watcher/src/reporter.rs index abda3763..eee9a2f1 100644 --- a/yazi-watcher/src/reporter.rs +++ b/yazi-watcher/src/reporter.rs @@ -9,7 +9,7 @@ use crate::{WATCHED, local::LINKED}; #[derive(Clone)] pub(crate) struct Reporter { pub(super) local_tx: mpsc::UnboundedSender, - pub(super) remote_tx: mpsc::UnboundedSender, + pub(super) remote_tx: mpsc::UnboundedSender<(UrlBuf, bool)>, } impl Reporter { @@ -49,9 +49,9 @@ impl Reporter { let Some(dir) = watched.find_by_cache(parent.loc()) else { continue }; let Some(name) = url.name() else { continue }; if let Ok(u) = dir.try_join(Cow::from(percent_decode(name.encoded_bytes()))) { - self.remote_tx.send(u).ok(); + self.remote_tx.send((u, true)).ok(); } - self.remote_tx.send(dir).ok(); + self.remote_tx.send((dir, false)).ok(); } } @@ -61,7 +61,7 @@ impl Reporter { return; } - self.remote_tx.send(parent.to_owned()).ok(); - self.remote_tx.send(url.into_owned()).ok(); + self.remote_tx.send((parent.to_owned(), false)).ok(); + self.remote_tx.send((url.into_owned(), false)).ok(); } }