diff --git a/src/prompts/fuzzy_select.rs b/src/prompts/fuzzy_select.rs index e0c66575..0a2aa9f5 100644 --- a/src/prompts/fuzzy_select.rs +++ b/src/prompts/fuzzy_select.rs @@ -38,6 +38,7 @@ pub struct FuzzySelect<'a> { report: bool, clear: bool, highlight_matches: bool, + enable_vim_mode: bool, max_length: Option, theme: &'a dyn Theme, /// Search string that a fuzzy search with start with. @@ -118,6 +119,17 @@ impl FuzzySelect<'_> { self } + /// Indicated whether to allow the use of vim mode + /// + /// Vim mode can be entered by pressing Escape. + /// This then allows the user to navigate using hjkl. + /// + /// The default is to disable vim mode. + pub fn vim_mode(mut self, val: bool) -> Self { + self.enable_vim_mode = val; + self + } + /// Sets the maximum number of visible options. /// /// The default is the height of the terminal minus 2. @@ -209,6 +221,8 @@ impl FuzzySelect<'_> { term.hide_cursor()?; + let mut vim_mode = false; + loop { render.clear()?; render.fuzzy_select_prompt(self.prompt.as_str(), &search_term, position)?; @@ -240,8 +254,11 @@ impl FuzzySelect<'_> { } term.flush()?; - match (term.read_key()?, sel) { - (Key::Escape, _) if allow_quit => { + match (term.read_key()?, sel, vim_mode) { + (Key::Escape, _, false) if self.enable_vim_mode => { + vim_mode = true; + } + (Key::Escape, _, false) | (Key::Char('q'), _, true) if allow_quit => { if self.clear { render.clear()?; term.flush()?; @@ -249,7 +266,12 @@ impl FuzzySelect<'_> { term.show_cursor()?; return Ok(None); } - (Key::ArrowUp | Key::BackTab, _) if !filtered_list.is_empty() => { + (Key::Char('i' | 'a'), _, true) => { + vim_mode = false; + } + (Key::ArrowUp | Key::BackTab, _, _) | (Key::Char('k'), _, true) + if !filtered_list.is_empty() => + { if sel == Some(0) { starting_row = filtered_list.len().max(visible_term_rows) - visible_term_rows; @@ -266,7 +288,9 @@ impl FuzzySelect<'_> { }; term.flush()?; } - (Key::ArrowDown | Key::Tab, _) if !filtered_list.is_empty() => { + (Key::ArrowDown | Key::Tab, _, _) | (Key::Char('j'), _, true) + if !filtered_list.is_empty() => + { sel = match sel { None => Some(0), Some(sel) => { @@ -280,15 +304,17 @@ impl FuzzySelect<'_> { } term.flush()?; } - (Key::ArrowLeft, _) if position > 0 => { + (Key::ArrowLeft, _, _) | (Key::Char('h'), _, true) if position > 0 => { position -= 1; term.flush()?; } - (Key::ArrowRight, _) if position < search_term.len() => { + (Key::ArrowRight, _, _) | (Key::Char('l'), _, true) + if position < search_term.len() => + { position += 1; term.flush()?; } - (Key::Enter, Some(sel)) if !filtered_list.is_empty() => { + (Key::Enter, Some(sel), _) if !filtered_list.is_empty() => { if self.clear { render.clear()?; } @@ -305,12 +331,12 @@ impl FuzzySelect<'_> { term.show_cursor()?; return Ok(sel_string_pos_in_items); } - (Key::Backspace, _) if position > 0 => { + (Key::Backspace, _, _) if position > 0 => { position -= 1; search_term.remove(position); term.flush()?; } - (Key::Char(chr), _) if !chr.is_ascii_control() => { + (Key::Char(chr), _, _) if !chr.is_ascii_control() => { search_term.insert(position, chr); position += 1; term.flush()?; @@ -349,6 +375,7 @@ impl<'a> FuzzySelect<'a> { report: true, clear: true, highlight_matches: true, + enable_vim_mode: false, max_length: None, theme, initial_text: "".into(),