diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/interactive/app/eventloop.rs | 76 | ||||
-rw-r--r-- | src/interactive/app/handlers.rs | 5 | ||||
-rw-r--r-- | src/interactive/app/state.rs | 10 | ||||
-rw-r--r-- | src/interactive/app/terminal.rs | 2 | ||||
-rw-r--r-- | src/interactive/app/tests/journeys_readonly.rs | 2 | ||||
-rw-r--r-- | src/interactive/app/tree_view.rs | 6 | ||||
-rw-r--r-- | src/interactive/widgets/entries.rs | 26 | ||||
-rw-r--r-- | src/interactive/widgets/footer.rs | 2 | ||||
-rw-r--r-- | src/interactive/widgets/help.rs | 38 |
9 files changed, 104 insertions, 63 deletions
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index 2377cc0..e7c91a4 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -1,3 +1,4 @@ +use crate::interactive::state::FilesystemScan; use crate::interactive::{ app::navigation::Navigation, state::FocussedPane, @@ -64,7 +65,7 @@ impl AppState { } pub fn traverse(&mut self, traversal: &Traversal, input: Vec<PathBuf>) -> Result<()> { - let background_traversal = BackgroundTraversal::start( + let traverasal = BackgroundTraversal::start( traversal.root_index, &self.walk_options, input, @@ -72,7 +73,10 @@ impl AppState { true, )?; self.navigation_mut().view_root = traversal.root_index; - self.active_traversal = Some((background_traversal, None)); + self.scan = Some(FilesystemScan { + active_traversal: traverasal, + previous_selection: None, + }); Ok(()) } @@ -130,7 +134,11 @@ impl AppState { where B: Backend, { - if let Some((active_traversal, selected_name)) = &mut self.active_traversal { + if let Some(FilesystemScan { + active_traversal, + previous_selection, + }) = self.scan.as_mut() + { crossbeam::select! { recv(events) -> event => { let Ok(event) = event else { @@ -153,13 +161,13 @@ impl AppState { if let Some(is_finished) = active_traversal.integrate_traversal_event(traversal, event) { self.stats = active_traversal.stats; - let selected_name = selected_name.clone(); + let previous_selection = previous_selection.clone(); if is_finished { let root_index = active_traversal.root_idx; self.recompute_sizes_recursively(traversal, root_index); - self.active_traversal = None; + self.scan = None; } - self.update_state_during_traversal(traversal, selected_name.as_ref(), is_finished); + self.update_state_during_traversal(traversal, previous_selection.as_ref(), is_finished); self.refresh_screen(window, traversal, display, terminal)?; }; } @@ -182,16 +190,21 @@ impl AppState { fn update_state_during_traversal( &mut self, traversal: &mut Traversal, - selected_name: Option<&PathBuf>, + previous_selection: Option<&(PathBuf, usize)>, is_finished: bool, ) { let tree_view = self.tree_view(traversal); self.entries = tree_view.sorted_entries(self.navigation().view_root, self.sorting); if !self.received_events { - let selected_entry = selected_name - .and_then(|selected_name| self.entries.iter().find(|e| e.name == *selected_name)); - if let Some(selected_entry) = selected_entry { + let previously_selected_entry = + previous_selection.and_then(|(selected_name, selected_idx)| { + self.entries + .iter() + .find(|e| e.name == *selected_name) + .or_else(|| self.entries.get(*selected_idx)) + }); + if let Some(selected_entry) = previously_selected_entry { self.navigation_mut().selected = Some(selected_entry.index); } else if is_finished { self.navigation_mut().selected = self.entries.first().map(|b| b.index); @@ -239,8 +252,12 @@ impl AppState { Tab => { self.cycle_focus(window); } - Char('/') if !glob_focussed && self.active_traversal.is_none() => { - self.toggle_glob_search(window); + Char('/') if !glob_focussed => { + if self.scan.is_some() { + self.message = Some("glob search disabled during traversal".into()); + } else { + self.toggle_glob_search(window); + } } Char('?') if !glob_focussed => self.toggle_help_pane(window), Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) && !glob_focussed => { @@ -338,18 +355,32 @@ impl AppState { what: Refresh, ) -> anyhow::Result<()> { // If another traversal is already running do not do anything. - if self.active_traversal.is_some() { + if self.scan.is_some() { + self.message = Some("Traversal already running".into()); return Ok(()); } - // If we are displaing root of the glob search results then cancel the search. + let previous_selection = self.navigation().selected.and_then(|sel_index| { + tree.tree().node_weight(sel_index).map(|w| { + ( + w.name.clone(), + self.entries + .iter() + .enumerate() + .find_map(|(idx, e)| (e.index == sel_index).then_some(idx)) + .expect("selected item is always in entries"), + ) + }) + }); + + // If we are displaying the root of the glob search results then cancel the search. if let Some(glob_tree_root) = tree.glob_tree_root { if glob_tree_root == self.navigation().view_root { self.quit_glob_mode(tree, window) } } - let (remove_index, skip_root, index, parent_index) = match what { + let (remove_root_node, skip_root, index, parent_index) = match what { Refresh::Selected => { let Some(selected) = self.navigation().selected else { return Ok(()); @@ -367,31 +398,26 @@ impl AppState { ), }; - let selected_name = self - .navigation() - .selected - .and_then(|w| tree.tree().node_weight(w).map(|w| w.name.clone())); - let mut path = tree.path_of(index); if path.to_str() == Some("") { path = PathBuf::from("."); } - tree.remove_entries(index, remove_index); + tree.remove_entries(index, remove_root_node); tree.recompute_sizes_recursively(parent_index); self.entries = tree.sorted_entries(self.navigation().view_root, self.sorting); self.navigation_mut().selected = self.entries.first().map(|e| e.index); - self.active_traversal = Some(( - BackgroundTraversal::start( + self.scan = Some(FilesystemScan { + active_traversal: BackgroundTraversal::start( parent_index, &self.walk_options, vec![path], skip_root, false, )?, - selected_name, - )); + previous_selection, + }); self.received_events = false; Ok(()) diff --git a/src/interactive/app/handlers.rs b/src/interactive/app/handlers.rs index 5601cf1..c320c59 100644 --- a/src/interactive/app/handlers.rs +++ b/src/interactive/app/handlers.rs @@ -162,7 +162,7 @@ impl AppState { } pub fn reset_message(&mut self) { - if self.active_traversal.is_some() { + if self.scan.is_some() { self.message = Some("-> scanning <-".into()); } else { self.message = None; @@ -312,7 +312,8 @@ impl AppState { let parent_idx = tree_view .fs_parent_of(index) .expect("us being unable to delete the root index"); - let entries_deleted = tree_view.remove_entries(index, true); + let entries_deleted = + tree_view.remove_entries(index, true /* remove node at `index` */); if !tree_view.exists(self.navigation().view_root) { self.go_to_root(tree_view); diff --git a/src/interactive/app/state.rs b/src/interactive/app/state.rs index 47f681b..aee7542 100644 --- a/src/interactive/app/state.rs +++ b/src/interactive/app/state.rs @@ -24,6 +24,12 @@ pub struct Cursor { pub y: u16, } +pub(crate) struct FilesystemScan { + pub active_traversal: BackgroundTraversal, + /// The selected item prior to starting the traversal, if available, based on its name or index into [`AppState::entries`]. + pub previous_selection: Option<(PathBuf, usize)>, +} + pub struct AppState { pub navigation: Navigation, pub glob_navigation: Option<Navigation>, @@ -33,7 +39,7 @@ pub struct AppState { pub message: Option<String>, pub focussed: FocussedPane, pub received_events: bool, - pub active_traversal: Option<(BackgroundTraversal, Option<PathBuf>)>, + pub scan: Option<FilesystemScan>, pub stats: TraversalStats, pub walk_options: WalkOptions, } @@ -49,7 +55,7 @@ impl AppState { message: None, focussed: Default::default(), received_events: false, - active_traversal: None, + scan: None, stats: TraversalStats::default(), walk_options, } diff --git a/src/interactive/app/terminal.rs b/src/interactive/app/terminal.rs index d2ae955..f29834f 100644 --- a/src/interactive/app/terminal.rs +++ b/src/interactive/app/terminal.rs @@ -99,7 +99,7 @@ mod tests { where B: Backend, { - while self.state.active_traversal.is_some() { + while self.state.scan.is_some() { if let Some(res) = self.state.process_event( &mut self.window, &mut self.traversal, diff --git a/src/interactive/app/tests/journeys_readonly.rs b/src/interactive/app/tests/journeys_readonly.rs index b6dc579..f307294 100644 --- a/src/interactive/app/tests/journeys_readonly.rs +++ b/src/interactive/app/tests/journeys_readonly.rs @@ -40,7 +40,7 @@ fn simple_user_journey_read_only() -> Result<()> { ); assert!( - app.state.active_traversal.is_none(), + app.state.scan.is_none(), "it will not think it is still scanning as there is no traversal" ); diff --git a/src/interactive/app/tree_view.rs b/src/interactive/app/tree_view.rs index 612582b..8a80cf0 100644 --- a/src/interactive/app/tree_view.rs +++ b/src/interactive/app/tree_view.rs @@ -59,12 +59,12 @@ impl TreeView<'_> { current_path(&self.traversal.tree, view_root, self.glob_tree_root) } - pub fn remove_entries(&mut self, index: TreeIndex, remove_index: bool) -> usize { + pub fn remove_entries(&mut self, root_index: TreeIndex, remove_root_node: bool) -> usize { let mut entries_deleted = 0; - let mut bfs = Bfs::new(self.tree(), index); + let mut bfs = Bfs::new(self.tree(), root_index); while let Some(nx) = bfs.next(&self.tree()) { - if nx == index && !remove_index { + if nx == root_index && !remove_root_node { continue; } self.tree_mut().remove_node(nx); diff --git a/src/interactive/widgets/entries.rs b/src/interactive/widgets/entries.rs index 4005739..c4794f4 100644 --- a/src/interactive/widgets/entries.rs +++ b/src/interactive/widgets/entries.rs @@ -63,12 +63,18 @@ impl Entries { let list = &mut self.list; let total: u128 = entries.iter().map(|b| b.size).sum(); - let (item_count, item_size): (u64, u128) = entries + let (recursive_item_count, item_size): (u64, u128) = entries .iter() .map(|f| (f.entry_count.unwrap_or(1), f.size)) .reduce(|a, b| (a.0 + b.0, a.1 + b.1)) .unwrap_or_default(); - let title = title(current_path, item_count, *display, item_size); + let title = title( + current_path, + entries.len(), + recursive_item_count, + *display, + item_size, + ); let title_block = title_block(&title, *border_style); let inner_area = title_block.inner(area); let entry_in_view = entry_in_view(*selected, entries); @@ -155,15 +161,17 @@ fn title_block(title: &str, border_style: Style) -> Block<'_> { .borders(Borders::ALL) } -fn title(current_path: &str, item_count: u64, display: DisplayOptions, size: u128) -> String { +fn title( + current_path: &str, + item_count: usize, + recursive_item_count: u64, + display: DisplayOptions, + size: u128, +) -> String { format!( - " {} ({} file{}, {}) ", + " {} ({item_count} visible, {} total, {}) ", current_path, - COUNT.format(item_count as f64), - match item_count { - 1 => "", - _ => "s", - }, + COUNT.format(recursive_item_count as f64), display.byte_format.display(size) ) } diff --git a/src/interactive/widgets/footer.rs b/src/interactive/widgets/footer.rs index 98251c7..80bd352 100644 --- a/src/interactive/widgets/footer.rs +++ b/src/interactive/widgets/footer.rs @@ -37,7 +37,7 @@ impl Footer { let spans = vec![ Span::from(format!( - "Sort mode: {} Total disk usage: {} Processed {} items {progress} ", + "Sort mode: {} Total disk usage: {} Processed {} entries {progress} ", match sort_mode { SortMode::SizeAscending => "size ascending", SortMode::SizeDescending => "size descending", diff --git a/src/interactive/widgets/help.rs b/src/interactive/widgets/help.rs index d5107dd..8cf83f6 100644 --- a/src/interactive/widgets/help.rs +++ b/src/interactive/widgets/help.rs @@ -123,8 +123,8 @@ impl HelpPane { } title("Navigation"); { - hotkey("j/<Down>", "Move down 1 item.", None); - hotkey("k/<Up>", "Move up 1 item.", None); + hotkey("j/<Down>", "Move down 1 entry.", None); + hotkey("k/<Up>", "Move up 1 entry.", None); hotkey("o/l/<Enter>", "Descent into the selected directory.", None); hotkey("<Right>", "^", None); hotkey( @@ -133,9 +133,9 @@ impl HelpPane { None, ); hotkey("<Backspace>", "^", None); - hotkey("Ctrl + d", "Move down 10 items.", None); + hotkey("Ctrl + d", "Move down 10 entries.", None); hotkey("<Page Down>", "^", None); - hotkey("Ctrl + u", "Move up 10 items.", None); + hotkey("Ctrl + u", "Move up 10 entries.", None); hotkey("<Page Up>", "^", None); hotkey("H/<Home>", "Move to the top of the list.", None); hotkey("G/<End>", "Move to the bottom of the list.", None); @@ -150,8 +150,8 @@ impl HelpPane { None, ); hotkey("M", "Show/hide modified time.", None); - hotkey("c", "Toggle sort by items descending/ascending.", None); - hotkey("C", "Show/hide item count.", None); + hotkey("c", "Toggle sort by entries descending/ascending.", None); + hotkey("C", "Show/hide entry count.", None); hotkey( "g/S", "Cycle through percentage display and bar options.", @@ -163,48 +163,48 @@ impl HelpPane { { hotkey( "Shift + o", - "Open the selected item with the associated program.", + "Open the selected entry with the associated program.", None, ); hotkey( "d", - "Toggle the currently selected item and move down.", + "Toggle the currently selected entry and move down.", None, ); hotkey( "x", - "Mark the currently selected item for deletion and move down.", + "Mark the currently selected entry for deletion and move down.", None, ); - hotkey("<Space>", "Toggle the currently selected item.", None); - hotkey("a", "Toggle all items.", None); + hotkey("<Space>", "Toggle the currently selected entry.", None); + hotkey("a", "Toggle all entries.", None); hotkey( "/", "Git-style glob search, case-insensitive.", Some("Search starts from the current directory."), ); - hotkey("r", "Refresh only the selected item.", None); - hotkey("R", "Refresh all items in the current view.", None); + hotkey("r", "Refresh only the selected entry.", None); + hotkey("R", "Refresh all entries in the current view.", None); spacer(); } - title("Mark items pane"); + title("Mark entries pane"); { hotkey( "x/d/<Space>", - "Remove the selected item from the list.", + "Remove the selected entry from the list.", None, ); - hotkey("a", "Remove all items from the list.", None); + hotkey("a", "Remove all entries from the list.", None); hotkey( "Ctrl + r", - "Permanently delete all marked items without prompt.", + "Permanently delete all marked entries without prompt.", Some("This operation cannot be undone!"), ); #[cfg(feature = "trash-move")] hotkey( "Ctrl + t", - "Move all marked items to the trash bin.", - Some("The items can be restored from the trash bin."), + "Move all marked entries to the trash bin.", + Some("The entries can be restored from the trash bin."), ); spacer(); } |