summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--fuzzy.c110
-rw-r--r--fuzzy.h6
-rw-r--r--main.c36
4 files changed, 138 insertions, 16 deletions
diff --git a/Makefile b/Makefile
index 149aba0..3b8ed05 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
EXE = xmenu
OBJ != find . -name '*.c' | sed -e 's/\.c$$/.o/' -e 's|^\./||'
-CFLAGS += -Wall -Wpedantic -I/usr/X11R7/include -g
+CFLAGS += -Wall -Wpedantic -I/usr/X11R7/include -O0 -g
LDFLAGS += -lX11 -lxcb -lXt -lXau -lXdmcp -L/usr/X11R7/lib -static
PREFIX ?= ${HOME}
diff --git a/fuzzy.c b/fuzzy.c
new file mode 100644
index 0000000..f5d9b50
--- /dev/null
+++ b/fuzzy.c
@@ -0,0 +1,110 @@
+#include <stdint.h>
+#include <string.h>
+
+#include "str.h"
+#include "arena.h"
+
+typedef struct {
+ uint16_t score, start;
+} FuzzyScore;
+
+static inline char
+to_lower(char c)
+{
+ /* TODO: consider utf-8 support */
+ if (c >= 'A' && c <= 'Z') return c + ('a' - 'A');
+ return c;
+}
+
+static inline int
+fz_start_score(Str s, Str p)
+{
+ if (!p.n || str_eql(s, p)) return 0;
+ int n = 1;
+ int pi = 0, si = 0;
+ for (;;) {
+ if (to_lower(p.s[pi]) == to_lower(s.s[si])) {
+ si++;
+ pi++;
+ if (si == s.n) {
+ return pi < p.n ? -1 : n;
+ } else if (pi == p.n) {
+ return n;
+ }
+ } else {
+ si++;
+ n++;
+ if (si == s.n) return -1;
+ }
+ }
+}
+
+static inline FuzzyScore
+fz_score(Str s, Str p)
+{
+ if (!p.n) return (FuzzyScore) { 0, 0 };
+ int score = -1;
+ int score_start = 0;
+ for (int i = 0; i < s.n; i++) {
+ int f = fz_start_score(str_skip(s, i), p);
+ if (f == -1) continue;
+ if (score == -1 || f < score) {
+ score = f;
+ score_start = i;
+ }
+ }
+ if (score == -1) return (FuzzyScore) { 0xffff, 0 };
+ return (FuzzyScore) { score, score_start };
+}
+
+static inline int
+fz_cmp(FuzzyScore a, FuzzyScore b, Str sa, Str sb)
+{
+ if (a.score < b.score) return -1;
+ if (b.score < a.score) return 1;
+ if (sa.n < sb.n) return -1;
+ if (sb.n < sa.n) return 1;
+ return (b.start < a.start) - (a.start < b.start);
+}
+
+void
+fz_qsort_opt(Str * restrict dest, FuzzyScore * restrict scr, int n)
+{
+ int i = 0, j = n - 1;
+ FuzzyScore x = scr[n / 2];
+ Str xs = dest[n / 2];
+ do {
+ while (fz_cmp(scr[i], x, dest[i], xs) < 0) i++;
+ while (fz_cmp(x, scr[j], xs, dest[j]) < 0) j--;
+ if (i <= j) {
+ Str t0 = dest[i];
+ dest[i] = dest[j];
+ dest[j] = t0;
+ FuzzyScore t1 = scr[i];
+ scr[i] = scr[j];
+ scr[j] = t1;
+ i++;
+ j--;
+ }
+ } while (i <= j);
+ if (0 < j) fz_qsort_opt(dest, scr, j + 1);
+ if (i+1 < n) fz_qsort_opt(dest + i, scr + i, n - i);
+}
+
+int
+fz_sort(Str * restrict out, const Str * restrict src, int n, Str pat, Arena *scratch)
+{
+ FuzzyScore *scr = new_arr(scratch, FuzzyScore, n);
+ int scrn = 0;
+ for (int i = 0; i < n; i++) {
+ FuzzyScore s = fz_score(src[i], pat);
+ if (s.score != 0xffff) {
+ scr[scrn] = s;
+ out[scrn] = src[i];
+ scrn++;
+ }
+ }
+ if (pat.n > 0)
+ fz_qsort_opt(out, scr, scrn);
+ return scrn;
+}
diff --git a/fuzzy.h b/fuzzy.h
new file mode 100644
index 0000000..a8dd39b
--- /dev/null
+++ b/fuzzy.h
@@ -0,0 +1,6 @@
+#ifndef FUZZY_H
+#define FUZZY_H
+
+int fz_sort(Str * restrict out, const Str * restrict src, int n, Str pat, Arena *scratch);
+
+#endif
diff --git a/main.c b/main.c
index c2b9946..f5bfb51 100644
--- a/main.c
+++ b/main.c
@@ -13,6 +13,7 @@
#include "dynarr.h"
#include "str.h"
#include "ui.h"
+#include "fuzzy.h"
typedef DYNARR(char) DynStr;
@@ -64,23 +65,30 @@ read_all(FILE *f, DynStr *out, Arena *a)
int
main(int argc, char **argv)
{
- Arena a = { 0 };
+ Arena perm = { 0 };
+ Arena scratch = { 0 };
DynStr input = { 0 };
int inpi = 0;
int seli = 0;
- UiOpts opt = { 0 };
+ UiOpts opt_src = { 0 };
DynStr buf = { 0 };
- if (read_all(stdin, &buf, &a)) err(1, "stdin");
+ if (read_all(stdin, &buf, &perm)) err(1, "stdin");
for (Str src = { buf.v, buf.n }; src.n > 0; ) {
Cut c = str_cut(src, '\n');
- DA_APUSH(&opt, &a, c.head);
+ DA_APUSH(&opt_src, &perm, c.head);
src = c.tail;
}
- if (!opt.n) return 0;
+ if (!opt_src.n) return 0;
- ui_init(argc, argv, opt);
+ UiOpts opt = {
+ .v = new_arr(&perm, Str, opt_src.n),
+ .n = opt_src.n,
+ };
+
+ ui_init(argc, argv, opt_src);
+ DA_FIT(&input, 32);
for (UiEvent ev; ui_wait_event(&ev); ) {
switch (ev.type) {
@@ -121,14 +129,11 @@ main(int argc, char **argv)
}
break;
case UI_REDRAW:
- draw:
- ui_draw(
- (Str) { input.v, input.n },
- inpi,
- seli,
- opt
- );
- break;
+ draw: {
+ Str s = { input.v, input.n };
+ opt.n = fz_sort(opt.v, opt_src.v, opt_src.n, s, &scratch);
+ ui_draw(s, inpi, seli, opt);
+ } break;
case UI_QUIT:
goto done;
default:
@@ -138,6 +143,7 @@ main(int argc, char **argv)
done:
ui_fini();
- arena_free(&a);
+ arena_free(&perm);
+ arena_free(&scratch);
return 0;
}