Revert "fix: selection vs render issue (#2706)" (#2748)

This reverts commit cd5d337b52.
This commit is contained in:
Ellie Huxtable
2025-05-08 14:04:01 -07:00
committed by GitHub
parent 45460d4c31
commit f49599599e

View File

@@ -49,7 +49,7 @@ use ratatui::{
const TAB_TITLES: [&str; 2] = ["Search", "Inspect"]; const TAB_TITLES: [&str; 2] = ["Search", "Inspect"];
pub enum InputAction { pub enum InputAction {
Accept(History), Accept(usize),
Copy(usize), Copy(usize),
Delete(usize), Delete(usize),
ReturnOriginal, ReturnOriginal,
@@ -110,14 +110,13 @@ impl State {
settings: &Settings, settings: &Settings,
input: &Event, input: &Event,
w: &mut W, w: &mut W,
results: &[History],
) -> Result<InputAction> ) -> Result<InputAction>
where where
W: Write, W: Write,
{ {
execute!(w, EnableMouseCapture)?; execute!(w, EnableMouseCapture)?;
let r = match input { let r = match input {
Event::Key(k) => self.handle_key_input(settings, k, results), Event::Key(k) => self.handle_key_input(settings, k),
Event::Mouse(m) => self.handle_mouse_input(*m), Event::Mouse(m) => self.handle_mouse_input(*m),
Event::Paste(d) => self.handle_paste_input(d), Event::Paste(d) => self.handle_paste_input(d),
_ => InputAction::Continue, _ => InputAction::Continue,
@@ -199,12 +198,7 @@ impl State {
} }
} }
fn handle_key_input( fn handle_key_input(&mut self, settings: &Settings, input: &KeyEvent) -> InputAction {
&mut self,
settings: &Settings,
input: &KeyEvent,
results: &[History],
) -> InputAction {
if input.kind == event::KeyEventKind::Release { if input.kind == event::KeyEventKind::Release {
return InputAction::Continue; return InputAction::Continue;
} }
@@ -229,23 +223,13 @@ impl State {
KeyCode::Char('c' | 'g') if ctrl => Some(InputAction::ReturnOriginal), KeyCode::Char('c' | 'g') if ctrl => Some(InputAction::ReturnOriginal),
KeyCode::Esc if esc_allow_exit => Some(Self::handle_key_exit(settings)), KeyCode::Esc if esc_allow_exit => Some(Self::handle_key_exit(settings)),
KeyCode::Char('[') if ctrl && esc_allow_exit => Some(Self::handle_key_exit(settings)), KeyCode::Char('[') if ctrl && esc_allow_exit => Some(Self::handle_key_exit(settings)),
KeyCode::Tab => { KeyCode::Tab => Some(InputAction::Accept(self.results_state.selected())),
let selected = self.results_state.selected(); KeyCode::Right if cursor_at_end_of_line && settings.keys.accept_past_line_end => {
if !results.is_empty() && selected < results.len() { Some(InputAction::Accept(self.results_state.selected()))
Some(InputAction::Accept(results[selected].clone()))
} else {
Some(InputAction::ReturnQuery)
} }
KeyCode::Left if cursor_at_start_of_line && settings.keys.exit_past_line_start => {
Some(Self::handle_key_exit(settings))
} }
KeyCode::Right if cursor_at_end_of_line => {
let selected = self.results_state.selected();
if !results.is_empty() && selected < results.len() {
Some(InputAction::Accept(results[selected].clone()))
} else {
Some(InputAction::ReturnQuery)
}
}
KeyCode::Left if cursor_at_start_of_line => Some(Self::handle_key_exit(settings)),
KeyCode::Char('o') if ctrl => { KeyCode::Char('o') if ctrl => {
self.tab_index = (self.tab_index + 1) % TAB_TITLES.len(); self.tab_index = (self.tab_index + 1) % TAB_TITLES.len();
Some(InputAction::Continue) Some(InputAction::Continue)
@@ -261,7 +245,7 @@ impl State {
// handle tab-specific input // handle tab-specific input
let action = match self.tab_index { let action = match self.tab_index {
0 => self.handle_search_input(settings, input, results), 0 => self.handle_search_input(settings, input),
1 => super::inspector::input(self, settings, self.results_state.selected(), input), 1 => super::inspector::input(self, settings, self.results_state.selected(), input),
@@ -298,26 +282,16 @@ impl State {
self.handle_search_scroll_one_line(settings, enable_exit, !settings.invert) self.handle_search_scroll_one_line(settings, enable_exit, !settings.invert)
} }
fn handle_search_accept(&mut self, settings: &Settings, results: &[History]) -> InputAction { fn handle_search_accept(&mut self, settings: &Settings) -> InputAction {
if settings.enter_accept { if settings.enter_accept {
self.accept = true; self.accept = true;
} }
let selected = self.results_state.selected(); InputAction::Accept(self.results_state.selected())
if !results.is_empty() && selected < results.len() {
InputAction::Accept(results[selected].clone())
} else {
InputAction::ReturnQuery
}
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]
fn handle_search_input( fn handle_search_input(&mut self, settings: &Settings, input: &KeyEvent) -> InputAction {
&mut self,
settings: &Settings,
input: &KeyEvent,
results: &[History],
) -> InputAction {
let ctrl = input.modifiers.contains(KeyModifiers::CONTROL); let ctrl = input.modifiers.contains(KeyModifiers::CONTROL);
let alt = input.modifiers.contains(KeyModifiers::ALT); let alt = input.modifiers.contains(KeyModifiers::ALT);
@@ -408,20 +382,14 @@ impl State {
} }
match input.code { match input.code {
KeyCode::Enter => return self.handle_search_accept(settings, results), KeyCode::Enter => return self.handle_search_accept(settings),
KeyCode::Char('m') if ctrl => return self.handle_search_accept(settings, results), KeyCode::Char('m') if ctrl => return self.handle_search_accept(settings),
KeyCode::Char('y') if ctrl => { KeyCode::Char('y') if ctrl => {
return InputAction::Copy(self.results_state.selected()); return InputAction::Copy(self.results_state.selected());
} }
KeyCode::Char(c @ '1'..='9') if modfr => { KeyCode::Char(c @ '1'..='9') if modfr => {
let selected = self.results_state.selected();
return c.to_digit(10).map_or(InputAction::Continue, |c| { return c.to_digit(10).map_or(InputAction::Continue, |c| {
let new_index = selected + c as usize; InputAction::Accept(self.results_state.selected() + c as usize)
if !results.is_empty() && new_index < results.len() {
InputAction::Accept(results[new_index].clone())
} else {
InputAction::ReturnQuery
}
}); });
} }
KeyCode::Left if ctrl => self KeyCode::Left if ctrl => self
@@ -1173,11 +1141,8 @@ pub async fn history(
event_ready = event_ready => { event_ready = event_ready => {
if event_ready?? { if event_ready?? {
loop { loop {
match app.handle_input(settings, &event::read()?, &mut std::io::stdout(), &results)? { match app.handle_input(settings, &event::read()?, &mut std::io::stdout())? {
InputAction::Continue => { InputAction::Continue => {},
// Redraw the UI to keep it in sync with the selection state
terminal.draw(|f| app.draw(f, &results, stats.clone(), settings, theme))?;
},
InputAction::Delete(index) => { InputAction::Delete(index) => {
if results.is_empty() { if results.is_empty() {
break; break;
@@ -1229,12 +1194,8 @@ pub async fn history(
stats = if app.tab_index == 0 { stats = if app.tab_index == 0 {
None None
} else if !results.is_empty() { } else if !results.is_empty() {
let selected = app.results_state.selected(); let selected = results[app.results_state.selected()].clone();
if selected < results.len() { Some(db.stats(&selected).await?)
Some(db.stats(&results[selected]).await?)
} else {
None
}
} else { } else {
None None
}; };
@@ -1247,26 +1208,29 @@ pub async fn history(
} }
match result { match result {
InputAction::Accept(history) => { InputAction::Accept(index) if index < results.len() => {
let mut command = history.command; let mut command = results.swap_remove(index).command;
if accept if accept
&& (utils::is_zsh() || utils::is_fish() || utils::is_bash() || utils::is_xonsh()) && (utils::is_zsh() || utils::is_fish() || utils::is_bash() || utils::is_xonsh())
{ {
command = String::from("__atuin_accept__:") + &command; command = String::from("__atuin_accept__:") + &command;
} }
// We have the actual history entry, so use it directly // index is in bounds so we return that entry
Ok(command) Ok(command)
} }
InputAction::ReturnOriginal => Ok(String::new()), InputAction::ReturnOriginal => Ok(String::new()),
InputAction::Copy(index) => { InputAction::Copy(index) => {
if !results.is_empty() && index < results.len() { let cmd = results.swap_remove(index).command;
let cmd = results[index].command.clone();
set_clipboard(cmd); set_clipboard(cmd);
}
Ok(String::new()) Ok(String::new())
} }
InputAction::ReturnQuery => Ok(app.search.input.into_inner()), InputAction::ReturnQuery | InputAction::Accept(_) => {
// Either:
// * index == RETURN_QUERY, in which case we should return the input
// * out of bounds -> usually implies no selected entry so we return the input
Ok(app.search.input.into_inner())
}
InputAction::Continue | InputAction::Redraw | InputAction::Delete(_) => { InputAction::Continue | InputAction::Redraw | InputAction::Delete(_) => {
unreachable!("should have been handled!") unreachable!("should have been handled!")
} }