From a9d2d9cd2bd478fe6db02ec51295e97b5aac4745 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: Thu, 8 Aug 2024 02:02:01 +0800 Subject: [PATCH] fix: newly created directories with the same name causing a false positive in directory loading optimization due to having the same modification time (#1434) --- yazi-core/src/manager/commands/peek.rs | 4 ++-- yazi-core/src/manager/watcher.rs | 14 +++++++------- yazi-core/src/tab/commands/search.rs | 4 ++-- yazi-core/src/tab/preview.rs | 10 ++++++---- yazi-fs/src/files.rs | 14 +++++++------- yazi-fs/src/folder.rs | 16 ++++++++-------- yazi-shared/src/fs/cha.rs | 3 +++ yazi-shared/src/fs/file.rs | 2 +- yazi-shared/src/fs/op.rs | 8 ++++---- 9 files changed, 40 insertions(+), 35 deletions(-) diff --git a/yazi-core/src/manager/commands/peek.rs b/yazi-core/src/manager/commands/peek.rs index 0d31033f..daf62492 100644 --- a/yazi-core/src/manager/commands/peek.rs +++ b/yazi-core/src/manager/commands/peek.rs @@ -30,7 +30,7 @@ impl Manager { return render!(self.active_mut().preview.reset()); }; - let folder = self.active().hovered_folder().map(|f| (f.offset, f.mtime)); + let folder = self.active().hovered_folder().map(|f| (f.offset, f.cha)); if !self.active().preview.same_url(&hovered.url) { self.active_mut().preview.skip = folder.map(|f| f.0).unwrap_or_default(); render!(self.active_mut().preview.reset()); @@ -51,7 +51,7 @@ impl Manager { } if hovered.is_dir() { - self.active_mut().preview.go_folder(hovered, folder.and_then(|f| f.1), opt.force); + self.active_mut().preview.go_folder(hovered, folder.map(|f| f.1), opt.force); return; } diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index 968e6d62..4944926f 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -1,4 +1,4 @@ -use std::{collections::{HashMap, HashSet}, time::{Duration, SystemTime}}; +use std::{collections::{HashMap, HashSet}, time::Duration}; use anyhow::Result; use notify::{RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; @@ -9,7 +9,7 @@ use tracing::error; use yazi_fs::{Files, Folder}; use yazi_plugin::isolate; use yazi_proxy::WATCHER; -use yazi_shared::{fs::{symlink_realname, File, FilesOp, Url}, RoCell}; +use yazi_shared::{fs::{symlink_realname, Cha, File, FilesOp, Url}, RoCell}; use super::Linked; @@ -58,21 +58,21 @@ impl Watcher { pub(super) fn trigger_dirs(&self, folders: &[&Folder]) { let todo: Vec<_> = - folders.iter().filter(|&f| f.cwd.is_regular()).map(|&f| (f.cwd.clone(), f.mtime)).collect(); + folders.iter().filter(|&f| f.cwd.is_regular()).map(|&f| (f.cwd.clone(), f.cha)).collect(); if todo.is_empty() { return; } - async fn go(url: Url, mtime: Option) { - let Some(meta) = Files::assert_stale(&url, mtime).await else { return }; + async fn go(url: Url, cha: Cha) { + let Some(cha) = Files::assert_stale(&url, cha).await else { return }; if let Ok(files) = Files::from_dir_bulk(&url).await { - FilesOp::Full(url, files, meta.modified().ok()).emit(); + FilesOp::Full(url, files, cha).emit(); } } tokio::spawn(async move { - futures::future::join_all(todo.into_iter().map(|(url, mtime)| go(url, mtime))).await; + futures::future::join_all(todo.into_iter().map(|(url, cha)| go(url, cha))).await; }); } diff --git a/yazi-core/src/tab/commands/search.rs b/yazi-core/src/tab/commands/search.rs index 0c3ca2ea..b516baf3 100644 --- a/yazi-core/src/tab/commands/search.rs +++ b/yazi-core/src/tab/commands/search.rs @@ -6,7 +6,7 @@ use tracing::error; use yazi_config::popup::InputCfg; use yazi_plugin::external; use yazi_proxy::{options::{SearchOpt, SearchOptVia}, AppProxy, InputProxy, ManagerProxy, TabProxy}; -use yazi_shared::fs::FilesOp; +use yazi_shared::fs::{Cha, FilesOp}; use crate::tab::Tab; @@ -72,7 +72,7 @@ impl Tab { while let Some(chunk) = rx.next().await { FilesOp::Part(cwd.clone(), chunk, ticket).emit(); } - FilesOp::Done(cwd, None, ticket).emit(); + FilesOp::Done(cwd, Cha::dummy(), ticket).emit(); Ok(()) })); diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index e25bad2e..5db41225 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -1,4 +1,4 @@ -use std::time::{Duration, SystemTime}; +use std::time::Duration; use tokio::{pin, task::JoinHandle}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; @@ -37,7 +37,7 @@ impl Preview { } } - pub fn go_folder(&mut self, file: File, mtime: Option, force: bool) { + pub fn go_folder(&mut self, file: File, check: Option, force: bool) { if !force && self.content_unchanged(&file.url, &file.cha) { return; } @@ -49,7 +49,9 @@ impl Preview { self.folder_loader = Some(( url.clone(), tokio::spawn(async move { - let Some(meta) = Files::assert_stale(&url, mtime).await else { return }; + let Some(cha) = Files::assert_stale(&url, check.unwrap_or(Cha::dummy())).await else { + return; + }; let Ok(rx) = Files::from_dir(&url).await else { return }; let stream = @@ -60,7 +62,7 @@ impl Preview { while let Some(chunk) = stream.next().await { FilesOp::Part(url.clone(), chunk, ticket).emit(); } - FilesOp::Done(url, meta.modified().ok(), ticket).emit(); + FilesOp::Done(url, cha, ticket).emit(); }), )); } diff --git a/yazi-fs/src/files.rs b/yazi-fs/src/files.rs index b0ee40f0..0bececdd 100644 --- a/yazi-fs/src/files.rs +++ b/yazi-fs/src/files.rs @@ -1,8 +1,8 @@ -use std::{collections::{HashMap, HashSet}, fs::Metadata, mem, ops::Deref, sync::atomic::Ordering, time::SystemTime}; +use std::{collections::{HashMap, HashSet}, mem, ops::Deref, sync::atomic::Ordering}; use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}}; use yazi_config::{manager::SortBy, MANAGER}; -use yazi_shared::fs::{maybe_exists, File, FilesOp, Url, FILES_TICKET}; +use yazi_shared::fs::{maybe_exists, Cha, File, FilesOp, Url, FILES_TICKET}; use super::{FilesSorter, Filter}; @@ -96,14 +96,14 @@ impl Files { ) } - pub async fn assert_stale(url: &Url, mtime: Option) -> Option { - match fs::metadata(url).await { - Ok(m) if !m.is_dir() => { + pub async fn assert_stale(url: &Url, cha: Cha) -> Option { + match fs::metadata(url).await.map(Cha::from) { + Ok(c) if !c.is_dir() => { // FIXME: use `ErrorKind::NotADirectory` instead once it gets stabilized FilesOp::IOErr(url.clone(), std::io::ErrorKind::AlreadyExists).emit(); } - Ok(m) if mtime == m.modified().ok() => {} - Ok(m) => return Some(m), + Ok(c) if c.hits(cha) => {} + Ok(c) => return Some(c), Err(e) => { if maybe_exists(url).await { FilesOp::IOErr(url.clone(), e.kind()).emit(); diff --git a/yazi-fs/src/folder.rs b/yazi-fs/src/folder.rs index 74c5aa02..8b8380fe 100644 --- a/yazi-fs/src/folder.rs +++ b/yazi-fs/src/folder.rs @@ -1,9 +1,9 @@ -use std::{mem, time::SystemTime}; +use std::mem; use ratatui::layout::Rect; use yazi_config::{LAYOUT, MANAGER}; use yazi_proxy::ManagerProxy; -use yazi_shared::fs::{File, FilesOp, Url}; +use yazi_shared::fs::{Cha, File, FilesOp, Url}; use super::FolderStage; use crate::{Files, Step}; @@ -11,8 +11,8 @@ use crate::{Files, Step}; #[derive(Default)] pub struct Folder { pub cwd: Url, + pub cha: Cha, pub files: Files, - pub mtime: Option, pub stage: FolderStage, pub offset: usize, @@ -30,17 +30,17 @@ impl Folder { pub fn update(&mut self, op: FilesOp) -> bool { let (stage, revision) = (self.stage, self.files.revision); match op { - FilesOp::Full(_, _, mtime) => { - (self.mtime, self.stage) = (mtime, FolderStage::Loaded); + FilesOp::Full(_, _, cha) => { + (self.cha, self.stage) = (cha, FolderStage::Loaded); } FilesOp::Part(_, _, ticket) if ticket == self.files.ticket() => { self.stage = FolderStage::Loading; } - FilesOp::Done(_, mtime, ticket) if ticket == self.files.ticket() => { - (self.mtime, self.stage) = (mtime, FolderStage::Loaded); + FilesOp::Done(_, cha, ticket) if ticket == self.files.ticket() => { + (self.cha, self.stage) = (cha, FolderStage::Loaded); } FilesOp::IOErr(_, kind) => { - (self.mtime, self.stage) = (None, FolderStage::Failed(kind)); + (self.cha, self.stage) = (Cha::dummy(), FolderStage::Failed(kind)); } _ => {} } diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index 82d1d64e..6f76c5d5 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -115,6 +115,9 @@ impl From for Cha { } impl Cha { + #[inline] + pub fn dummy() -> Self { Self { kind: ChaKind::DUMMY, ..Default::default() } } + #[inline] pub fn with_kind(mut self, kind: ChaKind) -> Self { self.kind |= kind; diff --git a/yazi-shared/src/fs/file.rs b/yazi-shared/src/fs/file.rs index b32b877a..7b33843e 100644 --- a/yazi-shared/src/fs/file.rs +++ b/yazi-shared/src/fs/file.rs @@ -64,7 +64,7 @@ impl File { #[inline] pub fn from_dummy(url: Url, ft: Option) -> Self { - Self { cha: ft.map_or_else(Cha::default, Cha::from), url: url.to_owned(), ..Default::default() } + Self { cha: ft.map_or_else(Cha::dummy, Cha::from), url: url.to_owned(), ..Default::default() } } } diff --git a/yazi-shared/src/fs/op.rs b/yazi-shared/src/fs/op.rs index 43c6c6c9..7585cb76 100644 --- a/yazi-shared/src/fs/op.rs +++ b/yazi-shared/src/fs/op.rs @@ -1,15 +1,15 @@ -use std::{collections::HashMap, sync::atomic::{AtomicU64, Ordering}, time::SystemTime}; +use std::{collections::HashMap, sync::atomic::{AtomicU64, Ordering}}; -use super::File; +use super::{Cha, File}; use crate::{emit, event::Cmd, fs::Url, Layer}; pub static FILES_TICKET: AtomicU64 = AtomicU64::new(0); #[derive(Clone, Debug)] pub enum FilesOp { - Full(Url, Vec, Option), + Full(Url, Vec, Cha), Part(Url, Vec, u64), - Done(Url, Option, u64), + Done(Url, Cha, u64), Size(Url, HashMap), IOErr(Url, std::io::ErrorKind),