diff options
Diffstat (limited to 'vendor/dialoguer/src/prompts/fuzzy_select.rs')
-rw-r--r-- | vendor/dialoguer/src/prompts/fuzzy_select.rs | 326 |
1 files changed, 0 insertions, 326 deletions
diff --git a/vendor/dialoguer/src/prompts/fuzzy_select.rs b/vendor/dialoguer/src/prompts/fuzzy_select.rs deleted file mode 100644 index 9b7f992..0000000 --- a/vendor/dialoguer/src/prompts/fuzzy_select.rs +++ /dev/null @@ -1,326 +0,0 @@ -use crate::theme::{SimpleTheme, TermThemeRenderer, Theme}; -use console::{Key, Term}; -use fuzzy_matcher::FuzzyMatcher; -use std::{io, ops::Rem}; - -/// Renders a selection menu that user can fuzzy match to reduce set. -/// -/// User can use fuzzy search to limit selectable items. -/// Interaction returns index of an item selected in the order they appear in `item` invocation or `items` slice. -/// -/// ## Examples -/// -/// ```rust,no_run -/// use dialoguer::{ -/// FuzzySelect, -/// theme::ColorfulTheme -/// }; -/// use console::Term; -/// -/// fn main() -> std::io::Result<()> { -/// let items = vec!["Item 1", "item 2"]; -/// let selection = FuzzySelect::with_theme(&ColorfulTheme::default()) -/// .items(&items) -/// .default(0) -/// .interact_on_opt(&Term::stderr())?; -/// -/// match selection { -/// Some(index) => println!("User selected item : {}", items[index]), -/// None => println!("User did not select anything") -/// } -/// -/// Ok(()) -/// } -/// ``` - -pub struct FuzzySelect<'a> { - default: Option<usize>, - items: Vec<String>, - prompt: String, - report: bool, - clear: bool, - highlight_matches: bool, - max_length: Option<usize>, - theme: &'a dyn Theme, - /// Search string that a fuzzy search with start with. - /// Defaults to an empty string. - initial_text: String, -} - -impl Default for FuzzySelect<'static> { - fn default() -> Self { - Self::new() - } -} - -impl FuzzySelect<'static> { - /// Creates the prompt with a specific text. - pub fn new() -> Self { - Self::with_theme(&SimpleTheme) - } -} - -impl FuzzySelect<'_> { - /// Sets the clear behavior of the menu. - /// - /// The default is to clear the menu. - pub fn clear(&mut self, val: bool) -> &mut Self { - self.clear = val; - self - } - - /// Sets a default for the menu - pub fn default(&mut self, val: usize) -> &mut Self { - self.default = Some(val); - self - } - - /// Add a single item to the fuzzy selector. - pub fn item<T: ToString>(&mut self, item: T) -> &mut Self { - self.items.push(item.to_string()); - self - } - - /// Adds multiple items to the fuzzy selector. - pub fn items<T: ToString>(&mut self, items: &[T]) -> &mut Self { - for item in items { - self.items.push(item.to_string()); - } - self - } - - /// Sets the search text that a fuzzy search starts with. - pub fn with_initial_text<S: Into<String>>(&mut self, initial_text: S) -> &mut Self { - self.initial_text = initial_text.into(); - self - } - - /// Prefaces the menu with a prompt. - /// - /// When a prompt is set the system also prints out a confirmation after - /// the fuzzy selection. - pub fn with_prompt<S: Into<String>>(&mut self, prompt: S) -> &mut Self { - self.prompt = prompt.into(); - self - } - - /// Indicates whether to report the selected value after interaction. - /// - /// The default is to report the selection. - pub fn report(&mut self, val: bool) -> &mut Self { - self.report = val; - self - } - - /// Indicates whether to highlight matched indices - /// - /// The default is to highlight the indices - pub fn highlight_matches(&mut self, val: bool) -> &mut Self { - self.highlight_matches = val; - self - } - - /// Sets the maximum number of visible options. - /// - /// The default is the height of the terminal minus 2. - pub fn max_length(&mut self, rows: usize) -> &mut Self { - self.max_length = Some(rows); - self - } - - /// Enables user interaction and returns the result. - /// - /// The user can select the items using 'Enter' and the index of selected item will be returned. - /// The dialog is rendered on stderr. - /// Result contains `index` of selected item if user hit 'Enter'. - /// This unlike [interact_opt](#method.interact_opt) does not allow to quit with 'Esc' or 'q'. - #[inline] - pub fn interact(&self) -> io::Result<usize> { - self.interact_on(&Term::stderr()) - } - - /// Enables user interaction and returns the result. - /// - /// The user can select the items using 'Enter' and the index of selected item will be returned. - /// The dialog is rendered on stderr. - /// Result contains `Some(index)` if user hit 'Enter' or `None` if user cancelled with 'Esc' or 'q'. - #[inline] - pub fn interact_opt(&self) -> io::Result<Option<usize>> { - self.interact_on_opt(&Term::stderr()) - } - - /// Like `interact` but allows a specific terminal to be set. - #[inline] - pub fn interact_on(&self, term: &Term) -> io::Result<usize> { - self._interact_on(term, false)? - .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Quit not allowed in this case")) - } - - /// Like `interact` but allows a specific terminal to be set. - #[inline] - pub fn interact_on_opt(&self, term: &Term) -> io::Result<Option<usize>> { - self._interact_on(term, true) - } - - /// Like `interact` but allows a specific terminal to be set. - fn _interact_on(&self, term: &Term, allow_quit: bool) -> io::Result<Option<usize>> { - // Place cursor at the end of the search term - let mut position = self.initial_text.len(); - let mut search_term = self.initial_text.to_owned(); - - let mut render = TermThemeRenderer::new(term, self.theme); - let mut sel = self.default; - - let mut size_vec = Vec::new(); - for items in self.items.iter().as_slice() { - let size = &items.len(); - size_vec.push(*size); - } - - // Fuzzy matcher - let matcher = fuzzy_matcher::skim::SkimMatcherV2::default(); - - // Subtract -2 because we need space to render the prompt. - let visible_term_rows = (term.size().0 as usize).max(3) - 2; - let visible_term_rows = self - .max_length - .unwrap_or(visible_term_rows) - .min(visible_term_rows); - // Variable used to determine if we need to scroll through the list. - let mut starting_row = 0; - - term.hide_cursor()?; - - loop { - render.clear()?; - render.fuzzy_select_prompt(self.prompt.as_str(), &search_term, position)?; - - // Maps all items to a tuple of item and its match score. - let mut filtered_list = self - .items - .iter() - .map(|item| (item, matcher.fuzzy_match(item, &search_term))) - .filter_map(|(item, score)| score.map(|s| (item, s))) - .collect::<Vec<_>>(); - - // Renders all matching items, from best match to worst. - filtered_list.sort_unstable_by(|(_, s1), (_, s2)| s2.cmp(s1)); - - for (idx, (item, _)) in filtered_list - .iter() - .enumerate() - .skip(starting_row) - .take(visible_term_rows) - { - render.fuzzy_select_prompt_item( - item, - Some(idx) == sel, - self.highlight_matches, - &matcher, - &search_term, - )?; - } - term.flush()?; - - match (term.read_key()?, sel) { - (Key::Escape, _) if allow_quit => { - if self.clear { - render.clear()?; - term.flush()?; - } - term.show_cursor()?; - return Ok(None); - } - (Key::ArrowUp | Key::BackTab, _) if !filtered_list.is_empty() => { - if sel == Some(0) { - starting_row = - filtered_list.len().max(visible_term_rows) - visible_term_rows; - } else if sel == Some(starting_row) { - starting_row -= 1; - } - sel = match sel { - None => Some(filtered_list.len() - 1), - Some(sel) => Some( - ((sel as i64 - 1 + filtered_list.len() as i64) - % (filtered_list.len() as i64)) - as usize, - ), - }; - term.flush()?; - } - (Key::ArrowDown | Key::Tab, _) if !filtered_list.is_empty() => { - sel = match sel { - None => Some(0), - Some(sel) => { - Some((sel as u64 + 1).rem(filtered_list.len() as u64) as usize) - } - }; - if sel == Some(visible_term_rows + starting_row) { - starting_row += 1; - } else if sel == Some(0) { - starting_row = 0; - } - term.flush()?; - } - (Key::ArrowLeft, _) if position > 0 => { - position -= 1; - term.flush()?; - } - (Key::ArrowRight, _) if position < search_term.len() => { - position += 1; - term.flush()?; - } - (Key::Enter, Some(sel)) if !filtered_list.is_empty() => { - if self.clear { - render.clear()?; - } - - if self.report { - render - .input_prompt_selection(self.prompt.as_str(), filtered_list[sel].0)?; - } - - let sel_string = filtered_list[sel].0; - let sel_string_pos_in_items = - self.items.iter().position(|item| item.eq(sel_string)); - - term.show_cursor()?; - return Ok(sel_string_pos_in_items); - } - (Key::Backspace, _) if position > 0 => { - position -= 1; - search_term.remove(position); - term.flush()?; - } - (Key::Char(chr), _) if !chr.is_ascii_control() => { - search_term.insert(position, chr); - position += 1; - term.flush()?; - sel = Some(0); - starting_row = 0; - } - - _ => {} - } - - render.clear_preserve_prompt(&size_vec)?; - } - } -} - -impl<'a> FuzzySelect<'a> { - /// Same as `new` but with a specific theme. - pub fn with_theme(theme: &'a dyn Theme) -> Self { - Self { - default: None, - items: vec![], - prompt: "".into(), - report: true, - clear: true, - highlight_matches: true, - max_length: None, - theme, - initial_text: "".into(), - } - } -} |