mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41: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());
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue