~jzck/dmenu

7a9c6c7c0a9bcfb8e4b17b732fbe4859bd0f60a7 — Jack Halford 2 years ago 98e3a75
patch: fuzzy search
1 files changed, 87 insertions(+), 4 deletions(-)

M dmenu.c
M dmenu.c => dmenu.c +87 -4
@@ 32,6 32,7 @@ struct item {
	char *text;
	struct item *left, *right;
	int out;
	int distance;
};

static char text[BUFSIZ] = "";


@@ 44,6 45,7 @@ static struct item *items = NULL;
static struct item *matches, *matchend;
static struct item *prev, *curr, *next, *sel;
static int mon = -1, screen;
static int sorted_results = 1;

static Atom clip, utf8;
static Display *dpy;


@@ 273,6 275,84 @@ match(void)
	calcoffsets();
}

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
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 (sorted_results && 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
insert(const char *str, ssize_t n)
{


@@ 283,7 363,7 @@ insert(const char *str, ssize_t n)
	if (n > 0)
		memcpy(&text[cursor], str, n);
	cursor += n;
	match();
	fuzzymatch();
}

static size_t


@@ 352,7 432,7 @@ keypress(XKeyEvent *ev)

		case XK_k: /* delete right */
			text[cursor] = '\0';
			match();
			fuzzymatch();
			break;
		case XK_u: /* delete left */
			insert(NULL, 0 - cursor);


@@ 501,7 581,7 @@ insert:
		strncpy(text, sel->text, sizeof text - 1);
		text[sizeof text - 1] = '\0';
		cursor = strlen(text);
		match();
		fuzzymatch();
		break;
	}



@@ 662,7 742,7 @@ setup(void)
		y = (wa.height - mh) / 2;
	}
	inputw = MIN(inputw, mw/3);
	match();
	fuzzymatch();

	/* create menu window */
	swa.override_redirect = True;


@@ 718,6 798,9 @@ main(int argc, char *argv[])
			topbar = 0;
		else if (!strcmp(argv[i], "-f"))   /* grabs keyboard before reading stdin */
			fast = 1;
		else if (!strcmp(argv[i], "-n")) { /* don't sort items */
			sorted_results = 0;
		}
		else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
			fstrncmp = strncasecmp;
			fstrstr = cistrstr;