|
| 1 | + |
| 2 | +namespace AstalApps { |
| 3 | + |
| 4 | +private int max(int a, int b) { |
| 5 | + return a > b ? a : b; |
| 6 | +} |
| 7 | + |
| 8 | +public int fuzzy_match_string(string pattern, string str) { |
| 9 | + const int unmatched_letter_penalty = -1; |
| 10 | + int score = 100; |
| 11 | + |
| 12 | + if (pattern.length == 0) return score; |
| 13 | + if (str.length < pattern.length) return int.MIN; |
| 14 | + |
| 15 | + score += unmatched_letter_penalty * (str.length - pattern.length); |
| 16 | + score = fuzzy_match_recurse(pattern, str, score, true); |
| 17 | + |
| 18 | + return score; |
| 19 | +} |
| 20 | + |
| 21 | +private int fuzzy_match_recurse(string pattern, string str, int score, bool first_char) { |
| 22 | + if (pattern.length == 0) return score; |
| 23 | + |
| 24 | + int match_idx = 0; |
| 25 | + int offset = 0; |
| 26 | + unichar search = pattern.casefold().get_char(0); |
| 27 | + int best_score = int.MIN; |
| 28 | + |
| 29 | + while ((match_idx = str.casefold().substring(offset).index_of_char(search)) >= 0) { |
| 30 | + offset += match_idx; |
| 31 | + int subscore = fuzzy_match_recurse( |
| 32 | + pattern.substring(1), |
| 33 | + str.substring(offset + 1), |
| 34 | + compute_score(offset, first_char, str, offset), false); |
| 35 | + best_score = max(best_score, subscore); |
| 36 | + offset++; |
| 37 | + } |
| 38 | + |
| 39 | + if (best_score == int.MIN) return int.MIN; |
| 40 | + return score + best_score; |
| 41 | +} |
| 42 | + |
| 43 | +private int compute_score(int jump, bool first_char, string match, int idx) { |
| 44 | + const int adjacency_bonus = 15; |
| 45 | + const int separator_bonus = 30; |
| 46 | + const int camel_bonus = 30; |
| 47 | + const int first_letter_bonus = 15; |
| 48 | + const int leading_letter_penalty = -5; |
| 49 | + const int max_leading_letter_penalty = -15; |
| 50 | + |
| 51 | + int score = 0; |
| 52 | + |
| 53 | + if (!first_char && jump == 0) { |
| 54 | + score += adjacency_bonus; |
| 55 | + } |
| 56 | + if (!first_char || jump > 0) { |
| 57 | + if (match[idx].isupper() && match[idx-1].islower()) { |
| 58 | + score += camel_bonus; |
| 59 | + } |
| 60 | + if (match[idx].isalnum() && !match[idx-1].isalnum()) { |
| 61 | + score += separator_bonus; |
| 62 | + } |
| 63 | + } |
| 64 | + if (first_char && jump == 0) { |
| 65 | + score += first_letter_bonus; |
| 66 | + } |
| 67 | + if (first_char) { |
| 68 | + score += max(leading_letter_penalty * jump, max_leading_letter_penalty); |
| 69 | + } |
| 70 | + |
| 71 | + return score; |
| 72 | +} |
| 73 | +} |
0 commit comments