Add a matches method instead of remove_last

This commit is contained in:
ysthakur
2024-10-21 00:42:27 -04:00
parent 6442596949
commit 433fcb2eda
2 changed files with 58 additions and 47 deletions

View File

@ -59,7 +59,7 @@ impl CommandCompletion {
.max_results .max_results
> executables.len() as i64 > executables.len() as i64
{ {
continue; break;
} }
let Ok(name) = item.file_name().into_string() else { let Ok(name) = item.file_name().into_string() else {
continue; continue;
@ -72,8 +72,12 @@ impl CommandCompletion {
} else { } else {
name.clone() name.clone()
}; };
if matcher.add( if matcher.matches(&name) && is_executable::is_executable(item.path()) {
name.clone(), executables.insert(name.clone());
// This causes the matcher to match the executable name twice,
// but it's likely a cost we can eat
matcher.add(
name,
SemanticSuggestion { SemanticSuggestion {
suggestion: Suggestion { suggestion: Suggestion {
value, value,
@ -84,12 +88,7 @@ impl CommandCompletion {
// TODO: is there a way to create a test? // TODO: is there a way to create a test?
kind: None, kind: None,
}, },
) { );
if is_executable::is_executable(item.path()) {
executables.insert(name);
} else {
matcher.remove_last();
}
} }
} }
} }

View File

@ -34,6 +34,7 @@ enum State<T> {
items: Vec<(String, T)>, items: Vec<(String, T)>,
}, },
Fuzzy { Fuzzy {
matcher: Box<SkimMatcherV2>,
/// Holds (haystack, item, score) /// Holds (haystack, item, score)
items: Vec<(String, T, i64)>, items: Vec<(String, T, i64)>,
}, },
@ -57,63 +58,74 @@ impl<T> NuMatcher<T> {
needle: lowercase_needle, needle: lowercase_needle,
state: State::Prefix { items: Vec::new() }, state: State::Prefix { items: Vec::new() },
}, },
MatchAlgorithm::Fuzzy => NuMatcher { MatchAlgorithm::Fuzzy => {
let mut matcher = SkimMatcherV2::default();
if options.case_sensitive {
matcher = matcher.respect_case();
} else {
matcher = matcher.ignore_case();
};
NuMatcher {
options, options,
needle: orig_needle.to_owned(), needle: orig_needle.to_owned(),
state: State::Fuzzy { items: Vec::new() }, state: State::Fuzzy {
matcher: Box::new(matcher),
items: Vec::new(),
}, },
} }
} }
}
}
/// Add the given item if the given haystack matches. /// Add the given item if the given haystack matches.
/// ///
/// Returns whether the item was added. /// Returns whether the item was added.
pub fn add(&mut self, haystack: String, item: T) -> bool { pub fn add(&mut self, haystack: String, item: T) -> bool {
let haystack = trim_quotes_str(&haystack);
match &mut self.state { match &mut self.state {
State::Prefix { items } => { State::Prefix { items } => {
let haystack = trim_quotes_str(&haystack).to_owned(); let haystack = if self.options.case_sensitive {
let haystack_lowercased = if self.options.case_sensitive { Cow::Borrowed(haystack)
Cow::Borrowed(&haystack)
} else { } else {
Cow::Owned(haystack.to_folded_case()) Cow::Owned(haystack.to_folded_case())
}; };
let matches = if self.options.positional { let matches = if self.options.positional {
haystack_lowercased.starts_with(self.needle.as_str()) haystack.starts_with(self.needle.as_str())
} else { } else {
haystack_lowercased.contains(self.needle.as_str()) haystack.contains(self.needle.as_str())
}; };
if matches { if matches {
items.push((haystack, item)); items.push((haystack.to_string(), item));
} }
matches matches
} }
State::Fuzzy { items } => { State::Fuzzy { items, matcher } => {
let mut matcher = SkimMatcherV2::default(); let Some(score) = matcher.fuzzy_match(haystack, &self.needle) else {
if self.options.case_sensitive {
matcher = matcher.respect_case();
} else {
matcher = matcher.ignore_case();
};
let Some(score) = matcher.fuzzy_match(&haystack, &self.needle) else {
return false; return false;
}; };
items.push((haystack, item, score)); items.push((haystack.to_string(), item, score));
true true
} }
} }
} }
/// Remove the last added item. This is useful if you want to filter matched /// Returns whether the item was added.
/// completions by some expensive condition. You can call `add`, run the expensive condition, pub fn matches(&mut self, haystack: &str) -> bool {
/// then call `remove_last` if the expensive condition is false. let haystack = trim_quotes_str(haystack).to_owned();
pub fn remove_last(&mut self) {
match &mut self.state { match &mut self.state {
State::Prefix { items } => { State::Prefix { .. } => {
items.pop(); let haystack = if self.options.case_sensitive {
Cow::Borrowed(&haystack)
} else {
Cow::Owned(haystack.to_folded_case())
};
if self.options.positional {
haystack.starts_with(self.needle.as_str())
} else {
haystack.contains(self.needle.as_str())
} }
State::Fuzzy { items } => {
items.pop();
} }
State::Fuzzy { matcher, .. } => matcher.fuzzy_match(&haystack, &self.needle).is_some(),
} }
} }