spmenu/libs/match.c

178 lines
5.3 KiB
C
Raw Normal View History

2023-05-14 00:21:16 +02:00
/* See LICENSE file for copyright and license details. */
void fuzzymatch(void) {
2023-05-08 23:00:45 +02:00
struct item *it;
struct item **fuzzymatches = NULL;
2023-03-13 21:21:40 +01:00
struct item *lhpprefix, *hpprefixend;
2023-05-08 23:00:45 +02:00
lhpprefix = hpprefixend = NULL;
char c;
int number_of_matches = 0, i, pidx, sidx, eidx;
int text_len = strlen(text), itext_len;
matches = matchend = NULL;
// walk through all items
for (it = items; it && it->text; it++) {
if (text_len) {
itext_len = strlen(it->text);
pidx = 0; // pointer
sidx = eidx = -1; // start of match, end of match
// walk through item text
for (i = 0; i < itext_len && (c = it->text[i]); i++) {
// fuzzy match pattern
if (!fstrncmp(&text[pidx], &c, 1)) {
if(sidx == -1)
sidx = i;
pidx++;
if (pidx == text_len) {
eidx = i;
break;
}
}
}
// build list of matches
if (eidx != -1) {
it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len);
appenditem(it, &matches, &matchend);
number_of_matches++;
}
} else {
appenditem(it, &matches, &matchend);
}
}
if (number_of_matches) {
// initialize array with matches
if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*))))
die("spmenu: cannot realloc %u bytes:", number_of_matches * sizeof(struct item*));
for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) {
fuzzymatches[i] = it;
}
// sort matches according to distance
2023-03-13 21:21:40 +01:00
if (sortmatches) qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance);
2023-05-08 23:00:45 +02:00
// rebuild list of matches
matches = matchend = NULL;
for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \
it->text; i++, it = fuzzymatches[i]) {
2023-03-13 21:21:40 +01:00
if (sortmatches && it->hp)
appenditem(it, &lhpprefix, &hpprefixend);
appenditem(it, &matches, &matchend);
2023-05-08 23:00:45 +02:00
}
free(fuzzymatches);
}
2023-03-13 21:21:40 +01:00
if (lhpprefix) {
2023-05-08 23:00:45 +02:00
hpprefixend->right = matches;
matches = lhpprefix;
}
2023-03-13 21:21:40 +01:00
2023-05-08 23:00:45 +02:00
curr = sel = matches;
2023-03-13 21:21:40 +01:00
for (i = 0; i < preselected; i++) {
2023-05-08 23:00:45 +02:00
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
}
}
calcoffsets();
2023-03-13 21:21:40 +01:00
}
void match(void) {
get_width();
2023-05-08 23:00:45 +02:00
if (fuzzy) {
fuzzymatch();
return;
}
2023-05-08 23:00:45 +02:00
static char **tokv = NULL;
static int tokn = 0;
char buf[sizeof text], *s;
int i, tokc = 0;
size_t len, textsize;
2023-03-13 21:21:40 +01:00
struct item *item, *lhpprefix, *lprefix, *lsubstr, *hpprefixend, *prefixend, *substrend;
2023-05-08 23:00:45 +02:00
strcpy(buf, text);
// separate input text into tokens to be matched individually
for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
die("spmenu: cannot realloc %u bytes:", tokn * sizeof *tokv);
2023-05-08 23:00:45 +02:00
len = tokc ? strlen(tokv[0]) : 0;
2023-03-13 21:21:40 +01:00
matches = lhpprefix = lprefix = lsubstr = matchend = hpprefixend = prefixend = substrend = NULL;
2023-05-08 23:00:45 +02:00
textsize = strlen(text) + 1;
for (item = items; item && item->text; item++) {
for (i = 0; i < tokc; i++)
if (!fstrstr(item->text, tokv[i]))
break;
if (i != tokc) // not all tokens match
continue;
2023-03-13 21:21:40 +01:00
if (!sortmatches)
2023-05-08 23:00:45 +02:00
appenditem(item, &matches, &matchend);
2023-03-13 21:21:40 +01:00
else {
// exact matches go first, then prefixes with high priority, then prefixes, then substrings
2023-03-13 21:21:40 +01:00
if (item->hp && !fstrncmp(tokv[0], item->text, len))
2023-05-08 23:00:45 +02:00
appenditem(item, &lhpprefix, &hpprefixend);
2023-03-13 21:21:40 +01:00
else if (!tokc || !fstrncmp(text, item->text, textsize))
2023-05-08 23:00:45 +02:00
appenditem(item, &matches, &matchend);
else if (!fstrncmp(tokv[0], item->text, len))
appenditem(item, &lprefix, &prefixend);
else
appenditem(item, &lsubstr, &substrend);
}
}
2023-03-13 22:45:04 +01:00
2023-03-13 21:21:40 +01:00
if (lhpprefix) {
2023-05-08 23:00:45 +02:00
if (matches) {
matchend->right = lhpprefix;
lhpprefix->left = matchend;
} else
matches = lhpprefix;
matchend = hpprefixend;
}
if (lprefix) {
if (matches) {
matchend->right = lprefix;
lprefix->left = matchend;
} else
matches = lprefix;
matchend = prefixend;
}
if (lsubstr) {
if (matches) {
matchend->right = lsubstr;
lsubstr->left = matchend;
} else
matches = lsubstr;
matchend = substrend;
}
curr = sel = matches;
2023-03-14 21:31:24 +01:00
for (i = 0; i < preselected; i++) {
2023-05-08 23:00:45 +02:00
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
}
}
2023-03-14 21:31:24 +01:00
2023-05-08 23:00:45 +02:00
calcoffsets();
2023-03-13 21:21:40 +01:00
}
int compare_distance(const void *a, const void *b) {
2023-05-08 23:00:45 +02:00
struct item *da = *(struct item **) a;
struct item *db = *(struct item **) b;
2023-03-13 21:21:40 +01:00
2023-05-08 23:00:45 +02:00
if (!db)
return 1;
if (!da)
return -1;
2023-03-13 21:21:40 +01:00
2023-05-08 23:00:45 +02:00
return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1;
2023-03-13 21:21:40 +01:00
}