summaryrefslogtreecommitdiff
path: root/helix-term/src/ui/fuzzy_match.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src/ui/fuzzy_match.rs')
-rw-r--r--helix-term/src/ui/fuzzy_match.rs74
1 files changed, 74 insertions, 0 deletions
diff --git a/helix-term/src/ui/fuzzy_match.rs b/helix-term/src/ui/fuzzy_match.rs
new file mode 100644
index 00000000..e25d7328
--- /dev/null
+++ b/helix-term/src/ui/fuzzy_match.rs
@@ -0,0 +1,74 @@
+use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
+use fuzzy_matcher::FuzzyMatcher;
+
+#[cfg(test)]
+mod test;
+
+pub struct FuzzyQuery {
+ queries: Vec<String>,
+}
+
+impl FuzzyQuery {
+ pub fn new(query: &str) -> FuzzyQuery {
+ let mut saw_backslash = false;
+ let queries = query
+ .split(|c| {
+ saw_backslash = match c {
+ ' ' if !saw_backslash => return true,
+ '\\' => true,
+ _ => false,
+ };
+ false
+ })
+ .filter_map(|query| {
+ if query.is_empty() {
+ None
+ } else {
+ Some(query.replace("\\ ", " "))
+ }
+ })
+ .collect();
+ FuzzyQuery { queries }
+ }
+
+ pub fn fuzzy_match(&self, item: &str, matcher: &Matcher) -> Option<i64> {
+ // use the rank of the first query for the rank, because merging ranks is not really possible
+ // this behaviour matches fzf and skim
+ let score = matcher.fuzzy_match(item, self.queries.get(0)?)?;
+ if self
+ .queries
+ .iter()
+ .any(|query| matcher.fuzzy_match(item, query).is_none())
+ {
+ return None;
+ }
+ Some(score)
+ }
+
+ pub fn fuzzy_indicies(&self, item: &str, matcher: &Matcher) -> Option<(i64, Vec<usize>)> {
+ if self.queries.len() == 1 {
+ return matcher.fuzzy_indices(item, &self.queries[0]);
+ }
+
+ // use the rank of the first query for the rank, because merging ranks is not really possible
+ // this behaviour matches fzf and skim
+ let (score, mut indicies) = matcher.fuzzy_indices(item, self.queries.get(0)?)?;
+
+ // fast path for the common case of not using a space
+ // during matching this branch should be free thanks to branch prediction
+ if self.queries.len() == 1 {
+ return Some((score, indicies));
+ }
+
+ for query in &self.queries[1..] {
+ let (_, matched_indicies) = matcher.fuzzy_indices(item, query)?;
+ indicies.extend_from_slice(&matched_indicies);
+ }
+
+ // deadup and remove duplicate matches
+ indicies.sort_unstable();
+ indicies.dedup();
+
+ Some((score, indicies))
+ }
+}