mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
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:
parent
1e08e09899
commit
a9d2d9cd2b
9 changed files with 40 additions and 35 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue