Skip to content

Commit

Permalink
fzf: add SearchMode enum to FzfQuery
Browse files Browse the repository at this point in the history
this speeds up the common case where the query is a single fuzzy pattern
  • Loading branch information
noib3 committed Nov 6, 2023
1 parent 903b47f commit 7f47696
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 31 deletions.
16 changes: 4 additions & 12 deletions src/algos/fzf/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ pub fn parse(s: &str) -> FzfQuery<'static> {

#[cfg(test)]
mod parse_tests {
use super::super::query::MatchType;
use super::super::query::*;
use super::*;

#[test]
Expand All @@ -331,17 +331,9 @@ mod parse_tests {
fn parse_query_single_fuzzy() {
let query = parse("foo");

let conditions = query.conditions();

assert_eq!(conditions.len(), 1);

let condition = conditions.iter().next().unwrap();

let mut patterns = condition.or_patterns();

assert_eq!(patterns.len(), 1);

let pattern = patterns.next().unwrap();
let SearchMode::NotExtended(pattern) = query.search_mode else {
panic!();
};

assert_eq!(pattern.into_string(), "foo");

Expand Down
64 changes: 49 additions & 15 deletions src/algos/fzf/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,67 @@ type FuzzyAlgo<T> = fn(
/// TODO: docs.
#[derive(Clone, Copy)]
pub struct FzfQuery<'a> {
conditions: &'a [Condition<'a>],
pub(super) search_mode: SearchMode<'a>,
}

/// TODO: docs
#[derive(Clone, Copy)]
pub(super) enum SearchMode<'a> {
/// TODO: docs
Extended(&'a [Condition<'a>]),

/// TODO: docs
NotExtended(Pattern<'a>),
}

impl core::fmt::Debug for FzfQuery<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let s = self
.conditions
.iter()
.map(|condition| format!("{:?}", condition))
.collect::<Vec<_>>()
.join(" && ");
let s = match self.search_mode {
SearchMode::Extended(conditions) => conditions
.iter()
.map(|condition| format!("{:?}", condition))
.collect::<Vec<_>>()
.join(" && "),

SearchMode::NotExtended(pattern) => pattern.into_string(),
};

f.debug_tuple("FzfQuery").field(&s).finish()
}
}

impl<'a> FzfQuery<'a> {
/// TODO: docs
#[inline(always)]
pub(super) fn conditions(&self) -> &[Condition<'a>] {
self.conditions
}

/// TODO: docs
#[inline]
pub(super) fn is_empty(&self) -> bool {
self.conditions.is_empty()
match self.search_mode {
SearchMode::Extended(conditions) => conditions.is_empty(),
SearchMode::NotExtended(pattern) => pattern.is_empty(),
}
}

/// TODO: docs
#[inline]
pub(super) fn new(conditions: &'a [Condition<'a>]) -> Self {
Self { conditions }
// If there's only one condition with a single pattern, and that
// pattern is fuzzy, then we can use the non-extended search mode.
if conditions.len() == 1 {
let mut patterns = conditions[0].or_patterns();

let first_pattern = patterns
.next()
.expect("conditions always have at least one pattern");

if patterns.next().is_none()
&& matches!(first_pattern.match_type, MatchType::Fuzzy)
{
return Self {
search_mode: SearchMode::NotExtended(first_pattern),
};
}
}

Self { search_mode: SearchMode::Extended(conditions) }
}
}

Expand Down Expand Up @@ -149,6 +177,12 @@ impl<'a> Pattern<'a> {
self.text.iter().copied()
}

/// TODO: docs
#[inline]
pub(super) fn is_empty(&self) -> bool {
self.text.is_empty()
}

/// TODO: docs
#[inline]
pub(super) fn into_string(self) -> String {
Expand Down
5 changes: 4 additions & 1 deletion src/algos/fzf/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ impl Metric for FzfV1 {
return None;
}

let pattern = query.conditions()[0].or_patterns().next().unwrap();
let pattern = match query.search_mode {
SearchMode::NotExtended(pattern) => pattern,
SearchMode::Extended(_) => todo!(),
};

let case_matcher =
self.case_sensitivity.matcher(pattern.has_uppercase);
Expand Down
31 changes: 28 additions & 3 deletions src/algos/fzf/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,38 @@ impl Metric for FzfV2 {
return None;
}

let is_candidate_ascii = candidate.is_ascii();

let conditions = match query.search_mode {
SearchMode::NotExtended(pattern) => {
let is_case_sensitive = match self.case_sensitivity {
CaseSensitivity::Sensitive => true,
CaseSensitivity::Insensitive => false,
CaseSensitivity::Smart => pattern.has_uppercase,
};

let (score, matched_ranges) = fzf_v2(
pattern,
candidate,
&self.scheme,
is_case_sensitive,
self.with_matched_ranges,
(&mut self.slab, is_candidate_ascii),
)?;

let distance = FzfDistance::from_score(score);

return Some(Match::new(distance, matched_ranges));
},

SearchMode::Extended(conditions) => conditions,
};

let mut total_score = 0;

let mut matched_ranges = MatchedRanges::default();

let is_candidate_ascii = candidate.is_ascii();

for condition in query.conditions() {
for condition in conditions {
let (score, ranges) =
condition.or_patterns().find_map(|pattern| {
let is_case_sensitive = match self.case_sensitivity {
Expand Down

0 comments on commit 7f47696

Please sign in to comment.