Skip to content

Commit

Permalink
Fix with_starting_cursor not being respected on selectable prompts (#197
Browse files Browse the repository at this point in the history
)
  • Loading branch information
mikaelmello authored Dec 24, 2023
1 parent 1c7ece7 commit 70bf4ca
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 8 deletions.
10 changes: 7 additions & 3 deletions inquire/src/prompts/multiselect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ where
}

/// Sets the starting cursor index.
///
/// This index might be overriden if the `reset_cursor` option is set to true (default)
/// and starting_filter_input is set to something other than None.
pub fn with_starting_cursor(mut self, starting_cursor: usize) -> Self {
self.starting_cursor = starting_cursor;
self
Expand All @@ -313,9 +316,10 @@ where
self
}

/// Sets the reset_cursor behaviour.
/// Will reset cursor to first option on filter input change.
/// Defaults to true.
/// Sets the reset_cursor behaviour. Defaults to true.
///
/// When there's an input change that results in a different list of options being displayed,
/// whether by filtering or re-ordering, the cursor will be reset to highlight the first option.
pub fn with_reset_cursor(mut self, reset_cursor: bool) -> Self {
self.reset_cursor = reset_cursor;
self
Expand Down
9 changes: 8 additions & 1 deletion inquire/src/prompts/multiselect/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,14 @@ where
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();
let new_scored_options = options.iter().map(|(idx, _)| *idx).collect::<Vec<usize>>();

if self.scored_options == new_scored_options {
return;
}

self.scored_options = new_scored_options;

if self.config.reset_cursor {
let _ = self.update_cursor_position(0);
} else if self.scored_options.len() <= self.cursor_index {
Expand Down
65 changes: 65 additions & 0 deletions inquire/src/prompts/multiselect/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,68 @@ fn list_option_indexes_are_relative_to_input_vec() {

assert_eq!(vec![ListOption::new(1, 2), ListOption::new(2, 3)], ans);
}

#[test]
// Anti-regression test: https://github.com/mikaelmello/inquire/issues/195
fn starting_cursor_is_respected() {
let read: Vec<KeyEvent> = [KeyCode::Char(' '), KeyCode::Enter]
.iter()
.map(|c| KeyEvent::from(*c))
.collect();

let mut read = read.iter();

let options = vec![1, 2, 3];

let mut write: Vec<u8> = Vec::new();
let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();

let ans = MultiSelect::new("Question", options)
.with_starting_cursor(2)
.prompt_with_backend(&mut backend)
.unwrap();

assert_eq!(vec![ListOption::new(2, 3)], ans);
}

#[test]
fn naive_assert_fuzzy_match_as_default_scorer() {
let read: Vec<KeyEvent> = [
KeyCode::Char('w'),
KeyCode::Char('r'),
KeyCode::Char('r'),
KeyCode::Char('y'),
KeyCode::Char(' '),
KeyCode::Enter,
]
.iter()
.map(|c| KeyEvent::from(*c))
.collect();

let mut read = read.iter();

let options = vec![
"Banana",
"Apple",
"Strawberry",
"Grapes",
"Lemon",
"Tangerine",
"Watermelon",
"Orange",
"Pear",
"Avocado",
"Pineapple",
];

let mut write: Vec<u8> = Vec::new();
let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();

let ans = MultiSelect::new("Question", options)
.prompt_with_backend(&mut backend)
.unwrap();

assert_eq!(vec![ListOption::new(2, "Strawberry")], ans);
}
10 changes: 7 additions & 3 deletions inquire/src/prompts/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ where
}

/// Sets the starting cursor index.
///
/// This index might be overriden if the `reset_cursor` option is set to true (default)
/// and starting_filter_input is set to something other than None.
pub fn with_starting_cursor(mut self, starting_cursor: usize) -> Self {
self.starting_cursor = starting_cursor;
self
Expand All @@ -255,9 +258,10 @@ where
self
}

/// Sets the reset_cursor behaviour.
/// Will reset cursor to first option on filter input change.
/// Defaults to true.
/// Sets the reset_cursor behaviour. Defaults to true.
///
/// When there's an input change that results in a different list of options being displayed,
/// whether by filtering or re-ordering, the cursor will be reset to highlight the first option.
pub fn with_reset_cursor(mut self, reset_cursor: bool) -> Self {
self.reset_cursor = reset_cursor;
self
Expand Down
9 changes: 8 additions & 1 deletion inquire/src/prompts/select/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,14 @@ where
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();
let new_scored_options = options.iter().map(|(idx, _)| *idx).collect::<Vec<usize>>();

if self.scored_options == new_scored_options {
return;
}

self.scored_options = new_scored_options;

if self.config.reset_cursor {
let _ = self.update_cursor_position(0);
} else if self.scored_options.len() <= self.cursor_index {
Expand Down
64 changes: 64 additions & 0 deletions inquire/src/prompts/select/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,67 @@ fn down_arrow_on_empty_list_does_not_panic() {

assert_eq!(ListOption::new(0, 1), ans);
}

#[test]
// Anti-regression test: https://github.com/mikaelmello/inquire/issues/195
fn starting_cursor_is_respected() {
let read: Vec<KeyEvent> = [KeyCode::Enter]
.iter()
.map(|c| KeyEvent::from(*c))
.collect();

let mut read = read.iter();

let options = vec![1, 2, 3];

let mut write: Vec<u8> = Vec::new();
let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();

let ans = Select::new("Question", options)
.with_starting_cursor(2)
.prompt_with_backend(&mut backend)
.unwrap();

assert_eq!(ListOption::new(2, 3), ans);
}

#[test]
fn naive_assert_fuzzy_match_as_default_scorer() {
let read: Vec<KeyEvent> = [
KeyCode::Char('w'),
KeyCode::Char('r'),
KeyCode::Char('r'),
KeyCode::Char('y'),
KeyCode::Enter,
]
.iter()
.map(|c| KeyEvent::from(*c))
.collect();

let mut read = read.iter();

let options = vec![
"Banana",
"Apple",
"Strawberry",
"Grapes",
"Lemon",
"Tangerine",
"Watermelon",
"Orange",
"Pear",
"Avocado",
"Pineapple",
];

let mut write: Vec<u8> = Vec::new();
let terminal = CrosstermTerminal::new_with_io(&mut write, &mut read);
let mut backend = Backend::new(terminal, RenderConfig::default()).unwrap();

let ans = Select::new("Question", options)
.prompt_with_backend(&mut backend)
.unwrap();

assert_eq!(ListOption::new(2, "Strawberry"), ans);
}

0 comments on commit 70bf4ca

Please sign in to comment.