mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: auto fallback to parents of the folder that no longer exists
This commit is contained in:
parent
7071177bcd
commit
becf62417a
8 changed files with 76 additions and 29 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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()) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue