diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c55609..e747858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ## Changes - Log to stderr instead of stdout. +- Prefer earlier match with same score for input search. ## Fixes diff --git a/src/state/filtered_lines.rs b/src/state/filtered_lines.rs index d78f884..9c38ace 100644 --- a/src/state/filtered_lines.rs +++ b/src/state/filtered_lines.rs @@ -8,13 +8,19 @@ pub type ContinuousMatch<'a> = sublime_fuzzy::ContinuousMatches<'a>; pub struct FilteredLines(Either, usize>); +fn order_items(m1: &Match, m2: &Match) -> std::cmp::Ordering { + m2.score() + .cmp(&m1.score()) + .then_with(|| m1.matched_indices().cmp(m2.matched_indices())) +} + impl FilteredLines { pub fn searched<'a>(entries: impl Iterator, search_string: &str) -> Self { let mut v = entries .enumerate() .filter_map(|(i, e)| Some((i, sublime_fuzzy::best_match(search_string, e)?))) .collect::>(); - v.sort_by_key(|(_, m)| std::cmp::Reverse(m.score())); + v.sort_by(|(_, m1), (_, m2)| order_items(m1, m2)); Self(Either::Left(v)) } @@ -75,3 +81,28 @@ impl FilteredLines { .into_iter() } } + +#[cfg(test)] +mod tests { + use itertools::Itertools; + use test_case::test_case; + + use super::*; + + #[test_case(vec!["asd"], "asd", vec!["asd"])] + #[test_case(vec!["xy", "yx"], "xy", vec!["xy"])] + #[test_case(vec!["xy", "yx"], "x", vec!["xy", "yx"])] + #[test_case(vec!["xy", "yx"], "y", vec!["yx", "xy"])] + #[test_case(vec!["ab-cd", "ac-bd"], "cd", vec!["ab-cd", "ac-bd"])] + #[test_case(vec!["ab-cd", "cd-ab"], "ab", vec!["ab-cd", "cd-ab"])] + fn test_order_items(input: Vec<&str>, query: &str, expected: Vec<&str>) { + let result = input + .into_iter() + .filter_map(|x| Some(dbg!(x, sublime_fuzzy::best_match(query, x)?))) + .sorted_by(|(_, m1), (_, m2)| order_items(m1, m2)) + .map(|(x, _)| (x)) + .collect::>(); + + assert_eq!(result, expected) + } +}