summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Thiel <sebastian.thiel@icloud.com>2024-01-21 17:25:07 +0100
committerSebastian Thiel <sebastian.thiel@icloud.com>2024-01-21 17:25:07 +0100
commitf70d1a8e6ace812a7949cd7d0299507b71306d48 (patch)
treecee9e52898f075bc7857b35a981843dfca0c624f
parent322eeb1aa07dacdc82e147bae64f8bfd4d758e1d (diff)
parent9d976d0d76fcf45d1e0672bc5c1533b000a46ebf (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.rs67
-rw-r--r--src/interactive/app/state.rs5
-rw-r--r--src/interactive/app/terminal.rs13
-rw-r--r--src/interactive/app/tests/journeys_readonly.rs86
-rw-r--r--src/interactive/app/tests/utils.rs7
-rw-r--r--src/main.rs9
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);