diff --git a/src/interactive/app/common.rs b/src/interactive/app/common.rs index 9d1d0e42..1100cd87 100644 --- a/src/interactive/app/common.rs +++ b/src/interactive/app/common.rs @@ -70,21 +70,22 @@ pub fn sorted_entries( } tree.neighbors_directed(node_idx, Direction::Outgoing) .filter_map(|idx| { - tree.node_weight(idx).map(|w| { - let mut use_glob_path = false; - if let Some(glob_root) = glob_root { - use_glob_path = node_idx == glob_root; - } - let p = path_of(tree, idx, glob_root); - let pm = p.symlink_metadata(); + tree.node_weight(idx).map(|entry| { + let use_glob_path = glob_root.map_or(false, |glob_root| glob_root == node_idx); + let path = path_of(tree, idx, glob_root); + let meta = path.symlink_metadata(); EntryDataBundle { index: idx, - name: if use_glob_path { p } else { w.name.clone() }, - size: w.size, - mtime: w.mtime, - entry_count: w.entry_count, - exists: pm.is_ok(), - is_dir: pm.ok().map_or(false, |m| m.is_dir()), + name: if use_glob_path { + path + } else { + entry.name.clone() + }, + size: entry.size, + mtime: entry.mtime, + entry_count: entry.entry_count, + exists: meta.is_ok(), + is_dir: meta.ok().map_or(false, |m| m.is_dir()), } }) }) diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index f338c6e5..00b37e54 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -15,7 +15,7 @@ use std::path::PathBuf; use tui::backend::Backend; use tui_react::Terminal; -use super::tree_view::{GlobTreeView, NormalTreeView, TreeView}; +use super::tree_view::TreeView; #[derive(Default, Copy, Clone, PartialEq)] pub enum FocussedPane { @@ -63,7 +63,7 @@ impl AppState { pub fn draw( &mut self, window: &mut MainWindow, - tree_view: &dyn TreeView, + tree_view: &TreeView<'_>, display: DisplayOptions, terminal: &mut Terminal, ) -> Result<()> @@ -72,10 +72,10 @@ impl AppState { { let props = MainWindowProps { current_path: tree_view.current_path(self.navigation().view_root), - entries_traversed: tree_view.traversal().entries_traversed, - total_bytes: tree_view.traversal().total_bytes, - start: tree_view.traversal().start, - elapsed: tree_view.traversal().elapsed, + entries_traversed: tree_view.traversal.entries_traversed, + total_bytes: tree_view.traversal.total_bytes, + start: tree_view.traversal.start, + elapsed: tree_view.traversal.elapsed, display, state: self, }; @@ -109,7 +109,7 @@ impl AppState { { let tree_view = self.tree_view(traversal); - self.draw(window, tree_view.as_ref(), *display, terminal)?; + self.draw(window, &tree_view, *display, terminal)?; } for event in events { @@ -118,15 +118,14 @@ impl AppState { Event::Resize(_, _) => Alt('\r'), }; - let mut tree_view = self.tree_view(traversal); - self.reset_message(); let glob_focussed = self.focussed == Glob; + let mut tree_view = self.tree_view(traversal); let mut handled = true; match key { Esc => { - if let Some(value) = self.handle_quit(tree_view.as_mut(), window) { + if let Some(value) = self.handle_quit(&mut tree_view, window) { return value; } } @@ -139,11 +138,11 @@ impl AppState { Char('?') if !glob_focussed => self.toggle_help_pane(window), Ctrl('c') if !glob_focussed => { return Ok(ProcessingResult::ExitRequested(WalkResult { - num_errors: tree_view.traversal().io_errors, + num_errors: tree_view.traversal.io_errors, })) } Char('q') if !glob_focussed => { - if let Some(value) = self.handle_quit(tree_view.as_mut(), window) { + if let Some(value) = self.handle_quit(&mut tree_view, window) { return value; } } @@ -154,13 +153,9 @@ impl AppState { if !handled { match self.focussed { - Mark => self.dispatch_to_mark_pane( - key, - window, - tree_view.as_mut(), - *display, - terminal, - ), + Mark => { + self.dispatch_to_mark_pane(key, window, &mut tree_view, *display, terminal) + } Help => { window .help_pane @@ -172,39 +167,39 @@ impl AppState { let glob_pane = window.glob_pane.as_mut().expect("glob pane"); match key { Char('\n') => { - self.search_glob_pattern(tree_view.as_mut(), &glob_pane.input) + self.search_glob_pattern(&mut tree_view, &glob_pane.input) } _ => glob_pane.process_events(key), } } Main => match key { - Char('O') => self.open_that(tree_view.as_ref()), + Char('O') => self.open_that(&tree_view), Char(' ') => self.mark_entry( CursorMode::KeepPosition, MarkEntryMode::Toggle, window, - tree_view.as_ref(), + &tree_view, ), Char('d') => self.mark_entry( CursorMode::Advance, MarkEntryMode::Toggle, window, - tree_view.as_ref(), + &tree_view, ), Char('x') => self.mark_entry( CursorMode::Advance, MarkEntryMode::MarkForDeletion, window, - tree_view.as_ref(), + &tree_view, ), Char('a') => { - self.mark_all_entries(MarkEntryMode::Toggle, window, tree_view.as_ref()) + self.mark_all_entries(MarkEntryMode::Toggle, window, &tree_view) } Char('u') | Char('h') | Backspace | Left => { - self.exit_node_with_traversal(tree_view.as_ref()) + self.exit_node_with_traversal(&tree_view) } Char('o') | Char('l') | Char('\n') | Right => { - self.enter_node_with_traversal(tree_view.as_ref()) + self.enter_node_with_traversal(&tree_view) } Char('H') | Home => self.change_entry_selection(CursorDirection::ToTop), Char('G') | End => self.change_entry_selection(CursorDirection::ToBottom), @@ -214,34 +209,29 @@ impl AppState { Ctrl('d') | PageDown => { self.change_entry_selection(CursorDirection::PageDown) } - Char('s') => self.cycle_sorting(tree_view.as_ref()), - Char('m') => self.cycle_mtime_sorting(tree_view.as_ref()), - Char('c') => self.cycle_count_sorting(tree_view.as_ref()), + Char('s') => self.cycle_sorting(&tree_view), + Char('m') => self.cycle_mtime_sorting(&tree_view), + Char('c') => self.cycle_count_sorting(&tree_view), Char('g') => display.byte_vis.cycle(), _ => {} }, }; } - self.draw(window, tree_view.as_ref(), *display, terminal)?; + self.draw(window, &tree_view, *display, terminal)?; } Ok(ProcessingResult::Finished(WalkResult { num_errors: traversal.io_errors, })) } - fn tree_view<'a>(&mut self, traversal: &'a mut Traversal) -> Box { - let tree_view: Box = if let Some(glob_source) = &self.glob_navigation { - Box::new(GlobTreeView { - traversal, - glob_tree_root: glob_source.tree_root, - }) - } else { - Box::new(NormalTreeView { traversal }) - }; - tree_view + fn tree_view<'a>(&mut self, traversal: &'a mut Traversal) -> TreeView<'a> { + TreeView { + traversal, + glob_tree_root: self.glob_navigation.as_ref().map(|n| n.tree_root), + } } - fn search_glob_pattern(&mut self, tree_view: &mut dyn TreeView, glob_pattern: &str) { + fn search_glob_pattern(&mut self, tree_view: &mut TreeView, glob_pattern: &str) { use FocussedPane::*; match glob_search(tree_view.tree(), self.navigation.view_root, glob_pattern) { Ok(matches) if matches.is_empty() => { @@ -249,10 +239,10 @@ impl AppState { } Ok(matches) => { if let Some(glob_source) = &self.glob_navigation { - tree_view.tree_as_mut().remove_node(glob_source.tree_root); + tree_view.tree_mut().remove_node(glob_source.tree_root); } - let tree_root = tree_view.tree_as_mut().add_node(EntryData::default()); + let tree_root = tree_view.tree_mut().add_node(EntryData::default()); let glob_source = Navigation { tree_root, view_root: tree_root, @@ -262,12 +252,12 @@ impl AppState { self.glob_navigation = Some(glob_source); for idx in matches { - tree_view.tree_as_mut().add_edge(tree_root, idx, ()); + tree_view.tree_mut().add_edge(tree_root, idx, ()); } - let glob_tree_view = GlobTreeView { - traversal: tree_view.traversal_as_mut(), - glob_tree_root: tree_root, + let glob_tree_view = TreeView { + traversal: tree_view.traversal, + glob_tree_root: Some(tree_root), }; let new_entries = glob_tree_view.sorted_entries(tree_root, self.sorting); @@ -285,7 +275,7 @@ impl AppState { fn handle_quit( &mut self, - tree_view: &mut dyn TreeView, + tree_view: &mut TreeView<'_>, window: &mut MainWindow, ) -> Option> { use FocussedPane::*; @@ -295,7 +285,7 @@ impl AppState { self.handle_glob_quit(tree_view, window); } else { return Some(Ok(ProcessingResult::ExitRequested(WalkResult { - num_errors: tree_view.traversal().io_errors, + num_errors: tree_view.traversal.io_errors, }))); } } @@ -311,18 +301,16 @@ impl AppState { None } - fn handle_glob_quit(&mut self, tree_view: &mut dyn TreeView, window: &mut MainWindow) { + fn handle_glob_quit(&mut self, tree_view: &mut TreeView<'_>, window: &mut MainWindow) { use FocussedPane::*; self.focussed = Main; if let Some(glob_source) = &self.glob_navigation { - tree_view.tree_as_mut().remove_node(glob_source.tree_root); + tree_view.tree_mut().remove_node(glob_source.tree_root); } self.glob_navigation = None; window.glob_pane = None; - let tree_view = NormalTreeView { - traversal: tree_view.traversal_as_mut(), - }; + tree_view.glob_tree_root.take(); self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting); } } diff --git a/src/interactive/app/handlers.rs b/src/interactive/app/handlers.rs index 62fff820..6e12c3de 100644 --- a/src/interactive/app/handlers.rs +++ b/src/interactive/app/handlers.rs @@ -46,20 +46,20 @@ impl CursorDirection { } impl AppState { - pub fn open_that(&self, tree_view: &dyn TreeView) { + pub fn open_that(&self, tree_view: &TreeView<'_>) { if let Some(idx) = self.navigation().selected { open::that(tree_view.path_of(idx)).ok(); } } - pub fn exit_node_with_traversal(&mut self, tree_view: &dyn TreeView) { + pub fn exit_node_with_traversal(&mut self, tree_view: &TreeView<'_>) { let entries = self.entries_for_exit_node(tree_view); self.exit_node(entries); } fn entries_for_exit_node( &self, - tree_view: &dyn TreeView, + tree_view: &TreeView<'_>, ) -> Option<(TreeIndex, Vec)> { tree_view .view_parent_of(self.navigation().view_root) @@ -83,7 +83,7 @@ impl AppState { fn entries_for_enter_node( &self, - tree_view: &dyn TreeView, + tree_view: &TreeView<'_>, ) -> Option<(TreeIndex, Vec)> { self.navigation().selected.map(|previously_selected| { ( @@ -93,7 +93,7 @@ impl AppState { }) } - pub fn enter_node_with_traversal(&mut self, tree_view: &dyn TreeView) { + pub fn enter_node_with_traversal(&mut self, tree_view: &TreeView<'_>) { let new_entries = self.entries_for_enter_node(tree_view); self.enter_node(new_entries) } @@ -102,7 +102,7 @@ impl AppState { if let Some((previously_selected, new_entries)) = entries_at_selected { match self .navigation() - .get_previously_selected_index(previously_selected, &new_entries) + .previously_selected_index(previously_selected, &new_entries) { Some(selected) => { self.navigation_mut() @@ -115,21 +115,21 @@ impl AppState { } pub fn change_entry_selection(&mut self, direction: CursorDirection) { - let next_index = self.navigation().get_next_index(direction, &self.entries); + let next_index = self.navigation().next_index(direction, &self.entries); self.navigation_mut().select(next_index); } - pub fn cycle_sorting(&mut self, tree_view: &dyn TreeView) { + pub fn cycle_sorting(&mut self, tree_view: &TreeView<'_>) { self.sorting.toggle_size(); self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting); } - pub fn cycle_mtime_sorting(&mut self, tree_view: &dyn TreeView) { + pub fn cycle_mtime_sorting(&mut self, tree_view: &TreeView<'_>) { self.sorting.toggle_mtime(); self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting); } - pub fn cycle_count_sorting(&mut self, tree_view: &dyn TreeView) { + pub fn cycle_count_sorting(&mut self, tree_view: &TreeView<'_>) { self.sorting.toggle_count(); self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting); } @@ -197,7 +197,7 @@ impl AppState { &mut self, key: Key, window: &mut MainWindow, - tree_view: &mut dyn TreeView, + tree_view: &mut TreeView<'_>, display: DisplayOptions, terminal: &mut Terminal, ) where @@ -259,7 +259,7 @@ impl AppState { pub fn delete_entry( &mut self, index: TreeIndex, - tree_view: &mut dyn TreeView, + tree_view: &mut TreeView<'_>, ) -> Result { let mut entries_deleted = 0; if tree_view.exists(index) { @@ -274,7 +274,7 @@ impl AppState { pub fn trash_entry( &mut self, index: TreeIndex, - tree_view: &mut dyn TreeView, + tree_view: &mut TreeView<'_>, ) -> Result { let mut entries_deleted = 0; if tree_view.exists(index) { @@ -290,7 +290,7 @@ impl AppState { pub fn delete_entries_in_traversal( &mut self, index: TreeIndex, - tree_view: &mut dyn TreeView, + tree_view: &mut TreeView<'_>, ) -> usize { let parent_idx = tree_view .fs_parent_of(index) @@ -312,13 +312,12 @@ impl AppState { let idx = self.entries.first().map(|e| e.index); self.navigation_mut().select(idx); } - tree_view.recompute_sizes_recursively(parent_idx); entries_deleted } - fn go_to_root(&mut self, tree_view: &dyn TreeView) { + fn go_to_root(&mut self, tree_view: &TreeView<'_>) { let root = self.navigation().tree_root; let entries = tree_view.sorted_entries(root, self.sorting); self.navigation_mut().exit_node(root, &entries); @@ -334,7 +333,7 @@ impl AppState { index: TreeIndex, mode: MarkEntryMode, window: &mut MainWindow, - tree_view: &dyn TreeView, + tree_view: &TreeView<'_>, ) { let is_dir = self .entries @@ -359,7 +358,7 @@ impl AppState { cursor: CursorMode, mode: MarkEntryMode, window: &mut MainWindow, - tree_view: &dyn TreeView, + tree_view: &TreeView<'_>, ) { if let Some(index) = self.navigation().selected { self.mark_entry_by_index(index, mode, window, tree_view); @@ -373,7 +372,7 @@ impl AppState { &mut self, mode: MarkEntryMode, window: &mut MainWindow, - tree_view: &dyn TreeView, + tree_view: &TreeView<'_>, ) { for index in self.entries.iter().map(|e| e.index).collect::>() { self.mark_entry_by_index(index, mode, window, tree_view); diff --git a/src/interactive/app/navigation.rs b/src/interactive/app/navigation.rs index 8bb2e3e8..f5bec2df 100644 --- a/src/interactive/app/navigation.rs +++ b/src/interactive/app/navigation.rs @@ -13,7 +13,7 @@ pub struct Navigation { } impl Navigation { - pub fn get_previously_selected_index( + pub fn previously_selected_index( &self, view_root: TreeIndex, entries: &[EntryDataBundle], @@ -47,7 +47,7 @@ impl Navigation { .or_else(|| entries.first().map(|b| b.index)); } - pub fn get_next_index( + pub fn next_index( &self, direction: CursorDirection, entries: &[EntryDataBundle], diff --git a/src/interactive/app/tree_view.rs b/src/interactive/app/tree_view.rs index be6f5293..74ec4faa 100644 --- a/src/interactive/app/tree_view.rs +++ b/src/interactive/app/tree_view.rs @@ -4,173 +4,89 @@ use dua::traverse::{EntryData, Traversal, Tree, TreeIndex}; use petgraph::{visit::Bfs, Direction}; use std::path::{Path, PathBuf}; -pub trait TreeView { - fn traversal(&self) -> &Traversal; - fn traversal_as_mut(&mut self) -> &mut Traversal; - - fn tree(&self) -> &Tree; - fn tree_as_mut(&mut self) -> &mut Tree; - - fn fs_parent_of(&self, idx: TreeIndex) -> Option; - fn view_parent_of(&self, idx: TreeIndex) -> Option { - self.fs_parent_of(idx) - } - - fn exists(&self, idx: TreeIndex) -> bool { - self.tree().node_weight(idx).is_some() - } - - fn path_of(&self, node_idx: TreeIndex) -> PathBuf { - path_of(self.tree(), node_idx, None) - } - - fn sorted_entries(&self, view_root: TreeIndex, sorting: SortMode) -> Vec { - sorted_entries(self.tree(), view_root, sorting, None) - } - - fn current_path(&self, view_root: TreeIndex) -> String { - current_path(self.tree(), view_root, None) - } - - fn remove_entries(&mut self, index: TreeIndex) -> usize; - fn recompute_sizes_recursively(&mut self, index: TreeIndex); -} - -pub struct NormalTreeView<'a> { +pub struct TreeView<'a> { pub traversal: &'a mut Traversal, + pub glob_tree_root: Option, } -impl<'a> TreeView for NormalTreeView<'a> { - fn traversal(&self) -> &Traversal { - self.traversal - } - - fn traversal_as_mut(&mut self) -> &mut Traversal { - self.traversal - } - - fn tree(&self) -> &Tree { +impl TreeView<'_> { + pub fn tree(&self) -> &Tree { &self.traversal.tree } - fn tree_as_mut(&mut self) -> &mut Tree { + pub fn tree_mut(&mut self) -> &mut Tree { &mut self.traversal.tree } - fn fs_parent_of(&self, idx: TreeIndex) -> Option { + pub fn fs_parent_of(&self, idx: TreeIndex) -> Option { self.traversal .tree - .neighbors_directed(idx, Direction::Incoming) - .next() + .neighbors_directed(idx, petgraph::Incoming) + .find(|idx| match self.glob_tree_root { + None => true, + Some(glob_root) => *idx != glob_root, + }) } - fn remove_entries(&mut self, index: TreeIndex) -> usize { - remove_entries(self.traversal, index) - } - - fn recompute_sizes_recursively(&mut self, mut index: TreeIndex) { - loop { - self.traversal - .tree - .node_weight_mut(index) - .expect("valid index") - .size = neighbours_size(self.tree(), index); - - match self.fs_parent_of(index) { - None => break, - Some(parent) => index = parent, - } - } - self.traversal.total_bytes = self - .traversal - .tree - .node_weight(self.traversal.root_index) - .map(|w| w.size); - } -} - -pub struct GlobTreeView<'a> { - pub traversal: &'a mut Traversal, - pub glob_tree_root: TreeIndex, -} - -impl<'a> TreeView for GlobTreeView<'a> { - fn traversal(&self) -> &Traversal { - self.traversal - } - - fn traversal_as_mut(&mut self) -> &mut Traversal { - self.traversal - } - - fn tree(&self) -> &Tree { - &self.traversal.tree - } - - fn tree_as_mut(&mut self) -> &mut Tree { - &mut self.traversal.tree - } - - fn fs_parent_of(&self, idx: TreeIndex) -> Option { - let iter = self - .traversal - .tree - .neighbors_directed(idx, petgraph::Incoming); - - let mut parent = None; - for parent_idx in iter { - if parent_idx == self.glob_tree_root { - continue; - } - parent = Some(parent_idx); - } - parent - } - - fn view_parent_of(&self, idx: TreeIndex) -> Option { - let iter = self + pub fn view_parent_of(&self, idx: TreeIndex) -> Option { + let mut iter = self .traversal .tree .neighbors_directed(idx, petgraph::Incoming); - - let mut parent = None; - for parent_idx in iter { - parent = Some(parent_idx); - if parent_idx == self.glob_tree_root { - break; - } + match self.glob_tree_root { + None => iter.next(), + Some(glob_root) => iter + .clone() + .find(|idx| *idx == glob_root) + .or_else(|| iter.next()), } - parent } - fn path_of(&self, node_idx: TreeIndex) -> PathBuf { - path_of(&self.traversal.tree, node_idx, Some(self.glob_tree_root)) + pub fn path_of(&self, node_idx: TreeIndex) -> PathBuf { + path_of(&self.traversal.tree, node_idx, self.glob_tree_root) } - fn sorted_entries(&self, view_root: TreeIndex, sorting: SortMode) -> Vec { + pub fn sorted_entries(&self, view_root: TreeIndex, sorting: SortMode) -> Vec { sorted_entries( &self.traversal.tree, view_root, sorting, - Some(self.glob_tree_root), + self.glob_tree_root, ) } - fn current_path(&self, view_root: TreeIndex) -> String { - current_path(&self.traversal.tree, view_root, Some(self.glob_tree_root)) + pub fn current_path(&self, view_root: TreeIndex) -> String { + current_path(&self.traversal.tree, view_root, self.glob_tree_root) + } + + pub fn remove_entries(&mut self, index: TreeIndex) -> usize { + let mut entries_deleted = 0; + let mut bfs = Bfs::new(self.tree(), index); + + while let Some(nx) = bfs.next(&self.tree()) { + self.tree_mut().remove_node(nx); + self.traversal.entries_traversed -= 1; + entries_deleted += 1; + } + entries_deleted } - fn remove_entries(&mut self, index: TreeIndex) -> usize { - remove_entries(self.traversal, index) + pub fn exists(&self, idx: TreeIndex) -> bool { + self.tree().node_weight(idx).is_some() } - fn recompute_sizes_recursively(&mut self, mut index: TreeIndex) { + pub fn recompute_sizes_recursively(&mut self, mut index: TreeIndex) { loop { + let size_of_children = self + .tree() + .neighbors_directed(index, Direction::Outgoing) + .filter_map(|idx| self.tree().node_weight(idx).map(|w| w.size)) + .sum(); self.traversal .tree .node_weight_mut(index) .expect("valid index") - .size = neighbours_size(self.tree(), index); + .size = size_of_children; match self.fs_parent_of(index) { None => break, @@ -178,19 +94,12 @@ impl<'a> TreeView for GlobTreeView<'a> { } } self.traversal.total_bytes = self - .traversal - .tree + .tree() .node_weight(self.traversal.root_index) .map(|w| w.size); } } -fn neighbours_size(tree: &Tree, index: TreeIndex) -> u128 { - tree.neighbors_directed(index, Direction::Outgoing) - .filter_map(|idx| tree.node_weight(idx).map(|w| w.size)) - .sum() -} - fn current_path( tree: &petgraph::stable_graph::StableGraph, root: petgraph::stable_graph::NodeIndex, @@ -204,16 +113,3 @@ fn current_path( p => p, } } - -fn remove_entries(traversal: &mut Traversal, index: TreeIndex) -> usize { - let mut entries_deleted = 0; - - let mut bfs = Bfs::new(&traversal.tree, index); - while let Some(nx) = bfs.next(&traversal.tree) { - traversal.tree.remove_node(nx); - traversal.entries_traversed -= 1; - entries_deleted += 1; - } - - entries_deleted -} diff --git a/src/interactive/widgets/mark.rs b/src/interactive/widgets/mark.rs index 77ce1c86..63ed54f5 100644 --- a/src/interactive/widgets/mark.rs +++ b/src/interactive/widgets/mark.rs @@ -75,7 +75,7 @@ impl MarkPane { pub fn toggle_index( mut self, index: TreeIndex, - tree_view: &dyn TreeView, + tree_view: &TreeView<'_>, is_dir: bool, toggle: bool, ) -> Option {