#![allow(clippy::single_range_in_vec_init)] use norm::fzf::{bonus, penalty}; use norm::CaseSensitivity; use CaseSensitivity::*; pub fn upstream_empty() { let (_, m) = fzf::(Insensitive, "", "foo"); let m = m.unwrap(); assert_eq!(m.distance.into_score(), 0); assert!(m.matched_ranges.is_empty()); } pub fn upstream_fuzzy_1() { let (_, m) = fzf::(Insensitive, "oBZ", "fooBarbaz1"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + bonus::CAMEL_123 - penalty::GAP_START - 3 * penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [2..4, 8..9]); } pub fn upstream_fuzzy_2() { let (fzf, m) = fzf::(Insensitive, "fbb", "foo bar baz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * fzf.scheme().bonus_boundary_white - 2 * penalty::GAP_START - 4 * penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [0..1, 4..5, 8..9]); } pub fn upstream_fuzzy_3() { let (_, m) = fzf::(Insensitive, "rdoc", "/AutomatorDocument.icns"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 4 * bonus::MATCH + 2 * bonus::CONSECUTIVE + bonus::CAMEL_123 ); assert_eq!(m.matched_ranges, [9..13]); } pub fn upstream_fuzzy_4() { let (fzf, m) = fzf::(Insensitive, "zshc", "/man1/zshcompctl.1"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 4 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 3) * fzf.scheme().bonus_boundary_delimiter ); assert_eq!(m.matched_ranges, [6..10]); } pub fn upstream_fuzzy_5() { let (fzf, m) = fzf::(Insensitive, "zshc", "/.oh-my-zsh/cache"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 4 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * bonus::BOUNDARY + fzf.scheme().bonus_boundary_delimiter - penalty::GAP_START ); assert_eq!(m.matched_ranges, [8..11, 12..13]); } pub fn upstream_fuzzy_6() { let (_, m) = fzf::(Insensitive, "12356", "ab0123 456"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 5 * bonus::MATCH + 3 * bonus::CONSECUTIVE - penalty::GAP_START - penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [3..6, 8..10]); } pub fn upstream_fuzzy_7() { let (_, m) = fzf::(Insensitive, "12356", "abc123 456"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 5 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * bonus::CAMEL_123 + bonus::CONSECUTIVE - penalty::GAP_START - penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [3..6, 8..10]); } pub fn upstream_fuzzy_8() { let (fzf, m) = fzf::(Insensitive, "fbb", "foo/bar/baz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + bonus::FIRST_QUERY_CHAR_MULTIPLIER * fzf.scheme().bonus_boundary_white + 2 * fzf.scheme().bonus_boundary_delimiter - 2 * penalty::GAP_START - 4 * penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [0..1, 4..5, 8..9]); } pub fn upstream_fuzzy_9() { let (fzf, m) = fzf::(Insensitive, "fbb", "fooBarBaz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + bonus::FIRST_QUERY_CHAR_MULTIPLIER * fzf.scheme().bonus_boundary_white + 2 * bonus::CAMEL_123 - 2 * penalty::GAP_START - 2 * penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [0..1, 3..4, 6..7]); } pub fn upstream_fuzzy_10() { let (fzf, m) = fzf::(Insensitive, "fbb", "foo barbaz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 1) * fzf.scheme().bonus_boundary_white - 2 * penalty::GAP_START - 3 * penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [0..1, 4..5, 7..8]); } pub fn upstream_fuzzy_11() { let (fzf, m) = fzf::(Insensitive, "foob", "fooBar Baz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 4 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 3) * fzf.scheme().bonus_boundary_white ); assert_eq!(m.matched_ranges, [0..4]); } pub fn upstream_fuzzy_12() { let (_, m) = fzf::(Insensitive, "foo-b", "xFoo-Bar Baz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 5 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * bonus::CAMEL_123 + bonus::NON_WORD + bonus::BOUNDARY ); assert_eq!(m.matched_ranges, [1..6]); } pub fn upstream_fuzzy_13() { let (_, m) = fzf::(Sensitive, "oBz", "fooBarbaz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + bonus::CAMEL_123 - penalty::GAP_START - 3 * penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [2..4, 8..9]); } pub fn upstream_fuzzy_14() { let (fzf, m) = fzf::(Sensitive, "FBB", "Foo/Bar/Baz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + bonus::FIRST_QUERY_CHAR_MULTIPLIER * fzf.scheme().bonus_boundary_white + 2 * fzf.scheme().bonus_boundary_delimiter - 2 * penalty::GAP_START - 4 * penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [0..1, 4..5, 8..9]); } pub fn upstream_fuzzy_15() { let (fzf, m) = fzf::(Sensitive, "FBB", "FooBarBaz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + bonus::FIRST_QUERY_CHAR_MULTIPLIER * fzf.scheme().bonus_boundary_white + 2 * bonus::CAMEL_123 - 2 * penalty::GAP_START - 2 * penalty::GAP_EXTENSION ); assert_eq!(m.matched_ranges, [0..1, 3..4, 6..7]); } pub fn upstream_fuzzy_16() { let (fzf, m) = fzf::(Sensitive, "FooB", "FooBar Baz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 4 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * fzf.scheme().bonus_boundary_white + bonus::CAMEL_123.max(fzf.scheme().bonus_boundary_white) ); assert_eq!(m.matched_ranges, [0..4]); } pub fn upstream_fuzzy_17() { let (_, m) = fzf::(Sensitive, "o-ba", "foo-bar"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 4 * bonus::MATCH + 3 * bonus::BOUNDARY ); assert_eq!(m.matched_ranges, [2..6]); } pub fn upstream_fuzzy_18() { let (_, m) = fzf::(Sensitive, "oBZ", "fooBarbaz"); assert!(m.is_none()); } pub fn upstream_fuzzy_19() { let (_, m) = fzf::(Sensitive, "fbb", "Foo Bar Baz"); assert!(m.is_none()); } pub fn upstream_fuzzy_20() { let (_, m) = fzf::(Sensitive, "fooBarbazz", "fooBarbaz"); assert!(m.is_none()); } pub fn upstream_exact_1() { let (_, m) = fzf::(Sensitive, "'oBA", "fooBarbaz"); assert!(m.is_none()); } pub fn upstream_exact_2() { let (_, m) = fzf::(Sensitive, "'fooBarbazz", "fooBarbaz"); assert!(m.is_none()); } pub fn upstream_exact_3() { let (_, m) = fzf::(Insensitive, "'oBA", "fooBarbaz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + bonus::CAMEL_123 + bonus::CONSECUTIVE ); assert_eq!(m.matched_ranges, [2..5]); } pub fn upstream_exact_4() { let (_, m) = fzf::(Insensitive, "'rdoc", "/AutomatorDocument.icns"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 4 * bonus::MATCH + bonus::CAMEL_123 + 2 * bonus::CONSECUTIVE ); assert_eq!(m.matched_ranges, [9..13]); } pub fn upstream_exact_5() { let (fzf, m) = fzf::(Insensitive, "'zshc", "/man1/zshcompctl.1"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 4 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 3) * fzf.scheme().bonus_boundary_delimiter ); assert_eq!(m.matched_ranges, [6..10]); } pub fn upstream_exact_6() { let (fzf, m) = fzf::(Insensitive, "'zsh/c", "/.oh-my-zsh/cache"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 5 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 3) * bonus::BOUNDARY + fzf.scheme().bonus_boundary_delimiter ); assert_eq!(m.matched_ranges, [8..13]); } pub fn upstream_exact_7() { let (_, m) = fzf::(Insensitive, "'oo", "foobar foo"); let m = m.unwrap(); assert_eq!(m.distance.into_score(), 2 * bonus::MATCH + bonus::CONSECUTIVE); assert_eq!(m.matched_ranges, [1..3]); } pub fn upstream_prefix_1() { let (_, m) = fzf::(Sensitive, "^Foo", "fooBarbaz"); assert!(m.is_none()); } pub fn upstream_prefix_2() { let (_, m) = fzf::(Sensitive, "^baz", "fooBarBaz"); assert!(m.is_none()); } pub fn upstream_prefix_3() { let (fzf, m) = fzf::(Insensitive, "^Foo", "fooBarbaz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * fzf.scheme().bonus_boundary_white ); assert_eq!(m.matched_ranges, [0..3]); } pub fn upstream_prefix_4() { let (fzf, m) = fzf::(Insensitive, "^foo", "foOBarBaZ"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * fzf.scheme().bonus_boundary_white ); assert_eq!(m.matched_ranges, [0..3]); } pub fn upstream_prefix_5() { let (fzf, m) = fzf::(Insensitive, "^f-o", "f-oBarbaz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * fzf.scheme().bonus_boundary_white ); assert_eq!(m.matched_ranges, [0..3]); } pub fn upstream_prefix_6() { let (fzf, m) = fzf::(Insensitive, "^foo", " fooBar"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * fzf.scheme().bonus_boundary_white ); assert_eq!(m.matched_ranges, [1..4]); } pub fn upstream_prefix_7() { let (fzf, m) = fzf::(Insensitive, "\\ fo", " fooBar"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * fzf.scheme().bonus_boundary_white ); assert_eq!(m.matched_ranges, [0..3]); } pub fn upstream_prefix_8() { let (_, m) = fzf::(Insensitive, "^foo", " fo"); assert!(m.is_none()); } pub fn upstream_suffix_1() { let (_, m) = fzf::(Sensitive, "Baz$", "fooBarbaz"); assert!(m.is_none()); } pub fn upstream_suffix_2() { let (_, m) = fzf::(Insensitive, "Foo$", "fooBarBaz"); assert!(m.is_none()); } pub fn upstream_suffix_3() { let (_, m) = fzf::(Insensitive, "baz$", "fooBarbaz"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + 2 * bonus::CONSECUTIVE ); assert_eq!(m.matched_ranges, [6..9]); } pub fn upstream_suffix_4() { let (_, m) = fzf::(Insensitive, "baz$", "fooBarBaZ"); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + (bonus::FIRST_QUERY_CHAR_MULTIPLIER + 2) * bonus::CAMEL_123 ); assert_eq!(m.matched_ranges, [6..9]); } pub fn upstream_suffix_5() { let (_, m) = fzf::(Insensitive, "baz$", "fooBarbaz "); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 3 * bonus::MATCH + 2 * bonus::CONSECUTIVE ); assert_eq!(m.matched_ranges, [6..9]); } pub fn upstream_suffix_6() { let (fzf, m) = fzf::(Insensitive, "baz\\ $", "fooBarbaz "); let m = m.unwrap(); assert_eq!( m.distance.into_score(), 4 * bonus::MATCH + 2 * bonus::CONSECUTIVE + fzf.scheme().bonus_boundary_white ); assert_eq!(m.matched_ranges, [6..10]); } pub use utils::*; mod utils { use core::ops::Range; pub struct FzfMatch { pub distance: FzfDistance, pub matched_ranges: Vec>, } use norm::fzf::{FzfDistance, FzfParser, FzfQuery, FzfV1, FzfV2}; use norm::{CaseSensitivity, Metric}; /// TODO: docs pub trait SortedRanges { fn sorted(&self) -> Vec>; } impl SortedRanges for &[Range] { fn sorted(&self) -> Vec> { let mut sorted = self.to_vec(); sorted.sort_by_key(|r| r.start); sorted } } pub trait Fzf: Default + for<'a> Metric = FzfQuery<'a>, Distance = FzfDistance> { fn set_case_sensitivity( &mut self, case_sensitivity: CaseSensitivity, ) -> &mut Self; fn scheme(&self) -> &norm::fzf::Scheme; } impl Fzf for FzfV1 { fn set_case_sensitivity( &mut self, case_sensitivity: CaseSensitivity, ) -> &mut Self { self.set_case_sensitivity(case_sensitivity) } fn scheme(&self) -> &norm::fzf::Scheme { #[cfg(feature = "__tests")] { self.scheme() } #[cfg(not(feature = "__tests"))] { unreachable!() } } } impl Fzf for FzfV2 { fn set_case_sensitivity( &mut self, case_sensitivity: CaseSensitivity, ) -> &mut Self { self.set_case_sensitivity(case_sensitivity) } fn scheme(&self) -> &norm::fzf::Scheme { #[cfg(feature = "__tests")] { self.scheme() } #[cfg(not(feature = "__tests"))] { unreachable!() } } } pub(super) fn fzf( case_sensitivity: CaseSensitivity, query: &str, candidate: &str, ) -> (F, Option) { let mut fzf = F::default(); fzf.set_case_sensitivity(case_sensitivity); let mut parser = FzfParser::new(); let mut ranges = Vec::new(); let Some(distance) = fzf.distance_and_ranges( parser.parse(query), candidate, &mut ranges, ) else { return (fzf, None); }; (fzf, Some(FzfMatch { distance, matched_ranges: ranges })) } }