From 6f66eee596429f2c635b2bb2d27a08aeaf229a05 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sun, 27 Aug 2023 14:51:15 +0800 Subject: [PATCH 01/15] refactor: simplify filesystem design to improve performance --- app/src/app.rs | 4 +- app/src/manager/folder.rs | 17 +-- core/src/files/file.rs | 19 ++- core/src/files/files.rs | 198 +++++++++++++++++++---------- core/src/files/iterator.rs | 29 +++++ core/src/files/mod.rs | 4 + core/src/files/op.rs | 30 +++++ core/src/files/sorter.rs | 26 ++-- core/src/manager/folder.rs | 60 ++------- core/src/manager/mode.rs | 6 + core/src/manager/tab.rs | 38 +++--- core/src/manager/watcher.rs | 12 +- core/src/tasks/tasks.rs | 7 +- core/src/tasks/workers/precache.rs | 27 ++-- 14 files changed, 271 insertions(+), 206 deletions(-) create mode 100644 core/src/files/iterator.rs create mode 100644 core/src/files/op.rs diff --git a/app/src/app.rs b/app/src/app.rs index 4b653c126..c61bf1894 100644 --- a/app/src/app.rs +++ b/app/src/app.rs @@ -131,7 +131,7 @@ impl App { let calc = matches!(op, FilesOp::Read(..) | FilesOp::Search(..)); let b = match op { FilesOp::Read(..) => manager.update_read(op), - FilesOp::Sort(..) => manager.update_read(op), + FilesOp::Size(..) => manager.update_read(op), FilesOp::Search(..) => manager.update_search(op), FilesOp::IOErr(..) => manager.update_ioerr(op), }; @@ -144,7 +144,7 @@ impl App { } Event::Pages(page) => { if manager.current().page == page { - let targets = self.cx.manager.current().paginate().into_iter().map(|(_, f)| f).collect(); + let targets = self.cx.manager.current().paginate(); tasks.precache_mime(targets, &self.cx.manager.mimetype); } } diff --git a/app/src/manager/folder.rs b/app/src/manager/folder.rs index 755f120c2..a4e5f4360 100644 --- a/app/src/manager/folder.rs +++ b/app/src/manager/folder.rs @@ -56,16 +56,17 @@ impl<'a> Widget for Folder<'a> { let items = window .iter() .enumerate() - .map(|(i, (k, v))| { + .map(|(i, f)| { let icon = THEME .icons .iter() - .find(|x| x.name.match_path(k, Some(v.meta.is_dir()))) + .find(|x| x.name.match_path(&f.path, Some(f.meta.is_dir()))) .map(|x| x.display.as_ref()) .unwrap_or(""); - if (!self.is_selection && v.is_selected) - || (self.is_selection && mode.pending(i, v.is_selected)) + let is_selected = self.folder.files.is_selected(&f.path); + if (!self.is_selection && is_selected) + || (self.is_selection && mode.pending(i, is_selected)) { buf.set_style( Rect { x: area.x.saturating_sub(1), y: i as u16 + 1, width: 1, height: 1 }, @@ -77,17 +78,17 @@ impl<'a> Widget for Folder<'a> { ); } - let hovered = matches!(self.folder.hovered, Some(ref h) if h.path == *k); + let hovered = matches!(self.folder.hovered, Some(ref h) if h.path == f.path); let style = if self.is_preview && hovered { THEME.preview.hovered.get() } else if hovered { THEME.selection.hovered.get() } else { - self.file_style(v) + self.file_style(f) }; - let mut path = format!(" {icon} {}", readable_path(k, &self.folder.cwd)); - if let Some(ref link_to) = v.link_to { + let mut path = format!(" {icon} {}", readable_path(&f.path, &self.folder.cwd)); + if let Some(ref link_to) = f.link_to { if MANAGER.show_symlink { path.push_str(&format!(" -> {}", link_to.display())); } diff --git a/core/src/files/file.rs b/core/src/files/file.rs index f0bb37f95..c66420fcc 100644 --- a/core/src/files/file.rs +++ b/core/src/files/file.rs @@ -5,13 +5,12 @@ use tokio::fs; #[derive(Clone, Debug)] pub struct File { - pub path: PathBuf, - pub meta: Metadata, - pub length: Option, - pub link_to: Option, - pub is_link: bool, - pub is_hidden: bool, - pub is_selected: bool, + pub path: PathBuf, + pub meta: Metadata, + pub length: Option, + pub link_to: Option, + pub is_link: bool, + pub is_hidden: bool, } impl File { @@ -32,7 +31,7 @@ impl File { let length = if meta.is_dir() { None } else { Some(meta.len()) }; let is_hidden = path.file_name().map(|s| s.to_string_lossy().starts_with('.')).unwrap_or(false); - File { path: path.to_path_buf(), meta, length, link_to, is_link, is_hidden, is_selected: false } + File { path: path.to_path_buf(), meta, length, link_to, is_link, is_hidden } } } @@ -41,8 +40,8 @@ impl File { pub fn path(&self) -> PathBuf { self.path.clone() } #[inline] - pub fn set_path(mut self, path: &Path) -> Self { - self.path = path.to_path_buf(); + pub fn set_path(mut self, path: PathBuf) -> Self { + self.path = path; self } diff --git a/core/src/files/files.rs b/core/src/files/files.rs index 255af694b..6f3abcbd9 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -1,22 +1,32 @@ -use std::{collections::BTreeMap, ops::{Deref, DerefMut}, path::{Path, PathBuf}}; +use std::{collections::{BTreeMap, BTreeSet}, ops::Range, path::{Path, PathBuf}}; use anyhow::Result; use config::MANAGER; -use indexmap::IndexMap; use tokio::fs; -use super::{File, FilesSorter}; +use super::{File, FilesSorter, NonHiddenFiles}; pub struct Files { - items: IndexMap, + items: Vec, + length: usize, + + sizes: BTreeMap, + selected: BTreeSet, + pub sorter: FilesSorter, + // TODO: XXX pub show_hidden: bool, } impl Default for Files { fn default() -> Self { Self { - items: Default::default(), + items: Default::default(), + length: Default::default(), + + sizes: Default::default(), + selected: Default::default(), + sorter: Default::default(), show_hidden: MANAGER.show_hidden, } @@ -24,86 +34,111 @@ impl Default for Files { } impl Files { - pub async fn read(paths: Vec) -> BTreeMap { - let mut items = BTreeMap::new(); + pub async fn read(paths: &[impl AsRef]) -> Vec { + let mut items = Vec::with_capacity(paths.len()); for path in paths { - if let Ok(file) = File::from(&path).await { - items.insert(path, file); + if let Ok(file) = File::from(path.as_ref()).await { + items.push(file); } } items } - pub async fn read_dir(path: &Path) -> Result> { + pub async fn read_dir(path: &Path) -> Result> { let mut it = fs::read_dir(path).await?; - let mut items = BTreeMap::new(); + let mut items = Vec::new(); 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); + items.push(File::from_meta(&item.path(), meta).await); } } Ok(items) } #[inline] - pub fn duplicate(&self, idx: usize) -> Option { - self.items.get_index(idx).map(|(_, file)| file.clone()) - } + pub fn select(&mut self, path: &Path, state: Option) -> bool { + let old = self.selected.contains(path); + let new = if let Some(new) = state { new } else { !old }; - #[inline] - pub fn set_sorter(&mut self, sort: FilesSorter) -> bool { - if self.sorter == sort { + if new == old { return false; } - self.sorter = sort; - self.sorter.sort(&mut self.items) + + if new { + self.selected.insert(path.to_owned()); + } else { + self.selected.remove(path); + } + true } - pub fn update_read(&mut self, mut items: BTreeMap) -> bool { - if !self.show_hidden { - items.retain(|_, item| !item.is_hidden); + pub fn select_many(&mut self, path: Option<&Path>, state: Option) -> bool { + if let Some(path) = path { + return self.select(path, state); } - for (path, item) in &mut items { - if let Some(old) = self.items.get(path) { - item.is_selected = old.is_selected; + let mut applied = false; + for item in self.iter() { + todo!(); + // applied |= self.select(&item.path, state); + } + applied + } - // Calculate the size of directories is expensive, so we keep the old value, - // before a new value is calculated and comes to. - if item.meta.is_dir() { - item.length = old.length; - } - } + pub fn select_index(&mut self, indices: &BTreeSet, state: Option) -> bool { + let mut applied = false; + for item in self.pick(indices) { + todo!(); + // applied |= self.select(&item.path, state); } + applied + } - self.items.clear(); - self.items.extend(items); - self.sorter.sort(&mut self.items); - true + #[inline] + pub fn set_sorter(&mut self, sorter: FilesSorter) -> bool { + if self.sorter == sorter { + return false; + } + self.sorter = sorter; + self.sorter.sort(&mut self.items) } - pub fn update_sort(&mut self, mut items: BTreeMap) -> bool { - for (path, item) in &mut items { - if let Some(old) = self.items.get(path) { - item.is_selected = old.is_selected; - } + #[inline] + pub fn set_show_hidden(&mut self, state: bool) -> bool { + if self.show_hidden == state { + return false; } - self.items.extend(items); + self.length = + if state { self.items.len() } else { self.items.iter().filter(|f| !f.is_hidden).count() }; + self.show_hidden = state; + true + } + + pub fn update_read(&mut self, mut items: Vec) -> bool { + self.sorter.sort(&mut items); + self.length = + if self.show_hidden { items.len() } else { items.iter().filter(|f| !f.is_hidden).count() }; + self.items = items; + true + } + + pub fn update_size(&mut self, items: BTreeMap) -> bool { + self.sizes.extend(items); self.sorter.sort(&mut self.items); true } - pub fn update_search(&mut self, items: BTreeMap) -> bool { + pub fn update_search(&mut self, items: Vec) -> bool { if !items.is_empty() { + self.length = items.len(); self.items.extend(items); self.sorter.sort(&mut self.items); return true; } if !self.items.is_empty() { + self.length = 0; self.items.clear(); return true; } @@ -112,39 +147,64 @@ impl Files { } } -impl Deref for Files { - type Target = IndexMap; +impl Files { + #[inline] + pub fn len(&self) -> usize { self.length } - fn deref(&self) -> &Self::Target { &self.items } -} + #[inline] + pub fn iter<'a>(&'a self) -> Box + 'a> { + if self.show_hidden { + return Box::new(self.items.iter()); + } + Box::new(NonHiddenFiles::new(&self.items, self.length)) + } -impl DerefMut for Files { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.items } -} + #[inline] + pub fn range(&self, range: Range) -> Vec<&File> { + self.iter().skip(range.start).take(range.end - range.start).collect() + } -#[derive(Debug)] -pub enum FilesOp { - Read(PathBuf, BTreeMap), - Sort(PathBuf, BTreeMap), - Search(PathBuf, BTreeMap), - IOErr(PathBuf), -} + pub fn pick<'a>(&'a self, indices: &BTreeSet) -> Vec<&'a File> { + let mut items = Vec::with_capacity(indices.len()); + for (i, item) in self.iter().enumerate() { + if indices.contains(&i) { + items.push(item); + } + } + items + } -impl FilesOp { #[inline] - pub fn path(&self) -> PathBuf { - match self { - Self::Read(path, _) => path, - Self::Sort(path, _) => path, - Self::Search(path, _) => path, - Self::IOErr(path) => path, + pub fn position(&self, path: &Path) -> Option { self.iter().position(|f| f.path == path) } + + #[inline] + pub fn duplicate(&self, idx: usize) -> Option { self.items.get(idx).cloned() } + + pub fn selected(&self, pending: &BTreeSet, unset: bool) -> Vec<&File> { + if self.selected.is_empty() && (unset || pending.is_empty()) { + return Default::default(); } - .clone() + + let mut items = Vec::with_capacity(self.selected.len() + pending.len()); + for (i, item) in self.iter().enumerate() { + let b = self.selected.contains(&item.path); + if !unset && (b || pending.contains(&i)) { + items.push(item); + } else if unset && b && !pending.contains(&i) { + items.push(item); + } + } + items } #[inline] - pub fn read_empty(path: &Path) -> Self { Self::Read(path.to_path_buf(), BTreeMap::new()) } + pub fn is_selected(&self, path: &Path) -> bool { self.selected.contains(path) } #[inline] - pub fn search_empty(path: &Path) -> Self { Self::Search(path.to_path_buf(), BTreeMap::new()) } + pub fn has_selected(&self) -> bool { + if self.selected.is_empty() { + return false; + } + self.iter().any(|f| self.selected.contains(&f.path)) + } } diff --git a/core/src/files/iterator.rs b/core/src/files/iterator.rs new file mode 100644 index 000000000..090400dc0 --- /dev/null +++ b/core/src/files/iterator.rs @@ -0,0 +1,29 @@ +use super::File; + +pub struct NonHiddenFiles<'a> { + items: &'a Vec, + + cur: usize, + max: usize, +} + +impl<'a> NonHiddenFiles<'a> { + pub fn new(items: &'a Vec, max: usize) -> Self { Self { items, cur: 0, max } } +} + +impl<'a> Iterator for NonHiddenFiles<'a> { + type Item = &'a File; + + fn next(&mut self) -> Option { + while self.cur < self.items.len() { + let item = &self.items[self.cur]; + self.cur += 1; + if !item.is_hidden { + return Some(&item); + } + } + None + } + + fn size_hint(&self) -> (usize, Option) { (self.max, Some(self.max)) } +} diff --git a/core/src/files/mod.rs b/core/src/files/mod.rs index 9d437be9a..e3e55b87b 100644 --- a/core/src/files/mod.rs +++ b/core/src/files/mod.rs @@ -1,7 +1,11 @@ mod file; mod files; +mod iterator; +mod op; mod sorter; pub use file::*; pub use files::*; +pub use iterator::*; +pub use op::*; pub use sorter::*; diff --git a/core/src/files/op.rs b/core/src/files/op.rs new file mode 100644 index 000000000..c9208162c --- /dev/null +++ b/core/src/files/op.rs @@ -0,0 +1,30 @@ +use std::{collections::BTreeMap, path::{Path, PathBuf}}; + +use super::File; + +#[derive(Debug)] +pub enum FilesOp { + Read(PathBuf, Vec), + Size(PathBuf, BTreeMap), + Search(PathBuf, Vec), + IOErr(PathBuf), +} + +impl FilesOp { + #[inline] + pub fn path(&self) -> PathBuf { + match self { + Self::Read(path, _) => path, + Self::Size(path, _) => path, + Self::Search(path, _) => path, + Self::IOErr(path) => path, + } + .clone() + } + + #[inline] + pub fn read_empty(path: &Path) -> Self { Self::Read(path.to_path_buf(), Vec::new()) } + + #[inline] + pub fn search_empty(path: &Path) -> Self { Self::Search(path.to_path_buf(), Vec::new()) } +} diff --git a/core/src/files/sorter.rs b/core/src/files/sorter.rs index c7dc7bc5d..0be05287b 100644 --- a/core/src/files/sorter.rs +++ b/core/src/files/sorter.rs @@ -1,7 +1,6 @@ -use std::{cmp::Ordering, path::PathBuf}; +use std::cmp::Ordering; use config::{manager::SortBy, MANAGER}; -use indexmap::IndexMap; use super::File; @@ -23,41 +22,41 @@ impl Default for FilesSorter { } impl FilesSorter { - pub(super) fn sort(&self, items: &mut IndexMap) -> bool { + pub(super) fn sort(&self, items: &mut Vec) -> bool { if items.is_empty() { return false; } match self.by { SortBy::Alphabetical => { - items.sort_unstable_by(|_, a, _, b| self.cmp(&a.path, &b.path, self.promote(a, b))) + items.sort_unstable_by(|a, b| self.cmp(&a.path, &b.path, self.promote(a, b))) } - SortBy::Created => items.sort_unstable_by(|_, a, _, b| { + SortBy::Created => items.sort_unstable_by(|a, b| { if let (Ok(aa), Ok(bb)) = (a.meta.created(), b.meta.created()) { return self.cmp(aa, bb, self.promote(a, b)); } Ordering::Equal }), - SortBy::Modified => items.sort_unstable_by(|_, a, _, b| { + SortBy::Modified => items.sort_unstable_by(|a, b| { if let (Ok(aa), Ok(bb)) = (a.meta.modified(), b.meta.modified()) { return self.cmp(aa, bb, self.promote(a, b)); } Ordering::Equal }), SortBy::Natural => self.sort_naturally(items), - SortBy::Size => items.sort_unstable_by(|_, a, _, b| { + SortBy::Size => items.sort_unstable_by(|a, b| { self.cmp(a.length.unwrap_or(0), b.length.unwrap_or(0), self.promote(a, b)) }), } true } - fn sort_naturally(&self, items: &mut IndexMap) { + fn sort_naturally(&self, items: &mut Vec) { let mut indices = Vec::with_capacity(items.len()); let mut entities = Vec::with_capacity(items.len()); - for (i, (path, file)) in items.into_iter().enumerate() { + for (i, file) in items.into_iter().enumerate() { indices.push(i); - entities.push((path.to_string_lossy(), file)); + entities.push((file.path.to_string_lossy(), &*file)); } indices.sort_unstable_by(|&a, &b| { @@ -71,12 +70,7 @@ impl FilesSorter { } }); - let mut new = IndexMap::with_capacity(indices.len()); - for i in indices { - let file = entities[i].1.clone(); - new.insert(file.path(), file); - } - *items = new; + items.sort_unstable_by_key(|_| indices.pop().unwrap()); } #[inline] diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index f633e5a09..750ddadcd 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -1,7 +1,6 @@ use std::path::{Path, PathBuf}; use config::MANAGER; -use indexmap::map::Slice; use ratatui::layout::Rect; use crate::{emit, files::{File, Files, FilesOp}}; @@ -28,7 +27,7 @@ impl Folder { pub fn update(&mut self, op: FilesOp) -> bool { let b = match op { FilesOp::Read(_, items) => self.files.update_read(items), - FilesOp::Sort(_, items) => self.files.update_sort(items), + FilesOp::Size(_, items) => self.files.update_size(items), FilesOp::Search(_, items) => self.files.update_search(items), _ => unreachable!(), }; @@ -103,53 +102,20 @@ impl Folder { } #[inline] - pub fn window(&self) -> &Slice { + pub fn window(&self) -> Vec<&File> { let end = (self.offset + MANAGER.layout.folder_height()).min(self.files.len()); - self.files.get_range(self.offset..end).unwrap() + self.files.range(self.offset..end) } #[inline] - pub fn window_for(&self, offset: usize) -> &Slice { + pub fn window_for(&self, offset: usize) -> Vec<&File> { let start = offset.min(self.files.len().saturating_sub(1)); let end = (offset + MANAGER.layout.folder_height()).min(self.files.len()); - self.files.get_range(start..end).unwrap() - } - - pub fn select(&mut self, idx: Option, state: Option) -> bool { - let len = self.files.len(); - let mut apply = |idx: usize, state: Option| -> bool { - let Some(state) = state else { - self.files[idx].is_selected = !self.files[idx].is_selected; - return true; - }; - - if state != self.files[idx].is_selected { - self.files[idx].is_selected = state; - return true; - } - - false - }; - - if let Some(idx) = idx { - if idx < len { - return apply(idx, state); - } - } else { - let mut applied = false; - for i in 0..len { - if apply(i, state) { - applied = true; - } - } - return applied; - } - - false + self.files.range(start..end) } pub fn hover(&mut self, path: &Path) -> bool { - let new = self.position(path).unwrap_or(self.cursor); + let new = self.files.position(path).unwrap_or(self.cursor); if new > self.cursor { self.next(new - self.cursor) } else { self.prev(self.cursor - new) } } @@ -170,25 +136,17 @@ impl Folder { #[inline] pub fn cursor(&self) -> usize { self.cursor } - #[inline] - pub fn position(&self, path: &Path) -> Option { - self.files.iter().position(|(p, _)| p == path) - } - - pub fn paginate(&self) -> &Slice { + pub fn paginate(&self) -> Vec<&File> { let len = self.files.len(); let limit = MANAGER.layout.folder_height(); let start = (self.page * limit).min(len.saturating_sub(1)); let end = (start + limit).min(len); - self.files.get_range(start..end).unwrap() + self.files.range(start..end) } - #[inline] - pub fn has_selected(&self) -> bool { self.files.iter().any(|(_, f)| f.is_selected) } - pub fn rect_current(&self, path: &Path) -> Option { - let y = self.position(path)? - self.offset; + let y = self.files.position(path)? - self.offset; let mut rect = MANAGER.layout.folder_rect(); rect.y = rect.y.saturating_sub(1) + y as u16; diff --git a/core/src/manager/mode.rs b/core/src/manager/mode.rs index a51453061..5dacbd6ba 100644 --- a/core/src/manager/mode.rs +++ b/core/src/manager/mode.rs @@ -49,6 +49,12 @@ impl Mode { } impl Mode { + #[inline] + pub fn is_select(&self) -> bool { matches!(self, Mode::Select(..)) } + + #[inline] + pub fn is_unset(&self) -> bool { matches!(self, Mode::Unset(..)) } + #[inline] pub fn is_visual(&self) -> bool { matches!(self, Mode::Select(..) | Mode::Unset(..)) } } diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index f1287162c..ac8ff7f4c 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -1,4 +1,4 @@ -use std::{collections::{BTreeMap, BTreeSet}, ffi::{OsStr, OsString}, mem, path::{Path, PathBuf}}; +use std::{borrow::Cow, collections::{BTreeMap, BTreeSet}, ffi::{OsStr, OsString}, mem, path::{Path, PathBuf}}; use anyhow::{Error, Result}; use config::{open::Opener, MANAGER}; @@ -36,11 +36,7 @@ impl Tab { pub fn escape(&mut self) -> bool { if let Some((_, indices)) = self.mode.visual() { - let b = matches!(self.mode, Mode::Select(..)); - for idx in indices.iter() { - self.current.select(Some(*idx), Some(b)); - } - + self.current.files.select_index(indices, Some(self.mode.is_select())); self.mode = Mode::Normal; return true; } @@ -187,11 +183,15 @@ impl Tab { pub fn forward(&mut self) -> bool { false } pub fn select(&mut self, state: Option) -> bool { - let idx = Some(self.current.cursor()); - self.current.select(idx, state) + if let Some(ref hovered) = self.current.hovered { + return self.current.files.select(&hovered.path, state); + } + false } - pub fn select_all(&mut self, state: Option) -> bool { self.current.select(None, state) } + pub fn select_all(&mut self, state: Option) -> bool { + self.current.files.select_many(None, state) + } pub fn visual_mode(&mut self, unset: bool) -> bool { let idx = self.current.cursor(); @@ -243,7 +243,7 @@ impl Tab { emit!(Files(FilesOp::search_empty(&cwd))); while let Some(chunk) = rx.next().await { - emit!(Files(FilesOp::Search(cwd.clone(), Files::read(chunk).await))); + emit!(Files(FilesOp::Search(cwd.clone(), Files::read(&chunk).await))); } Ok(()) })); @@ -363,19 +363,9 @@ impl Tab { pub fn selected(&self) -> Vec<&File> { let mode = self.mode(); - let files = &self.current.files; - - let selected: Vec<_> = if !mode.is_visual() { - files.iter().filter(|(_, f)| f.is_selected).map(|(_, f)| f).collect() - } else { - files - .iter() - .enumerate() - .filter(|(i, (_, f))| mode.pending(*i, f.is_selected)) - .map(|(_, (_, f))| f) - .collect() - }; + let pending = mode.visual().map(|(_, p)| Cow::Borrowed(p)).unwrap_or_default(); + let selected = self.current.files.selected(&pending, mode.is_unset()); if selected.is_empty() { self.current.hovered.as_ref().map(|h| vec![h]).unwrap_or_default() } else { @@ -384,7 +374,9 @@ impl Tab { } #[inline] - pub fn in_selecting(&self) -> bool { self.mode().is_visual() || self.current.has_selected() } + pub fn in_selecting(&self) -> bool { + self.mode().is_visual() || self.current.files.has_selected() + } #[inline] pub fn history(&self, path: &Path) -> Option<&Folder> { self.history.get(path) } diff --git a/core/src/manager/watcher.rs b/core/src/manager/watcher.rs index f1ff33ba8..cc2c8b755 100644 --- a/core/src/manager/watcher.rs +++ b/core/src/manager/watcher.rs @@ -1,4 +1,4 @@ -use std::{collections::{BTreeMap, BTreeSet}, path::{Path, PathBuf}, sync::Arc, time::Duration}; +use std::{collections::BTreeSet, path::{Path, PathBuf}, sync::Arc, time::Duration}; use futures::StreamExt; use indexmap::IndexMap; @@ -170,11 +170,11 @@ impl Watcher { for ori in linked { emit!(Files(match &result { Ok(items) => { - let files = BTreeMap::from_iter(items.iter().map(|(p, f)| { - let p = ori.join(p.strip_prefix(path).unwrap()); - let f = f.clone().set_path(&p); - (p, f) - })); + let mut files = Vec::with_capacity(items.len()); + for item in items { + let file = item.clone().set_path(ori.join(item.path.strip_prefix(path).unwrap())); + files.push(file); + } FilesOp::Read(ori, files) } Err(_) => FilesOp::IOErr(ori), diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index 2ef1ca952..82ffe6bc2 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -216,11 +216,8 @@ impl Tasks { return false; } - let targets = targets - .iter() - .filter(|(_, f)| f.meta.is_dir() && f.length.is_none()) - .map(|(p, _)| p.clone()) - .collect::>(); + let targets: Vec<_> = + targets.iter().filter(|f| f.meta.is_dir() && f.length.is_none()).map(|f| f.path()).collect(); if !targets.is_empty() { self.scheduler.precache_size(targets); diff --git a/core/src/tasks/workers/precache.rs b/core/src/tasks/workers/precache.rs index 9f92f2119..02f57b71c 100644 --- a/core/src/tasks/workers/precache.rs +++ b/core/src/tasks/workers/precache.rs @@ -7,7 +7,7 @@ use parking_lot::Mutex; use shared::{calculate_size, Throttle}; use tokio::{fs, sync::mpsc}; -use crate::{emit, external, files::{File, FilesOp}, tasks::TaskOp}; +use crate::{emit, external, files::FilesOp, tasks::TaskOp}; pub(crate) struct Precache { rx: async_channel::Receiver, @@ -29,7 +29,7 @@ pub(crate) enum PrecacheOp { pub(crate) struct PrecacheOpSize { pub id: usize, pub target: PathBuf, - pub throttle: Arc>, + pub throttle: Arc>, } #[derive(Debug)] @@ -121,21 +121,16 @@ impl Precache { pub(crate) async fn size(&self, task: PrecacheOpSize) -> Result<()> { self.sch.send(TaskOp::New(task.id, 0))?; - let length = Some(calculate_size(&task.target).await); - if let Ok(mut file) = File::from(&task.target).await { - file.length = length; - task.throttle.done((task.target, file), |buf| { - let mut handing = self.size_handing.lock(); - for (path, _) in &buf { - handing.remove(path); - } + let length = calculate_size(&task.target).await; + task.throttle.done((task.target, length), |buf| { + let mut handing = self.size_handing.lock(); + for (path, _) in &buf { + handing.remove(path); + } - let parent = buf[0].0.parent().unwrap().to_path_buf(); - emit!(Files(FilesOp::Sort(parent, BTreeMap::from_iter(buf)))); - }); - } else { - self.size_handing.lock().remove(&task.target); - }; + let parent = buf[0].0.parent().unwrap().to_path_buf(); + emit!(Files(FilesOp::Size(parent, BTreeMap::from_iter(buf)))); + }); self.sch.send(TaskOp::Adv(task.id, 1, 0))?; self.done(task.id) From c09f0546555c2fe72d72eae554061a66c63de65c Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sun, 27 Aug 2023 16:27:18 +0800 Subject: [PATCH 02/15] .. --- core/src/files/files.rs | 75 +++++++++++++++++++++----------------- core/src/files/iterator.rs | 29 --------------- core/src/files/mod.rs | 2 - core/src/manager/folder.rs | 16 ++++---- core/src/manager/tab.rs | 2 +- core/src/tasks/tasks.rs | 4 +- 6 files changed, 52 insertions(+), 76 deletions(-) delete mode 100644 core/src/files/iterator.rs diff --git a/core/src/files/files.rs b/core/src/files/files.rs index 6f3abcbd9..c71490695 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -1,28 +1,27 @@ -use std::{collections::{BTreeMap, BTreeSet}, ops::Range, path::{Path, PathBuf}}; +use std::{collections::{BTreeMap, BTreeSet}, mem, ops::Deref, path::{Path, PathBuf}}; use anyhow::Result; use config::MANAGER; use tokio::fs; -use super::{File, FilesSorter, NonHiddenFiles}; +use super::{File, FilesSorter}; pub struct Files { items: Vec, - length: usize, + hidden: Vec, sizes: BTreeMap, selected: BTreeSet, - pub sorter: FilesSorter, - // TODO: XXX - pub show_hidden: bool, + pub sorter: FilesSorter, + show_hidden: bool, } impl Default for Files { fn default() -> Self { Self { items: Default::default(), - length: Default::default(), + hidden: Default::default(), sizes: Default::default(), selected: Default::default(), @@ -33,6 +32,12 @@ impl Default for Files { } } +impl Deref for Files { + type Target = Vec; + + fn deref(&self) -> &Self::Target { &self.items } +} + impl Files { pub async fn read(paths: &[impl AsRef]) -> Vec { let mut items = Vec::with_capacity(paths.len()); @@ -54,7 +59,9 @@ impl Files { } Ok(items) } +} +impl Files { #[inline] pub fn select(&mut self, path: &Path, state: Option) -> bool { let old = self.selected.contains(path); @@ -104,21 +111,31 @@ impl Files { } #[inline] - pub fn set_show_hidden(&mut self, state: bool) -> bool { - if self.show_hidden == state { + pub fn set_show_hidden(&mut self, state: Option) -> bool { + let state = state.unwrap_or(!self.show_hidden); + if state == self.show_hidden { + return false; + } else if state && self.hidden.is_empty() { return false; } - self.length = - if state { self.items.len() } else { self.items.iter().filter(|f| !f.is_hidden).count() }; + if state { + self.items.append(&mut self.hidden); + self.sorter.sort(&mut self.items); + } else { + let items = mem::take(&mut self.items); + (self.hidden, self.items) = items.into_iter().partition(|f| f.is_hidden); + } + self.show_hidden = state; true } pub fn update_read(&mut self, mut items: Vec) -> bool { + if !self.show_hidden { + (self.hidden, items) = items.into_iter().partition(|f| f.is_hidden); + } self.sorter.sort(&mut items); - self.length = - if self.show_hidden { items.len() } else { items.iter().filter(|f| !f.is_hidden).count() }; self.items = items; true } @@ -131,15 +148,16 @@ impl Files { pub fn update_search(&mut self, items: Vec) -> bool { if !items.is_empty() { - self.length = items.len(); + let (hidden, items): (Vec<_>, Vec<_>) = items.into_iter().partition(|f| f.is_hidden); self.items.extend(items); + self.hidden.extend(hidden); self.sorter.sort(&mut self.items); return true; } if !self.items.is_empty() { - self.length = 0; self.items.clear(); + self.hidden.clear(); return true; } @@ -148,23 +166,7 @@ impl Files { } impl Files { - #[inline] - pub fn len(&self) -> usize { self.length } - - #[inline] - pub fn iter<'a>(&'a self) -> Box + 'a> { - if self.show_hidden { - return Box::new(self.items.iter()); - } - Box::new(NonHiddenFiles::new(&self.items, self.length)) - } - - #[inline] - pub fn range(&self, range: Range) -> Vec<&File> { - self.iter().skip(range.start).take(range.end - range.start).collect() - } - - pub fn pick<'a>(&'a self, indices: &BTreeSet) -> Vec<&'a File> { + pub fn pick(&self, indices: &BTreeSet) -> Vec<&File> { let mut items = Vec::with_capacity(indices.len()); for (i, item) in self.iter().enumerate() { if indices.contains(&i) { @@ -185,7 +187,9 @@ impl Files { return Default::default(); } - let mut items = Vec::with_capacity(self.selected.len() + pending.len()); + let mut items = + Vec::with_capacity(self.selected.len() + if !unset { pending.len() } else { 0 }); + for (i, item) in self.iter().enumerate() { let b = self.selected.contains(&item.path); if !unset && (b || pending.contains(&i)) { @@ -208,3 +212,8 @@ impl Files { self.iter().any(|f| self.selected.contains(&f.path)) } } + +impl Files { + #[inline] + pub fn show_hidden(&self) -> bool { self.show_hidden } +} diff --git a/core/src/files/iterator.rs b/core/src/files/iterator.rs deleted file mode 100644 index 090400dc0..000000000 --- a/core/src/files/iterator.rs +++ /dev/null @@ -1,29 +0,0 @@ -use super::File; - -pub struct NonHiddenFiles<'a> { - items: &'a Vec, - - cur: usize, - max: usize, -} - -impl<'a> NonHiddenFiles<'a> { - pub fn new(items: &'a Vec, max: usize) -> Self { Self { items, cur: 0, max } } -} - -impl<'a> Iterator for NonHiddenFiles<'a> { - type Item = &'a File; - - fn next(&mut self) -> Option { - while self.cur < self.items.len() { - let item = &self.items[self.cur]; - self.cur += 1; - if !item.is_hidden { - return Some(&item); - } - } - None - } - - fn size_hint(&self) -> (usize, Option) { (self.max, Some(self.max)) } -} diff --git a/core/src/files/mod.rs b/core/src/files/mod.rs index e3e55b87b..59df8d548 100644 --- a/core/src/files/mod.rs +++ b/core/src/files/mod.rs @@ -1,11 +1,9 @@ mod file; mod files; -mod iterator; mod op; mod sorter; pub use file::*; pub use files::*; -pub use iterator::*; pub use op::*; pub use sorter::*; diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index 750ddadcd..9b976d648 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -93,25 +93,23 @@ impl Folder { } pub fn hidden(&mut self, show: Option) -> bool { - if show.is_none() || self.files.show_hidden != show.unwrap() { - self.files.show_hidden = !self.files.show_hidden; + if self.files.set_show_hidden(show) { emit!(Refresh); } - false } #[inline] - pub fn window(&self) -> Vec<&File> { + pub fn window(&self) -> &[File] { let end = (self.offset + MANAGER.layout.folder_height()).min(self.files.len()); - self.files.range(self.offset..end) + &self.files[self.offset..end] } #[inline] - pub fn window_for(&self, offset: usize) -> Vec<&File> { + pub fn window_for(&self, offset: usize) -> &[File] { let start = offset.min(self.files.len().saturating_sub(1)); let end = (offset + MANAGER.layout.folder_height()).min(self.files.len()); - self.files.range(start..end) + &self.files[start..end] } pub fn hover(&mut self, path: &Path) -> bool { @@ -136,13 +134,13 @@ impl Folder { #[inline] pub fn cursor(&self) -> usize { self.cursor } - pub fn paginate(&self) -> Vec<&File> { + pub fn paginate(&self) -> &[File] { let len = self.files.len(); let limit = MANAGER.layout.folder_height(); let start = (self.page * limit).min(len.saturating_sub(1)); let end = (start + limit).min(len); - self.files.range(start..end) + &self.files[start..end] } pub fn rect_current(&self, path: &Path) -> Option { diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index ac8ff7f4c..b381739ac 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -230,7 +230,7 @@ impl Tab { } let cwd = self.current.cwd.clone(); - let hidden = self.current.files.show_hidden; + let hidden = self.current.files.show_hidden(); self.search = Some(tokio::spawn(async move { let subject = emit!(Input(InputOpt::top("Search:"))).await?; diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index 82ffe6bc2..1428a5229 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -227,9 +227,9 @@ impl Tasks { } #[inline] - pub fn precache_mime(&self, targets: Vec<&File>, mimetype: &HashMap) -> bool { + pub fn precache_mime(&self, targets: &[File], mimetype: &HashMap) -> bool { let targets = targets - .into_iter() + .iter() .filter(|f| f.meta.is_file() && !mimetype.contains_key(&f.path)) .map(|f| f.path.clone()) .collect::>(); From 99acd09d26804af7908416e3f09bbc10f4af3d2c Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sun, 27 Aug 2023 17:20:15 +0800 Subject: [PATCH 03/15] .. --- core/src/files/files.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/core/src/files/files.rs b/core/src/files/files.rs index c71490695..a7053f8fc 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -84,19 +84,32 @@ impl Files { return self.select(path, state); } - let mut applied = false; - for item in self.iter() { - todo!(); - // applied |= self.select(&item.path, state); + match state { + Some(true) => { + self.selected = self.iter().map(|f| f.path()).collect(); + } + Some(false) => { + self.selected.clear(); + } + None => { + for item in &self.items { + if self.selected.contains(&item.path) { + self.selected.remove(&item.path); + } else { + self.selected.insert(item.path()); + } + } + } } - applied + !self.items.is_empty() } pub fn select_index(&mut self, indices: &BTreeSet, state: Option) -> bool { let mut applied = false; - for item in self.pick(indices) { - todo!(); - // applied |= self.select(&item.path, state); + let paths: Vec<_> = self.pick(indices).iter().map(|f| f.path()).collect(); + + for path in paths { + applied |= self.select(&path, state); } applied } From 1af7d728d6400d0caa419e0081ba3f71e171f5ff Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 13:15:34 +0800 Subject: [PATCH 04/15] .. --- app/src/executor.rs | 2 +- core/src/files/file.rs | 2 +- core/src/files/files.rs | 6 ++-- core/src/manager/folder.rs | 2 +- core/src/manager/manager.rs | 24 +++++++-------- core/src/manager/watcher.rs | 59 ++++++++++++++++++++++--------------- core/src/tasks/scheduler.rs | 5 ++-- shared/src/fs.rs | 4 +-- 8 files changed, 57 insertions(+), 47 deletions(-) diff --git a/app/src/executor.rs b/app/src/executor.rs index 687024794..025475c21 100644 --- a/app/src/executor.rs +++ b/app/src/executor.rs @@ -106,7 +106,7 @@ impl Executor { } } "remove" => { - let targets = cx.manager.selected().into_iter().map(|p| p.path()).collect(); + let targets = cx.manager.selected().into_iter().map(|f| f.path().clone()).collect(); cx.tasks.file_remove(targets, exec.named.contains_key("permanently")) } "create" => cx.manager.create(), diff --git a/core/src/files/file.rs b/core/src/files/file.rs index c66420fcc..533310154 100644 --- a/core/src/files/file.rs +++ b/core/src/files/file.rs @@ -37,7 +37,7 @@ impl File { impl File { #[inline] - pub fn path(&self) -> PathBuf { self.path.clone() } + pub fn path(&self) -> &PathBuf { &self.path } #[inline] pub fn set_path(mut self, path: PathBuf) -> Self { diff --git a/core/src/files/files.rs b/core/src/files/files.rs index a7053f8fc..a2681ced4 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -86,7 +86,7 @@ impl Files { match state { Some(true) => { - self.selected = self.iter().map(|f| f.path()).collect(); + self.selected = self.iter().map(|f| f.path().clone()).collect(); } Some(false) => { self.selected.clear(); @@ -96,7 +96,7 @@ impl Files { if self.selected.contains(&item.path) { self.selected.remove(&item.path); } else { - self.selected.insert(item.path()); + self.selected.insert(item.path().clone()); } } } @@ -106,7 +106,7 @@ impl Files { pub fn select_index(&mut self, indices: &BTreeSet, state: Option) -> bool { let mut applied = false; - let paths: Vec<_> = self.pick(indices).iter().map(|f| f.path()).collect(); + let paths: Vec<_> = self.pick(indices).iter().map(|f| f.path().clone()).collect(); for path in paths { applied |= self.select(&path, state); diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index 9b976d648..5a0ca30af 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -40,7 +40,7 @@ impl Folder { self.cursor = self.cursor.min(len.saturating_sub(1)); self.set_page(true); - if let Some(h) = self.hovered.as_ref().map(|h| h.path()) { + if let Some(h) = self.hovered.as_ref().map(|h| h.path().clone()) { self.hover(&h); } self.hovered = self.files.duplicate(self.cursor); diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index 678fe5ca3..98e334a98 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -39,15 +39,15 @@ impl Manager { let mut to_watch = BTreeSet::new(); for tab in self.tabs.iter() { - to_watch.insert(tab.current.cwd.clone()); - if let Some(ref p) = tab.parent { - to_watch.insert(p.cwd.clone()); - } + to_watch.insert(&tab.current.cwd); if let Some(ref h) = tab.current.hovered { if h.meta.is_dir() { to_watch.insert(h.path()); } } + if let Some(ref p) = tab.parent { + to_watch.insert(&p.cwd); + } } self.watcher.watch(to_watch); } @@ -83,10 +83,8 @@ impl Manager { } pub fn yank(&mut self, cut: bool) -> bool { - let selected = self.selected().into_iter().map(|f| f.path()).collect::>(); self.yanked.0 = cut; - self.yanked.1.clear(); - self.yanked.1.extend(selected); + self.yanked.1 = self.selected().into_iter().map(|f| f.path().clone()).collect(); false } @@ -124,7 +122,7 @@ impl Manager { .into_iter() .map(|f| { ( - f.path().into_os_string(), + f.path().as_os_str().to_owned(), if f.meta.is_dir() { Some(MIME_DIR.to_owned()) } else { @@ -203,7 +201,7 @@ impl Manager { return self.bulk_rename(); } - let Some(hovered) = self.hovered().map(|h| h.path()) else { + let Some(hovered) = self.hovered().map(|h| h.path().clone()) else { return false; }; @@ -221,10 +219,10 @@ impl Manager { } pub fn bulk_rename(&self) -> bool { - let mut old: Vec<_> = self.selected().iter().map(|&f| f.path()).collect(); + let old: Vec<_> = self.selected().into_iter().map(|f| f.path()).collect(); let root = max_common_root(&old); - old.iter_mut().for_each(|p| *p = p.strip_prefix(&root).unwrap().to_owned()); + let old: Vec<_> = old.into_iter().map(|p| p.strip_prefix(&root).unwrap().to_owned()).collect(); let tmp = BOOT.tmpfile("bulk"); tokio::spawn(async move { @@ -327,7 +325,7 @@ impl Manager { pub fn update_read(&mut self, op: FilesOp) -> bool { let path = op.path(); let cwd = self.cwd().to_owned(); - let hovered = self.hovered().map(|h| h.path()); + let hovered = self.hovered().map(|h| h.path().clone()); let mut b = if cwd == path && !self.current().in_search { self.current_mut().update(op) @@ -347,7 +345,7 @@ impl Manager { b |= self.active_mut().parent.as_mut().map_or(false, |p| p.hover(&cwd)); b |= hovered.as_ref().map_or(false, |h| self.current_mut().hover(h)); - if hovered != self.hovered().map(|h| h.path()) { + if hovered.as_ref() != self.hovered().map(|h| h.path()) { emit!(Hover); } b diff --git a/core/src/manager/watcher.rs b/core/src/manager/watcher.rs index cc2c8b755..3848cbc02 100644 --- a/core/src/manager/watcher.rs +++ b/core/src/manager/watcher.rs @@ -69,44 +69,55 @@ impl Watcher { instance } - pub(super) fn watch(&mut self, mut to_watch: BTreeSet) { - let keys = self.watched.read().keys().cloned().collect::>(); - for p in keys.difference(&to_watch) { - self.watcher.unwatch(p).ok(); + pub(super) fn watch(&mut self, mut watched: BTreeSet<&PathBuf>) { + let (to_unwatch, to_watch): (BTreeSet<_>, BTreeSet<_>) = { + let guard = self.watched.read(); + let keys = guard.keys().collect::>(); + ( + keys.difference(&watched).map(|&x| x.clone()).collect(), + watched.difference(&keys).map(|&x| x.clone()).collect(), + ) + }; + + for p in to_unwatch { + self.watcher.unwatch(&p).ok(); } - for p in to_watch.clone().difference(&keys) { - if self.watcher.watch(p, RecursiveMode::NonRecursive).is_err() { - to_watch.remove(p); + for p in to_watch { + if self.watcher.watch(&p, RecursiveMode::NonRecursive).is_err() { + watched.remove(&p); } } - let mut todo = Vec::new(); - let mut watched = self.watched.write(); - *watched = IndexMap::from_iter(to_watch.into_iter().map(|k| { - if let Some(v) = watched.remove(&k) { - (k, v) - } else { - todo.push(k.clone()); - (k, None) - } - })); - watched.sort_unstable_by(|_, a, _, b| b.cmp(a)); + let mut to_resolve = Vec::new(); + let mut guard = self.watched.write(); + *guard = watched + .into_iter() + .map(|k| { + if let Some((k, v)) = guard.remove_entry(k) { + (k, v) + } else { + to_resolve.push(k.clone()); + (k.clone(), None) + } + }) + .collect(); + guard.sort_unstable_by(|_, a, _, b| b.cmp(a)); - let watched = self.watched.clone(); + let lock = self.watched.clone(); tokio::spawn(async move { let mut ext = IndexMap::new(); - for k in todo { + for k in to_resolve { match fs::canonicalize(&k).await { - Ok(v) if v != k => { + Ok(v) if v != *k => { ext.insert(k, Some(v)); } _ => {} } } - let mut watched = watched.write(); - watched.extend(ext); - watched.sort_unstable_by(|_, a, _, b| b.cmp(a)); + let mut guard = lock.write(); + guard.extend(ext); + guard.sort_unstable_by(|_, a, _, b| b.cmp(a)); }); } diff --git a/core/src/tasks/scheduler.rs b/core/src/tasks/scheduler.rs index ac2ca0d24..912c23ef7 100644 --- a/core/src/tasks/scheduler.rs +++ b/core/src/tasks/scheduler.rs @@ -318,13 +318,13 @@ impl Scheduler { }); } - pub(super) fn precache_size(&self, targets: Vec) { + pub(super) fn precache_size(&self, targets: Vec<&PathBuf>) { let throttle = Arc::new(Throttle::new(targets.len(), Duration::from_millis(300))); let mut handing = self.precache.size_handing.lock(); let mut running = self.running.write(); for target in targets { - if !handing.contains(&target) { + if !handing.contains(target) { handing.insert(target.clone()); } else { continue; @@ -333,6 +333,7 @@ impl Scheduler { let id = running.add(format!("Calculate the size of {:?}", target)); let _ = self.todo.send_blocking({ let precache = self.precache.clone(); + let target = target.clone(); let throttle = throttle.clone(); async move { precache.size(PrecacheOpSize { id, target, throttle }).await.ok(); diff --git a/shared/src/fs.rs b/shared/src/fs.rs index 351e990aa..8a5cb5fbc 100644 --- a/shared/src/fs.rs +++ b/shared/src/fs.rs @@ -148,12 +148,12 @@ pub fn file_mode(mode: u32) -> String { // Find the max common root of a list of files // e.g. /a/b/c, /a/b/d -> /a/b // /aa/bb/cc, /aa/dd/ee -> /aa -pub fn max_common_root(files: &[PathBuf]) -> PathBuf { +pub fn max_common_root(files: &[impl AsRef]) -> PathBuf { if files.is_empty() { return PathBuf::new(); } - let mut it = files.iter().map(|p| p.parent().unwrap_or(Path::new("")).components()); + let mut it = files.iter().map(|p| p.as_ref().parent().unwrap_or(Path::new("")).components()); let mut root = it.next().unwrap().collect::(); for components in it { let mut new_root = PathBuf::new(); From 0feb025dcf8a4c738c8d295a0b5f701af8ef61b3 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 13:35:09 +0800 Subject: [PATCH 05/15] .. --- app/src/context.rs | 2 +- app/src/manager/folder.rs | 10 +++++----- app/src/manager/preview.rs | 2 +- config/src/theme/filetype.rs | 4 ++-- core/src/files/file.rs | 17 +++++++---------- core/src/manager/folder.rs | 2 +- core/src/manager/manager.rs | 18 +++++++++--------- core/src/manager/tab.rs | 22 +++++++++++----------- core/src/manager/watcher.rs | 3 ++- core/src/tasks/tasks.rs | 8 ++++---- 10 files changed, 43 insertions(+), 45 deletions(-) diff --git a/app/src/context.rs b/app/src/context.rs index e5b982fe0..652f255b5 100644 --- a/app/src/context.rs +++ b/app/src/context.rs @@ -36,7 +36,7 @@ impl Ctx { } Position::Hovered(rect @ Rect { mut x, y, width, height }) => { let Some(r) = - self.manager.hovered().and_then(|h| self.manager.current().rect_current(&h.path)) + self.manager.hovered().and_then(|h| self.manager.current().rect_current(h.path())) else { return self.area(&Position::Top(*rect)); }; diff --git a/app/src/manager/folder.rs b/app/src/manager/folder.rs index a4e5f4360..aafb81f56 100644 --- a/app/src/manager/folder.rs +++ b/app/src/manager/folder.rs @@ -36,7 +36,7 @@ impl<'a> Folder<'a> { THEME .filetypes .iter() - .find(|x| x.matches(&file.path, mimetype.get(&file.path).cloned(), file.meta.is_dir())) + .find(|x| x.matches(file.path(), mimetype.get(file.path()), file.meta.is_dir())) .map(|x| x.style.get()) .unwrap_or_else(Style::new) } @@ -60,11 +60,11 @@ impl<'a> Widget for Folder<'a> { let icon = THEME .icons .iter() - .find(|x| x.name.match_path(&f.path, Some(f.meta.is_dir()))) + .find(|x| x.name.match_path(f.path(), Some(f.meta.is_dir()))) .map(|x| x.display.as_ref()) .unwrap_or(""); - let is_selected = self.folder.files.is_selected(&f.path); + let is_selected = self.folder.files.is_selected(f.path()); if (!self.is_selection && is_selected) || (self.is_selection && mode.pending(i, is_selected)) { @@ -78,7 +78,7 @@ impl<'a> Widget for Folder<'a> { ); } - let hovered = matches!(self.folder.hovered, Some(ref h) if h.path == f.path); + let hovered = matches!(self.folder.hovered, Some(ref h) if h.path() == f.path()); let style = if self.is_preview && hovered { THEME.preview.hovered.get() } else if hovered { @@ -87,7 +87,7 @@ impl<'a> Widget for Folder<'a> { self.file_style(f) }; - let mut path = format!(" {icon} {}", readable_path(&f.path, &self.folder.cwd)); + let mut path = format!(" {icon} {}", readable_path(f.path(), &self.folder.cwd)); if let Some(ref link_to) = f.link_to { if MANAGER.show_symlink { path.push_str(&format!(" -> {}", link_to.display())); diff --git a/app/src/manager/preview.rs b/app/src/manager/preview.rs index 3986cae95..4890ec34b 100644 --- a/app/src/manager/preview.rs +++ b/app/src/manager/preview.rs @@ -17,7 +17,7 @@ impl<'a> Preview<'a> { impl<'a> Widget for Preview<'a> { fn render(self, area: Rect, buf: &mut Buffer) { let manager = &self.cx.manager; - let Some(hovered) = manager.hovered().map(|h| &h.path) else { + let Some(hovered) = manager.hovered().map(|h| h.path()) else { return; }; diff --git a/config/src/theme/filetype.rs b/config/src/theme/filetype.rs index 6512269d9..cd39f9489 100644 --- a/config/src/theme/filetype.rs +++ b/config/src/theme/filetype.rs @@ -12,12 +12,12 @@ pub struct Filetype { } impl Filetype { - pub fn matches(&self, path: &Path, mime: Option, is_dir: bool) -> bool { + pub fn matches(&self, path: &Path, mime: Option>, is_dir: bool) -> bool { if self.name.as_ref().map_or(false, |e| e.match_path(path, Some(is_dir))) { return true; } if let Some(mime) = mime { - return self.mime.as_ref().map_or(false, |m| m.matches(&mime)); + return self.mime.as_ref().map_or(false, |m| m.matches(mime)); } false } diff --git a/core/src/files/file.rs b/core/src/files/file.rs index 533310154..b0393f052 100644 --- a/core/src/files/file.rs +++ b/core/src/files/file.rs @@ -5,12 +5,12 @@ use tokio::fs; #[derive(Clone, Debug)] pub struct File { - pub path: PathBuf, - pub meta: Metadata, - pub length: Option, - pub link_to: Option, - pub is_link: bool, - pub is_hidden: bool, + pub(super) path: PathBuf, + pub meta: Metadata, + pub length: Option, + pub link_to: Option, + pub is_link: bool, + pub is_hidden: bool, } impl File { @@ -40,10 +40,7 @@ impl File { pub fn path(&self) -> &PathBuf { &self.path } #[inline] - pub fn set_path(mut self, path: PathBuf) -> Self { - self.path = path; - self - } + pub fn set_path(&mut self, path: PathBuf) { self.path = path; } #[inline] pub fn name(&self) -> Option> { self.path.file_name().map(|s| s.to_string_lossy()) } diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index 5a0ca30af..2a2401020 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -118,7 +118,7 @@ impl Folder { } pub fn hover_force(&mut self, file: File) -> bool { - if self.hover(&file.path) { + if self.hover(file.path()) { return true; } diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index 98e334a98..620ee99b9 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -63,11 +63,11 @@ impl Manager { let mime = if hovered.meta.is_dir() { MIME_DIR.to_owned() - } else if let Some(m) = self.mimetype.get(&hovered.path).cloned() { + } else if let Some(m) = self.mimetype.get(hovered.path()).cloned() { m } else { tokio::spawn(async move { - if let Ok(mimes) = external::file(&[hovered.path]).await { + if let Ok(mimes) = external::file(&[hovered.path()]).await { emit!(Mimetype(mimes)); } }); @@ -75,9 +75,9 @@ impl Manager { }; if sequent { - self.active_mut().preview.sequent(&hovered.path, &mime, show_image); + self.active_mut().preview.sequent(hovered.path(), &mime, show_image); } else { - self.active_mut().preview.go(&hovered.path, &mime, show_image); + self.active_mut().preview.go(hovered.path(), &mime, show_image); } false } @@ -117,7 +117,7 @@ impl Manager { } pub fn open(&mut self, interactive: bool) -> bool { - let mut files = self + let mut files: Vec<_> = self .selected() .into_iter() .map(|f| { @@ -126,11 +126,11 @@ impl Manager { if f.meta.is_dir() { Some(MIME_DIR.to_owned()) } else { - self.mimetype.get(&f.path).cloned() + self.mimetype.get(f.path()).cloned() }, ) }) - .collect::>(); + .collect(); if files.is_empty() { return false; @@ -339,7 +339,7 @@ impl Manager { .or_insert_with(|| Folder::new(&path)) .update(op); - matches!(self.hovered(), Some(h) if h.path == path) + matches!(self.hovered(), Some(h) if h.path() == &path) }; b |= self.active_mut().parent.as_mut().map_or(false, |p| p.hover(&cwd)); @@ -402,7 +402,7 @@ impl Manager { }; if hovered.meta.is_dir() { - self.watcher.trigger_dirs(&[&hovered.path]); + self.watcher.trigger_dirs(&[hovered.path()]); } b } diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index b381739ac..f04dbc192 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -131,7 +131,7 @@ impl Tab { return false; } - let rep = self.history_new(&hovered.path); + let rep = self.history_new(hovered.path()); let rep = mem::replace(&mut self.current, rep); if !rep.in_search { self.history.insert(rep.cwd.clone(), rep); @@ -140,7 +140,7 @@ impl Tab { if let Some(rep) = self.parent.take() { self.history.insert(rep.cwd.clone(), rep); } - self.parent = Some(self.history_new(hovered.path.parent().unwrap())); + self.parent = Some(self.history_new(hovered.path().parent().unwrap())); emit!(Refresh); true @@ -151,7 +151,7 @@ impl Tab { .current .hovered .as_ref() - .and_then(|h| h.path.parent()) + .and_then(|h| h.path().parent()) .and_then(|p| if p == self.current.cwd { None } else { Some(p) }) .or_else(|| self.current.cwd.parent()); @@ -184,7 +184,7 @@ impl Tab { pub fn select(&mut self, state: Option) -> bool { if let Some(ref hovered) = self.current.hovered { - return self.current.files.select(&hovered.path, state); + return self.current.files.select(hovered.path(), state); } false } @@ -209,10 +209,10 @@ impl Tab { let mut it = self.selected().into_iter().peekable(); while let Some(f) = it.next() { s.push(match type_ { - "path" => f.path.as_os_str(), - "dirname" => f.path.parent().map_or(OsStr::new(""), |p| p.as_os_str()), - "filename" => f.path.file_name().unwrap_or(OsStr::new("")), - "name_without_ext" => f.path.file_stem().unwrap_or(OsStr::new("")), + "path" => f.path().as_os_str(), + "dirname" => f.path().parent().map_or(OsStr::new(""), |p| p.as_os_str()), + "filename" => f.path().file_name().unwrap_or(OsStr::new("")), + "name_without_ext" => f.path().file_stem().unwrap_or(OsStr::new("")), _ => return false, }); if it.peek().is_some() { @@ -288,7 +288,7 @@ impl Tab { let selected: Vec<_> = self .selected() .into_iter() - .map(|f| (f.path.as_os_str().to_owned(), Default::default())) + .map(|f| (f.path().as_os_str().to_owned(), Default::default())) .collect(); let mut exec = exec.to_owned(); @@ -315,7 +315,7 @@ impl Tab { return; }; - if path.as_ref().map(|p| *p != hovered.path).unwrap_or(false) { + if path.as_ref().map(|p| p != hovered.path()).unwrap_or(false) { return; } else if !self.preview.arrow(step, path.is_some()) { return; @@ -333,7 +333,7 @@ impl Tab { } pub fn update_preview(&mut self, lock: PreviewLock) -> bool { - let Some(hovered) = self.current.hovered.as_ref().map(|h| &h.path) else { + let Some(hovered) = self.current.hovered.as_ref().map(|h| h.path()) else { return self.preview.reset(); }; diff --git a/core/src/manager/watcher.rs b/core/src/manager/watcher.rs index 3848cbc02..42a70f992 100644 --- a/core/src/manager/watcher.rs +++ b/core/src/manager/watcher.rs @@ -183,7 +183,8 @@ impl Watcher { Ok(items) => { let mut files = Vec::with_capacity(items.len()); for item in items { - let file = item.clone().set_path(ori.join(item.path.strip_prefix(path).unwrap())); + let mut file = item.clone(); + file.set_path(ori.join(item.path().strip_prefix(path).unwrap())); files.push(file); } FilesOp::Read(ori, files) diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index 1428a5229..ef1cd5948 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -228,11 +228,11 @@ impl Tasks { #[inline] pub fn precache_mime(&self, targets: &[File], mimetype: &HashMap) -> bool { - let targets = targets + let targets: Vec<_> = targets .iter() - .filter(|f| f.meta.is_file() && !mimetype.contains_key(&f.path)) - .map(|f| f.path.clone()) - .collect::>(); + .filter(|f| f.meta.is_file() && !mimetype.contains_key(f.path())) + .map(|f| f.path().clone()) + .collect(); if !targets.is_empty() { self.scheduler.precache_mime(targets); From ab27c7a0694fa522891c75a2aeae37163c618938 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 13:45:32 +0800 Subject: [PATCH 06/15] .. --- app/src/manager/folder.rs | 4 ++-- app/src/status/right.rs | 2 +- core/src/files/file.rs | 13 ++++++++++++- core/src/files/sorter.rs | 6 +++--- core/src/manager/manager.rs | 12 ++++-------- core/src/manager/tab.rs | 4 ++-- core/src/tasks/tasks.rs | 4 ++-- 7 files changed, 26 insertions(+), 19 deletions(-) diff --git a/app/src/manager/folder.rs b/app/src/manager/folder.rs index aafb81f56..7ff7b6120 100644 --- a/app/src/manager/folder.rs +++ b/app/src/manager/folder.rs @@ -36,7 +36,7 @@ impl<'a> Folder<'a> { THEME .filetypes .iter() - .find(|x| x.matches(file.path(), mimetype.get(file.path()), file.meta.is_dir())) + .find(|x| x.matches(file.path(), mimetype.get(file.path()), file.is_dir())) .map(|x| x.style.get()) .unwrap_or_else(Style::new) } @@ -60,7 +60,7 @@ impl<'a> Widget for Folder<'a> { let icon = THEME .icons .iter() - .find(|x| x.name.match_path(f.path(), Some(f.meta.is_dir()))) + .find(|x| x.name.match_path(f.path(), Some(f.is_dir()))) .map(|x| x.display.as_ref()) .unwrap_or(""); diff --git a/app/src/status/right.rs b/app/src/status/right.rs index 3543ae9ad..a0425f7b5 100644 --- a/app/src/status/right.rs +++ b/app/src/status/right.rs @@ -71,7 +71,7 @@ impl Widget for Right<'_> { #[cfg(not(target_os = "windows"))] if let Some(h) = &manager.hovered { use std::os::unix::prelude::PermissionsExt; - spans.extend(self.permissions(&shared::file_mode(h.meta.permissions().mode()))) + spans.extend(self.permissions(&shared::file_mode(h.meta().permissions().mode()))) } // Position diff --git a/core/src/files/file.rs b/core/src/files/file.rs index b0393f052..8107ee4dc 100644 --- a/core/src/files/file.rs +++ b/core/src/files/file.rs @@ -6,7 +6,7 @@ use tokio::fs; #[derive(Clone, Debug)] pub struct File { pub(super) path: PathBuf, - pub meta: Metadata, + pub(super) meta: Metadata, pub length: Option, pub link_to: Option, pub is_link: bool, @@ -36,6 +36,7 @@ impl File { } impl File { + // --- Path #[inline] pub fn path(&self) -> &PathBuf { &self.path } @@ -44,4 +45,14 @@ impl File { #[inline] pub fn name(&self) -> Option> { self.path.file_name().map(|s| s.to_string_lossy()) } + + // --- Meta + #[inline] + pub fn meta(&self) -> &Metadata { &self.meta } + + #[inline] + pub fn is_file(&self) -> bool { self.meta.is_file() } + + #[inline] + pub fn is_dir(&self) -> bool { self.meta.is_dir() } } diff --git a/core/src/files/sorter.rs b/core/src/files/sorter.rs index 0be05287b..268438de3 100644 --- a/core/src/files/sorter.rs +++ b/core/src/files/sorter.rs @@ -54,9 +54,9 @@ impl FilesSorter { fn sort_naturally(&self, items: &mut Vec) { let mut indices = Vec::with_capacity(items.len()); let mut entities = Vec::with_capacity(items.len()); - for (i, file) in items.into_iter().enumerate() { + for (i, file) in items.iter().enumerate() { indices.push(i); - entities.push((file.path.to_string_lossy(), &*file)); + entities.push((file.path.to_string_lossy(), file)); } indices.sort_unstable_by(|&a, &b| { @@ -85,6 +85,6 @@ impl FilesSorter { #[inline] fn promote(&self, a: &File, b: &File) -> Ordering { - if self.dir_first { b.meta.is_dir().cmp(&a.meta.is_dir()) } else { Ordering::Equal } + if self.dir_first { b.is_dir().cmp(&a.is_dir()) } else { Ordering::Equal } } } diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index 620ee99b9..18d80175d 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -41,7 +41,7 @@ impl Manager { for tab in self.tabs.iter() { to_watch.insert(&tab.current.cwd); if let Some(ref h) = tab.current.hovered { - if h.meta.is_dir() { + if h.is_dir() { to_watch.insert(h.path()); } } @@ -61,7 +61,7 @@ impl Manager { self.active_mut().preview_reset_image(); } - let mime = if hovered.meta.is_dir() { + let mime = if hovered.is_dir() { MIME_DIR.to_owned() } else if let Some(m) = self.mimetype.get(hovered.path()).cloned() { m @@ -123,11 +123,7 @@ impl Manager { .map(|f| { ( f.path().as_os_str().to_owned(), - if f.meta.is_dir() { - Some(MIME_DIR.to_owned()) - } else { - self.mimetype.get(f.path()).cloned() - }, + if f.is_dir() { Some(MIME_DIR.to_owned()) } else { self.mimetype.get(f.path()).cloned() }, ) }) .collect(); @@ -401,7 +397,7 @@ impl Manager { return b; }; - if hovered.meta.is_dir() { + if hovered.is_dir() { self.watcher.trigger_dirs(&[hovered.path()]); } b diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index f04dbc192..bcb4405a5 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -78,7 +78,7 @@ impl Tab { }; let mut hovered = None; - if !file.meta.is_dir() { + if !file.is_dir() { hovered = Some(file); target = target.parent().unwrap().to_path_buf(); } @@ -127,7 +127,7 @@ impl Tab { let Some(hovered) = self.current.hovered.clone() else { return false; }; - if !hovered.meta.is_dir() { + if !hovered.is_dir() { return false; } diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index ef1cd5948..82a8af198 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -217,7 +217,7 @@ impl Tasks { } let targets: Vec<_> = - targets.iter().filter(|f| f.meta.is_dir() && f.length.is_none()).map(|f| f.path()).collect(); + targets.iter().filter(|f| f.is_dir() && f.length.is_none()).map(|f| f.path()).collect(); if !targets.is_empty() { self.scheduler.precache_size(targets); @@ -230,7 +230,7 @@ impl Tasks { pub fn precache_mime(&self, targets: &[File], mimetype: &HashMap) -> bool { let targets: Vec<_> = targets .iter() - .filter(|f| f.meta.is_file() && !mimetype.contains_key(f.path())) + .filter(|f| f.is_file() && !mimetype.contains_key(f.path())) .map(|f| f.path().clone()) .collect(); From 0967a701f98c0387d2d7840c7bea3470721fce1d Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 13:57:06 +0800 Subject: [PATCH 07/15] .. --- app/src/status/left.rs | 2 +- core/src/files/file.rs | 16 ++++++++++------ core/src/files/sorter.rs | 2 +- core/src/tasks/tasks.rs | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/src/status/left.rs b/app/src/status/left.rs index e9c34d783..9e036bf1e 100644 --- a/app/src/status/left.rs +++ b/app/src/status/left.rs @@ -35,7 +35,7 @@ impl<'a> Widget for Left<'a> { if let Some(h) = &manager.hovered { // Length - if let Some(len) = h.length { + if let Some(len) = h.length() { spans.push(Span::styled(format!(" {} ", readable_size(len)), body.bg().fg(**primary))); spans.push(Span::styled(&separator.closing, body.fg())); } diff --git a/core/src/files/file.rs b/core/src/files/file.rs index 8107ee4dc..e17a25634 100644 --- a/core/src/files/file.rs +++ b/core/src/files/file.rs @@ -5,12 +5,12 @@ use tokio::fs; #[derive(Clone, Debug)] pub struct File { - pub(super) path: PathBuf, - pub(super) meta: Metadata, - pub length: Option, - pub link_to: Option, - pub is_link: bool, - pub is_hidden: bool, + pub(super) path: PathBuf, + pub(super) meta: Metadata, + pub(super) length: Option, + pub link_to: Option, + pub is_link: bool, + pub is_hidden: bool, } impl File { @@ -55,4 +55,8 @@ impl File { #[inline] pub fn is_dir(&self) -> bool { self.meta.is_dir() } + + // --- Length + #[inline] + pub fn length(&self) -> Option { self.length } } diff --git a/core/src/files/sorter.rs b/core/src/files/sorter.rs index 268438de3..2f4bc857b 100644 --- a/core/src/files/sorter.rs +++ b/core/src/files/sorter.rs @@ -45,7 +45,7 @@ impl FilesSorter { }), SortBy::Natural => self.sort_naturally(items), SortBy::Size => items.sort_unstable_by(|a, b| { - self.cmp(a.length.unwrap_or(0), b.length.unwrap_or(0), self.promote(a, b)) + self.cmp(a.length().unwrap_or(0), b.length().unwrap_or(0), self.promote(a, b)) }), } true diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index 82a8af198..1cf688ebc 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -217,7 +217,7 @@ impl Tasks { } let targets: Vec<_> = - targets.iter().filter(|f| f.is_dir() && f.length.is_none()).map(|f| f.path()).collect(); + targets.iter().filter(|f| f.is_dir() && f.length().is_none()).map(|f| f.path()).collect(); if !targets.is_empty() { self.scheduler.precache_size(targets); From d7e2e1bc93dc0f0a648a0248c62953896ca08abb Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 13:59:58 +0800 Subject: [PATCH 08/15] .. --- app/src/manager/folder.rs | 2 +- core/src/files/file.rs | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/manager/folder.rs b/app/src/manager/folder.rs index 7ff7b6120..8ccc8e8b1 100644 --- a/app/src/manager/folder.rs +++ b/app/src/manager/folder.rs @@ -88,7 +88,7 @@ impl<'a> Widget for Folder<'a> { }; let mut path = format!(" {icon} {}", readable_path(f.path(), &self.folder.cwd)); - if let Some(ref link_to) = f.link_to { + if let Some(link_to) = f.link_to() { if MANAGER.show_symlink { path.push_str(&format!(" -> {}", link_to.display())); } diff --git a/core/src/files/file.rs b/core/src/files/file.rs index e17a25634..d35df9608 100644 --- a/core/src/files/file.rs +++ b/core/src/files/file.rs @@ -5,12 +5,12 @@ use tokio::fs; #[derive(Clone, Debug)] pub struct File { - pub(super) path: PathBuf, - pub(super) meta: Metadata, - pub(super) length: Option, - pub link_to: Option, - pub is_link: bool, - pub is_hidden: bool, + pub(super) path: PathBuf, + pub(super) meta: Metadata, + pub(super) length: Option, + pub(super) link_to: Option, + pub(super) is_link: bool, + pub(super) is_hidden: bool, } impl File { @@ -59,4 +59,8 @@ impl File { // --- Length #[inline] pub fn length(&self) -> Option { self.length } + + // --- Link to + #[inline] + pub fn link_to(&self) -> Option<&PathBuf> { self.link_to.as_ref() } } From 05964eb26c4fa3211f3409d607582497be4e9ded Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 14:18:03 +0800 Subject: [PATCH 09/15] .. --- app/src/executor.rs | 2 +- app/src/status/left.rs | 2 +- core/src/files/file.rs | 21 +++++++++++++++++++-- core/src/files/files.rs | 6 +++--- core/src/manager/folder.rs | 2 +- core/src/manager/manager.rs | 8 ++++---- core/src/manager/tab.rs | 14 +++++++------- core/src/tasks/tasks.rs | 2 +- 8 files changed, 37 insertions(+), 20 deletions(-) diff --git a/app/src/executor.rs b/app/src/executor.rs index 025475c21..51a477a13 100644 --- a/app/src/executor.rs +++ b/app/src/executor.rs @@ -106,7 +106,7 @@ impl Executor { } } "remove" => { - let targets = cx.manager.selected().into_iter().map(|f| f.path().clone()).collect(); + let targets = cx.manager.selected().into_iter().map(|f| f.path_owned()).collect(); cx.tasks.file_remove(targets, exec.named.contains_key("permanently")) } "create" => cx.manager.create(), diff --git a/app/src/status/left.rs b/app/src/status/left.rs index 9e036bf1e..cf72e39d3 100644 --- a/app/src/status/left.rs +++ b/app/src/status/left.rs @@ -41,7 +41,7 @@ impl<'a> Widget for Left<'a> { } // Filename - spans.push(Span::raw(format!(" {} ", h.name().unwrap()))); + spans.push(Span::raw(format!(" {} ", h.name_display().unwrap()))); } Paragraph::new(Line::from(spans)).render(area, buf); diff --git a/core/src/files/file.rs b/core/src/files/file.rs index d35df9608..6b4ba4a68 100644 --- a/core/src/files/file.rs +++ b/core/src/files/file.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fs::Metadata, path::{Path, PathBuf}}; +use std::{borrow::Cow, ffi::OsStr, fs::Metadata, path::{Path, PathBuf}}; use anyhow::Result; use tokio::fs; @@ -44,7 +44,24 @@ impl File { pub fn set_path(&mut self, path: PathBuf) { self.path = path; } #[inline] - pub fn name(&self) -> Option> { self.path.file_name().map(|s| s.to_string_lossy()) } + pub fn path_owned(&self) -> PathBuf { self.path.clone() } + + #[inline] + pub fn path_os_str(&self) -> &OsStr { self.path.as_os_str() } + + #[inline] + pub fn name(&self) -> Option<&OsStr> { self.path.file_name() } + + #[inline] + pub fn name_display(&self) -> Option> { + self.path.file_name().map(|s| s.to_string_lossy()) + } + + #[inline] + pub fn stem(&self) -> Option<&OsStr> { self.path.file_stem() } + + #[inline] + pub fn parent(&self) -> Option<&Path> { self.path.parent() } // --- Meta #[inline] diff --git a/core/src/files/files.rs b/core/src/files/files.rs index a2681ced4..3d7417762 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -86,7 +86,7 @@ impl Files { match state { Some(true) => { - self.selected = self.iter().map(|f| f.path().clone()).collect(); + self.selected = self.iter().map(|f| f.path_owned()).collect(); } Some(false) => { self.selected.clear(); @@ -96,7 +96,7 @@ impl Files { if self.selected.contains(&item.path) { self.selected.remove(&item.path); } else { - self.selected.insert(item.path().clone()); + self.selected.insert(item.path_owned()); } } } @@ -106,7 +106,7 @@ impl Files { pub fn select_index(&mut self, indices: &BTreeSet, state: Option) -> bool { let mut applied = false; - let paths: Vec<_> = self.pick(indices).iter().map(|f| f.path().clone()).collect(); + let paths: Vec<_> = self.pick(indices).iter().map(|f| f.path_owned()).collect(); for path in paths { applied |= self.select(&path, state); diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index 2a2401020..0647a2769 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -40,7 +40,7 @@ impl Folder { self.cursor = self.cursor.min(len.saturating_sub(1)); self.set_page(true); - if let Some(h) = self.hovered.as_ref().map(|h| h.path().clone()) { + if let Some(h) = self.hovered.as_ref().map(|h| h.path_owned()) { self.hover(&h); } self.hovered = self.files.duplicate(self.cursor); diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index 18d80175d..ae6df786a 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -84,7 +84,7 @@ impl Manager { pub fn yank(&mut self, cut: bool) -> bool { self.yanked.0 = cut; - self.yanked.1 = self.selected().into_iter().map(|f| f.path().clone()).collect(); + self.yanked.1 = self.selected().into_iter().map(|f| f.path_owned()).collect(); false } @@ -122,7 +122,7 @@ impl Manager { .into_iter() .map(|f| { ( - f.path().as_os_str().to_owned(), + f.path_os_str().to_owned(), if f.is_dir() { Some(MIME_DIR.to_owned()) } else { self.mimetype.get(f.path()).cloned() }, ) }) @@ -197,7 +197,7 @@ impl Manager { return self.bulk_rename(); } - let Some(hovered) = self.hovered().map(|h| h.path().clone()) else { + let Some(hovered) = self.hovered().map(|h| h.path_owned()) else { return false; }; @@ -321,7 +321,7 @@ impl Manager { pub fn update_read(&mut self, op: FilesOp) -> bool { let path = op.path(); let cwd = self.cwd().to_owned(); - let hovered = self.hovered().map(|h| h.path().clone()); + let hovered = self.hovered().map(|h| h.path_owned()); let mut b = if cwd == path && !self.current().in_search { self.current_mut().update(op) diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index bcb4405a5..7d9ba6a08 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -140,7 +140,7 @@ impl Tab { if let Some(rep) = self.parent.take() { self.history.insert(rep.cwd.clone(), rep); } - self.parent = Some(self.history_new(hovered.path().parent().unwrap())); + self.parent = Some(self.history_new(hovered.parent().unwrap())); emit!(Refresh); true @@ -151,7 +151,7 @@ impl Tab { .current .hovered .as_ref() - .and_then(|h| h.path().parent()) + .and_then(|h| h.parent()) .and_then(|p| if p == self.current.cwd { None } else { Some(p) }) .or_else(|| self.current.cwd.parent()); @@ -209,10 +209,10 @@ impl Tab { let mut it = self.selected().into_iter().peekable(); while let Some(f) = it.next() { s.push(match type_ { - "path" => f.path().as_os_str(), - "dirname" => f.path().parent().map_or(OsStr::new(""), |p| p.as_os_str()), - "filename" => f.path().file_name().unwrap_or(OsStr::new("")), - "name_without_ext" => f.path().file_stem().unwrap_or(OsStr::new("")), + "path" => f.path_os_str(), + "dirname" => f.parent().map_or(OsStr::new(""), |p| p.as_os_str()), + "filename" => f.name().unwrap_or(OsStr::new("")), + "name_without_ext" => f.stem().unwrap_or(OsStr::new("")), _ => return false, }); if it.peek().is_some() { @@ -288,7 +288,7 @@ impl Tab { let selected: Vec<_> = self .selected() .into_iter() - .map(|f| (f.path().as_os_str().to_owned(), Default::default())) + .map(|f| (f.path_os_str().to_owned(), Default::default())) .collect(); let mut exec = exec.to_owned(); diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index 1cf688ebc..7464ca65a 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -231,7 +231,7 @@ impl Tasks { let targets: Vec<_> = targets .iter() .filter(|f| f.is_file() && !mimetype.contains_key(f.path())) - .map(|f| f.path().clone()) + .map(|f| f.path_owned()) .collect(); if !targets.is_empty() { From 4b98c65559aa4fc662e069483bd5ff0fc11ee239 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 14:23:12 +0800 Subject: [PATCH 10/15] .. --- core/src/files/files.rs | 70 ++++++++++++++++++++++------------------- core/src/tasks/tasks.rs | 2 +- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/core/src/files/files.rs b/core/src/files/files.rs index 3d7417762..a4df3152a 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -13,7 +13,7 @@ pub struct Files { sizes: BTreeMap, selected: BTreeSet, - pub sorter: FilesSorter, + sorter: FilesSorter, show_hidden: bool, } @@ -114,36 +114,6 @@ impl Files { applied } - #[inline] - pub fn set_sorter(&mut self, sorter: FilesSorter) -> bool { - if self.sorter == sorter { - return false; - } - self.sorter = sorter; - self.sorter.sort(&mut self.items) - } - - #[inline] - pub fn set_show_hidden(&mut self, state: Option) -> bool { - let state = state.unwrap_or(!self.show_hidden); - if state == self.show_hidden { - return false; - } else if state && self.hidden.is_empty() { - return false; - } - - if state { - self.items.append(&mut self.hidden); - self.sorter.sort(&mut self.items); - } else { - let items = mem::take(&mut self.items); - (self.hidden, self.items) = items.into_iter().partition(|f| f.is_hidden); - } - - self.show_hidden = state; - true - } - pub fn update_read(&mut self, mut items: Vec) -> bool { if !self.show_hidden { (self.hidden, items) = items.into_iter().partition(|f| f.is_hidden); @@ -179,6 +149,7 @@ impl Files { } impl Files { + // --- Items pub fn pick(&self, indices: &BTreeSet) -> Vec<&File> { let mut items = Vec::with_capacity(indices.len()); for (i, item) in self.iter().enumerate() { @@ -195,6 +166,7 @@ impl Files { #[inline] pub fn duplicate(&self, idx: usize) -> Option { self.items.get(idx).cloned() } + // --- Selected pub fn selected(&self, pending: &BTreeSet, unset: bool) -> Vec<&File> { if self.selected.is_empty() && (unset || pending.is_empty()) { return Default::default(); @@ -224,9 +196,41 @@ impl Files { } self.iter().any(|f| self.selected.contains(&f.path)) } -} -impl Files { + // --- Sorter + pub fn sorter(&self) -> &FilesSorter { &self.sorter } + + #[inline] + pub fn set_sorter(&mut self, sorter: FilesSorter) -> bool { + if self.sorter == sorter { + return false; + } + self.sorter = sorter; + self.sorter.sort(&mut self.items) + } + + // --- Show hidden #[inline] pub fn show_hidden(&self) -> bool { self.show_hidden } + + #[inline] + pub fn set_show_hidden(&mut self, state: Option) -> bool { + let state = state.unwrap_or(!self.show_hidden); + if state == self.show_hidden { + return false; + } else if state && self.hidden.is_empty() { + return false; + } + + if state { + self.items.append(&mut self.hidden); + self.sorter.sort(&mut self.items); + } else { + let items = mem::take(&mut self.items); + (self.hidden, self.items) = items.into_iter().partition(|f| f.is_hidden); + } + + self.show_hidden = state; + true + } } diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index 7464ca65a..8bb6e7bf9 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -212,7 +212,7 @@ impl Tasks { #[inline] pub fn precache_size(&self, targets: &Files) -> bool { - if targets.sorter.by != SortBy::Size { + if targets.sorter().by != SortBy::Size { return false; } From 9d1c5041fd203a06647b33596434ea0a34181304 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 15:34:27 +0800 Subject: [PATCH 11/15] .. --- app/src/executor.rs | 12 ++++---- core/src/files/files.rs | 9 ++---- core/src/manager/folder.rs | 7 ----- core/src/manager/manager.rs | 2 ++ core/src/manager/tab.rs | 60 ++++++++++++++++++++++++++++++------- core/src/manager/tabs.rs | 12 ++++++-- 6 files changed, 70 insertions(+), 32 deletions(-) diff --git a/app/src/executor.rs b/app/src/executor.rs index 51a477a13..5c27d2a0a 100644 --- a/app/src/executor.rs +++ b/app/src/executor.rs @@ -117,11 +117,13 @@ impl Executor { exec.named.contains_key("block"), exec.named.contains_key("confirm"), ), - "hidden" => cx.manager.current_mut().hidden(match exec.args.get(0).map(|s| s.as_str()) { - Some("show") => Some(true), - Some("hide") => Some(false), - _ => None, - }), + "hidden" => { + cx.manager.active_mut().set_show_hidden(match exec.args.get(0).map(|s| s.as_str()) { + Some("show") => Some(true), + Some("hide") => Some(false), + _ => None, + }) + } "search" => match exec.args.get(0).map(|s| s.as_str()).unwrap_or("") { "rg" => cx.manager.active_mut().search(true), "fd" => cx.manager.active_mut().search(false), diff --git a/core/src/files/files.rs b/core/src/files/files.rs index a4df3152a..de377c9a2 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -1,7 +1,6 @@ use std::{collections::{BTreeMap, BTreeSet}, mem, ops::Deref, path::{Path, PathBuf}}; use anyhow::Result; -use config::MANAGER; use tokio::fs; use super::{File, FilesSorter}; @@ -27,7 +26,7 @@ impl Default for Files { selected: Default::default(), sorter: Default::default(), - show_hidden: MANAGER.show_hidden, + show_hidden: true, } } } @@ -211,11 +210,7 @@ impl Files { // --- Show hidden #[inline] - pub fn show_hidden(&self) -> bool { self.show_hidden } - - #[inline] - pub fn set_show_hidden(&mut self, state: Option) -> bool { - let state = state.unwrap_or(!self.show_hidden); + pub fn set_show_hidden(&mut self, state: bool) -> bool { if state == self.show_hidden { return false; } else if state && self.hidden.is_empty() { diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index 0647a2769..e813231bf 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -92,13 +92,6 @@ impl Folder { old != self.cursor } - pub fn hidden(&mut self, show: Option) -> bool { - if self.files.set_show_hidden(show) { - emit!(Refresh); - } - false - } - #[inline] pub fn window(&self) -> &[File] { let end = (self.offset + MANAGER.layout.folder_height()).min(self.files.len()); diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index ae6df786a..c0a26167b 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -30,6 +30,8 @@ impl Manager { pub fn refresh(&mut self) { env::set_current_dir(self.cwd()).ok(); + self.active_mut().apply_show_hidden(); + if let Some(f) = self.parent() { self.watcher.trigger_dirs(&[self.cwd(), &f.cwd]); } else { diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index 7d9ba6a08..ddb49a3d1 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -14,10 +14,11 @@ pub struct Tab { pub(super) current: Folder, pub(super) parent: Option, - search: Option>>, - pub(super) history: BTreeMap, pub(super) preview: Preview, + + search: Option>>, + pub(super) show_hidden: bool, } impl Tab { @@ -27,10 +28,11 @@ impl Tab { current: Folder::new(path), parent: path.parent().map(Folder::new), - search: None, - history: Default::default(), preview: Default::default(), + + search: None, + show_hidden: true, } } @@ -230,7 +232,7 @@ impl Tab { } let cwd = self.current.cwd.clone(); - let hidden = self.current.files.show_hidden(); + let hidden = self.show_hidden; self.search = Some(tokio::spawn(async move { let subject = emit!(Input(InputOpt::top("Search:"))).await?; @@ -347,9 +349,16 @@ impl Tab { } impl Tab { + // --- Mode #[inline] pub fn mode(&self) -> &Mode { &self.mode } + #[inline] + pub fn in_selecting(&self) -> bool { + self.mode().is_visual() || self.current.files.has_selected() + } + + // --- Current #[inline] pub fn name(&self) -> &str { self @@ -373,11 +382,7 @@ impl Tab { } } - #[inline] - pub fn in_selecting(&self) -> bool { - self.mode().is_visual() || self.current.files.has_selected() - } - + // --- History #[inline] pub fn history(&self, path: &Path) -> Option<&Folder> { self.history.get(path) } @@ -386,6 +391,7 @@ impl Tab { self.history.remove(path).unwrap_or_else(|| Folder::new(path)) } + // --- Preview #[inline] pub fn preview(&self) -> &Preview { &self.preview } @@ -394,4 +400,38 @@ impl Tab { #[inline] pub fn preview_reset_image(&mut self) -> bool { self.preview.reset_image() } + + // --- Show hidden + pub fn set_show_hidden(&mut self, state: Option) -> bool { + let state = state.unwrap_or(!self.show_hidden); + if state == self.show_hidden { + return false; + } + + self.show_hidden = state; + self.apply_show_hidden(); + true + } + + pub fn apply_show_hidden(&mut self) -> bool { + let state = self.show_hidden; + + let mut applied = false; + applied |= self.current.files.set_show_hidden(state); + + if let Some(parent) = self.parent.as_mut() { + applied |= parent.files.set_show_hidden(state); + } + + applied |= match self.current.hovered { + Some(ref hovered) if hovered.is_dir() => self + .history + .get_mut(hovered.path()) + .map(|f| f.files.set_show_hidden(state)) + .unwrap_or(false), + _ => false, + }; + + applied + } } diff --git a/core/src/manager/tabs.rs b/core/src/manager/tabs.rs index 3539aa991..ef25393cd 100644 --- a/core/src/manager/tabs.rs +++ b/core/src/manager/tabs.rs @@ -1,6 +1,6 @@ use std::path::Path; -use config::BOOT; +use config::{BOOT, MANAGER}; use super::Tab; use crate::emit; @@ -14,7 +14,10 @@ pub struct Tabs { impl Tabs { pub fn make() -> Self { - let mut tabs = Self { idx: usize::MAX, items: vec![Tab::new(&BOOT.cwd)] }; + let mut tab = Tab::new(&BOOT.cwd); + tab.set_show_hidden(Some(MANAGER.show_hidden)); + + let mut tabs = Self { idx: usize::MAX, items: vec![tab] }; tabs.set_idx(0); tabs } @@ -24,7 +27,10 @@ impl Tabs { return false; } - self.items.insert(self.idx + 1, Tab::new(path)); + let mut tab = Tab::new(path); + tab.set_show_hidden(Some(self.active().show_hidden)); + + self.items.insert(self.idx + 1, tab); self.set_idx(self.idx + 1); true } From 0d11c52e4df1891743ad96fe5b8743672375d5fe Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 17:31:56 +0800 Subject: [PATCH 12/15] .. --- app/src/executor.rs | 2 +- core/src/files/files.rs | 16 ++++++++++++---- core/src/manager/folder.rs | 10 ++++++---- core/src/manager/tab.rs | 21 +++++++++++++++------ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/app/src/executor.rs b/app/src/executor.rs index 5c27d2a0a..6028691a6 100644 --- a/app/src/executor.rs +++ b/app/src/executor.rs @@ -137,7 +137,7 @@ impl Executor { // Sorting "sort" => { - let b = cx.manager.current_mut().files.set_sorter(FilesSorter { + let b = cx.manager.active_mut().set_sorter(FilesSorter { by: SortBy::try_from(exec.args.get(0).cloned().unwrap_or_default()) .unwrap_or_default(), reverse: exec.named.contains_key("reverse"), diff --git a/core/src/files/files.rs b/core/src/files/files.rs index de377c9a2..c89b93991 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -1,6 +1,7 @@ use std::{collections::{BTreeMap, BTreeSet}, mem, ops::Deref, path::{Path, PathBuf}}; use anyhow::Result; +use config::manager::SortBy; use tokio::fs; use super::{File, FilesSorter}; @@ -124,15 +125,22 @@ impl Files { pub fn update_size(&mut self, items: BTreeMap) -> bool { self.sizes.extend(items); - self.sorter.sort(&mut self.items); + if self.sorter.by == SortBy::Size { + self.sorter.sort(&mut self.items); + } true } pub fn update_search(&mut self, items: Vec) -> bool { if !items.is_empty() { - let (hidden, items): (Vec<_>, Vec<_>) = items.into_iter().partition(|f| f.is_hidden); - self.items.extend(items); - self.hidden.extend(hidden); + if self.show_hidden { + self.items.extend(items); + } else { + let (hidden, items): (Vec<_>, Vec<_>) = items.into_iter().partition(|f| f.is_hidden); + self.items.extend(items); + self.hidden.extend(hidden); + } + self.sorter.sort(&mut self.items); return true; } diff --git a/core/src/manager/folder.rs b/core/src/manager/folder.rs index e813231bf..9cb3ff1e1 100644 --- a/core/src/manager/folder.rs +++ b/core/src/manager/folder.rs @@ -40,11 +40,8 @@ impl Folder { self.cursor = self.cursor.min(len.saturating_sub(1)); self.set_page(true); - if let Some(h) = self.hovered.as_ref().map(|h| h.path_owned()) { - self.hover(&h); - } + self.hover_repos(); self.hovered = self.files.duplicate(self.cursor); - true } @@ -110,6 +107,11 @@ impl Folder { if new > self.cursor { self.next(new - self.cursor) } else { self.prev(self.cursor - new) } } + #[inline] + pub fn hover_repos(&mut self) -> bool { + self.hover(&self.hovered.as_ref().map(|h| h.path_owned()).unwrap_or_default()) + } + pub fn hover_force(&mut self, file: File) -> bool { if self.hover(file.path()) { return true; diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index ddb49a3d1..06e50b7cf 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -7,7 +7,7 @@ use shared::{Defer, MIME_DIR}; use tokio::task::JoinHandle; use super::{Folder, Mode, Preview, PreviewLock}; -use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp}, input::InputOpt, Event, BLOCKER}; +use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, Files, FilesOp, FilesSorter}, input::InputOpt, Event, BLOCKER}; pub struct Tab { pub(super) mode: Mode, @@ -401,6 +401,16 @@ impl Tab { #[inline] pub fn preview_reset_image(&mut self) -> bool { self.preview.reset_image() } + // --- Sorter + pub fn set_sorter(&mut self, sorter: FilesSorter) -> bool { + if !self.current.files.set_sorter(sorter) { + return false; + } + + self.current.hover_repos(); + true + } + // --- Show hidden pub fn set_show_hidden(&mut self, state: Option) -> bool { let state = state.unwrap_or(!self.show_hidden); @@ -424,14 +434,13 @@ impl Tab { } applied |= match self.current.hovered { - Some(ref hovered) if hovered.is_dir() => self - .history - .get_mut(hovered.path()) - .map(|f| f.files.set_show_hidden(state)) - .unwrap_or(false), + Some(ref h) if h.is_dir() => { + self.history.get_mut(h.path()).map(|f| f.files.set_show_hidden(state)).unwrap_or(false) + } _ => false, }; + self.current.hover_repos(); applied } } From 46e7c4d10258c105592952cda9a97658527cc631 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 28 Aug 2023 19:13:39 +0800 Subject: [PATCH 13/15] .. --- core/src/files/file.rs | 6 +++--- core/src/files/sorter.rs | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/core/src/files/file.rs b/core/src/files/file.rs index 6b4ba4a68..355e932da 100644 --- a/core/src/files/file.rs +++ b/core/src/files/file.rs @@ -15,12 +15,12 @@ pub struct File { impl File { #[inline] - pub async fn from(path: &Path) -> Result { + pub async fn from(path: &Path) -> Result { let meta = fs::metadata(path).await?; Ok(Self::from_meta(path, meta).await) } - pub async fn from_meta(path: &Path, mut meta: Metadata) -> File { + pub async fn from_meta(path: &Path, mut meta: Metadata) -> Self { let is_link = meta.is_symlink(); let mut link_to = None; @@ -31,7 +31,7 @@ impl File { let length = if meta.is_dir() { None } else { Some(meta.len()) }; let is_hidden = path.file_name().map(|s| s.to_string_lossy().starts_with('.')).unwrap_or(false); - File { path: path.to_path_buf(), meta, length, link_to, is_link, is_hidden } + Self { path: path.to_path_buf(), meta, length, link_to, is_link, is_hidden } } } diff --git a/core/src/files/sorter.rs b/core/src/files/sorter.rs index 2f4bc857b..f1404122c 100644 --- a/core/src/files/sorter.rs +++ b/core/src/files/sorter.rs @@ -1,4 +1,4 @@ -use std::cmp::Ordering; +use std::{cmp::Ordering, mem, path::PathBuf}; use config::{manager::SortBy, MANAGER}; @@ -70,7 +70,20 @@ impl FilesSorter { } }); - items.sort_unstable_by_key(|_| indices.pop().unwrap()); + let dummy = File { + path: PathBuf::new(), + meta: items[0].meta.clone(), + length: None, + link_to: None, + is_link: false, + is_hidden: false, + }; + + let mut new = Vec::with_capacity(indices.len()); + for i in indices { + new.push(mem::replace(&mut items[i], dummy.clone())); + } + *items = new; } #[inline] From b1f03ba841ec0847f09f9ddcde47c03f67ba9c37 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 29 Aug 2023 11:47:20 +0800 Subject: [PATCH 14/15] .. --- app/src/manager/folder.rs | 2 +- core/src/files/files.rs | 30 +++++++++++++++++------------- core/src/manager/tab.rs | 4 +--- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/app/src/manager/folder.rs b/app/src/manager/folder.rs index 8ccc8e8b1..0d1fc1041 100644 --- a/app/src/manager/folder.rs +++ b/app/src/manager/folder.rs @@ -66,7 +66,7 @@ impl<'a> Widget for Folder<'a> { let is_selected = self.folder.files.is_selected(f.path()); if (!self.is_selection && is_selected) - || (self.is_selection && mode.pending(i, is_selected)) + || (self.is_selection && mode.pending(self.folder.offset() + i, is_selected)) { buf.set_style( Rect { x: area.x.saturating_sub(1), y: i as u16 + 1, width: 1, height: 1 }, diff --git a/core/src/files/files.rs b/core/src/files/files.rs index c89b93991..69d0ce1d3 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -79,11 +79,7 @@ impl Files { true } - pub fn select_many(&mut self, path: Option<&Path>, state: Option) -> bool { - if let Some(path) = path { - return self.select(path, state); - } - + pub fn select_all(&mut self, state: Option) -> bool { match state { Some(true) => { self.selected = self.iter().map(|f| f.path_owned()).collect(); @@ -176,19 +172,27 @@ impl Files { // --- Selected pub fn selected(&self, pending: &BTreeSet, unset: bool) -> Vec<&File> { if self.selected.is_empty() && (unset || pending.is_empty()) { - return Default::default(); + return Vec::new(); } - let mut items = - Vec::with_capacity(self.selected.len() + if !unset { pending.len() } else { 0 }); + let selected: BTreeSet<_> = self.selected.iter().collect(); + let pending: BTreeSet<_> = + pending.iter().filter_map(|&i| self.items.get(i)).map(|f| &f.path).collect(); - for (i, item) in self.iter().enumerate() { - let b = self.selected.contains(&item.path); - if !unset && (b || pending.contains(&i)) { - items.push(item); - } else if unset && b && !pending.contains(&i) { + let selected: BTreeSet<_> = if unset { + selected.difference(&pending).cloned().collect() + } else { + selected.union(&pending).cloned().collect() + }; + + let mut items = Vec::with_capacity(selected.len()); + for item in &self.items { + if selected.contains(&item.path) { items.push(item); } + if items.len() == selected.len() { + break; + } } items } diff --git a/core/src/manager/tab.rs b/core/src/manager/tab.rs index 06e50b7cf..de6f6b983 100644 --- a/core/src/manager/tab.rs +++ b/core/src/manager/tab.rs @@ -191,9 +191,7 @@ impl Tab { false } - pub fn select_all(&mut self, state: Option) -> bool { - self.current.files.select_many(None, state) - } + pub fn select_all(&mut self, state: Option) -> bool { self.current.files.select_all(state) } pub fn visual_mode(&mut self, unset: bool) -> bool { let idx = self.current.cursor(); From b2baf154284088bdacd5486c04274b5d17af3eda Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 29 Aug 2023 11:53:32 +0800 Subject: [PATCH 15/15] .. --- shared/src/fs.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/shared/src/fs.rs b/shared/src/fs.rs index 8a5cb5fbc..72c7058f6 100644 --- a/shared/src/fs.rs +++ b/shared/src/fs.rs @@ -170,16 +170,24 @@ pub fn max_common_root(files: &[impl AsRef]) -> PathBuf { #[test] fn test_max_common_root() { - assert_eq!(max_common_root(&[]).as_os_str(), ""); - assert_eq!(max_common_root(&["".into()]).as_os_str(), ""); - assert_eq!(max_common_root(&["a".into()]).as_os_str(), ""); - assert_eq!(max_common_root(&["/a".into()]).as_os_str(), "/"); - assert_eq!(max_common_root(&["/a/b".into()]).as_os_str(), "/a"); - assert_eq!(max_common_root(&["/a/b/c".into(), "/a/b/d".into()]).as_os_str(), "/a/b"); - assert_eq!(max_common_root(&["/aa/bb/cc".into(), "/aa/dd/ee".into()]).as_os_str(), "/aa"); + assert_eq!(max_common_root(&[] as &[PathBuf]).as_os_str(), ""); + assert_eq!(max_common_root(&["".into()] as &[PathBuf]).as_os_str(), ""); + assert_eq!(max_common_root(&["a".into()] as &[PathBuf]).as_os_str(), ""); + assert_eq!(max_common_root(&["/a".into()] as &[PathBuf]).as_os_str(), "/"); + assert_eq!(max_common_root(&["/a/b".into()] as &[PathBuf]).as_os_str(), "/a"); assert_eq!( - max_common_root(&["/aa/bb/cc".into(), "/aa/bb/cc/dd/ee".into(), "/aa/bb/cc/ff".into()]) - .as_os_str(), + max_common_root(&["/a/b/c".into(), "/a/b/d".into()] as &[PathBuf]).as_os_str(), + "/a/b" + ); + assert_eq!( + max_common_root(&["/aa/bb/cc".into(), "/aa/dd/ee".into()] as &[PathBuf]).as_os_str(), + "/aa" + ); + assert_eq!( + max_common_root( + &["/aa/bb/cc".into(), "/aa/bb/cc/dd/ee".into(), "/aa/bb/cc/ff".into()] as &[PathBuf] + ) + .as_os_str(), "/aa/bb" ); }