summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSebastian Thiel <sebastian.thiel@icloud.com>2024-01-09 09:53:50 +0100
committerSebastian Thiel <sebastian.thiel@icloud.com>2024-01-09 13:53:34 +0100
commit30da672a83c1063eb6f4c5483cb47f5d69c1dc35 (patch)
treea41a506e9f848e0986c8d7ef7340c6732c2d0107 /src
parentc4efba87179636afeb26e472353a029a4030086c (diff)
refactor
- avoid `app_` prefix in `app` module. - cleanup root-index logic a little
Diffstat (limited to 'src')
-rw-r--r--src/interactive/app/eventloop.rs65
-rw-r--r--src/interactive/app/handlers.rs4
-rw-r--r--src/interactive/app/mod.rs4
-rw-r--r--src/interactive/app/state.rs (renamed from src/interactive/app/app_state.rs)11
-rw-r--r--src/interactive/app/terminal.rs (renamed from src/interactive/app/terminal_app.rs)25
-rw-r--r--src/interactive/app/tests/journeys_readonly.rs4
-rw-r--r--src/interactive/app/tests/journeys_with_writes.rs11
-rw-r--r--src/interactive/app/tests/utils.rs6
-rw-r--r--src/interactive/widgets/glob.rs2
-rw-r--r--src/interactive/widgets/main.rs2
-rw-r--r--src/main.rs2
-rw-r--r--src/traverse.rs37
12 files changed, 76 insertions, 97 deletions
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs
index ad326a0..cd91a27 100644
--- a/src/interactive/app/eventloop.rs
+++ b/src/interactive/app/eventloop.rs
@@ -1,7 +1,7 @@
use crate::interactive::{
app::navigation::Navigation,
- app_state::FocussedPane,
sorted_entries,
+ state::FocussedPane,
widgets::{glob_search, MainWindow, MainWindowProps},
CursorDirection, CursorMode, DisplayOptions, MarkEntryMode,
};
@@ -10,14 +10,14 @@ use crossbeam::channel::Receiver;
use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use crosstermion::input::Event;
use dua::{
- traverse::{EntryData, RunningTraversal, Traversal, TraversalProcessingEvent},
+ traverse::{BackgroundTraversal, EntryData, Traversal},
WalkOptions, WalkResult,
};
use std::path::PathBuf;
use tui::backend::Backend;
use tui_react::Terminal;
-use super::app_state::{AppState, Cursor, ProcessingResult};
+use super::state::{AppState, Cursor};
use super::tree_view::TreeView;
impl AppState {
@@ -70,8 +70,10 @@ impl AppState {
walk_options: &WalkOptions,
input: Vec<PathBuf>,
) -> Result<()> {
- let running_traversal = RunningTraversal::start(traversal.root_index, walk_options, input)?;
- self.running_traversal = Some(running_traversal);
+ let background_traversal =
+ BackgroundTraversal::start(traversal.root_index, walk_options, input)?;
+ self.navigation_mut().view_root = traversal.root_index;
+ self.active_traversal = Some(background_traversal);
Ok(())
}
@@ -90,6 +92,7 @@ impl AppState {
Ok(())
}
+ /// This method ends once the user quits the application or there are no more inputs to process.
pub fn process_events<B>(
&mut self,
window: &mut MainWindow,
@@ -97,7 +100,7 @@ impl AppState {
display: &mut DisplayOptions,
terminal: &mut Terminal<B>,
events: Receiver<Event>,
- ) -> Result<ProcessingResult>
+ ) -> Result<WalkResult>
where
B: Backend,
{
@@ -119,47 +122,43 @@ impl AppState {
display: &mut DisplayOptions,
terminal: &mut Terminal<B>,
events: &Receiver<Event>,
- ) -> Result<Option<ProcessingResult>>
+ ) -> Result<Option<WalkResult>>
where
B: Backend,
{
- if let Some(running_traversal) = &mut self.running_traversal {
+ if let Some(active_traversal) = &mut self.active_traversal {
crossbeam::select! {
recv(events) -> event => {
let Ok(event) = event else {
- return Ok(Some(ProcessingResult::ExitRequested(WalkResult { num_errors: 0 })));
+ return Ok(Some(WalkResult { num_errors: 0 }));
};
- let result = self.process_terminal_event(
+ let res = self.process_terminal_event(
window,
traversal,
display,
terminal,
event)?;
- if let Some(processing_result) = result {
- return Ok(Some(processing_result));
+ if let Some(res) = res {
+ return Ok(Some(res));
}
},
- recv(&running_traversal.event_rx) -> event => {
+ recv(&active_traversal.event_rx) -> event => {
let Ok(event) = event else {
return Ok(None);
};
- let result = running_traversal.process_event(traversal, event);
- if result != TraversalProcessingEvent::None {
- if result == TraversalProcessingEvent::Finished {
- self.is_scanning = false;
- self.running_traversal = None;
+ if let Some(is_finished) = active_traversal.integrate_traversal_event(traversal, event) {
+ if is_finished {
+ self.active_traversal = None;
}
self.update_state(traversal);
self.refresh_screen(window, traversal, display, terminal)?;
- }
+ };
}
}
} else {
let Ok(event) = events.recv() else {
- return Ok(Some(ProcessingResult::ExitRequested(WalkResult {
- num_errors: 0,
- })));
+ return Ok(Some(WalkResult { num_errors: 0 }));
};
let result =
self.process_terminal_event(window, traversal, display, terminal, event)?;
@@ -171,17 +170,13 @@ impl AppState {
}
fn update_state(&mut self, traversal: &Traversal) {
- let received_events = self.received_event;
- if !received_events {
- self.navigation_mut().view_root = traversal.root_index;
- }
self.entries = sorted_entries(
&traversal.tree,
self.navigation().view_root,
self.sorting,
self.glob_root(),
);
- if !received_events {
+ if !self.received_events {
self.navigation_mut().selected = self.entries.first().map(|b| b.index);
}
self.reset_message(); // force "scanning" to appear
@@ -194,7 +189,7 @@ impl AppState {
display: &mut DisplayOptions,
terminal: &mut Terminal<B>,
event: Event,
- ) -> Result<Option<ProcessingResult>>
+ ) -> Result<Option<WalkResult>>
where
B: Backend,
{
@@ -203,8 +198,8 @@ impl AppState {
let key = match event {
Event::Key(key) if key.kind != KeyEventKind::Release => {
- if key.code != KeyCode::Char('\r') {
- self.received_event = true;
+ if key != refresh_key() {
+ self.received_events = true;
}
key
}
@@ -231,9 +226,9 @@ impl AppState {
}
Char('?') if !glob_focussed => self.toggle_help_pane(window),
Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) && !glob_focussed => {
- return Ok(Some(ProcessingResult::ExitRequested(WalkResult {
+ return Ok(Some(WalkResult {
num_errors: tree_view.traversal.io_errors,
- })))
+ }))
}
Char('q') if !glob_focussed => {
if let Some(result) = self.handle_quit(&mut tree_view, window) {
@@ -367,16 +362,16 @@ impl AppState {
&mut self,
tree_view: &mut TreeView<'_>,
window: &mut MainWindow,
- ) -> Option<std::result::Result<ProcessingResult, anyhow::Error>> {
+ ) -> Option<std::result::Result<WalkResult, anyhow::Error>> {
use FocussedPane::*;
match self.focussed {
Main => {
if self.glob_navigation.is_some() {
self.handle_glob_quit(tree_view, window);
} else {
- return Some(Ok(ProcessingResult::ExitRequested(WalkResult {
+ return Some(Ok(WalkResult {
num_errors: tree_view.traversal.io_errors,
- })));
+ }));
}
}
Mark => self.focussed = Main,
diff --git a/src/interactive/app/handlers.rs b/src/interactive/app/handlers.rs
index 611c2a0..d6b6582 100644
--- a/src/interactive/app/handlers.rs
+++ b/src/interactive/app/handlers.rs
@@ -9,7 +9,7 @@ use std::{fs, io, path::PathBuf};
use tui::backend::Backend;
use tui_react::Terminal;
-use super::app_state::{AppState, FocussedPane::*};
+use super::state::{AppState, FocussedPane::*};
#[derive(Copy, Clone)]
pub enum CursorMode {
@@ -146,7 +146,7 @@ impl AppState {
}
pub fn reset_message(&mut self) {
- if self.is_scanning {
+ if self.active_traversal.is_some() {
self.message = Some("-> scanning <-".into());
} else {
self.message = None;
diff --git a/src/interactive/app/mod.rs b/src/interactive/app/mod.rs
index f4f6d3e..355d587 100644
--- a/src/interactive/app/mod.rs
+++ b/src/interactive/app/mod.rs
@@ -1,11 +1,11 @@
-pub mod app_state;
mod bytevis;
mod common;
mod eventloop;
mod handlers;
pub mod input;
mod navigation;
-pub mod terminal_app;
+pub mod state;
+pub mod terminal;
pub mod tree_view;
pub use bytevis::*;
diff --git a/src/interactive/app/app_state.rs b/src/interactive/app/state.rs
index 3d32f80..9157da1 100644
--- a/src/interactive/app/app_state.rs
+++ b/src/interactive/app/state.rs
@@ -1,4 +1,4 @@
-use dua::{traverse::RunningTraversal, WalkResult};
+use dua::traverse::BackgroundTraversal;
use super::{navigation::Navigation, EntryDataBundle, SortMode};
@@ -26,11 +26,6 @@ pub struct AppState {
pub sorting: SortMode,
pub message: Option<String>,
pub focussed: FocussedPane,
- pub is_scanning: bool,
- pub received_event: bool,
- pub running_traversal: Option<RunningTraversal>,
-}
-
-pub enum ProcessingResult {
- ExitRequested(WalkResult),
+ pub received_events: bool,
+ pub active_traversal: Option<BackgroundTraversal>,
}
diff --git a/src/interactive/app/terminal_app.rs b/src/interactive/app/terminal.rs
index ae1c130..2109e00 100644
--- a/src/interactive/app/terminal_app.rs
+++ b/src/interactive/app/terminal.rs
@@ -12,10 +12,7 @@ use tui_react::Terminal;
use crate::interactive::widgets::MainWindow;
-use super::{
- app_state::{AppState, ProcessingResult},
- sorted_entries, DisplayOptions,
-};
+use super::{sorted_entries, state::AppState, DisplayOptions};
/// State and methods representing the interactive disk usage analyser for the terminal
pub struct TerminalApp {
@@ -41,10 +38,7 @@ impl TerminalApp {
let display = DisplayOptions::new(byte_format);
let window = MainWindow::default();
- let mut state = AppState {
- is_scanning: false,
- ..Default::default()
- };
+ let mut state = AppState::default();
let traversal = {
let mut tree = Tree::new();
@@ -93,15 +87,13 @@ impl TerminalApp {
where
B: Backend,
{
- match self.state.process_events(
+ self.state.process_events(
&mut self.window,
&mut self.traversal,
&mut self.display,
terminal,
events,
- )? {
- ProcessingResult::ExitRequested(res) => Ok(res),
- }
+ )
}
}
@@ -120,18 +112,15 @@ mod tests {
where
B: Backend,
{
- while self.state.running_traversal.is_some() {
- match self.state.process_event(
+ while self.state.active_traversal.is_some() {
+ if let Some(res) = self.state.process_event(
&mut self.window,
&mut self.traversal,
&mut self.display,
terminal,
&events,
)? {
- Some(ProcessingResult::ExitRequested(res)) => {
- return Ok(res);
- }
- _ => {}
+ return Ok(res);
}
}
Ok(WalkResult { num_errors: 0 })
diff --git a/src/interactive/app/tests/journeys_readonly.rs b/src/interactive/app/tests/journeys_readonly.rs
index 28e0529..b6dc579 100644
--- a/src/interactive/app/tests/journeys_readonly.rs
+++ b/src/interactive/app/tests/journeys_readonly.rs
@@ -40,8 +40,8 @@ fn simple_user_journey_read_only() -> Result<()> {
);
assert!(
- !app.state.is_scanning,
- "it will not think it is still scanning"
+ app.state.active_traversal.is_none(),
+ "it will not think it is still scanning as there is no traversal"
);
let first_selected_path = OsString::from(format!("{}/{}", FIXTURE_PATH, long_root));
diff --git a/src/interactive/app/tests/journeys_with_writes.rs b/src/interactive/app/tests/journeys_with_writes.rs
index 52431da..cbe18f1 100644
--- a/src/interactive/app/tests/journeys_with_writes.rs
+++ b/src/interactive/app/tests/journeys_with_writes.rs
@@ -28,13 +28,10 @@ fn basic_user_journey_with_deletion() -> Result<()> {
// When selecting the marker window and pressing the combination to delete entries
app.process_events(
&mut terminal,
- into_events(
- vec![
- Event::Key(KeyCode::Tab.into()),
- Event::Key(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL)),
- ]
- .into_iter(),
- ),
+ into_events([
+ Event::Key(KeyCode::Tab.into()),
+ Event::Key(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL)),
+ ]),
)?;
assert!(
app.window.mark_pane.is_none(),
diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs
index 8f7bf97..a54fa8f 100644
--- a/src/interactive/app/tests/utils.rs
+++ b/src/interactive/app/tests/utils.rs
@@ -19,12 +19,14 @@ use std::{
use tui::backend::TestBackend;
use tui_react::Terminal;
-use crate::interactive::{app::tests::FIXTURE_PATH, terminal_app::TerminalApp};
+use crate::interactive::{app::tests::FIXTURE_PATH, terminal::TerminalApp};
pub fn into_events<'a>(events: impl IntoIterator<Item = Event> + 'a) -> Receiver<Event> {
let (key_send, key_receive) = crossbeam::channel::unbounded();
for event in events {
- _ = key_send.send(event);
+ key_send
+ .send(event)
+ .expect("event is stored in the channel for later retrieval");
}
key_receive
}
diff --git a/src/interactive/widgets/glob.rs b/src/interactive/widgets/glob.rs
index 9f385a2..bafc69e 100644
--- a/src/interactive/widgets/glob.rs
+++ b/src/interactive/widgets/glob.rs
@@ -18,7 +18,7 @@ use tui_react::{draw_text_nowrap_fn, Terminal};
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
-use crate::interactive::app_state::Cursor;
+use crate::interactive::state::Cursor;
pub struct GlobPaneProps {
pub border_style: Style,
diff --git a/src/interactive/widgets/main.rs b/src/interactive/widgets/main.rs
index 3df375a..cd3c330 100644
--- a/src/interactive/widgets/main.rs
+++ b/src/interactive/widgets/main.rs
@@ -1,5 +1,5 @@
use crate::interactive::{
- app_state::{AppState, Cursor, FocussedPane},
+ state::{AppState, Cursor, FocussedPane},
widgets::{
Entries, EntriesProps, Footer, FooterProps, GlobPane, GlobPaneProps, Header, HelpPane,
HelpPaneProps, MarkPane, MarkPaneProps, COLOR_MARKED,
diff --git a/src/main.rs b/src/main.rs
index 2743377..105cf58 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,7 +10,7 @@ use std::{fs, io, io::Write, path::PathBuf, process};
#[cfg(feature = "tui-crossplatform")]
use crate::interactive::input::input_channel;
#[cfg(feature = "tui-crossplatform")]
-use crate::interactive::terminal_app::TerminalApp;
+use crate::interactive::terminal::TerminalApp;
mod crossdev;
#[cfg(feature = "tui-crossplatform")]
diff --git a/src/traverse.rs b/src/traverse.rs
index 3b6b291..6c24788 100644
--- a/src/traverse.rs
+++ b/src/traverse.rs
@@ -131,7 +131,8 @@ pub enum TraversalEvent {
Finished(u64),
}
-pub struct RunningTraversal {
+/// An in-progress traversal which exposes newly obtained entries
+pub struct BackgroundTraversal {
walk_options: WalkOptions,
previous_node_idx: TreeIndex,
parent_node_idx: TreeIndex,
@@ -143,19 +144,14 @@ pub struct RunningTraversal {
pub event_rx: Receiver<TraversalEvent>,
}
-#[derive(PartialEq)]
-pub enum TraversalProcessingEvent {
- None,
- UpdateIsReady,
- Finished,
-}
-
-impl RunningTraversal {
+impl BackgroundTraversal {
+ /// Start a background thread to perform the actual tree walk, and dispatch the results
+ /// as events to be received on [BackgroundTraversal::event_rx].
pub fn start(
root_idx: TreeIndex,
walk_options: &WalkOptions,
input: Vec<PathBuf>,
- ) -> anyhow::Result<RunningTraversal> {
+ ) -> anyhow::Result<BackgroundTraversal> {
let (entry_tx, entry_rx) = crossbeam::channel::bounded(100);
std::thread::Builder::new()
.name("dua-fs-walk-dispatcher".to_string())
@@ -210,11 +206,18 @@ impl RunningTraversal {
})
}
- pub fn process_event(
+ /// Integrate `event` into traversal `t` so its information is represented by it.
+ /// This builds the traversal tree from a directory-walk.
+ ///
+ /// Returns
+ /// * `Some(true)` if the traversal is finished
+ /// * `Some(false)` if the caller may update its state after throttling kicked in
+ /// * `None` - the event was written into the traversal, but there is nothing else to do
+ pub fn integrate_traversal_event(
&mut self,
t: &mut Traversal,
event: TraversalEvent,
- ) -> TraversalProcessingEvent {
+ ) -> Option<bool> {
match event {
TraversalEvent::Entry(entry, root_path, device_id) => {
t.entries_traversed += 1;
@@ -334,10 +337,8 @@ impl RunningTraversal {
}
}
- if let Some(throttle) = &self.throttle {
- if throttle.can_update() {
- return TraversalProcessingEvent::UpdateIsReady;
- }
+ if self.throttle.as_ref().map_or(false, |t| t.can_update()) {
+ return Some(false);
}
}
TraversalEvent::Finished(io_errors) => {
@@ -371,10 +372,10 @@ impl RunningTraversal {
t.total_bytes = Some(root_size);
t.elapsed = Some(t.start.elapsed());
- return TraversalProcessingEvent::Finished;
+ return Some(true);
}
}
- TraversalProcessingEvent::None
+ None
}
}