Skip to content

Commit

Permalink
Merge pull request #18 from SergioRibera/fix_fuzzy_match
Browse files Browse the repository at this point in the history
fix: 🐛 fuzzy finder behaviour
  • Loading branch information
romancitodev authored Jul 25, 2024
2 parents 089e27e + 1959d5d commit 1141355
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 15 deletions.
5 changes: 3 additions & 2 deletions src/tui/steps/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::io::Stderr;

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

use crate::tui::widgets::Autocomplete;
use crate::tui::widgets::{Autocomplete, AutocompletePriority};
use crate::{
config::SimpleCommitsConfig,
tui::{structs::COMMIT_TYPES, Step, StepResult},
Expand All @@ -20,10 +20,11 @@ impl Step for _Step {
) -> StepResult {
let commit = p.prompt(&mut Autocomplete::new(
"Select a type",
true,
AutocompletePriority::Label,
COMMIT_TYPES
.map(|c| SelectOption::new(c, c.label.to_owned()).with_hint(c.hint))
.to_vec(),
true,
));

state._type = commit?;
Expand Down
5 changes: 3 additions & 2 deletions src/tui/steps/emoji.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use promptuity::prompts::SelectOption;

use crate::config::SimpleCommitsConfig;
use crate::gen::EMOJIS;
use crate::tui::widgets::Autocomplete;
use crate::tui::widgets::{Autocomplete, AutocompletePriority};
use crate::tui::{Step, StepResult};

#[derive(Default)]
Expand All @@ -29,8 +29,9 @@ impl Step for _Step {
.to_vec();
let emoji = p.prompt(&mut Autocomplete::new(
"Select an emoji (optional)",
emojis_mapped,
false,
AutocompletePriority::Hint,
emojis_mapped,
))?;
state.emoji = Some(emoji);
Ok(())
Expand Down
8 changes: 6 additions & 2 deletions src/tui/steps/scopes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use promptuity::prompts::SelectOption;

use crate::{
config::SimpleCommitsConfig,
tui::{widgets::Autocomplete, Step, StepResult},
tui::{
widgets::{Autocomplete, AutocompletePriority},
Step, StepResult,
},
};

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

let scope = (!scope.is_empty()).then_some(scope);
Expand Down
2 changes: 1 addition & 1 deletion src/tui/widgets.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mod autocompleter;

pub use autocompleter::Autocomplete;
pub use autocompleter::{Autocomplete, AutocompletePriority};
55 changes: 47 additions & 8 deletions src/tui/widgets/autocompleter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,42 @@ use promptuity::pagination::paginate;
use promptuity::prompts::{DefaultSelectFormatter, SelectFormatter, SelectOption};
use promptuity::style::*;
use promptuity::{Error, InputCursor, Prompt, PromptBody, PromptInput, PromptState, RenderPayload};

pub struct Autocomplete {
formatter: DefaultSelectFormatter,
message: String,
page_size: usize,
options: Vec<SelectOption<String>>,
filtered_options: Vec<usize>,
filtered_options: Vec<(usize, i64)>,
index: usize,
input: InputCursor,
matcher: SkimMatcherV2,
priority: AutocompletePriority,
strict: bool,
skip: bool,
}

#[derive(Clone, Copy)]
pub enum AutocompletePriority {
Hint,
Label,
}

impl From<AutocompletePriority> for (i64, i64) {
fn from(value: AutocompletePriority) -> (i64, i64) {
match value {
AutocompletePriority::Hint => (1, 4),
AutocompletePriority::Label => (4, 1),
}
}
}

impl Autocomplete {
pub fn new(
message: impl std::fmt::Display,
options: Vec<SelectOption<String>>,
strict: bool,
priority: AutocompletePriority,
options: Vec<SelectOption<String>>,
) -> Self {
Self {
formatter: DefaultSelectFormatter::new(),
Expand All @@ -33,13 +51,15 @@ impl Autocomplete {
index: 0,
input: InputCursor::default(),
matcher: SkimMatcherV2::default(),
priority,
strict,
skip: false,
}
}

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

self.filtered_options = self
.options
Expand All @@ -48,19 +68,38 @@ impl Autocomplete {
.filter_map(|(i, option)| {
let label = &option.label;
let hint = option.hint.clone().unwrap_or_default();
self.matcher
.fuzzy_match(&format!("{label} {hint}"), &pattern)
.map(|_| i)
let a = self
.matcher
.fuzzy_match(label, &pattern)
.unwrap_or_default();
let b = self
.matcher
.fuzzy_match(&hint, &pattern)
.unwrap_or_default();

let c = (a.saturating_mul(priority_label))
.saturating_add(b.saturating_mul(priority_hint))
.saturating_sub(i as i64);

log::trace!("{pattern} -> {label}; {a} & {b} = {c}");
if c <= 0 && !pattern.is_empty() {
return None;
}

Some((i, c))
})
.collect::<Vec<_>>();

self.filtered_options.sort_by_key(|(_, s)| *s);
self.filtered_options.reverse();

self.index = std::cmp::min(self.filtered_options.len().saturating_sub(1), self.index);
}

fn current_option(&self) -> Option<&SelectOption<String>> {
self.filtered_options
.get(self.index)
.and_then(|idx| self.options.get(*idx))
.and_then(|(idx, _)| self.options.get(*idx))
}
}

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

self.filtered_options = (0..self.options.len()).collect();
self.filtered_options = (0..self.options.len()).map(|i| (i, 0)).collect();

Ok(())
}
Expand Down Expand Up @@ -197,7 +236,7 @@ impl Prompt for Autocomplete {
.items
.iter()
.enumerate()
.map(|(i, idx)| {
.map(|(i, (idx, _))| {
let option = self.options.get(*idx).unwrap();
let active = i == page.cursor;
self.formatter.option(
Expand Down

0 comments on commit 1141355

Please sign in to comment.