2023-05-14 00:21:16 +02:00
|
|
|
/* See LICENSE file for copyright and license details. */
|
|
|
|
|
2023-07-05 01:40:35 +02:00
|
|
|
#if USEREGEX
|
|
|
|
#include "regex/regex.c"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int matchregex(const char *t, const char *itt) {
|
|
|
|
#if USEREGEX
|
|
|
|
re_t reg = re_compile(t);
|
|
|
|
int len;
|
|
|
|
|
|
|
|
int ret = re_matchp(reg, itt, &len);
|
|
|
|
|
|
|
|
if (ret != -1) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
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;
|
2023-06-23 03:38:21 +02:00
|
|
|
int text_len = strlen(tx.text), itext_len;
|
2023-05-08 23:00:45 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2023-07-05 01:48:53 +02:00
|
|
|
// walk through item text
|
|
|
|
for (i = 0; i < itext_len && (c = it->text[i]); i++) {
|
|
|
|
if (!fstrncmp(&tx.text[pidx], &c, 1)) {
|
|
|
|
if(sidx == -1)
|
|
|
|
sidx = i;
|
|
|
|
pidx++;
|
|
|
|
if (pidx == text_len) {
|
|
|
|
eidx = i;
|
|
|
|
break;
|
2023-05-08 23:00:45 +02:00
|
|
|
}
|
2023-07-05 01:48:53 +02:00
|
|
|
} else if (matchregex(tx.text, it->text) && regex) {
|
|
|
|
eidx = i;
|
|
|
|
break;
|
2023-05-08 23:00:45 +02:00
|
|
|
}
|
|
|
|
}
|
2023-07-05 01:40:35 +02:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
// 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);
|
2023-07-11 17:21:30 +02:00
|
|
|
else
|
|
|
|
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-07-16 03:16:45 +02:00
|
|
|
currentitem = selecteditem = matches;
|
2023-03-13 21:21:40 +01:00
|
|
|
|
|
|
|
for (i = 0; i < preselected; i++) {
|
2023-07-16 03:16:45 +02:00
|
|
|
if (selecteditem && selecteditem->right && (selecteditem = selecteditem->right) == nextitem) {
|
|
|
|
currentitem = nextitem;
|
2023-05-08 23:00:45 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-02 18:37:51 +02:00
|
|
|
|
|
|
|
calcoffsets();
|
2023-03-13 21:21:40 +01:00
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02:00
|
|
|
void match(void) {
|
2023-06-02 18:37:51 +02:00
|
|
|
get_width();
|
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
if (fuzzy) {
|
|
|
|
fuzzymatch();
|
|
|
|
return;
|
|
|
|
}
|
2023-06-02 18:37:51 +02:00
|
|
|
|
2023-05-08 23:00:45 +02:00
|
|
|
static char **tokv = NULL;
|
|
|
|
static int tokn = 0;
|
|
|
|
|
2023-06-23 03:38:21 +02:00
|
|
|
char buf[sizeof tx.text], *s;
|
2023-05-08 23:00:45 +02:00
|
|
|
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-06-23 03:38:21 +02:00
|
|
|
sp_strncpy(buf, tx.text, sizeof(tx.text));
|
2023-05-08 23:00:45 +02:00
|
|
|
// 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-06-02 18:37:51 +02:00
|
|
|
|
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-06-23 03:38:21 +02:00
|
|
|
textsize = strlen(tx.text) + 1;
|
2023-05-08 23:00:45 +02:00
|
|
|
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
|
2023-07-05 01:40:35 +02:00
|
|
|
if (!(matchregex(tx.text, item->text) && regex)) {
|
|
|
|
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 {
|
2023-03-16 16:54:36 +01:00
|
|
|
// exact matches go first, then prefixes with high priority, then prefixes, then substrings
|
2023-07-11 17:21:30 +02:00
|
|
|
if (!tokc || !fstrncmp(tx.text, item->text, textsize))
|
2023-05-08 23:00:45 +02:00
|
|
|
appenditem(item, &matches, &matchend);
|
2023-07-11 17:21:30 +02:00
|
|
|
else if (item->hp && !fstrncmp(tokv[0], item->text, len))
|
|
|
|
appenditem(item, &lhpprefix, &hpprefixend);
|
2023-05-08 23:00:45 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-07-16 03:16:45 +02:00
|
|
|
|
|
|
|
currentitem = selecteditem = matches;
|
2023-03-14 21:31:24 +01:00
|
|
|
|
|
|
|
for (i = 0; i < preselected; i++) {
|
2023-07-16 03:16:45 +02:00
|
|
|
if (selecteditem && selecteditem->right && (selecteditem = selecteditem->right) == nextitem) {
|
|
|
|
currentitem = nextitem;
|
2023-05-08 23:00:45 +02:00
|
|
|
}
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2023-05-06 14:29:45 +02: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
|
|
|
}
|