dmenu-fuzzymatch-4.9.diff 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. From 94353eb52055927d9079f3d9e33da1c954abf386 Mon Sep 17 00:00:00 2001
  2. From: aleks <aleks.stier@icloud.com>
  3. Date: Wed, 26 Jun 2019 13:25:10 +0200
  4. Subject: [PATCH] Add support for fuzzy-matching
  5. ---
  6. config.def.h | 1 +
  7. config.mk | 2 +-
  8. dmenu.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++
  9. 3 files changed, 91 insertions(+), 1 deletion(-)
  10. diff --git a/config.def.h b/config.def.h
  11. index 1edb647..51612b9 100644
  12. --- a/config.def.h
  13. +++ b/config.def.h
  14. @@ -2,6 +2,7 @@
  15. /* Default settings; can be overriden by command line. */
  16. static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
  17. +static int fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */
  18. /* -fn option overrides fonts[0]; default X11 font or font set */
  19. static const char *fonts[] = {
  20. "monospace:size=10"
  21. diff --git a/config.mk b/config.mk
  22. index 0929b4a..d14309a 100644
  23. --- a/config.mk
  24. +++ b/config.mk
  25. @@ -20,7 +20,7 @@ FREETYPEINC = /usr/include/freetype2
  26. # includes and libs
  27. INCS = -I$(X11INC) -I$(FREETYPEINC)
  28. -LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
  29. +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm
  30. # flags
  31. CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
  32. diff --git a/dmenu.c b/dmenu.c
  33. index 6b8f51b..96ddc98 100644
  34. --- a/dmenu.c
  35. +++ b/dmenu.c
  36. @@ -1,6 +1,7 @@
  37. /* See LICENSE file for copyright and license details. */
  38. #include <ctype.h>
  39. #include <locale.h>
  40. +#include <math.h>
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <string.h>
  44. @@ -32,6 +33,7 @@ struct item {
  45. char *text;
  46. struct item *left, *right;
  47. int out;
  48. + double distance;
  49. };
  50. static char text[BUFSIZ] = "";
  51. @@ -210,9 +212,94 @@ grabkeyboard(void)
  52. die("cannot grab keyboard");
  53. }
  54. +int
  55. +compare_distance(const void *a, const void *b)
  56. +{
  57. + struct item *da = *(struct item **) a;
  58. + struct item *db = *(struct item **) b;
  59. +
  60. + if (!db)
  61. + return 1;
  62. + if (!da)
  63. + return -1;
  64. +
  65. + return da->distance == db->distance ? 0 : da->distance < db->distance ? -1 : 1;
  66. +}
  67. +
  68. +void
  69. +fuzzymatch(void)
  70. +{
  71. + /* bang - we have so much memory */
  72. + struct item *it;
  73. + struct item **fuzzymatches = NULL;
  74. + char c;
  75. + int number_of_matches = 0, i, pidx, sidx, eidx;
  76. + int text_len = strlen(text), itext_len;
  77. +
  78. + matches = matchend = NULL;
  79. +
  80. + /* walk through all items */
  81. + for (it = items; it && it->text; it++) {
  82. + if (text_len) {
  83. + itext_len = strlen(it->text);
  84. + pidx = 0; /* pointer */
  85. + sidx = eidx = -1; /* start of match, end of match */
  86. + /* walk through item text */
  87. + for (i = 0; i < itext_len && (c = it->text[i]); i++) {
  88. + /* fuzzy match pattern */
  89. + if (!fstrncmp(&text[pidx], &c, 1)) {
  90. + if(sidx == -1)
  91. + sidx = i;
  92. + pidx++;
  93. + if (pidx == text_len) {
  94. + eidx = i;
  95. + break;
  96. + }
  97. + }
  98. + }
  99. + /* build list of matches */
  100. + if (eidx != -1) {
  101. + /* compute distance */
  102. + /* add penalty if match starts late (log(sidx+2))
  103. + * add penalty for long a match without many matching characters */
  104. + it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len);
  105. + /* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */
  106. + appenditem(it, &matches, &matchend);
  107. + number_of_matches++;
  108. + }
  109. + } else {
  110. + appenditem(it, &matches, &matchend);
  111. + }
  112. + }
  113. +
  114. + if (number_of_matches) {
  115. + /* initialize array with matches */
  116. + if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*))))
  117. + die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*));
  118. + for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) {
  119. + fuzzymatches[i] = it;
  120. + }
  121. + /* sort matches according to distance */
  122. + qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance);
  123. + /* rebuild list of matches */
  124. + matches = matchend = NULL;
  125. + for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \
  126. + it->text; i++, it = fuzzymatches[i]) {
  127. + appenditem(it, &matches, &matchend);
  128. + }
  129. + free(fuzzymatches);
  130. + }
  131. + curr = sel = matches;
  132. + calcoffsets();
  133. +}
  134. +
  135. static void
  136. match(void)
  137. {
  138. + if (fuzzy) {
  139. + fuzzymatch();
  140. + return;
  141. + }
  142. static char **tokv = NULL;
  143. static int tokn = 0;
  144. @@ -702,6 +789,8 @@ main(int argc, char *argv[])
  145. topbar = 0;
  146. else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
  147. fast = 1;
  148. + else if (!strcmp(argv[i], "-F")) /* grabs keyboard before reading stdin */
  149. + fuzzy = 0;
  150. else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
  151. fstrncmp = strncasecmp;
  152. fstrstr = cistrstr;
  153. --
  154. 2.22.0