From becf62417a2a004f48889058475e660a55784b2e Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 11 Jul 2023 15:56:57 +0800 Subject: [PATCH] feat: auto fallback to parents of the folder that no longer exists --- src/core/files/file.rs | 2 +- src/core/files/files.rs | 22 +++++++++-------- src/core/manager/folder.rs | 1 + src/core/manager/manager.rs | 22 +++++++++++++++-- src/core/manager/preview.rs | 8 +++++-- src/core/manager/tab.rs | 2 +- src/core/manager/watcher.rs | 47 +++++++++++++++++++++++++++---------- src/ui/app.rs | 1 + 8 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/core/files/file.rs b/src/core/files/file.rs index c50525cb..97e95087 100644 --- a/src/core/files/file.rs +++ b/src/core/files/file.rs @@ -3,7 +3,7 @@ use std::{fs::Metadata, path::{Path, PathBuf}}; use anyhow::Result; use tokio::fs; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct File { pub name: String, pub path: PathBuf, diff --git a/src/core/files/files.rs b/src/core/files/files.rs index ca38c4ff..3fa0c05c 100644 --- a/src/core/files/files.rs +++ b/src/core/files/files.rs @@ -1,10 +1,11 @@ use std::{ops::{Deref, DerefMut}, path::{Path, PathBuf}}; +use anyhow::Result; use indexmap::IndexMap; use tokio::fs; use super::File; -use crate::{config::{manager::SortBy, MANAGER}, emit}; +use crate::config::{manager::SortBy, MANAGER}; #[derive(Default)] pub struct Files { @@ -14,7 +15,7 @@ pub struct Files { } impl Files { - pub async fn from(paths: Vec) -> IndexMap { + pub async fn read(paths: Vec) -> IndexMap { let mut items = IndexMap::new(); for path in paths { if let Ok(file) = File::from(&path).await { @@ -24,21 +25,17 @@ impl Files { items } - pub async fn read(path: &Path) { - let mut iter = match fs::read_dir(path).await { - Ok(it) => it, - Err(_) => return, - }; - + pub async fn read_dir(path: &Path) -> Result> { + let mut it = fs::read_dir(path).await?; let mut items = IndexMap::new(); - while let Ok(Some(item)) = iter.next_entry().await { + while let Ok(Some(item)) = it.next_entry().await { if let Ok(meta) = item.metadata().await { let path = item.path(); let file = File::from_meta(&path, meta).await; items.insert(path, file); } } - emit!(Files(FilesOp::Read(path.to_path_buf(), items))); + Ok(items) } pub fn sort(&mut self) { @@ -121,6 +118,7 @@ impl Default for FilesSort { pub enum FilesOp { Read(PathBuf, IndexMap), + IOErr(PathBuf), Search(PathBuf, IndexMap), } @@ -129,8 +127,12 @@ impl FilesOp { pub fn path(&self) -> PathBuf { match self { Self::Read(path, _) => path, + Self::IOErr(path) => path, Self::Search(path, _) => path, } .clone() } + + #[inline] + pub fn read_empty(path: &Path) -> Self { Self::Read(path.to_path_buf(), IndexMap::new()) } } diff --git a/src/core/manager/folder.rs b/src/core/manager/folder.rs index e65450fb..397a321a 100644 --- a/src/core/manager/folder.rs +++ b/src/core/manager/folder.rs @@ -27,6 +27,7 @@ impl Folder { let b = match op { FilesOp::Read(_, items) => self.files.update_read(items), FilesOp::Search(_, items) => self.files.update_search(items), + _ => unreachable!(), }; if !b { return false; diff --git a/src/core/manager/manager.rs b/src/core/manager/manager.rs index db3e991e..67066f61 100644 --- a/src/core/manager/manager.rs +++ b/src/core/manager/manager.rs @@ -38,7 +38,9 @@ impl Manager { to_watch.insert(p.cwd.clone()); } if let Some(ref h) = tab.current.hovered() { - to_watch.insert(h.path.clone()); + if h.meta.is_dir() { + to_watch.insert(h.path.clone()); + } } } self.watcher.watch(to_watch); @@ -169,8 +171,8 @@ impl Manager { pub fn update_read(&mut self, op: FilesOp) -> bool { let path = op.path(); let cwd = self.current().cwd.clone(); - let hovered = self.hovered().map(|h| h.path.clone()); + let mut b = if cwd == path && !self.current().in_search { self.current_mut().update(op) } else if matches!(self.parent(), Some(p) if p.cwd == path) { @@ -195,6 +197,22 @@ impl Manager { b } + pub fn update_ioerr(&mut self, op: FilesOp) -> bool { + let path = op.path(); + let op = FilesOp::read_empty(&path); + + if path == self.current().cwd { + self.current_mut().update(op); + } else if matches!(self.parent(), Some(p) if p.cwd == path) { + self.active_mut().parent.as_mut().unwrap().update(op); + } else { + return false; + } + + self.active_mut().leave(); + true + } + pub fn update_search(&mut self, op: FilesOp) -> bool { let path = op.path(); if !self.current().in_search || self.current().cwd != path { diff --git a/src/core/manager/preview.rs b/src/core/manager/preview.rs index 0f5e4e68..c3e56ec5 100644 --- a/src/core/manager/preview.rs +++ b/src/core/manager/preview.rs @@ -6,7 +6,7 @@ use syntect::{easy::HighlightLines, highlighting::{Theme, ThemeSet}, parsing::Sy use tokio::{fs, task::JoinHandle}; use super::{ALL_RATIO, PREVIEW_BORDER, PREVIEW_PADDING, PREVIEW_RATIO}; -use crate::{config::{PREVIEW, THEME}, core::{adapter::Kitty, files::Files, tasks::Precache}, emit, misc::{first_n_lines, tty_ratio, tty_size}}; +use crate::{config::{PREVIEW, THEME}, core::{adapter::Kitty, files::{Files, FilesOp}, tasks::Precache}, emit, misc::{first_n_lines, tty_ratio, tty_size}}; static SYNTECT_SYNTAX: OnceLock = OnceLock::new(); static SYNTECT_THEME: OnceLock = OnceLock::new(); @@ -74,7 +74,11 @@ impl Preview { } pub async fn folder(path: &Path) -> Result { - Files::read(&path).await; + emit!(Files(match Files::read_dir(&path).await { + Ok(items) => FilesOp::Read(path.to_path_buf(), items), + Err(_) => FilesOp::IOErr(path.to_path_buf()), + })); + Ok(PreviewData::Folder) } diff --git a/src/core/manager/tab.rs b/src/core/manager/tab.rs index 6f0d2a05..9972f124 100644 --- a/src/core/manager/tab.rs +++ b/src/core/manager/tab.rs @@ -158,7 +158,7 @@ impl Tab { if chunk.is_empty() { break; } - emit!(Files(FilesOp::Search(cwd.clone(), Files::from(chunk).await))); + emit!(Files(FilesOp::Search(cwd.clone(), Files::read(chunk).await))); } Ok(()) })); diff --git a/src/core/manager/watcher.rs b/src/core/manager/watcher.rs index 5de97a40..00267c87 100644 --- a/src/core/manager/watcher.rs +++ b/src/core/manager/watcher.rs @@ -1,9 +1,9 @@ use std::{collections::BTreeSet, path::{Path, PathBuf}}; -use notify::{RecommendedWatcher, Watcher as _Watcher}; +use notify::{event::{MetadataKind, ModifyKind}, EventKind, RecommendedWatcher, Watcher as _Watcher}; use tokio::sync::mpsc::{self, Sender}; -use crate::core::files::Files; +use crate::{core::files::{Files, FilesOp}, emit}; pub struct Watcher { tx: Sender, @@ -31,20 +31,38 @@ impl Watcher { } let event = res.unwrap(); - match event.kind { - notify::EventKind::Create(_) => {} - notify::EventKind::Modify(_) => {} - notify::EventKind::Remove(_) => {} - _ => return, - } - - let path = if event.paths.len() > 0 { - event.paths[0].parent().unwrap_or(&event.paths[0]) + let path = if let Some(first) = event.paths.first() { + first.clone() } else { return; }; - tx.blocking_send(path.to_path_buf()).ok(); + let parent = path.parent().unwrap_or(&path).to_path_buf(); + match event.kind { + EventKind::Create(_) => { + tx.blocking_send(parent).ok(); + } + EventKind::Modify(kind) => { + match kind { + ModifyKind::Metadata(kind) => match kind { + MetadataKind::Permissions => {} + MetadataKind::Ownership => {} + MetadataKind::Extended => {} + _ => return, + }, + ModifyKind::Name(_) => {} + _ => return, + }; + + tx.blocking_send(path).ok(); + tx.blocking_send(parent).ok(); + } + EventKind::Remove(_) => { + tx.blocking_send(path).ok(); + tx.blocking_send(parent).ok(); + } + _ => return, + } } }, notify::Config::default(), @@ -53,7 +71,10 @@ impl Watcher { tokio::spawn(async move { while let Some(path) = rx.recv().await { - Files::read(&path).await; + emit!(Files(match Files::read_dir(&path).await { + Ok(items) => FilesOp::Read(path, items), + Err(_) => FilesOp::IOErr(path), + })); } }); diff --git a/src/ui/app.rs b/src/ui/app.rs index 80b55fe8..9eddf575 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -77,6 +77,7 @@ impl App { Event::Files(op) => { let b = match op { FilesOp::Read(..) => manager.update_read(op), + FilesOp::IOErr(..) => manager.update_ioerr(op), FilesOp::Search(..) => manager.update_search(op), }; if b {