diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs index f0af499..863a5b9 100644 --- a/atuin-client/src/settings.rs +++ b/atuin-client/src/settings.rs @@ -24,7 +24,7 @@ pub enum SearchMode { Fuzzy, } -#[derive(Clone, Debug, Deserialize, Copy)] +#[derive(Clone, Debug, Deserialize, Copy, PartialEq)] pub enum FilterMode { #[serde(rename = "global")] Global, diff --git a/src/command/client/event.rs b/src/command/client/event.rs index f818d72..8044e27 100644 --- a/src/command/client/event.rs +++ b/src/command/client/event.rs @@ -1,6 +1,6 @@ use std::{thread, time::Duration}; -use crossbeam_channel::{bounded, TrySendError}; +use crossbeam_channel::unbounded; use termion::{event::Event as TermEvent, event::Key, input::TermRead}; pub enum Event { @@ -35,22 +35,16 @@ impl Events { } pub fn with_config(config: Config) -> Events { - // Keep channel small so scroll events don't stack for ages. - let (tx, rx) = bounded(1); + let (tx, rx) = unbounded(); { let tx = tx.clone(); thread::spawn(move || { let tty = termion::get_tty().expect("Could not find tty"); for event in tty.events().flatten() { - if let Err(err) = tx.try_send(Event::Input(event)) { - if let TrySendError::Full(_) = err { - // Silently ignore send fails when buffer is full. - // This will most likely be scroll wheel spam and we can drop some events. - } else { - eprintln!("{}", err); - return; - } + if let Err(err) = tx.send(Event::Input(event)) { + eprintln!("{}", err); + return; } } }) @@ -69,4 +63,8 @@ impl Events { pub fn next(&self) -> Result, crossbeam_channel::RecvError> { self.rx.recv() } + + pub fn try_next(&self) -> Result, crossbeam_channel::TryRecvError> { + self.rx.try_recv() + } } diff --git a/src/command/client/search.rs b/src/command/client/search.rs index 4e0de68..c50c492 100644 --- a/src/command/client/search.rs +++ b/src/command/client/search.rs @@ -307,12 +307,7 @@ fn remove_char_from_input(app: &mut State, i: usize) -> char { } #[allow(clippy::too_many_lines)] -async fn key_handler( - input: TermEvent, - search_mode: SearchMode, - db: &mut impl Database, - app: &mut State, -) -> Option { +fn key_handler(input: &TermEvent, app: &mut State) -> Option { match input { TermEvent::Key(Key::Esc | Key::Ctrl('c' | 'd' | 'g')) => return Some(String::from("")), TermEvent::Key(Key::Char('\n')) => { @@ -324,7 +319,7 @@ async fn key_handler( .map_or(app.input.clone(), |h| h.command.clone()), ); } - TermEvent::Key(Key::Alt(c)) if ('1'..='9').contains(&c) => { + TermEvent::Key(Key::Alt(c)) if ('1'..='9').contains(c) => { let c = c.to_digit(10)? as usize; let i = app.results_state.selected()? + c; @@ -351,9 +346,8 @@ async fn key_handler( app.cursor_index = app.input.chars().count(); } TermEvent::Key(Key::Char(c)) => { - insert_char_into_input(app, app.cursor_index, c); + insert_char_into_input(app, app.cursor_index, *c); app.cursor_index += 1; - query_results(app, search_mode, db).await.unwrap(); } TermEvent::Key(Key::Backspace) => { if app.cursor_index == 0 { @@ -361,7 +355,6 @@ async fn key_handler( } remove_char_from_input(app, app.cursor_index); app.cursor_index -= 1; - query_results(app, search_mode, db).await.unwrap(); } TermEvent::Key(Key::Ctrl('w')) => { let mut stop_on_next_whitespace = false; @@ -379,12 +372,10 @@ async fn key_handler( } app.cursor_index -= 1; } - query_results(app, search_mode, db).await.unwrap(); } TermEvent::Key(Key::Ctrl('u')) => { app.input = String::from(""); app.cursor_index = 0; - query_results(app, search_mode, db).await.unwrap(); } TermEvent::Key(Key::Ctrl('r')) => { app.filter_mode = match app.filter_mode { @@ -393,8 +384,6 @@ async fn key_handler( FilterMode::Session => FilterMode::Directory, FilterMode::Directory => FilterMode::Global, }; - - query_results(app, search_mode, db).await.unwrap(); } TermEvent::Key(Key::Down | Key::Ctrl('n' | 'j')) | TermEvent::Mouse(MouseEvent::Press(MouseButton::WheelDown, _, _)) => { @@ -632,13 +621,27 @@ async fn select_history( loop { let history_count = db.history_count().await?; + let initial_input = app.input.clone(); + let initial_filter_mode = app.filter_mode; + // Handle input if let Event::Input(input) = events.next()? { - if let Some(output) = key_handler(input, search_mode, db, &mut app).await { + if let Some(output) = key_handler(&input, &mut app) { return Ok(output); } } + // After we receive input process the whole event channel before query/render. + while let Ok(Event::Input(input)) = events.try_next() { + if let Some(output) = key_handler(&input, &mut app) { + return Ok(output); + } + } + + if initial_input != app.input || initial_filter_mode != app.filter_mode { + query_results(&mut app, search_mode, db).await?; + } + let compact = match style { atuin_client::settings::Style::Auto => { terminal.size().map(|size| size.height < 14).unwrap_or(true)