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:
| M | dmenu.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;