fix: newly created directories with the same name causing a false positive in directory loading optimization due to having the same modification time (#1434)

This commit is contained in:
三咲雅 · Misaki Masa 2024-08-08 02:02:01 +08:00 committed by GitHub
parent 1e08e09899
commit a9d2d9cd2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 40 additions and 35 deletions

View file

@ -30,7 +30,7 @@ impl Manager {
return render!(self.active_mut().preview.reset()); 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) { if !self.active().preview.same_url(&hovered.url) {
self.active_mut().preview.skip = folder.map(|f| f.0).unwrap_or_default(); self.active_mut().preview.skip = folder.map(|f| f.0).unwrap_or_default();
render!(self.active_mut().preview.reset()); render!(self.active_mut().preview.reset());
@ -51,7 +51,7 @@ impl Manager {
} }
if hovered.is_dir() { 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; return;
} }

View file

@ -1,4 +1,4 @@
use std::{collections::{HashMap, HashSet}, time::{Duration, SystemTime}}; use std::{collections::{HashMap, HashSet}, time::Duration};
use anyhow::Result; use anyhow::Result;
use notify::{RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; use notify::{RecommendedWatcher, RecursiveMode, Watcher as _Watcher};
@ -9,7 +9,7 @@ use tracing::error;
use yazi_fs::{Files, Folder}; use yazi_fs::{Files, Folder};
use yazi_plugin::isolate; use yazi_plugin::isolate;
use yazi_proxy::WATCHER; 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; use super::Linked;
@ -58,21 +58,21 @@ impl Watcher {
pub(super) fn trigger_dirs(&self, folders: &[&Folder]) { pub(super) fn trigger_dirs(&self, folders: &[&Folder]) {
let todo: Vec<_> = 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() { if todo.is_empty() {
return; return;
} }
async fn go(url: Url, mtime: Option<SystemTime>) { async fn go(url: Url, cha: Cha) {
let Some(meta) = Files::assert_stale(&url, mtime).await else { return }; let Some(cha) = Files::assert_stale(&url, cha).await else { return };
if let Ok(files) = Files::from_dir_bulk(&url).await { 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 { 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;
}); });
} }

View file

@ -6,7 +6,7 @@ use tracing::error;
use yazi_config::popup::InputCfg; use yazi_config::popup::InputCfg;
use yazi_plugin::external; use yazi_plugin::external;
use yazi_proxy::{options::{SearchOpt, SearchOptVia}, AppProxy, InputProxy, ManagerProxy, TabProxy}; 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; use crate::tab::Tab;
@ -72,7 +72,7 @@ impl Tab {
while let Some(chunk) = rx.next().await { while let Some(chunk) = rx.next().await {
FilesOp::Part(cwd.clone(), chunk, ticket).emit(); FilesOp::Part(cwd.clone(), chunk, ticket).emit();
} }
FilesOp::Done(cwd, None, ticket).emit(); FilesOp::Done(cwd, Cha::dummy(), ticket).emit();
Ok(()) Ok(())
})); }));

View file

@ -1,4 +1,4 @@
use std::time::{Duration, SystemTime}; use std::time::Duration;
use tokio::{pin, task::JoinHandle}; use tokio::{pin, task::JoinHandle};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
@ -37,7 +37,7 @@ impl Preview {
} }
} }
pub fn go_folder(&mut self, file: File, mtime: Option<SystemTime>, force: bool) { pub fn go_folder(&mut self, file: File, check: Option<Cha>, force: bool) {
if !force && self.content_unchanged(&file.url, &file.cha) { if !force && self.content_unchanged(&file.url, &file.cha) {
return; return;
} }
@ -49,7 +49,9 @@ impl Preview {
self.folder_loader = Some(( self.folder_loader = Some((
url.clone(), url.clone(),
tokio::spawn(async move { 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 Ok(rx) = Files::from_dir(&url).await else { return };
let stream = let stream =
@ -60,7 +62,7 @@ impl Preview {
while let Some(chunk) = stream.next().await { while let Some(chunk) = stream.next().await {
FilesOp::Part(url.clone(), chunk, ticket).emit(); FilesOp::Part(url.clone(), chunk, ticket).emit();
} }
FilesOp::Done(url, meta.modified().ok(), ticket).emit(); FilesOp::Done(url, cha, ticket).emit();
}), }),
)); ));
} }

View file

@ -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 tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}};
use yazi_config::{manager::SortBy, MANAGER}; 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}; use super::{FilesSorter, Filter};
@ -96,14 +96,14 @@ impl Files {
) )
} }
pub async fn assert_stale(url: &Url, mtime: Option<SystemTime>) -> Option<Metadata> { pub async fn assert_stale(url: &Url, cha: Cha) -> Option<Cha> {
match fs::metadata(url).await { match fs::metadata(url).await.map(Cha::from) {
Ok(m) if !m.is_dir() => { Ok(c) if !c.is_dir() => {
// FIXME: use `ErrorKind::NotADirectory` instead once it gets stabilized // FIXME: use `ErrorKind::NotADirectory` instead once it gets stabilized
FilesOp::IOErr(url.clone(), std::io::ErrorKind::AlreadyExists).emit(); FilesOp::IOErr(url.clone(), std::io::ErrorKind::AlreadyExists).emit();
} }
Ok(m) if mtime == m.modified().ok() => {} Ok(c) if c.hits(cha) => {}
Ok(m) => return Some(m), Ok(c) => return Some(c),
Err(e) => { Err(e) => {
if maybe_exists(url).await { if maybe_exists(url).await {
FilesOp::IOErr(url.clone(), e.kind()).emit(); FilesOp::IOErr(url.clone(), e.kind()).emit();

View file

@ -1,9 +1,9 @@
use std::{mem, time::SystemTime}; use std::mem;
use ratatui::layout::Rect; use ratatui::layout::Rect;
use yazi_config::{LAYOUT, MANAGER}; use yazi_config::{LAYOUT, MANAGER};
use yazi_proxy::ManagerProxy; use yazi_proxy::ManagerProxy;
use yazi_shared::fs::{File, FilesOp, Url}; use yazi_shared::fs::{Cha, File, FilesOp, Url};
use super::FolderStage; use super::FolderStage;
use crate::{Files, Step}; use crate::{Files, Step};
@ -11,8 +11,8 @@ use crate::{Files, Step};
#[derive(Default)] #[derive(Default)]
pub struct Folder { pub struct Folder {
pub cwd: Url, pub cwd: Url,
pub cha: Cha,
pub files: Files, pub files: Files,
pub mtime: Option<SystemTime>,
pub stage: FolderStage, pub stage: FolderStage,
pub offset: usize, pub offset: usize,
@ -30,17 +30,17 @@ impl Folder {
pub fn update(&mut self, op: FilesOp) -> bool { pub fn update(&mut self, op: FilesOp) -> bool {
let (stage, revision) = (self.stage, self.files.revision); let (stage, revision) = (self.stage, self.files.revision);
match op { match op {
FilesOp::Full(_, _, mtime) => { FilesOp::Full(_, _, cha) => {
(self.mtime, self.stage) = (mtime, FolderStage::Loaded); (self.cha, self.stage) = (cha, FolderStage::Loaded);
} }
FilesOp::Part(_, _, ticket) if ticket == self.files.ticket() => { FilesOp::Part(_, _, ticket) if ticket == self.files.ticket() => {
self.stage = FolderStage::Loading; self.stage = FolderStage::Loading;
} }
FilesOp::Done(_, mtime, ticket) if ticket == self.files.ticket() => { FilesOp::Done(_, cha, ticket) if ticket == self.files.ticket() => {
(self.mtime, self.stage) = (mtime, FolderStage::Loaded); (self.cha, self.stage) = (cha, FolderStage::Loaded);
} }
FilesOp::IOErr(_, kind) => { FilesOp::IOErr(_, kind) => {
(self.mtime, self.stage) = (None, FolderStage::Failed(kind)); (self.cha, self.stage) = (Cha::dummy(), FolderStage::Failed(kind));
} }
_ => {} _ => {}
} }

View file

@ -115,6 +115,9 @@ impl From<FileType> for Cha {
} }
impl Cha { impl Cha {
#[inline]
pub fn dummy() -> Self { Self { kind: ChaKind::DUMMY, ..Default::default() } }
#[inline] #[inline]
pub fn with_kind(mut self, kind: ChaKind) -> Self { pub fn with_kind(mut self, kind: ChaKind) -> Self {
self.kind |= kind; self.kind |= kind;

View file

@ -64,7 +64,7 @@ impl File {
#[inline] #[inline]
pub fn from_dummy(url: Url, ft: Option<FileType>) -> Self { pub fn from_dummy(url: Url, ft: Option<FileType>) -> 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() }
} }
} }

View file

@ -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}; use crate::{emit, event::Cmd, fs::Url, Layer};
pub static FILES_TICKET: AtomicU64 = AtomicU64::new(0); pub static FILES_TICKET: AtomicU64 = AtomicU64::new(0);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum FilesOp { pub enum FilesOp {
Full(Url, Vec<File>, Option<SystemTime>), Full(Url, Vec<File>, Cha),
Part(Url, Vec<File>, u64), Part(Url, Vec<File>, u64),
Done(Url, Option<SystemTime>, u64), Done(Url, Cha, u64),
Size(Url, HashMap<Url, u64>), Size(Url, HashMap<Url, u64>),
IOErr(Url, std::io::ErrorKind), IOErr(Url, std::io::ErrorKind),