Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 🐛 fuzzy finder behaviour #18

Merged
merged 1 commit into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading