diff --git a/CHANGELOG.md b/CHANGELOG.md index 5933ba97..36348086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Implement fuzzy search as default on Select and MultiSelect prompts. [#176](https://github.com/mikaelmello/inquire/pull/176) - Add new option on Select/MultiSelect prompts allowing to reset selection to the first item on filter-input changes. [#176](https://github.com/mikaelmello/inquire/pull/176) - Keybindings Ctrl-p and Ctrl-n added for Up and Down actions +- Added 'with_starting_filter_input' to both Select and MultiSelect, which allows for setting an initial value to the filter section of the prompt. - Keybindings Ctrl-j and Ctrl-g added for Enter and Cancel actions ### Fixes diff --git a/inquire/src/prompts/multiselect/mod.rs b/inquire/src/prompts/multiselect/mod.rs index e59c0a69..48a9b57e 100644 --- a/inquire/src/prompts/multiselect/mod.rs +++ b/inquire/src/prompts/multiselect/mod.rs @@ -44,6 +44,7 @@ static DEFAULT_MATCHER: Lazy = Lazy::new(|| SkimMatcherV2::defaul /// - **Options list**: Options displayed to the user. Must be **non-empty**. /// - **Default selections**: Options that are selected by default when the prompt is first rendered. The user can unselect them. If any of the indices is out-of-range of the option list, the prompt will fail with an [`InquireError::InvalidConfiguration`] error. /// - **Starting cursor**: Index of the cursor when the prompt is first rendered. Default is 0 (first option). If the index is out-of-range of the option list, the prompt will fail with an [`InquireError::InvalidConfiguration`] error. +/// - **Starting filter input**: Sets the initial value of the filter section of the prompt. /// - **Help message**: Message displayed at the line below the prompt. /// - **Formatter**: Custom formatter in case you need to pre-process the user input before showing it as the final answer. /// - Prints the selected options string value, joined using a comma as the separator, by default. @@ -83,6 +84,9 @@ pub struct MultiSelect<'a, T> { /// Starting cursor index of the selection. pub starting_cursor: usize, + /// Starting filter input + pub starting_filter_input: Option<&'a str>, + /// Reset cursor position to first option on filter input change. /// Defaults to true. pub reset_cursor: bool, @@ -214,6 +218,7 @@ where page_size: Self::DEFAULT_PAGE_SIZE, vim_mode: Self::DEFAULT_VIM_MODE, starting_cursor: Self::DEFAULT_STARTING_CURSOR, + starting_filter_input: None, reset_cursor: Self::DEFAULT_RESET_CURSOR, keep_filter: Self::DEFAULT_KEEP_FILTER, scorer: Self::DEFAULT_SCORER, @@ -302,6 +307,12 @@ where self } + /// Sets the starting filter input + pub fn with_starting_filter_input(mut self, starting_filter_input: &'a str) -> Self { + self.starting_filter_input = Some(starting_filter_input); + self + } + /// Sets the reset_cursor behaviour. /// Will reset cursor to first option on filter input change. /// Defaults to true. diff --git a/inquire/src/prompts/multiselect/prompt.rs b/inquire/src/prompts/multiselect/prompt.rs index 0681b0d8..22ed075d 100644 --- a/inquire/src/prompts/multiselect/prompt.rs +++ b/inquire/src/prompts/multiselect/prompt.rs @@ -74,7 +74,10 @@ where scored_options, help_message: mso.help_message, cursor_index: mso.starting_cursor, - input: Input::new(), + input: mso + .starting_filter_input + .map(Input::new_with) + .unwrap_or_else(Input::new), scorer: mso.scorer, formatter: mso.formatter, validator: mso.validator, @@ -191,6 +194,18 @@ where answer } + + fn run_scorer(&mut self) { + let mut options = self.score_options(); + options.sort_unstable_by_key(|(_idx, score)| Reverse(*score)); + + self.scored_options = options.into_iter().map(|(idx, _)| idx).collect(); + if self.config.reset_cursor { + let _ = self.update_cursor_position(0); + } else if self.scored_options.len() <= self.cursor_index { + let _ = self.update_cursor_position(self.scored_options.len().saturating_sub(1)); + } + } } impl<'a, Backend, T> Prompt for MultiSelectPrompt<'a, T> @@ -215,6 +230,11 @@ where (self.formatter)(&refs) } + fn setup(&mut self) -> InquireResult<()> { + self.run_scorer(); + Ok(()) + } + fn submit(&mut self) -> InquireResult>>> { let answer = match self.validate_current_answer()? { Validation::Valid => Some(self.get_final_answer()), @@ -263,16 +283,7 @@ where let result = self.input.handle(input_action); if let InputActionResult::ContentChanged = result { - let mut options = self.score_options(); - options.sort_unstable_by_key(|(_idx, score)| Reverse(*score)); - - self.scored_options = options.into_iter().map(|(idx, _)| idx).collect(); - if self.config.reset_cursor { - let _ = self.update_cursor_position(0); - } else if self.scored_options.len() <= self.cursor_index { - let _ = self - .update_cursor_position(self.scored_options.len().saturating_sub(1)); - } + self.run_scorer(); } result.into() diff --git a/inquire/src/prompts/select/mod.rs b/inquire/src/prompts/select/mod.rs index 43d0164f..9bfff8a8 100644 --- a/inquire/src/prompts/select/mod.rs +++ b/inquire/src/prompts/select/mod.rs @@ -43,6 +43,7 @@ static DEFAULT_MATCHER: Lazy = Lazy::new(|| SkimMatcherV2::defaul /// - **Prompt message**: Required when creating the prompt. /// - **Options list**: Options displayed to the user. Must be **non-empty**. /// - **Starting cursor**: Index of the cursor when the prompt is first rendered. Default is 0 (first option). If the index is out-of-range of the option list, the prompt will fail with an [`InquireError::InvalidConfiguration`] error. +/// - **Starting filter input**: Sets the initial value of the filter section of the prompt. /// - **Help message**: Message displayed at the line below the prompt. /// - **Formatter**: Custom formatter in case you need to pre-process the user input before showing it as the final answer. /// - Prints the selected option string value by default. @@ -89,6 +90,9 @@ pub struct Select<'a, T> { /// Starting cursor index of the selection. pub starting_cursor: usize, + /// Starting filter input + pub starting_filter_input: Option<&'a str>, + /// Reset cursor position to first option on filter input change. /// Defaults to true. pub reset_cursor: bool, @@ -199,6 +203,7 @@ where scorer: Self::DEFAULT_SCORER, formatter: Self::DEFAULT_FORMATTER, render_config: get_configuration(), + starting_filter_input: None, } } @@ -244,6 +249,12 @@ where self } + /// Sets the starting filter input + pub fn with_starting_filter_input(mut self, starting_filter_input: &'a str) -> Self { + self.starting_filter_input = Some(starting_filter_input); + self + } + /// Sets the reset_cursor behaviour. /// Will reset cursor to first option on filter input change. /// Defaults to true. diff --git a/inquire/src/prompts/select/prompt.rs b/inquire/src/prompts/select/prompt.rs index fa8f53cf..16a07b9b 100644 --- a/inquire/src/prompts/select/prompt.rs +++ b/inquire/src/prompts/select/prompt.rs @@ -57,7 +57,10 @@ where scored_options, help_message: so.help_message, cursor_index: so.starting_cursor, - input: Input::new(), + input: so + .starting_filter_input + .map(Input::new_with) + .unwrap_or_else(Input::new), scorer: so.scorer, formatter: so.formatter, }) @@ -130,6 +133,18 @@ where ListOption::new(index, value) } + + fn run_scorer(&mut self) { + let mut options = self.score_options(); + options.sort_unstable_by_key(|(_idx, score)| Reverse(*score)); + + self.scored_options = options.into_iter().map(|(idx, _)| idx).collect(); + if self.config.reset_cursor { + let _ = self.update_cursor_position(0); + } else if self.scored_options.len() <= self.cursor_index { + let _ = self.update_cursor_position(self.scored_options.len().saturating_sub(1)); + } + } } impl<'a, Backend, T> Prompt for SelectPrompt<'a, T> @@ -153,6 +168,11 @@ where (self.formatter)(answer.as_ref()) } + fn setup(&mut self) -> InquireResult<()> { + self.run_scorer(); + Ok(()) + } + fn submit(&mut self) -> InquireResult>> { let answer = match self.has_answer_highlighted() { true => Some(self.get_final_answer()), @@ -174,16 +194,7 @@ where let result = self.input.handle(input_action); if let InputActionResult::ContentChanged = result { - let mut options = self.score_options(); - options.sort_unstable_by_key(|(_idx, score)| Reverse(*score)); - - self.scored_options = options.into_iter().map(|(idx, _)| idx).collect(); - if self.config.reset_cursor { - let _ = self.update_cursor_position(0); - } else if self.scored_options.len() <= self.cursor_index { - let _ = self - .update_cursor_position(self.scored_options.len().saturating_sub(1)); - } + self.run_scorer(); } result.into()