1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
#include "completion.h"
#include <stdlib.h>
#include <string.h>
static int ecex_ascii_lower(int c) {
if (c >= 'A' && c <= 'Z') return c - 'A' + 'a';
return c;
}
int ecex_ascii_strncasecmp(const char *a, const char *b, size_t n) {
if (!a || !b) return a == b ? 0 : (a ? 1 : -1);
for (size_t i = 0; i < n; i++) {
int ac = ecex_ascii_lower((unsigned char)a[i]);
int bc = ecex_ascii_lower((unsigned char)b[i]);
if (ac != bc || ac == '\0' || bc == '\0') return ac - bc;
}
return 0;
}
int ecex_ascii_contains_ci(const char *haystack, const char *needle) {
if (!haystack || !needle) return 0;
if (needle[0] == '\0') return 1;
size_t needle_len = strlen(needle);
for (size_t i = 0; haystack[i]; i++) {
if (ecex_ascii_strncasecmp(haystack + i, needle, needle_len) == 0) return 1;
}
return 0;
}
int ecex_fuzzy_score(const char *candidate, const char *query) {
if (!candidate || !query) return -1;
if (query[0] == '\0') return 0;
int score = 0;
int consecutive = 0;
int last_match = -1;
size_t ci = 0;
size_t qi = 0;
while (candidate[ci] && query[qi]) {
char c = candidate[ci];
char q = query[qi];
if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
if (q >= 'A' && q <= 'Z') q = (char)(q - 'A' + 'a');
if (c == q) {
score += 10;
if ((int)ci == last_match + 1) {
consecutive++;
score += 5 * consecutive;
} else {
consecutive = 0;
}
if (ci == 0) score += 20;
if (ci > 0 && (candidate[ci - 1] == '-' || candidate[ci - 1] == '_' || candidate[ci - 1] == ' ')) {
score += 15;
}
last_match = (int)ci;
qi++;
}
ci++;
}
if (query[qi] != '\0') return -1;
score -= (int)strlen(candidate);
if (strncmp(candidate, query, strlen(query)) == 0) score += 100;
if (ecex_ascii_contains_ci(candidate, query)) score += 75;
return score;
}
int ecex_completion_item_compare(const void *a, const void *b) {
const ecex_completion_item_t *pa = (const ecex_completion_item_t *)a;
const ecex_completion_item_t *pb = (const ecex_completion_item_t *)b;
if (pa->score != pb->score) return pb->score - pa->score;
if (pa->is_dir != pb->is_dir) return pb->is_dir - pa->is_dir;
int cmp = strcmp(pa->value ? pa->value : "", pb->value ? pb->value : "");
if (cmp != 0) return cmp;
if (pa->order < pb->order) return -1;
if (pa->order > pb->order) return 1;
return 0;
}
void ecex_completion_items_free(ecex_completion_item_t *items, size_t count) {
if (!items) return;
for (size_t i = 0; i < count; i++) free(items[i].value);
free(items);
}
|