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());
};
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;
}

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 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<SystemTime>) {
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;
});
}

View file

@ -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(())
}));

View file

@ -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<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) {
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();
}),
));
}

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 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<SystemTime>) -> Option<Metadata> {
match fs::metadata(url).await {
Ok(m) if !m.is_dir() => {
pub async fn assert_stale(url: &Url, cha: Cha) -> Option<Cha> {
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();

View file

@ -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<SystemTime>,
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));
}
_ => {}
}

View file

@ -115,6 +115,9 @@ impl From<FileType> 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;

View file

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