feat: auto fallback to parents of the folder that no longer exists

This commit is contained in:
sxyazi 2023-07-11 15:56:57 +08:00
parent 7071177bcd
commit becf62417a
No known key found for this signature in database
8 changed files with 76 additions and 29 deletions

View file

@ -3,7 +3,7 @@ use std::{fs::Metadata, path::{Path, PathBuf}};
use anyhow::Result; use anyhow::Result;
use tokio::fs; use tokio::fs;
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct File { pub struct File {
pub name: String, pub name: String,
pub path: PathBuf, pub path: PathBuf,

View file

@ -1,10 +1,11 @@
use std::{ops::{Deref, DerefMut}, path::{Path, PathBuf}}; use std::{ops::{Deref, DerefMut}, path::{Path, PathBuf}};
use anyhow::Result;
use indexmap::IndexMap; use indexmap::IndexMap;
use tokio::fs; use tokio::fs;
use super::File; use super::File;
use crate::{config::{manager::SortBy, MANAGER}, emit}; use crate::config::{manager::SortBy, MANAGER};
#[derive(Default)] #[derive(Default)]
pub struct Files { pub struct Files {
@ -14,7 +15,7 @@ pub struct Files {
} }
impl Files { impl Files {
pub async fn from(paths: Vec<PathBuf>) -> IndexMap<PathBuf, File> { pub async fn read(paths: Vec<PathBuf>) -> IndexMap<PathBuf, File> {
let mut items = IndexMap::new(); let mut items = IndexMap::new();
for path in paths { for path in paths {
if let Ok(file) = File::from(&path).await { if let Ok(file) = File::from(&path).await {
@ -24,21 +25,17 @@ impl Files {
items items
} }
pub async fn read(path: &Path) { pub async fn read_dir(path: &Path) -> Result<IndexMap<PathBuf, File>> {
let mut iter = match fs::read_dir(path).await { let mut it = fs::read_dir(path).await?;
Ok(it) => it,
Err(_) => return,
};
let mut items = IndexMap::new(); 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 { if let Ok(meta) = item.metadata().await {
let path = item.path(); let path = item.path();
let file = File::from_meta(&path, meta).await; let file = File::from_meta(&path, meta).await;
items.insert(path, file); items.insert(path, file);
} }
} }
emit!(Files(FilesOp::Read(path.to_path_buf(), items))); Ok(items)
} }
pub fn sort(&mut self) { pub fn sort(&mut self) {
@ -121,6 +118,7 @@ impl Default for FilesSort {
pub enum FilesOp { pub enum FilesOp {
Read(PathBuf, IndexMap<PathBuf, File>), Read(PathBuf, IndexMap<PathBuf, File>),
IOErr(PathBuf),
Search(PathBuf, IndexMap<PathBuf, File>), Search(PathBuf, IndexMap<PathBuf, File>),
} }
@ -129,8 +127,12 @@ impl FilesOp {
pub fn path(&self) -> PathBuf { pub fn path(&self) -> PathBuf {
match self { match self {
Self::Read(path, _) => path, Self::Read(path, _) => path,
Self::IOErr(path) => path,
Self::Search(path, _) => path, Self::Search(path, _) => path,
} }
.clone() .clone()
} }
#[inline]
pub fn read_empty(path: &Path) -> Self { Self::Read(path.to_path_buf(), IndexMap::new()) }
} }

View file

@ -27,6 +27,7 @@ impl Folder {
let b = match op { let b = match op {
FilesOp::Read(_, items) => self.files.update_read(items), FilesOp::Read(_, items) => self.files.update_read(items),
FilesOp::Search(_, items) => self.files.update_search(items), FilesOp::Search(_, items) => self.files.update_search(items),
_ => unreachable!(),
}; };
if !b { if !b {
return false; return false;

View file

@ -38,7 +38,9 @@ impl Manager {
to_watch.insert(p.cwd.clone()); to_watch.insert(p.cwd.clone());
} }
if let Some(ref h) = tab.current.hovered() { 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); self.watcher.watch(to_watch);
@ -169,8 +171,8 @@ impl Manager {
pub fn update_read(&mut self, op: FilesOp) -> bool { pub fn update_read(&mut self, op: FilesOp) -> bool {
let path = op.path(); let path = op.path();
let cwd = self.current().cwd.clone(); let cwd = self.current().cwd.clone();
let hovered = self.hovered().map(|h| h.path.clone()); let hovered = self.hovered().map(|h| h.path.clone());
let mut b = if cwd == path && !self.current().in_search { let mut b = if cwd == path && !self.current().in_search {
self.current_mut().update(op) self.current_mut().update(op)
} else if matches!(self.parent(), Some(p) if p.cwd == path) { } else if matches!(self.parent(), Some(p) if p.cwd == path) {
@ -195,6 +197,22 @@ impl Manager {
b 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 { pub fn update_search(&mut self, op: FilesOp) -> bool {
let path = op.path(); let path = op.path();
if !self.current().in_search || self.current().cwd != path { if !self.current().in_search || self.current().cwd != path {

View file

@ -6,7 +6,7 @@ use syntect::{easy::HighlightLines, highlighting::{Theme, ThemeSet}, parsing::Sy
use tokio::{fs, task::JoinHandle}; use tokio::{fs, task::JoinHandle};
use super::{ALL_RATIO, PREVIEW_BORDER, PREVIEW_PADDING, PREVIEW_RATIO}; 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<SyntaxSet> = OnceLock::new(); static SYNTECT_SYNTAX: OnceLock<SyntaxSet> = OnceLock::new();
static SYNTECT_THEME: OnceLock<Theme> = OnceLock::new(); static SYNTECT_THEME: OnceLock<Theme> = OnceLock::new();
@ -74,7 +74,11 @@ impl Preview {
} }
pub async fn folder(path: &Path) -> Result<PreviewData> { pub async fn folder(path: &Path) -> Result<PreviewData> {
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) Ok(PreviewData::Folder)
} }

View file

@ -158,7 +158,7 @@ impl Tab {
if chunk.is_empty() { if chunk.is_empty() {
break; break;
} }
emit!(Files(FilesOp::Search(cwd.clone(), Files::from(chunk).await))); emit!(Files(FilesOp::Search(cwd.clone(), Files::read(chunk).await)));
} }
Ok(()) Ok(())
})); }));

View file

@ -1,9 +1,9 @@
use std::{collections::BTreeSet, path::{Path, PathBuf}}; 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 tokio::sync::mpsc::{self, Sender};
use crate::core::files::Files; use crate::{core::files::{Files, FilesOp}, emit};
pub struct Watcher { pub struct Watcher {
tx: Sender<PathBuf>, tx: Sender<PathBuf>,
@ -31,20 +31,38 @@ impl Watcher {
} }
let event = res.unwrap(); let event = res.unwrap();
match event.kind { let path = if let Some(first) = event.paths.first() {
notify::EventKind::Create(_) => {} first.clone()
notify::EventKind::Modify(_) => {}
notify::EventKind::Remove(_) => {}
_ => return,
}
let path = if event.paths.len() > 0 {
event.paths[0].parent().unwrap_or(&event.paths[0])
} else { } else {
return; 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(), notify::Config::default(),
@ -53,7 +71,10 @@ impl Watcher {
tokio::spawn(async move { tokio::spawn(async move {
while let Some(path) = rx.recv().await { 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),
}));
} }
}); });

View file

@ -77,6 +77,7 @@ impl App {
Event::Files(op) => { Event::Files(op) => {
let b = match op { let b = match op {
FilesOp::Read(..) => manager.update_read(op), FilesOp::Read(..) => manager.update_read(op),
FilesOp::IOErr(..) => manager.update_ioerr(op),
FilesOp::Search(..) => manager.update_search(op), FilesOp::Search(..) => manager.update_search(op),
}; };
if b { if b {