diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/interactive/app/eventloop.rs | 66 | ||||
-rw-r--r-- | src/interactive/app_test/journeys_readonly.rs | 39 | ||||
-rw-r--r-- | src/interactive/app_test/journeys_with_writes.rs | 8 | ||||
-rw-r--r-- | src/main.rs | 4 |
6 files changed, 72 insertions, 47 deletions
@@ -186,6 +186,7 @@ version = "3.0.0" dependencies = [ "atty", "byte-unit", + "crossbeam-channel", "failure", "failure-tools", "filesize", @@ -26,6 +26,7 @@ tui-react = { path = "./tui-react", version = "0.2" } num_cpus = "1.10.0" unicode-segmentation = "1.3.0" filesize = "0.2.0" +crossbeam-channel = "0.4.2" [[bin]] name="dua" diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index 02519bb..4baaa7f 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -10,6 +10,8 @@ use dua::{ use failure::Error; use std::{collections::BTreeMap, io, io::Write, path::PathBuf}; use termion::event::Key; +use termion::input::TermRead; +use termion::screen::ToMainScreen; use tui::backend::Backend; use tui_react::Terminal; @@ -61,7 +63,7 @@ impl AppState { window: &mut MainWindow, traversal: &mut Traversal, display: &mut DisplayOptions, - mut terminal: Terminal<B>, + terminal: &mut Terminal<B>, keys: impl Iterator<Item = Result<Key, io::Error>>, ) -> Result<WalkResult, Error> where @@ -70,24 +72,25 @@ impl AppState { use termion::event::Key::*; use FocussedPane::*; - fn exit_now<B: Backend>(terminal: Terminal<B>) -> ! { - drop(terminal); + fn exit_now() -> ! { + write!(io::stdout(), "{}", ToMainScreen).ok(); io::stdout().flush().ok(); // Exit 'quickly' to avoid having to wait for all memory to be freed by us. // Let the OS do it - we have nothing to lose, literally. std::process::exit(0); } - self.draw(window, traversal, display.clone(), &mut terminal)?; + self.draw(window, traversal, display.clone(), terminal)?; for key in keys.filter_map(Result::ok) { + self.reset_message(); match key { Char('?') => self.toggle_help_pane(window), Char('\t') => { self.cycle_focus(window); } - Ctrl('c') => exit_now(terminal), + Ctrl('c') => exit_now(), Char('q') | Esc => match self.focussed { - Main => exit_now(terminal), + Main => exit_now(), Mark => self.focussed = Main, Help => { self.focussed = Main; @@ -98,13 +101,9 @@ impl AppState { } match self.focussed { - FocussedPane::Mark => self.dispatch_to_mark_pane( - key, - window, - traversal, - display.clone(), - &mut terminal, - ), + FocussedPane::Mark => { + self.dispatch_to_mark_pane(key, window, traversal, display.clone(), terminal) + } FocussedPane::Help => { window.help_pane.as_mut().expect("help pane").key(key); } @@ -127,7 +126,7 @@ impl AppState { _ => {} }, }; - self.draw(window, traversal, display.clone(), &mut terminal)?; + self.draw(window, traversal, display.clone(), terminal)?; } Ok(WalkResult { num_errors: traversal.io_errors, @@ -160,7 +159,7 @@ pub struct TerminalApp { impl TerminalApp { pub fn process_events<B>( &mut self, - terminal: Terminal<B>, + terminal: &mut Terminal<B>, keys: impl Iterator<Item = Result<Key, io::Error>>, ) -> Result<WalkResult, Error> where @@ -189,21 +188,43 @@ impl TerminalApp { let mut display_options: DisplayOptions = options.clone().into(); display_options.byte_vis = ByteVisualization::Bar; let mut window = MainWindow::default(); + let (keys_tx, keys_rx) = crossbeam_channel::unbounded(); + match mode { + Interaction::None => drop(keys_tx), + Interaction::Full => drop(std::thread::spawn(move || { + let keys = std::io::stdin().keys(); + for key in keys { + if let Err(_) = keys_tx.try_send(key) { + break; + } + } + })), + } + + let fetch_buffered_key_events = || { + let mut keys = Vec::new(); + while let Ok(key) = keys_rx.try_recv() { + keys.push(key); + } + keys + }; let traversal = Traversal::from_walk(options, input, move |traversal| { - let state = AppState { + let mut state = AppState { root: traversal.root_index, sorting: Default::default(), message: Some("-> scanning <-".into()), entries: sorted_entries(&traversal.tree, traversal.root_index, Default::default()), ..Default::default() }; - let props = MainWindowProps { + state.process_events( + &mut window, traversal, - display: display_options, - state: &state, - }; - draw_window(&mut window, props, terminal) + &mut display_options, + terminal, + fetch_buffered_key_events().into_iter(), + )?; + Ok(()) })?; let sorting = Default::default(); @@ -227,6 +248,7 @@ impl TerminalApp { } pub enum Interaction { - Limited, + Full, + #[allow(dead_code)] None, } diff --git a/src/interactive/app_test/journeys_readonly.rs b/src/interactive/app_test/journeys_readonly.rs index 8f5eb1a..7acc85a 100644 --- a/src/interactive/app_test/journeys_readonly.rs +++ b/src/interactive/app_test/journeys_readonly.rs @@ -1,7 +1,7 @@ use crate::{ interactive::app_test::utils::{ - fixture_str, index_by_name, initialized_app_and_terminal_from_fixture, new_test_terminal, - node_by_index, node_by_name, + fixture_str, index_by_name, initialized_app_and_terminal_from_fixture, node_by_index, + node_by_name, }, interactive::app_test::FIXTURE_PATH, interactive::SortMode, @@ -15,7 +15,8 @@ use termion::input::TermRead; fn simple_user_journey_read_only() -> Result<(), Error> { let long_root = "sample-02/dir"; let short_root = "sample-01"; - let (terminal, mut app) = initialized_app_and_terminal_from_fixture(&[short_root, long_root])?; + let (mut terminal, mut app) = + initialized_app_and_terminal_from_fixture(&[short_root, long_root])?; // POST-INIT // after initialization, we expect that... @@ -48,7 +49,7 @@ fn simple_user_journey_read_only() -> Result<(), Error> { // SORTING { // when hitting the S key - app.process_events(terminal, b"s".keys())?; + app.process_events(&mut terminal, b"s".keys())?; assert_eq!( app.state.sorting, SortMode::SizeAscending, @@ -60,7 +61,7 @@ fn simple_user_journey_read_only() -> Result<(), Error> { "it recomputes the cached entries" ); // when hitting the S key again - app.process_events(new_test_terminal()?, b"s".keys())?; + app.process_events(&mut terminal, b"s".keys())?; assert_eq!( app.state.sorting, SortMode::SizeDescending, @@ -76,35 +77,35 @@ fn simple_user_journey_read_only() -> Result<(), Error> { // Entry-Navigation { // when hitting the j key - app.process_events(new_test_terminal()?, b"j".keys())?; + app.process_events(&mut terminal, b"j".keys())?; assert_eq!( node_by_name(&app, fixture_str(long_root)), node_by_index(&app, *app.state.selected.as_ref().unwrap()), "it moves the cursor down and selects the next entry based on the current sort mode" ); // when hitting it while there is nowhere to go - app.process_events(new_test_terminal()?, b"j".keys())?; + app.process_events(&mut terminal, b"j".keys())?; assert_eq!( node_by_name(&app, fixture_str(long_root)), node_by_index(&app, *app.state.selected.as_ref().unwrap()), "it stays at the previous position" ); // when hitting the k key - app.process_events(new_test_terminal()?, b"k".keys())?; + app.process_events(&mut terminal, b"k".keys())?; assert_eq!( node_by_name(&app, fixture_str(short_root)), node_by_index(&app, *app.state.selected.as_ref().unwrap()), "it moves the cursor up and selects the next entry based on the current sort mode" ); // when hitting the k key again - app.process_events(new_test_terminal()?, b"k".keys())?; + app.process_events(&mut terminal, b"k".keys())?; assert_eq!( node_by_name(&app, fixture_str(short_root)), node_by_index(&app, *app.state.selected.as_ref().unwrap()), "it stays at the current cursor position as there is nowhere to go" ); // when hitting the o key with a directory selected - app.process_events(new_test_terminal()?, b"o".keys())?; + app.process_events(&mut terminal, b"o".keys())?; { let new_root_idx = index_by_name(&app, fixture_str(short_root)); assert_eq!( @@ -118,7 +119,7 @@ fn simple_user_journey_read_only() -> Result<(), Error> { ); // when hitting the u key while inside a sub-directory - app.process_events(new_test_terminal()?, b"u".keys())?; + app.process_events(&mut terminal, b"u".keys())?; { assert_eq!( app.traversal.root_index, app.state.root, @@ -133,7 +134,7 @@ fn simple_user_journey_read_only() -> Result<(), Error> { } // when hitting the u key while inside of the root directory // We are moving the cursor down just to have a non-default selection - app.process_events(new_test_terminal()?, b"ju".keys())?; + app.process_events(&mut terminal, b"ju".keys())?; { assert_eq!( app.traversal.root_index, app.state.root, @@ -150,9 +151,9 @@ fn simple_user_journey_read_only() -> Result<(), Error> { // Deletion { // when hitting the 'd' key (also move cursor back to start) - app.process_events(new_test_terminal()?, b"k".keys())?; + app.process_events(&mut terminal, b"k".keys())?; let previously_selected_index = *app.state.selected.as_ref().unwrap(); - app.process_events(new_test_terminal()?, b"d".keys())?; + app.process_events(&mut terminal, b"d".keys())?; { assert_eq!( Some(1), @@ -174,7 +175,7 @@ fn simple_user_journey_read_only() -> Result<(), Error> { // when hitting the 'd' key again { - app.process_events(new_test_terminal()?, b"d".keys())?; + app.process_events(&mut terminal, b"d".keys())?; assert_eq!( Some(2), @@ -191,7 +192,7 @@ fn simple_user_journey_read_only() -> Result<(), Error> { // when hitting the 'd' key once again { - app.process_events(new_test_terminal()?, b"d".keys())?; + app.process_events(&mut terminal, b"d".keys())?; assert_eq!( Some(1), @@ -208,7 +209,7 @@ fn simple_user_journey_read_only() -> Result<(), Error> { } // when hitting the spacebar (after moving up to the first entry) { - app.process_events(new_test_terminal()?, b"k ".keys())?; + app.process_events(&mut terminal, b"k ".keys())?; assert_eq!( None, @@ -227,7 +228,7 @@ fn simple_user_journey_read_only() -> Result<(), Error> { // Marking { // select something - app.process_events(new_test_terminal()?, b" j ".keys())?; + app.process_events(&mut terminal, b" j ".keys())?; assert_eq!( Some(false), app.window.mark_pane.as_ref().map(|p| p.has_focus()), @@ -241,7 +242,7 @@ fn simple_user_journey_read_only() -> Result<(), Error> { ); // when advancing the selection to the marker pane - app.process_events(new_test_terminal()?, b"\t".keys())?; + app.process_events(&mut terminal, b"\t".keys())?; { assert_eq!( Some(true), diff --git a/src/interactive/app_test/journeys_with_writes.rs b/src/interactive/app_test/journeys_with_writes.rs index 465be54..cbd51cd 100644 --- a/src/interactive/app_test/journeys_with_writes.rs +++ b/src/interactive/app_test/journeys_with_writes.rs @@ -1,5 +1,5 @@ use crate::interactive::app_test::utils::{ - initialized_app_and_terminal_from_paths, new_test_terminal, WritableFixture, + initialized_app_and_terminal_from_paths, WritableFixture, }; use failure::Error; use pretty_assertions::assert_eq; @@ -9,10 +9,10 @@ use termion::input::TermRead; #[test] fn basic_user_journey_with_deletion() -> Result<(), Error> { let fixture = WritableFixture::from("sample-02"); - let (terminal, mut app) = initialized_app_and_terminal_from_paths(&[fixture.root.clone()])?; + let (mut terminal, mut app) = initialized_app_and_terminal_from_paths(&[fixture.root.clone()])?; // With a selection of items - app.process_events(terminal, b"doddd".keys())?; + app.process_events(&mut terminal, b"doddd".keys())?; assert_eq!( app.window.mark_pane.as_ref().map(|p| p.marked().len()), @@ -28,7 +28,7 @@ fn basic_user_journey_with_deletion() -> Result<(), Error> { // When selecting the marker window and pressing the combination to delete entries app.process_events( - new_test_terminal()?, + &mut terminal, vec![Ok(Key::Char('\t')), Ok(Key::Ctrl('r'))].into_iter(), )?; assert_eq!( diff --git a/src/main.rs b/src/main.rs index f5c6197..9b0faad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,9 +47,9 @@ fn run() -> Result<(), Error> { &mut terminal, walk_options, paths_from(input)?, - Interaction::Limited, + Interaction::Full, )?; - let res = app.process_events(terminal, io::stdin().keys())?; + let res = app.process_events(&mut terminal, io::stdin().keys())?; io::stdout().flush().ok(); res } |