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).