Skip to content

Commit 8159fb1

Browse files
glepnirchrisbra
authored andcommitted
patch 9.1.0598: fuzzy completion does not work with default completion
Problem: fuzzy completion does not work with default completion Solution: Make it work (glepnir) closes: #15193 Signed-off-by: glepnir <glephunter@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent fcc1b57 commit 8159fb1

7 files changed

Lines changed: 323 additions & 20 deletions

File tree

src/insexpand.c

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ static int compl_opt_suppress_empty = FALSE;
203203

204204
static int compl_selected_item = -1;
205205

206+
static int *compl_fuzzy_scores;
207+
206208
static 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);
207209
static void ins_compl_longest_match(compl_T *match);
208210
static void ins_compl_del_pum(void);
@@ -3322,7 +3324,8 @@ typedef struct
33223324
process_next_cpt_value(
33233325
ins_compl_next_state_T *st,
33243326
int *compl_type_arg,
3325-
pos_T *start_match_pos)
3327+
pos_T *start_match_pos,
3328+
int in_fuzzy)
33263329
{
33273330
int compl_type = -1;
33283331
int status = INS_COMPL_CPT_OK;
@@ -3338,7 +3341,7 @@ process_next_cpt_value(
33383341
st->first_match_pos = *start_match_pos;
33393342
// Move the cursor back one character so that ^N can match the
33403343
// word immediately after the cursor.
3341-
if (ctrl_x_mode_normal() && dec(&st->first_match_pos) < 0)
3344+
if (ctrl_x_mode_normal() && (!in_fuzzy && dec(&st->first_match_pos) < 0))
33423345
{
33433346
// Move the cursor to after the last character in the
33443347
// buffer, so that word at start of buffer is found
@@ -3505,6 +3508,18 @@ get_next_tag_completion(void)
35053508
p_ic = save_p_ic;
35063509
}
35073510

3511+
/*
3512+
* Compare function for qsort
3513+
*/
3514+
static int compare_scores(const void *a, const void *b)
3515+
{
3516+
int idx_a = *(const int *)a;
3517+
int idx_b = *(const int *)b;
3518+
int score_a = compl_fuzzy_scores[idx_a];
3519+
int score_b = compl_fuzzy_scores[idx_b];
3520+
return (score_a > score_b) ? -1 : (score_a < score_b) ? 1 : 0;
3521+
}
3522+
35083523
/*
35093524
* Get the next set of filename matching "compl_pattern".
35103525
*/
@@ -3513,6 +3528,21 @@ get_next_filename_completion(void)
35133528
{
35143529
char_u **matches;
35153530
int num_matches;
3531+
char_u *ptr;
3532+
garray_T fuzzy_indices;
3533+
int i;
3534+
int score;
3535+
char_u *leader = ins_compl_leader();
3536+
int leader_len = STRLEN(leader);
3537+
int in_fuzzy = ((get_cot_flags() & COT_FUZZY) != 0 && leader_len > 0);
3538+
char_u **sorted_matches;
3539+
int *fuzzy_indices_data;
3540+
3541+
if (in_fuzzy)
3542+
{
3543+
vim_free(compl_pattern);
3544+
compl_pattern = vim_strsave((char_u *)"*");
3545+
}
35163546

35173547
if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
35183548
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK)
@@ -3523,12 +3553,9 @@ get_next_filename_completion(void)
35233553
#ifdef BACKSLASH_IN_FILENAME
35243554
if (curbuf->b_p_csl[0] != NUL)
35253555
{
3526-
int i;
3527-
35283556
for (i = 0; i < num_matches; ++i)
35293557
{
3530-
char_u *ptr = matches[i];
3531-
3558+
ptr = matches[i];
35323559
while (*ptr != NUL)
35333560
{
35343561
if (curbuf->b_p_csl[0] == 's' && *ptr == '\\')
@@ -3540,6 +3567,41 @@ get_next_filename_completion(void)
35403567
}
35413568
}
35423569
#endif
3570+
3571+
if (in_fuzzy)
3572+
{
3573+
ga_init2(&fuzzy_indices, sizeof(int), 10);
3574+
compl_fuzzy_scores = (int *)alloc(sizeof(int) * num_matches);
3575+
3576+
for (i = 0; i < num_matches; i++)
3577+
{
3578+
ptr = matches[i];
3579+
score = fuzzy_match_str(ptr, leader);
3580+
if (score > 0)
3581+
{
3582+
if (ga_grow(&fuzzy_indices, 1) == OK)
3583+
{
3584+
((int *)fuzzy_indices.ga_data)[fuzzy_indices.ga_len] = i;
3585+
compl_fuzzy_scores[i] = score;
3586+
fuzzy_indices.ga_len++;
3587+
}
3588+
}
3589+
}
3590+
3591+
fuzzy_indices_data = (int *)fuzzy_indices.ga_data;
3592+
qsort(fuzzy_indices_data, fuzzy_indices.ga_len, sizeof(int), compare_scores);
3593+
3594+
sorted_matches = (char_u **)alloc(sizeof(char_u *) * fuzzy_indices.ga_len);
3595+
for (i = 0; i < fuzzy_indices.ga_len; ++i)
3596+
sorted_matches[i] = vim_strsave(matches[fuzzy_indices_data[i]]);
3597+
3598+
FreeWild(num_matches, matches);
3599+
matches = sorted_matches;
3600+
num_matches = fuzzy_indices.ga_len;
3601+
vim_free(compl_fuzzy_scores);
3602+
ga_clear(&fuzzy_indices);
3603+
}
3604+
35433605
ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
35443606
}
35453607

@@ -3687,8 +3749,10 @@ get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
36873749
int save_p_scs;
36883750
int save_p_ws;
36893751
int looped_around = FALSE;
3690-
char_u *ptr;
3691-
int len;
3752+
char_u *ptr = NULL;
3753+
int len = 0;
3754+
int in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0 && compl_length > 0;
3755+
char_u *leader = ins_compl_leader();
36923756

36933757
// If 'infercase' is set, don't use 'smartcase' here
36943758
save_p_scs = p_scs;
@@ -3702,7 +3766,7 @@ get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
37023766
save_p_ws = p_ws;
37033767
if (st->ins_buf != curbuf)
37043768
p_ws = FALSE;
3705-
else if (*st->e_cpt == '.')
3769+
else if (*st->e_cpt == '.' && !in_fuzzy)
37063770
p_ws = TRUE;
37073771
looped_around = FALSE;
37083772
for (;;)
@@ -3713,9 +3777,13 @@ get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
37133777

37143778
// ctrl_x_mode_line_or_eval() || word-wise search that
37153779
// has added a word that was at the beginning of the line
3716-
if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL))
3780+
if ((ctrl_x_mode_whole_line() && !in_fuzzy) || ctrl_x_mode_eval() || (compl_cont_status & CONT_SOL))
37173781
found_new_match = search_for_exact_line(st->ins_buf,
37183782
st->cur_match_pos, compl_direction, compl_pattern);
3783+
else if (in_fuzzy)
3784+
found_new_match = search_for_fuzzy_match(st->ins_buf,
3785+
st->cur_match_pos, leader, compl_direction,
3786+
start_pos, &len, &ptr, ctrl_x_mode_whole_line());
37193787
else
37203788
found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
37213789
NULL, compl_direction, compl_pattern, compl_patternlen,
@@ -3764,8 +3832,9 @@ get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
37643832
&& start_pos->col == st->cur_match_pos->col)
37653833
continue;
37663834

3767-
ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
3768-
&len, &cont_s_ipos);
3835+
if (!in_fuzzy)
3836+
ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
3837+
&len, &cont_s_ipos);
37693838
if (ptr == NULL)
37703839
continue;
37713840

@@ -3864,6 +3933,7 @@ ins_compl_get_exp(pos_T *ini)
38643933
int i;
38653934
int found_new_match;
38663935
int type = ctrl_x_mode;
3936+
int in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0;
38673937

38683938
if (!compl_started)
38693939
{
@@ -3889,8 +3959,11 @@ ins_compl_get_exp(pos_T *ini)
38893959
st.ins_buf = curbuf; // In case the buffer was wiped out.
38903960

38913961
compl_old_match = compl_curr_match; // remember the last current match
3892-
st.cur_match_pos = (compl_dir_forward())
3893-
? &st.last_match_pos : &st.first_match_pos;
3962+
if (in_fuzzy)
3963+
st.cur_match_pos = (compl_dir_forward())
3964+
? &st.last_match_pos : &st.first_match_pos;
3965+
else
3966+
st.cur_match_pos = &st.last_match_pos;
38943967

38953968
// For ^N/^P loop over all the flags/windows/buffers in 'complete'.
38963969
for (;;)
@@ -3904,7 +3977,7 @@ ins_compl_get_exp(pos_T *ini)
39043977
if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
39053978
&& (!compl_started || st.found_all))
39063979
{
3907-
int status = process_next_cpt_value(&st, &type, ini);
3980+
int status = process_next_cpt_value(&st, &type, ini, in_fuzzy);
39083981

39093982
if (status == INS_COMPL_CPT_END)
39103983
break;

src/proto/search.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ void f_matchfuzzy(typval_T *argvars, typval_T *rettv);
4141
void f_matchfuzzypos(typval_T *argvars, typval_T *rettv);
4242
int fuzzy_match_str(char_u *str, char_u *pat);
4343
garray_T *fuzzy_match_str_with_pos(char_u *str, char_u *pat);
44+
int search_for_fuzzy_match(buf_T *buf, pos_T *pos, char_u *pattern, int dir, pos_T *start_pos, int *len, char_u **ptr, int whole_line);
4445
void fuzmatch_str_free(fuzmatch_str_T *fuzmatch, int count);
4546
int fuzzymatches_to_strmatches(fuzmatch_str_T *fuzmatch, char_u ***matches, int count, int funcsort);
4647
/* vim: set ft=c : */

src/search.c

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ static int fuzzy_match_str_compare(const void *s1, const void *s2);
5353
static void fuzzy_match_str_sort(fuzmatch_str_T *fm, int sz);
5454
static int fuzzy_match_func_compare(const void *s1, const void *s2);
5555
static void fuzzy_match_func_sort(fuzmatch_str_T *fm, int sz);
56+
static int fuzzy_match_str_in_line(char_u **ptr, char_u *pat, int *len, pos_T *current_pos);
5657

5758
#define SEARCH_STAT_DEF_TIMEOUT 40L
5859
#define SEARCH_STAT_DEF_MAX_COUNT 99
@@ -5139,6 +5140,169 @@ fuzzy_match_str_with_pos(char_u *str UNUSED, char_u *pat UNUSED)
51395140
#endif
51405141
}
51415142

5143+
/*
5144+
* This function searches for a fuzzy match of the pattern `pat` within the
5145+
* line pointed to by `*ptr`. It splits the line into words, performs fuzzy
5146+
* matching on each word, and returns the length and position of the first
5147+
* matched word.
5148+
*/
5149+
static int
5150+
fuzzy_match_str_in_line(char_u **ptr, char_u *pat, int *len, pos_T *current_pos)
5151+
{
5152+
char_u *str = *ptr;
5153+
char_u *strBegin = str;
5154+
char_u *end = NULL;
5155+
char_u *start = NULL;
5156+
int found = FALSE;
5157+
int result;
5158+
char save_end;
5159+
5160+
if (str == NULL || pat == NULL)
5161+
return found;
5162+
5163+
while (*str != NUL)
5164+
{
5165+
// Skip non-word characters
5166+
start = find_word_start(str);
5167+
if (*start == NUL)
5168+
break;
5169+
end = find_word_end(start);
5170+
5171+
// Extract the word from start to end
5172+
save_end = *end;
5173+
*end = NUL;
5174+
5175+
// Perform fuzzy match
5176+
result = fuzzy_match_str(start, pat);
5177+
*end = save_end;
5178+
5179+
if (result > 0)
5180+
{
5181+
*len = (int)(end - start);
5182+
current_pos->col += (int)(end - strBegin);
5183+
found = TRUE;
5184+
*ptr = start;
5185+
break;
5186+
}
5187+
5188+
// Move to the end of the current word for the next iteration
5189+
str = end;
5190+
// Ensure we continue searching after the current word
5191+
while (*str != NUL && !vim_iswordp(str))
5192+
MB_PTR_ADV(str);
5193+
}
5194+
5195+
return found;
5196+
}
5197+
5198+
/*
5199+
* Search for the next fuzzy match in the specified buffer.
5200+
* This function attempts to find the next occurrence of the given pattern
5201+
* in the buffer, starting from the current position. It handles line wrapping
5202+
* and direction of search.
5203+
*
5204+
* Return TRUE if a match is found, otherwise FALSE.
5205+
*/
5206+
int
5207+
search_for_fuzzy_match(
5208+
buf_T *buf,
5209+
pos_T *pos,
5210+
char_u *pattern,
5211+
int dir,
5212+
pos_T *start_pos,
5213+
int *len,
5214+
char_u **ptr,
5215+
int whole_line)
5216+
{
5217+
pos_T current_pos = *pos;
5218+
pos_T circly_end;
5219+
int found_new_match = FAIL;
5220+
int looped_around = FALSE;
5221+
5222+
if (whole_line)
5223+
current_pos.lnum += dir;
5224+
5225+
do {
5226+
if (buf == curbuf)
5227+
circly_end = *start_pos;
5228+
else
5229+
{
5230+
circly_end.lnum = buf->b_ml.ml_line_count;
5231+
circly_end.col = 0;
5232+
circly_end.coladd = 0;
5233+
}
5234+
5235+
// Check if looped around and back to start position
5236+
if (looped_around && EQUAL_POS(current_pos, circly_end))
5237+
break;
5238+
5239+
// Ensure current_pos is valid
5240+
if (current_pos.lnum >= 1 && current_pos.lnum <= buf->b_ml.ml_line_count)
5241+
{
5242+
// Get the current line buffer
5243+
*ptr = ml_get_buf(buf, current_pos.lnum, FALSE);
5244+
// If ptr is end of line is reached, move to next line
5245+
// or previous line based on direction
5246+
if (**ptr != NUL)
5247+
{
5248+
if (!whole_line)
5249+
{
5250+
*ptr += current_pos.col;
5251+
// Try to find a fuzzy match in the current line starting from current position
5252+
found_new_match = fuzzy_match_str_in_line(ptr, pattern, len, &current_pos);
5253+
if (found_new_match)
5254+
{
5255+
*pos = current_pos;
5256+
break;
5257+
}
5258+
}
5259+
else
5260+
{
5261+
if (fuzzy_match_str(*ptr, pattern) > 0)
5262+
{
5263+
found_new_match = TRUE;
5264+
*pos = current_pos;
5265+
*len = STRLEN(*ptr);
5266+
break;
5267+
}
5268+
}
5269+
}
5270+
}
5271+
5272+
// Move to the next line or previous line based on direction
5273+
if (dir == FORWARD)
5274+
{
5275+
if (++current_pos.lnum > buf->b_ml.ml_line_count)
5276+
{
5277+
if (p_ws)
5278+
{
5279+
current_pos.lnum = 1;
5280+
looped_around = TRUE;
5281+
}
5282+
else
5283+
break;
5284+
}
5285+
}
5286+
else
5287+
{
5288+
if (--current_pos.lnum < 1)
5289+
{
5290+
if (p_ws)
5291+
{
5292+
current_pos.lnum = buf->b_ml.ml_line_count;
5293+
looped_around = TRUE;
5294+
}
5295+
else
5296+
break;
5297+
5298+
}
5299+
}
5300+
current_pos.col = 0;
5301+
} while (TRUE);
5302+
5303+
return found_new_match;
5304+
}
5305+
51425306
/*
51435307
* Free an array of fuzzy string matches "fuzmatch[count]".
51445308
*/

0 commit comments

Comments
 (0)