commit a1a70a3418292922c8f32f0a5a0df0f85719ed36
parent 48934897865fe57309e3b475af4e47c820f62bfa
Author: Jan Christoph Ebersbach <jceb@e-jc.de>
Date: Tue, 27 Jan 2015 19:42:34 +0100
add fuzzymatch patch
Diffstat:
3 files changed, 312 insertions(+), 0 deletions(-)
diff --git a/tools.suckless.org/dmenu/patches/dmenu-4.5-fuzzymatch.diff b/tools.suckless.org/dmenu/patches/dmenu-4.5-fuzzymatch.diff
@@ -0,0 +1,143 @@
+Author: Jan Christoph Ebersbach <jceb@e-jc.de>
+URL: no url in suckless wiki yet
+Add add fuzzy matching to dmenu
+
+Index: dmenu-patches/dmenu/dmenu.c
+===================================================================
+--- dmenu-patches.orig/dmenu/dmenu.c
++++ dmenu-patches/dmenu/dmenu.c
+@@ -22,12 +22,14 @@ typedef struct Item Item;
+ struct Item {
+ char *text;
+ Item *left, *right;
++ int distance;
+ };
+
+ static void appenditem(Item *item, Item **list, Item **last);
+ static void calcoffsets(void);
+ static char *cistrstr(const char *s, const char *sub);
+ static void drawmenu(void);
++static void fuzzymatch(void);
+ static void grabkeyboard(void);
+ static void insert(const char *str, ssize_t n);
+ static void keypress(XKeyEvent *ev);
+@@ -230,7 +232,7 @@ insert(const char *str, ssize_t n) {
+ if(n > 0)
+ memcpy(&text[cursor], str, n);
+ cursor += n;
+- match();
++ fuzzymatch();
+ }
+
+ void
+@@ -260,7 +262,7 @@ keypress(XKeyEvent *ev) {
+
+ case XK_k: /* delete right */
+ text[cursor] = '\0';
+- match();
++ fuzzymatch();
+ break;
+ case XK_u: /* delete left */
+ insert(NULL, 0 - cursor);
+@@ -379,7 +381,7 @@ keypress(XKeyEvent *ev) {
+ return;
+ strncpy(text, sel->text, sizeof text);
+ cursor = strlen(text);
+- match();
++ fuzzymatch();
+ break;
+ }
+ drawmenu();
+@@ -578,7 +580,7 @@ setup(void) {
+ }
+ promptw = prompt ? textw(dc, prompt) : 0;
+ inputw = MIN(inputw, mw/3);
+- match();
++ fuzzymatch();
+
+ /* create menu window */
+ swa.override_redirect = True;
+Index: dmenu-patches/dmenu/fuzzymatch.c
+===================================================================
+--- /dev/null
++++ dmenu-patches/dmenu/fuzzymatch.c
+@@ -0,0 +1,79 @@
++int
++compare_distance(const void *a, const void *b) {
++ Item const *da = *(Item **) a;
++ Item const *db = *(Item **) b;
++ if(!db)
++ return 1;
++ if(!da)
++ return -1;
++ return da->distance - db->distance;
++}
++
++void
++fuzzymatch(void) {
++ /* bang - we have so much memory */
++ Item *item;
++ Item **fuzzymatches = NULL;
++ char c;
++ int number_of_matches = 0, i, pidx, sidx, eidx;
++ int text_len = strlen(text), itext_len;
++
++ matches = matchend = NULL;
++
++ /* suppress compiler warning for unused match function */
++ if(0)
++ match();
++
++ /* 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(Item*))))
++ eprintf("cannot realloc %u bytes:", number_of_matches * sizeof(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(Item*), compare_distance);
++ /* rebuild list of matches */
++ matches = matchend = NULL;
++ for(i = 0, item = fuzzymatches[i]; i < number_of_matches && item && \
++ item->text; i++, item = fuzzymatches[i]) {
++ appenditem(item, &matches, &matchend);
++ }
++ free(fuzzymatches);
++ }
++ curr = sel = matches;
++ calcoffsets();
++}
diff --git a/tools.suckless.org/dmenu/patches/dmenu-git-fuzzymatch.diff b/tools.suckless.org/dmenu/patches/dmenu-git-fuzzymatch.diff
@@ -0,0 +1,143 @@
+Author: Jan Christoph Ebersbach <jceb@e-jc.de>
+URL: http://tools.suckless.org/dmenu/patches/fuzzymatch
+Add add fuzzy matching to dmenu
+
+Index: dmenu-patches/dmenu/dmenu.c
+===================================================================
+--- dmenu-patches.orig/dmenu/dmenu.c
++++ dmenu-patches/dmenu/dmenu.c
+@@ -23,12 +23,14 @@ struct Item {
+ char *text;
+ Item *left, *right;
+ Bool out;
++ int distance;
+ };
+
+ static void appenditem(Item *item, Item **list, Item **last);
+ static void calcoffsets(void);
+ static char *cistrstr(const char *s, const char *sub);
+ static void drawmenu(void);
++static void fuzzymatch(void);
+ static void grabkeyboard(void);
+ static void insert(const char *str, ssize_t n);
+ static void keypress(XKeyEvent *ev);
+@@ -231,7 +233,7 @@ insert(const char *str, ssize_t n) {
+ if(n > 0)
+ memcpy(&text[cursor], str, n);
+ cursor += n;
+- match();
++ fuzzymatch();
+ }
+
+ void
+@@ -264,7 +266,7 @@ keypress(XKeyEvent *ev) {
+
+ case XK_k: /* delete right */
+ text[cursor] = '\0';
+- match();
++ fuzzymatch();
+ break;
+ case XK_u: /* delete left */
+ insert(NULL, 0 - cursor);
+@@ -393,7 +395,7 @@ keypress(XKeyEvent *ev) {
+ strncpy(text, sel->text, sizeof text - 1);
+ text[sizeof text - 1] = '\0';
+ cursor = strlen(text);
+- match();
++ fuzzymatch();
+ break;
+ }
+ drawmenu();
+@@ -597,7 +599,7 @@ setup(void) {
+ }
+ promptw = (prompt && *prompt) ? textw(dc, prompt) : 0;
+ inputw = MIN(inputw, mw/3);
+- match();
++ fuzzymatch();
+
+ /* create menu window */
+ swa.override_redirect = True;
+Index: dmenu-patches/dmenu/fuzzymatch.c
+===================================================================
+--- /dev/null
++++ dmenu-patches/dmenu/fuzzymatch.c
+@@ -0,0 +1,79 @@
++int
++compare_distance(const void *a, const void *b) {
++ Item const *da = *(Item **) a;
++ Item const *db = *(Item **) b;
++ if(!db)
++ return 1;
++ if(!da)
++ return -1;
++ return da->distance - db->distance;
++}
++
++void
++fuzzymatch(void) {
++ /* bang - we have so much memory */
++ Item *item;
++ Item **fuzzymatches = NULL;
++ char c;
++ int number_of_matches = 0, i, pidx, sidx, eidx;
++ int text_len = strlen(text), itext_len;
++
++ matches = matchend = NULL;
++
++ /* suppress compiler warning for unused match function */
++ if(0)
++ match();
++
++ /* 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(Item*))))
++ eprintf("cannot realloc %u bytes:", number_of_matches * sizeof(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(Item*), compare_distance);
++ /* rebuild list of matches */
++ matches = matchend = NULL;
++ for(i = 0, item = fuzzymatches[i]; i < number_of_matches && item && \
++ item->text; i++, item = fuzzymatches[i]) {
++ appenditem(item, &matches, &matchend);
++ }
++ free(fuzzymatches);
++ }
++ curr = sel = matches;
++ calcoffsets();
++}
diff --git a/tools.suckless.org/dmenu/patches/fuzzymatch.md b/tools.suckless.org/dmenu/patches/fuzzymatch.md
@@ -0,0 +1,26 @@
+Fuzzy matching support
+======================
+
+Description
+-----------
+
+This patch adds support for fuzzy matching to dmenu. It allows you to type
+non-consecutive portions of the string you want to match.
+
+Usage
+-----
+
+* Apply patch and include `fuzzymatch.c` in `config.h`.
+
+`#include fuzzymatch.c`
+
+Download
+--------
+
+* [dmenu git](dmenu-git-fuzzymatch.diff) applies cleanly against 13a529ce63364544bdc851dfd5d3aa2ef8740914
+* [dmenu 4.5](dmenu-4.5-fuzzymatch.diff)
+
+History
+------
+
+Created by [Jan Christoph Ebersbach](https://github.com/jceb/dmenu-patches).