From cd338d0d1b585f7b04c51845577d0a8917832b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sat, 18 Jan 2025 22:43:47 +0800 Subject: [PATCH] feat: more complete hovered file cursor tracking (#2218) --- yazi-core/src/manager/commands/hover.rs | 14 +++---- .../src/manager/commands/update_files.rs | 18 ++++----- yazi-core/src/tab/commands/filter_do.rs | 2 +- yazi-core/src/tab/folder.rs | 40 ++++++++++--------- yazi-core/src/tab/tab.rs | 8 +--- yazi-fs/src/stage.rs | 5 +++ yazi-shared/src/url/urn.rs | 4 ++ 7 files changed, 48 insertions(+), 43 deletions(-) diff --git a/yazi-core/src/manager/commands/hover.rs b/yazi-core/src/manager/commands/hover.rs index b24f6513..9679c119 100644 --- a/yazi-core/src/manager/commands/hover.rs +++ b/yazi-core/src/manager/commands/hover.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, path::PathBuf}; +use std::collections::HashSet; use yazi_dds::Pubsub; use yazi_macro::render; @@ -26,7 +26,7 @@ impl Manager { if let Some(u) = opt.url { self.hover_do(u, opt.tab); } else { - self.current_or_mut(opt.tab).repos(None); + self.current_or_mut(opt.tab).arrow(0); } // Repeek @@ -51,15 +51,15 @@ impl Manager { fn hover_do(&mut self, url: Url, tab: Option) { // Hover on the file - if let Ok(p) = url.strip_prefix(&self.current_or(tab).url).map(PathBuf::from) { - render!(self.current_or_mut(tab).repos(Some(Urn::new(&p)))); + if let Ok(p) = url.strip_prefix(&self.current_or(tab).url) { + render!(self.current_or_mut(tab).hover(Urn::new(p))); } // Turn on tracing - if self.current_or(tab).hovered().is_some_and(|f| url == f.url) { + if self.current_or(tab).hovered().is_some_and(|h| h.url == url) { // `hover(Some)` occurs after user actions, such as create, rename, reveal, etc. - // At this point, it's intuitive to track the location of this file regardless. - self.current_or_mut(tab).tracing = true; + // At this point, it's intuitive to track the location of the file regardless. + self.current_or_mut(tab).trace = Some(url.urn_owned()); } } } diff --git a/yazi-core/src/manager/commands/update_files.rs b/yazi-core/src/manager/commands/update_files.rs index 9498f7f3..0be74d12 100644 --- a/yazi-core/src/manager/commands/update_files.rs +++ b/yazi-core/src/manager/commands/update_files.rs @@ -70,16 +70,12 @@ impl Manager { } fn update_current(tab: &mut Tab, op: Cow, tasks: &Tasks) { - let hovered = tab.hovered().filter(|_| tab.current.tracing).map(|h| h.urn_owned()); let calc = !matches!(*op, FilesOp::Size(..) | FilesOp::Deleting(..)); - let foreign = matches!(op, Cow::Borrowed(_)); + if !tab.current.update_pub(tab.id, op.into_owned()) { return; - } - - tab.current.repos(hovered.as_ref().map(|u| u.as_urn())); - if foreign { + } else if foreign { return; } @@ -109,11 +105,11 @@ impl Manager { |(p, n)| matches!(*op, FilesOp::Deleting(ref parent, ref urns) if *parent == p && urns.contains(n)), ); - let folder = tab.history.entry(op.cwd().clone()).or_insert_with(|| Folder::from(op.cwd())); - let hovered = folder.hovered().filter(|_| folder.tracing).map(|h| h.urn_owned()); - if folder.update_pub(tab.id, op.into_owned()) { - folder.repos(hovered.as_ref().map(|u| u.as_urn())); - } + tab + .history + .entry(op.cwd().clone()) + .or_insert_with(|| Folder::from(op.cwd())) + .update_pub(tab.id, op.into_owned()); if leave { tab.leave(()); diff --git a/yazi-core/src/tab/commands/filter_do.rs b/yazi-core/src/tab/commands/filter_do.rs index 44606628..d8065776 100644 --- a/yazi-core/src/tab/commands/filter_do.rs +++ b/yazi-core/src/tab/commands/filter_do.rs @@ -25,7 +25,7 @@ impl Tab { return; } - self.current.repos(hovered.as_ref().map(|u| u.as_urn())); + self.current.repos(hovered.as_ref()); if self.hovered().map(|f| f.urn()) != hovered.as_ref().map(|u| u.as_urn()) { ManagerProxy::hover(None, self.id); } diff --git a/yazi-core/src/tab/folder.rs b/yazi-core/src/tab/folder.rs index ecadc240..71d57498 100644 --- a/yazi-core/src/tab/folder.rs +++ b/yazi-core/src/tab/folder.rs @@ -4,7 +4,7 @@ use yazi_config::{LAYOUT, MANAGER}; use yazi_dds::Pubsub; use yazi_fs::{Cha, File, Files, FilesOp, FolderStage, Step}; use yazi_proxy::ManagerProxy; -use yazi_shared::{Id, url::{Url, Urn}}; +use yazi_shared::{Id, url::{Url, Urn, UrnBuf}}; pub struct Folder { pub url: Url, @@ -15,21 +15,21 @@ pub struct Folder { pub offset: usize, pub cursor: usize, - pub page: usize, - pub tracing: bool, + pub page: usize, + pub trace: Option, } impl Default for Folder { fn default() -> Self { Self { - url: Default::default(), - cha: Default::default(), - files: Files::new(MANAGER.show_hidden), - stage: Default::default(), - offset: Default::default(), - cursor: Default::default(), - page: Default::default(), - tracing: Default::default(), + url: Default::default(), + cha: Default::default(), + files: Files::new(MANAGER.show_hidden), + stage: Default::default(), + offset: Default::default(), + cursor: Default::default(), + page: Default::default(), + trace: Default::default(), } } } @@ -45,6 +45,9 @@ impl Folder { FilesOp::Full(_, _, cha) => { (self.cha, self.stage) = (cha, FolderStage::Loaded); } + FilesOp::Part(_, ref files, _) if files.is_empty() => { + self.stage = FolderStage::Loading; + } FilesOp::Part(_, _, ticket) if ticket == self.files.ticket() => { self.stage = FolderStage::Loading; } @@ -73,7 +76,9 @@ impl Folder { FilesOp::Upserting(_, files) => self.files.update_upserting(files), } - self.arrow(0); + self.trace = self.trace.take_if(|_| !self.files.is_empty() || self.stage.is_loading()); + self.repos(self.trace.clone()); + (stage, revision) != (self.stage, self.files.revision) } @@ -90,15 +95,14 @@ impl Folder { pub fn arrow(&mut self, step: impl Into) -> bool { let step = step.into() as Step; let mut b = if self.files.is_empty() { - (self.cursor, self.offset, self.tracing) = (0, 0, false); - false + (mem::take(&mut self.cursor), mem::take(&mut self.offset)) != (0, 0) } else if step.is_positive() { self.next(step) } else { self.prev(step) }; - self.tracing |= b; + self.trace = self.hovered().filter(|_| b).map(|h| h.urn_owned()).or(self.trace.take()); b |= self.squeeze_offset(); self.sync_page(false); @@ -107,7 +111,7 @@ impl Folder { pub fn hover(&mut self, urn: &Urn) -> bool { if self.hovered().map(|h| h.urn()) == Some(urn) { - return false; + return self.arrow(0); } let new = self.files.position(urn).unwrap_or(self.cursor) as isize; @@ -115,8 +119,8 @@ impl Folder { } #[inline] - pub fn repos(&mut self, url: Option<&Urn>) -> bool { - if let Some(u) = url { self.hover(u) } else { self.arrow(0) } + pub fn repos(&mut self, urn: Option>) -> bool { + if let Some(u) = urn { self.hover(u.as_ref()) } else { self.arrow(0) } } pub fn sync_page(&mut self, force: bool) { diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index 4cf9b0e3..2e9b083e 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -123,22 +123,18 @@ impl Tab { return render!(); } - let hovered = f.hovered().filter(|_| f.tracing).map(|h| h.urn_owned()); f.files.set_show_hidden(self.pref.show_hidden); f.files.set_sorter(<_>::from(&self.pref)); render!(f.files.catchup_revision()); - render!(f.repos(hovered.as_ref().map(|u| u.as_urn()))); + render!(f.repos(f.trace.clone())); }; apply(&mut self.current); if let Some(parent) = &mut self.parent { apply(parent); - - // The parent should always track the CWD - parent.hover(self.current.url.urn()); - parent.tracing = parent.hovered().map(|h| &h.url) == Some(&self.current.url); + parent.hover(self.current.url.urn()); // The parent should always track the CWD } self diff --git a/yazi-fs/src/stage.rs b/yazi-fs/src/stage.rs index 009dd980..a5419bd1 100644 --- a/yazi-fs/src/stage.rs +++ b/yazi-fs/src/stage.rs @@ -8,6 +8,11 @@ pub enum FolderStage { Failed(std::io::ErrorKind), } +impl FolderStage { + #[inline] + pub fn is_loading(self) -> bool { self == Self::Loading } +} + impl Serialize for FolderStage { fn serialize(&self, serializer: S) -> Result { let mut map = serializer.serialize_map(Some(2))?; diff --git a/yazi-shared/src/url/urn.rs b/yazi-shared/src/url/urn.rs index 0d717ced..050ea500 100644 --- a/yazi-shared/src/url/urn.rs +++ b/yazi-shared/src/url/urn.rs @@ -55,6 +55,10 @@ impl Borrow for UrnBuf { fn borrow(&self) -> &Urn { Urn::new(&self.0) } } +impl AsRef for UrnBuf { + fn as_ref(&self) -> &Urn { self.borrow() } +} + impl AsRef for UrnBuf { fn as_ref(&self) -> &Path { &self.0 } }