Skip to content

Commit 1141355

Browse files
authored
Merge pull request #18 from SergioRibera/fix_fuzzy_match
fix: 🐛 fuzzy finder behaviour
2 parents 089e27e + 1959d5d commit 1141355

5 files changed

Lines changed: 60 additions & 15 deletions

File tree

src/tui/steps/commit.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::io::Stderr;
22

33
use promptuity::{prompts::SelectOption, Promptuity};
44

5-
use crate::tui::widgets::Autocomplete;
5+
use crate::tui::widgets::{Autocomplete, AutocompletePriority};
66
use crate::{
77
config::SimpleCommitsConfig,
88
tui::{structs::COMMIT_TYPES, Step, StepResult},
@@ -20,10 +20,11 @@ impl Step for _Step {
2020
) -> StepResult {
2121
let commit = p.prompt(&mut Autocomplete::new(
2222
"Select a type",
23+
true,
24+
AutocompletePriority::Label,
2325
COMMIT_TYPES
2426
.map(|c| SelectOption::new(c, c.label.to_owned()).with_hint(c.hint))
2527
.to_vec(),
26-
true,
2728
));
2829

2930
state._type = commit?;

src/tui/steps/emoji.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use promptuity::prompts::SelectOption;
22

33
use crate::config::SimpleCommitsConfig;
44
use crate::gen::EMOJIS;
5-
use crate::tui::widgets::Autocomplete;
5+
use crate::tui::widgets::{Autocomplete, AutocompletePriority};
66
use crate::tui::{Step, StepResult};
77

88
#[derive(Default)]
@@ -29,8 +29,9 @@ impl Step for _Step {
2929
.to_vec();
3030
let emoji = p.prompt(&mut Autocomplete::new(
3131
"Select an emoji (optional)",
32-
emojis_mapped,
3332
false,
33+
AutocompletePriority::Hint,
34+
emojis_mapped,
3435
))?;
3536
state.emoji = Some(emoji);
3637
Ok(())

src/tui/steps/scopes.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use promptuity::prompts::SelectOption;
33

44
use crate::{
55
config::SimpleCommitsConfig,
6-
tui::{widgets::Autocomplete, Step, StepResult},
6+
tui::{
7+
widgets::{Autocomplete, AutocompletePriority},
8+
Step, StepResult,
9+
},
710
};
811

912
#[derive(Default)]
@@ -27,8 +30,9 @@ impl Step for _Step {
2730
.collect::<Vec<_>>();
2831
let scope = p.prompt(&mut Autocomplete::new(
2932
"Select an scope",
30-
mapped_scopes,
3133
false,
34+
AutocompletePriority::Label,
35+
mapped_scopes,
3236
))?;
3337

3438
let scope = (!scope.is_empty()).then_some(scope);

src/tui/widgets.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
mod autocompleter;
22

3-
pub use autocompleter::Autocomplete;
3+
pub use autocompleter::{Autocomplete, AutocompletePriority};

src/tui/widgets/autocompleter.rs

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,42 @@ use promptuity::pagination::paginate;
55
use promptuity::prompts::{DefaultSelectFormatter, SelectFormatter, SelectOption};
66
use promptuity::style::*;
77
use promptuity::{Error, InputCursor, Prompt, PromptBody, PromptInput, PromptState, RenderPayload};
8+
89
pub struct Autocomplete {
910
formatter: DefaultSelectFormatter,
1011
message: String,
1112
page_size: usize,
1213
options: Vec<SelectOption<String>>,
13-
filtered_options: Vec<usize>,
14+
filtered_options: Vec<(usize, i64)>,
1415
index: usize,
1516
input: InputCursor,
1617
matcher: SkimMatcherV2,
18+
priority: AutocompletePriority,
1719
strict: bool,
1820
skip: bool,
1921
}
2022

23+
#[derive(Clone, Copy)]
24+
pub enum AutocompletePriority {
25+
Hint,
26+
Label,
27+
}
28+
29+
impl From<AutocompletePriority> for (i64, i64) {
30+
fn from(value: AutocompletePriority) -> (i64, i64) {
31+
match value {
32+
AutocompletePriority::Hint => (1, 4),
33+
AutocompletePriority::Label => (4, 1),
34+
}
35+
}
36+
}
37+
2138
impl Autocomplete {
2239
pub fn new(
2340
message: impl std::fmt::Display,
24-
options: Vec<SelectOption<String>>,
2541
strict: bool,
42+
priority: AutocompletePriority,
43+
options: Vec<SelectOption<String>>,
2644
) -> Self {
2745
Self {
2846
formatter: DefaultSelectFormatter::new(),
@@ -33,13 +51,15 @@ impl Autocomplete {
3351
index: 0,
3452
input: InputCursor::default(),
3553
matcher: SkimMatcherV2::default(),
54+
priority,
3655
strict,
3756
skip: false,
3857
}
3958
}
4059

4160
fn run_filter(&mut self) {
4261
let pattern = self.input.value();
62+
let (priority_label, priority_hint): (i64, i64) = self.priority.into();
4363

4464
self.filtered_options = self
4565
.options
@@ -48,19 +68,38 @@ impl Autocomplete {
4868
.filter_map(|(i, option)| {
4969
let label = &option.label;
5070
let hint = option.hint.clone().unwrap_or_default();
51-
self.matcher
52-
.fuzzy_match(&format!("{label} {hint}"), &pattern)
53-
.map(|_| i)
71+
let a = self
72+
.matcher
73+
.fuzzy_match(label, &pattern)
74+
.unwrap_or_default();
75+
let b = self
76+
.matcher
77+
.fuzzy_match(&hint, &pattern)
78+
.unwrap_or_default();
79+
80+
let c = (a.saturating_mul(priority_label))
81+
.saturating_add(b.saturating_mul(priority_hint))
82+
.saturating_sub(i as i64);
83+
84+
log::trace!("{pattern} -> {label}; {a} & {b} = {c}");
85+
if c <= 0 && !pattern.is_empty() {
86+
return None;
87+
}
88+
89+
Some((i, c))
5490
})
5591
.collect::<Vec<_>>();
5692

93+
self.filtered_options.sort_by_key(|(_, s)| *s);
94+
self.filtered_options.reverse();
95+
5796
self.index = std::cmp::min(self.filtered_options.len().saturating_sub(1), self.index);
5897
}
5998

6099
fn current_option(&self) -> Option<&SelectOption<String>> {
61100
self.filtered_options
62101
.get(self.index)
63-
.and_then(|idx| self.options.get(*idx))
102+
.and_then(|(idx, _)| self.options.get(*idx))
64103
}
65104
}
66105

@@ -78,7 +117,7 @@ impl Prompt for Autocomplete {
78117
return Err(Error::Config("options cannot be empty.".into()));
79118
}
80119

81-
self.filtered_options = (0..self.options.len()).collect();
120+
self.filtered_options = (0..self.options.len()).map(|i| (i, 0)).collect();
82121

83122
Ok(())
84123
}
@@ -197,7 +236,7 @@ impl Prompt for Autocomplete {
197236
.items
198237
.iter()
199238
.enumerate()
200-
.map(|(i, idx)| {
239+
.map(|(i, (idx, _))| {
201240
let option = self.options.get(*idx).unwrap();
202241
let active = i == page.cursor;
203242
self.formatter.option(

0 commit comments

Comments
 (0)