ui/components/command_palette/
fuzzy.rs1pub fn score(query: &str, candidate: &str) -> Option<i32> {
10 if query.is_empty() {
11 return Some(0);
12 }
13
14 let query_lower = query.to_lowercase();
15 let candidate_lower = candidate.to_lowercase();
16
17 if let Some(index) = candidate_lower.find(&query_lower) {
21 let position_bonus = (100 - (index as i32).min(100)) * 10;
22 let length_bonus = query_lower.chars().count() as i32;
23 return Some(1_000 + position_bonus + length_bonus);
24 }
25
26 subsequence_score(&query_lower, &candidate_lower)
27}
28
29fn subsequence_score(query: &str, candidate: &str) -> Option<i32> {
34 let query_chars: Vec<char> = query.chars().collect();
35 if query_chars.is_empty() {
36 return Some(0);
37 }
38
39 let mut query_ix = 0;
40 let mut run_length = 0;
41 let mut total_score = 0;
42 let mut previous_match_ix: Option<usize> = None;
43
44 for (candidate_ix, candidate_char) in candidate.chars().enumerate() {
45 if query_ix >= query_chars.len() {
46 break;
47 }
48 if candidate_char != query_chars[query_ix] {
49 continue;
50 }
51
52 let is_contiguous = previous_match_ix.map(|ix| ix + 1) == Some(candidate_ix);
53 run_length = if is_contiguous { run_length + 1 } else { 1 };
54 total_score += run_length * 5 - (candidate_ix as i32) / 4;
55 previous_match_ix = Some(candidate_ix);
56 query_ix += 1;
57 }
58
59 if query_ix == query_chars.len() {
60 Some(total_score)
61 } else {
62 None
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn empty_query_matches_everything() {
72 assert_eq!(score("", "New Profile"), Some(0));
73 assert_eq!(score("", ""), Some(0));
74 }
75
76 #[test]
77 fn substring_match_is_case_insensitive() {
78 assert!(score("prof", "New Profile").is_some());
79 assert!(score("PROF", "new profile").is_some());
80 assert!(score("Profile", "PROFILE").is_some());
81 }
82
83 #[test]
84 fn no_subsequence_match_returns_none() {
85 assert_eq!(score("xyz", "New Profile"), None);
86 assert_eq!(score("zzz", ""), None);
87 }
88
89 #[test]
90 fn scattered_subsequence_still_matches() {
91 assert!(score("newprof", "New Profile").is_some());
94 }
95
96 #[test]
97 fn contiguous_substring_ranks_above_scattered_subsequence() {
98 let contiguous = score("prof", "New Profile").unwrap();
99 let scattered = score("nwpf", "New Profile").unwrap();
100 assert!(contiguous > scattered);
101 }
102
103 #[test]
104 fn earlier_matches_rank_higher() {
105 let early = score("new", "New Profile").unwrap();
106 let late = score("file", "New Profile").unwrap();
107 assert!(early > late);
108 }
109
110 #[test]
111 fn out_of_order_characters_do_not_match() {
112 assert_eq!(score("ca", "abc"), None);
115 }
116}