dmenu

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 57852d9e44da3396c662f64befda55cc10ddd222
parent f652f21eb4131c5fa589c1e0914a542645e74fe4
Author: AndrewLockVI <andrew@laack.co>
Date:   Sun, 23 Feb 2025 12:21:40 -0600

Patched in fuzzy finding w/ string distances

Diffstat:
Mdmenu.c | 144+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
1 file changed, 86 insertions(+), 58 deletions(-)

diff --git a/dmenu.c b/dmenu.c @@ -31,6 +31,7 @@ struct item { char *text; struct item *left, *right; int out; + int distance; }; static char text[BUFSIZ] = ""; @@ -77,6 +78,21 @@ appenditem(struct item *item, struct item **list, struct item **last) *last = item; } + +static int +compare_distance(const void *a, const void *b) +{ + struct item const *da = *(struct item **) a; + struct item const *db = *(struct item **) b; + + if (!db) + return 1; + if (!da) + return -1; + return da->distance - db->distance; +} + + static void calcoffsets(void) { @@ -96,6 +112,72 @@ calcoffsets(void) } static void +fuzzymatch(void) +{ + struct item *item; + struct item **fuzzymatches = 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 (item = items; item && item->text; item++) { + if (text_len) { + itext_len = strlen(item->text); + pidx = 0; + sidx = eidx = -1; + /* walk through item text */ + for (i = 0; i < itext_len && (c = item->text[i]); i++) { + /* fuzzy match pattern */ + if (text[pidx] == c) { + if (sidx == -1) + sidx = i; + pidx++; + if (pidx == text_len) { + eidx = i; + break; + } + } + } + /* build list of matches */ + if (eidx != -1) { + /* compute distance */ + /* factor in 30% of sidx and distance between eidx and total + * text length .. let's see how it works */ + item->distance = eidx - sidx + (itext_len - eidx + sidx) / 3; + appenditem(item, &matches, &matchend); + number_of_matches++; + } + } + else + appenditem(item, &matches, &matchend); + } + + if (number_of_matches) { + /* initialize array with matches */ + if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*)))) + die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item *)); + for (i = 0, item = matches; item && i < number_of_matches; i++, item = item->right) + fuzzymatches[i] = item; + + /* sort matches according to distance */ + qsort(fuzzymatches, number_of_matches, sizeof(struct item *), compare_distance); + /* rebuild list of matches */ + matches = matchend = NULL; + for (i = 0, item = fuzzymatches[0]; i < number_of_matches && item && \ + item->text; item = fuzzymatches[i], i++) + appenditem(item, &matches, &matchend); + + free(fuzzymatches); + } + curr = sel = matches; + calcoffsets(); +} + + +static void cleanup(void) { size_t i; @@ -227,60 +309,6 @@ grabkeyboard(void) } static void -match(void) -{ - static char **tokv = NULL; - static int tokn = 0; - - char buf[sizeof text], *s; - int i, tokc = 0; - size_t len, textsize; - struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; - - 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("cannot realloc %zu bytes:", tokn * sizeof *tokv); - len = tokc ? strlen(tokv[0]) : 0; - - matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; - 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; - /* exact matches go first, then prefixes, then substrings */ - if (!tokc || !fstrncmp(text, item->text, textsize)) - appenditem(item, &matches, &matchend); - else if (!fstrncmp(tokv[0], item->text, len)) - appenditem(item, &lprefix, &prefixend); - else - appenditem(item, &lsubstr, &substrend); - } - 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; - calcoffsets(); -} - -static void insert(const char *str, ssize_t n) { if (strlen(text) + n > sizeof text - 1) @@ -290,7 +318,7 @@ insert(const char *str, ssize_t n) if (n > 0) memcpy(&text[cursor], str, n); cursor += n; - match(); + fuzzymatch(); } static size_t @@ -359,7 +387,7 @@ keypress(XKeyEvent *ev) case XK_k: /* delete right */ text[cursor] = '\0'; - match(); + fuzzymatch(); break; case XK_u: /* delete left */ insert(NULL, 0 - cursor); @@ -519,7 +547,7 @@ insert: cursor = strnlen(sel->text, sizeof text - 1); memcpy(text, sel->text, cursor); text[cursor] = '\0'; - match(); + fuzzymatch(); break; } @@ -678,7 +706,7 @@ setup(void) } promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; inputw = mw / 3; /* input width: ~33% of monitor width */ - match(); + fuzzymatch(); /* create menu window */ swa.override_redirect = True;