@@ -113,6 +113,7 @@ struct compl_S
113113 // cp_flags has CP_FREE_FNAME
114114 int cp_flags ; // CP_ values
115115 int cp_number ; // sequence number
116+ int cp_score ; // fuzzy match score
116117};
117118
118119// values for cp_flags
@@ -153,6 +154,7 @@ static int compl_no_select = FALSE; // FALSE: select & insert
153154 // TRUE: noselect
154155static int compl_longest = FALSE; // FALSE: insert full match
155156 // TRUE: insert longest prefix
157+ static int compl_fuzzy_match = FALSE; // True: fuzzy match enabled
156158
157159// Selected one of the matches. When FALSE the match was edited or using the
158160// longest common string.
@@ -207,6 +209,8 @@ static int compl_cont_status = 0;
207209static int compl_opt_refresh_always = FALSE;
208210static int compl_opt_suppress_empty = FALSE;
209211
212+ static int compl_selected_item = -1 ;
213+
210214static int ins_compl_add (char_u * str , int len , char_u * fname , char_u * * cptext , typval_T * user_data , int cdir , int flags , int adup );
211215static void ins_compl_longest_match (compl_T * match );
212216static void ins_compl_del_pum (void );
@@ -1059,12 +1063,15 @@ completeopt_was_set(void)
10591063 compl_no_insert = FALSE;
10601064 compl_no_select = FALSE;
10611065 compl_longest = FALSE;
1066+ compl_fuzzy_match = FALSE;
10621067 if (strstr ((char * )p_cot , "noselect" ) != NULL )
10631068 compl_no_select = TRUE;
10641069 if (strstr ((char * )p_cot , "noinsert" ) != NULL )
10651070 compl_no_insert = TRUE;
10661071 if (strstr ((char * )p_cot , "longest" ) != NULL )
10671072 compl_longest = TRUE;
1073+ if (strstr ((char * )p_cot , "fuzzy" ) != NULL )
1074+ compl_fuzzy_match = TRUE;
10681075}
10691076
10701077
@@ -1212,6 +1219,17 @@ trigger_complete_changed_event(int cur)
12121219}
12131220#endif
12141221
1222+ /*
1223+ * pumitem qsort compare func
1224+ */
1225+ static int
1226+ ins_compl_fuzzy_sort (const void * a , const void * b )
1227+ {
1228+ const int sa = (* (pumitem_T * )a ).pum_score ;
1229+ const int sb = (* (pumitem_T * )b ).pum_score ;
1230+ return sa == sb ? 0 : sa < sb ? 1 : -1 ;
1231+ }
1232+
12151233/*
12161234 * Build a popup menu to show the completion matches.
12171235 * Returns the popup menu entry that should be selected. Returns -1 if nothing
@@ -1227,6 +1245,7 @@ ins_compl_build_pum(void)
12271245 int i ;
12281246 int cur = -1 ;
12291247 int lead_len = 0 ;
1248+ int max_fuzzy_score = 0 ;
12301249
12311250 // Need to build the popup menu list.
12321251 compl_match_arraysize = 0 ;
@@ -1236,9 +1255,15 @@ ins_compl_build_pum(void)
12361255
12371256 do
12381257 {
1258+ // when completeopt include fuzzy option and leader is not null or empty
1259+ // set the cp_score for after compare.
1260+ if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0 )
1261+ compl -> cp_score = fuzzy_match_str (compl -> cp_str , compl_leader );
1262+
12391263 if (!match_at_original_text (compl )
12401264 && (compl_leader == NULL
1241- || ins_compl_equal (compl , compl_leader , lead_len )))
1265+ || ins_compl_equal (compl , compl_leader , lead_len )
1266+ || (compl_fuzzy_match && compl -> cp_score > 0 )))
12421267 ++ compl_match_arraysize ;
12431268 compl = compl -> cp_next ;
12441269 } while (compl != NULL && !is_first_match (compl ));
@@ -1267,9 +1292,10 @@ ins_compl_build_pum(void)
12671292 {
12681293 if (!match_at_original_text (compl )
12691294 && (compl_leader == NULL
1270- || ins_compl_equal (compl , compl_leader , lead_len )))
1295+ || ins_compl_equal (compl , compl_leader , lead_len )
1296+ || (compl_fuzzy_match && compl -> cp_score > 0 )))
12711297 {
1272- if (!shown_match_ok )
1298+ if (!shown_match_ok && ! compl_fuzzy_match )
12731299 {
12741300 if (compl == compl_shown_match || did_find_shown_match )
12751301 {
@@ -1285,6 +1311,24 @@ ins_compl_build_pum(void)
12851311 shown_compl = compl ;
12861312 cur = i ;
12871313 }
1314+ else if (compl_fuzzy_match )
1315+ {
1316+ if (compl -> cp_score > max_fuzzy_score )
1317+ {
1318+ did_find_shown_match = TRUE;
1319+ max_fuzzy_score = compl -> cp_score ;
1320+ compl_shown_match = compl ;
1321+ shown_match_ok = TRUE;
1322+ }
1323+
1324+ if (!compl_no_select
1325+ && (max_fuzzy_score > 0
1326+ || (compl_leader == NULL || lead_len == 0 )))
1327+ {
1328+ shown_match_ok = TRUE;
1329+ cur = 0 ;
1330+ }
1331+ }
12881332
12891333 if (compl -> cp_text [CPT_ABBR ] != NULL )
12901334 compl_match_array [i ].pum_text =
@@ -1293,14 +1337,15 @@ ins_compl_build_pum(void)
12931337 compl_match_array [i ].pum_text = compl -> cp_str ;
12941338 compl_match_array [i ].pum_kind = compl -> cp_text [CPT_KIND ];
12951339 compl_match_array [i ].pum_info = compl -> cp_text [CPT_INFO ];
1340+ compl_match_array [i ].pum_score = compl -> cp_score ;
12961341 if (compl -> cp_text [CPT_MENU ] != NULL )
12971342 compl_match_array [i ++ ].pum_extra =
12981343 compl -> cp_text [CPT_MENU ];
12991344 else
13001345 compl_match_array [i ++ ].pum_extra = compl -> cp_fname ;
13011346 }
13021347
1303- if (compl == compl_shown_match )
1348+ if (compl == compl_shown_match && ! compl_fuzzy_match )
13041349 {
13051350 did_find_shown_match = TRUE;
13061351
@@ -1320,6 +1365,10 @@ ins_compl_build_pum(void)
13201365 compl = compl -> cp_next ;
13211366 } while (compl != NULL && !is_first_match (compl ));
13221367
1368+ if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0 )
1369+ // sort by the largest score of fuzzy match
1370+ qsort (compl_match_array , (size_t )compl_match_arraysize , sizeof (pumitem_T ), ins_compl_fuzzy_sort );
1371+
13231372 if (!shown_match_ok ) // no displayed match at all
13241373 cur = -1 ;
13251374
@@ -1376,6 +1425,7 @@ ins_compl_show_pum(void)
13761425 // Use the cursor to get all wrapping and other settings right.
13771426 col = curwin -> w_cursor .col ;
13781427 curwin -> w_cursor .col = compl_col ;
1428+ compl_selected_item = cur ;
13791429 pum_display (compl_match_array , compl_match_arraysize , cur );
13801430 curwin -> w_cursor .col = col ;
13811431
@@ -4025,6 +4075,40 @@ ins_compl_show_filename(void)
40254075 redraw_cmdline = FALSE; // don't overwrite!
40264076}
40274077
4078+ static compl_T *
4079+ find_comp_when_fuzzy (void )
4080+ {
4081+ int score ;
4082+ char_u * str ;
4083+ int target_idx = -1 ;
4084+ int is_forward = compl_shows_dir_forward ();
4085+ int is_backward = compl_shows_dir_backward ();
4086+ compl_T * comp = NULL ;
4087+
4088+ if (compl_match_array == NULL ||
4089+ (is_forward && compl_selected_item == compl_match_arraysize - 1 )
4090+ || (is_backward && compl_selected_item == 0 ))
4091+ return compl_first_match ;
4092+
4093+ if (is_forward )
4094+ target_idx = compl_selected_item + 1 ;
4095+ else if (is_backward )
4096+ target_idx = compl_selected_item == -1 ? compl_match_arraysize - 1
4097+ : compl_selected_item - 1 ;
4098+
4099+ score = compl_match_array [target_idx ].pum_score ;
4100+ str = compl_match_array [target_idx ].pum_text ;
4101+
4102+ comp = compl_first_match ;
4103+ do {
4104+ if (comp -> cp_score == score && (str == comp -> cp_str || str == comp -> cp_text [CPT_ABBR ]))
4105+ return comp ;
4106+ comp = comp -> cp_next ;
4107+ } while (comp != NULL && !is_first_match (comp ));
4108+
4109+ return NULL ;
4110+ }
4111+
40284112/*
40294113 * Find the next set of matches for completion. Repeat the completion "todo"
40304114 * times. The number of matches found is returned in 'num_matches'.
@@ -4052,7 +4136,8 @@ find_next_completion_match(
40524136 {
40534137 if (compl_shows_dir_forward () && compl_shown_match -> cp_next != NULL )
40544138 {
4055- compl_shown_match = compl_shown_match -> cp_next ;
4139+ compl_shown_match = !compl_fuzzy_match ? compl_shown_match -> cp_next
4140+ : find_comp_when_fuzzy ();
40564141 found_end = (compl_first_match != NULL
40574142 && (is_first_match (compl_shown_match -> cp_next )
40584143 || is_first_match (compl_shown_match )));
@@ -4061,7 +4146,8 @@ find_next_completion_match(
40614146 && compl_shown_match -> cp_prev != NULL )
40624147 {
40634148 found_end = is_first_match (compl_shown_match );
4064- compl_shown_match = compl_shown_match -> cp_prev ;
4149+ compl_shown_match = !compl_fuzzy_match ? compl_shown_match -> cp_prev
4150+ : find_comp_when_fuzzy ();
40654151 found_end |= is_first_match (compl_shown_match );
40664152 }
40674153 else
@@ -4111,7 +4197,8 @@ find_next_completion_match(
41114197 if (!match_at_original_text (compl_shown_match )
41124198 && compl_leader != NULL
41134199 && !ins_compl_equal (compl_shown_match ,
4114- compl_leader , (int )STRLEN (compl_leader )))
4200+ compl_leader , (int )STRLEN (compl_leader ))
4201+ && !(compl_fuzzy_match && compl_shown_match -> cp_score > 0 ))
41154202 ++ todo ;
41164203 else
41174204 // Remember a matching item.
@@ -4167,7 +4254,9 @@ ins_compl_next(
41674254 if (compl_shown_match == NULL )
41684255 return -1 ;
41694256
4170- if (compl_leader != NULL && !match_at_original_text (compl_shown_match ))
4257+ if (compl_leader != NULL
4258+ && !match_at_original_text (compl_shown_match )
4259+ && !compl_fuzzy_match )
41714260 // Update "compl_shown_match" to the actually shown match
41724261 ins_compl_update_shown_match ();
41734262
0 commit comments