diff options
author | Sebastian Thiel <sebastian.thiel@icloud.com> | 2024-01-21 17:25:07 +0100 |
---|---|---|
committer | Sebastian Thiel <sebastian.thiel@icloud.com> | 2024-01-21 17:25:07 +0100 |
commit | f70d1a8e6ace812a7949cd7d0299507b71306d48 (patch) | |
tree | cee9e52898f075bc7857b35a981843dfca0c624f | |
parent | 322eeb1aa07dacdc82e147bae64f8bfd4d758e1d (diff) | |
parent | 9d976d0d76fcf45d1e0672bc5c1533b000a46ebf (diff) |
fix: Explicit refreshes with 'r and 'R' now work with multiple root paths as will.
This can happen in cases of `dua i root-a root-b` for instance.
-rw-r--r-- | src/interactive/app/eventloop.rs | 67 | ||||
-rw-r--r-- | src/interactive/app/state.rs | 5 | ||||
-rw-r--r-- | src/interactive/app/terminal.rs | 13 | ||||
-rw-r--r-- | src/interactive/app/tests/journeys_readonly.rs | 86 | ||||
-rw-r--r-- | src/interactive/app/tests/utils.rs | 7 | ||||
-rw-r--r-- | src/main.rs | 9 |
6 files changed, 156 insertions, 31 deletions
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index e7c91a4..f03c55b 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -64,11 +64,11 @@ impl AppState { result } - pub fn traverse(&mut self, traversal: &Traversal, input: Vec<PathBuf>) -> Result<()> { + pub fn traverse(&mut self, traversal: &Traversal) -> Result<()> { let traverasal = BackgroundTraversal::start( traversal.root_index, &self.walk_options, - input, + self.root_paths.clone(), false, true, )?; @@ -380,7 +380,7 @@ impl AppState { } } - let (remove_root_node, skip_root, index, parent_index) = match what { + let (paths, remove_root_node, skip_root, use_root_path, index, parent_index) = match what { Refresh::Selected => { let Some(selected) = self.navigation().selected else { return Ok(()); @@ -388,20 +388,55 @@ impl AppState { let parent_index = tree .fs_parent_of(selected) .expect("there is always a parent to a selection"); - (true, false, selected, parent_index) + + let mut path = tree.path_of(selected); + if path.to_str() == Some("") { + path = PathBuf::from("."); + } + + let (paths, use_root_path, skip_root) = if self.navigation().view_root + == tree.traversal.root_index + && self.root_paths.len() > 1 + { + (vec![path], true, false) + } else { + (vec![path], false, false) + }; + + ( + paths, + true, + skip_root, + use_root_path, + selected, + parent_index, + ) + } + Refresh::AllInView => { + let (paths, use_root_path, skip_root) = if self.navigation().view_root + == tree.traversal.root_index + && self.root_paths.len() > 1 + { + (self.root_paths.clone(), true, false) + } else { + let mut path = tree.path_of(self.navigation().view_root); + if path.to_str() == Some("") { + path = PathBuf::from("."); + } + (vec![path], false, true) + }; + + ( + paths, + false, + skip_root, + use_root_path, + self.navigation().view_root, + self.navigation().view_root, + ) } - Refresh::AllInView => ( - false, - true, - self.navigation().view_root, - self.navigation().view_root, - ), }; - let mut path = tree.path_of(index); - if path.to_str() == Some("") { - path = PathBuf::from("."); - } tree.remove_entries(index, remove_root_node); tree.recompute_sizes_recursively(parent_index); @@ -412,9 +447,9 @@ impl AppState { active_traversal: BackgroundTraversal::start( parent_index, &self.walk_options, - vec![path], + paths, skip_root, - false, + use_root_path, )?, previous_selection, }); diff --git a/src/interactive/app/state.rs b/src/interactive/app/state.rs index aee7542..8762b93 100644 --- a/src/interactive/app/state.rs +++ b/src/interactive/app/state.rs @@ -42,10 +42,12 @@ pub struct AppState { pub scan: Option<FilesystemScan>, pub stats: TraversalStats, pub walk_options: WalkOptions, + /// The paths used in the initial traversal, at least 1. + pub root_paths: Vec<PathBuf>, } impl AppState { - pub fn new(walk_options: WalkOptions) -> Self { + pub fn new(walk_options: WalkOptions, input: Vec<PathBuf>) -> Self { AppState { navigation: Default::default(), glob_navigation: None, @@ -58,6 +60,7 @@ impl AppState { scan: None, stats: TraversalStats::default(), walk_options, + root_paths: input, } } } diff --git a/src/interactive/app/terminal.rs b/src/interactive/app/terminal.rs index f29834f..2a8deb5 100644 --- a/src/interactive/app/terminal.rs +++ b/src/interactive/app/terminal.rs @@ -28,6 +28,7 @@ impl TerminalApp { terminal: &mut Terminal<B>, walk_options: WalkOptions, byte_format: ByteFormat, + input: Vec<PathBuf>, ) -> Result<TerminalApp> where B: Backend, @@ -38,7 +39,7 @@ impl TerminalApp { let display = DisplayOptions::new(byte_format); let window = MainWindow::default(); - let mut state = AppState::new(walk_options); + let mut state = AppState::new(walk_options, input); let traversal = Traversal::new(); let stats = TraversalStats::default(); @@ -61,8 +62,8 @@ impl TerminalApp { Ok(app) } - pub fn traverse(&mut self, input: Vec<PathBuf>) -> Result<()> { - self.state.traverse(&self.traversal, input)?; + pub fn traverse(&mut self) -> Result<()> { + self.state.traverse(&self.traversal)?; Ok(()) } @@ -100,15 +101,13 @@ mod tests { B: Backend, { while self.state.scan.is_some() { - if let Some(res) = self.state.process_event( + self.state.process_event( &mut self.window, &mut self.traversal, &mut self.display, terminal, &events, - )? { - return Ok(res); - } + )?; } Ok(WalkResult { num_errors: self.stats.io_errors, diff --git a/src/interactive/app/tests/journeys_readonly.rs b/src/interactive/app/tests/journeys_readonly.rs index f307294..cfa4375 100644 --- a/src/interactive/app/tests/journeys_readonly.rs +++ b/src/interactive/app/tests/journeys_readonly.rs @@ -1,9 +1,11 @@ use anyhow::Result; -use crosstermion::crossterm::event::KeyCode; +use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use crosstermion::input::Event; use pretty_assertions::assert_eq; use std::ffi::OsString; -use crate::interactive::app::tests::utils::into_codes; +use crate::interactive::app::tests::utils::{into_codes, into_events}; +use crate::interactive::widgets::Column; use crate::interactive::{ app::tests::{ utils::{ @@ -129,6 +131,86 @@ fn simple_user_journey_read_only() -> Result<()> { ); } + // Columns + { + app.process_events(&mut terminal, into_codes("C"))?; + assert!( + app.state.show_columns.contains(&Column::Count), + "hit the C key to show the entry count column" + ); + + app.process_events(&mut terminal, into_codes("C"))?; + assert!( + !app.state.show_columns.contains(&Column::Count), + "when hitting the C key again it hides the entry count column" + ); + + app.process_events(&mut terminal, into_codes("M"))?; + assert!( + app.state.show_columns.contains(&Column::MTime), + "hit the M key to show the entry count column" + ); + + app.process_events(&mut terminal, into_codes("M"))?; + assert!( + !app.state.show_columns.contains(&Column::MTime), + "when hitting the M key again it hides the entry count column" + ); + } + + // Glob pane open/close + { + app.process_events(&mut terminal, into_codes("/"))?; + assert!(app.window.glob_pane.is_some(), "'/' shows the glob pane"); + + app.process_events( + &mut terminal, + into_events([Event::Key(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE))]), + )?; + assert!(app.window.glob_pane.is_none(), "ESC closes the glob pane"); + } + + // explicit full refresh + { + assert!(app.state.scan.is_none(), "no refresh in progress"); + + app.process_events(&mut terminal, into_codes("R"))?; + assert!( + app.state.scan.is_some(), + "'R' refreshes all entries in the view" + ); + + app.run_until_traversed(&mut terminal, into_codes(""))?; + assert!(app.state.scan.is_none(), "refresh should finish eventually"); + } + + // explicit partial refresh + { + assert!(app.state.scan.is_none(), "no refresh in progress"); + + app.process_events(&mut terminal, into_codes("j"))?; + assert_eq!( + node_by_name(&app, fixture_str(long_root)), + node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), + "it moves the cursor down and selects the next item based on the current sort mode" + ); + + app.process_events(&mut terminal, into_codes("r"))?; + assert!( + app.state.scan.is_some(), + "'r' refreshes all entries in the view" + ); + + app.run_until_traversed(&mut terminal, into_events([]))?; + assert!(app.state.scan.is_none(), "Refresh should finish"); + + assert_eq!( + node_by_name(&app, fixture_str(long_root)), + node_by_index(&app, *app.state.navigation().selected.as_ref().unwrap()), + "previous selection is preserved after refresh" + ); + } + // Entry-Navigation { // when hitting the j key diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs index b075e35..0dfd65a 100644 --- a/src/interactive/app/tests/utils.rs +++ b/src/interactive/app/tests/utils.rs @@ -196,10 +196,11 @@ pub fn initialized_app_and_terminal_with_closure( }; let (_key_send, key_receive) = crossbeam::channel::bounded(0); - let mut app = TerminalApp::initialize(&mut terminal, walk_options, ByteFormat::Metric)?; - let input_paths = fixture_paths.iter().map(|c| convert(c.as_ref())).collect(); - app.traverse(input_paths)?; + + let mut app = + TerminalApp::initialize(&mut terminal, walk_options, ByteFormat::Metric, input_paths)?; + app.traverse()?; app.run_until_traversed(&mut terminal, key_receive)?; Ok((terminal, app)) diff --git a/src/main.rs b/src/main.rs index 105cf58..787d28e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,8 +77,13 @@ fn main() -> Result<()> { .with_context(|| "Could not instantiate terminal")?; let keys_rx = input_channel(); - let mut app = TerminalApp::initialize(&mut terminal, walk_options, byte_format)?; - app.traverse(extract_paths_maybe_set_cwd(input, !opt.stay_on_filesystem)?)?; + let mut app = TerminalApp::initialize( + &mut terminal, + walk_options, + byte_format, + extract_paths_maybe_set_cwd(input, !opt.stay_on_filesystem)?, + )?; + app.traverse()?; let res = app.process_events(&mut terminal, keys_rx); |