@@ -79,17 +79,32 @@ tokenize(struct bm_menu *menu, char ***out_tokv, uint32_t *out_tokc)
7979 return NULL ;
8080}
8181
82+ struct fuzzy_match {
83+ struct bm_item * item ;
84+ int distance ;
85+ };
86+
87+ static int fuzzy_match_comparator (const void * a , const void * b ) {
88+ const struct fuzzy_match * fa = (const struct fuzzy_match * )a ;
89+ const struct fuzzy_match * fb = (const struct fuzzy_match * )b ;
90+
91+ return fa -> distance - fb -> distance ;
92+ }
93+
94+
8295/**
83- * Dmenu filterer that accepts substring function.
96+ * Dmenu filterer that accepts substring function or fuzzy match .
8497 *
8598 * @param menu bm_menu instance to filter.
8699 * @param addition This will be 1, if filter is same as previous filter with something appended.
87100 * @param fstrstr Substring function used to match items.
101+ * @param fstrncmp String comparison function for exact matches.
88102 * @param out_nmemb uint32_t reference to filtered items count.
103+ * @param fuzzy Boolean flag to toggle fuzzy matching.
89104 * @return Pointer to array of bm_item pointers.
90105 */
91106static struct bm_item * *
92- filter_dmenu_fun (struct bm_menu * menu , char addition , char * (* fstrstr )(const char * a , const char * b ), int (* fstrncmp )(const char * a , const char * b , size_t len ), uint32_t * out_nmemb )
107+ filter_dmenu_fun (struct bm_menu * menu , char addition , char * (* fstrstr )(const char * a , const char * b ), int (* fstrncmp )(const char * a , const char * b , size_t len ), uint32_t * out_nmemb , bool fuzzy )
93108{
94109 assert (menu && fstrstr && fstrncmp && out_nmemb );
95110 * out_nmemb = 0 ;
@@ -114,13 +129,48 @@ filter_dmenu_fun(struct bm_menu *menu, char addition, char* (*fstrstr)(const cha
114129 goto fail ;
115130
116131 const char * filter = menu -> filter ? menu -> filter : "" ;
132+ if (strlen (filter ) == 0 ) {
133+ goto fail ;
134+ }
117135 size_t len = (tokc ? strlen (tokv [0 ]) : 0 );
118136 uint32_t i , f , e ;
119- for (e = f = i = 0 ; i < count ; ++ i ) {
137+ f = e = 0 ;
138+
139+ struct fuzzy_match * fuzzy_matches = NULL ;
140+
141+ int fuzzy_match_count = 0 ;
142+
143+ if (fuzzy && !(fuzzy_matches = calloc (count , sizeof (* fuzzy_matches ))))
144+ goto fail ;
145+
146+ for (i = 0 ; i < count ; ++ i ) {
120147 struct bm_item * item = items [i ];
121148 if (!item -> text && tokc != 0 )
122149 continue ;
123150
151+ if (fuzzy && tokc && item -> text ) {
152+ const char * text = item -> text ;
153+ int sidx = -1 , eidx = -1 , pidx = 0 , text_len = strlen (text ), distance = 0 ;
154+ for (int j = 0 ; j < text_len && text [j ]; ++ j ) {
155+ if (!fstrncmp (& text [j ], & filter [pidx ], 1 )) {
156+ if (sidx == -1 )
157+ sidx = j ;
158+ pidx ++ ;
159+ if (pidx == strlen (filter )) {
160+ eidx = j ;
161+ break ;
162+ }
163+ }
164+ }
165+ if (eidx != -1 ) {
166+ distance = eidx - sidx + (text_len - eidx + sidx ) / 3 ;
167+ fuzzy_matches [fuzzy_match_count ++ ] = (struct fuzzy_match ){ item , distance };
168+ continue ;
169+ }
170+ }
171+
172+ if (fuzzy ) continue ;
173+
124174 if (tokc && item -> text ) {
125175 uint32_t t ;
126176 for (t = 0 ; t < tokc && fstrstr (item -> text , tokv [t ]); ++ t );
@@ -142,12 +192,23 @@ filter_dmenu_fun(struct bm_menu *menu, char addition, char* (*fstrstr)(const cha
142192 f ++ ; /* where do all matches end */
143193 }
144194
195+ if (fuzzy && fuzzy_match_count > 0 ) {
196+ qsort (fuzzy_matches , fuzzy_match_count , sizeof (struct fuzzy_match ), fuzzy_match_comparator );
197+
198+ for (int j = 0 ; j < fuzzy_match_count ; ++ j ) {
199+ filtered [f ++ ] = fuzzy_matches [j ].item ;
200+ }
201+
202+ free (fuzzy_matches );
203+ }
204+
145205 free (buffer );
146206 free (tokv );
147207 return shrink_list (& filtered , menu -> items .count , (* out_nmemb = f ));
148208
149209fail :
150210 free (filtered );
211+ free (fuzzy_matches );
151212 free (buffer );
152213 return NULL ;
153214}
@@ -163,7 +224,7 @@ filter_dmenu_fun(struct bm_menu *menu, char addition, char* (*fstrstr)(const cha
163224struct bm_item * *
164225bm_filter_dmenu (struct bm_menu * menu , bool addition , uint32_t * out_nmemb )
165226{
166- return filter_dmenu_fun (menu , addition , strstr , strncmp , out_nmemb );
227+ return filter_dmenu_fun (menu , addition , strstr , strncmp , out_nmemb , menu -> fuzzy );
167228}
168229
169230/**
@@ -177,7 +238,7 @@ bm_filter_dmenu(struct bm_menu *menu, bool addition, uint32_t *out_nmemb)
177238struct bm_item * *
178239bm_filter_dmenu_case_insensitive (struct bm_menu * menu , bool addition , uint32_t * out_nmemb )
179240{
180- return filter_dmenu_fun (menu , addition , bm_strupstr , bm_strnupcmp , out_nmemb );
241+ return filter_dmenu_fun (menu , addition , bm_strupstr , bm_strnupcmp , out_nmemb , menu -> fuzzy );
181242}
182243
183244/* vim: set ts=8 sw=4 tw=0 :*/
0 commit comments