mirror of
https://github.com/neovim/neovim.git
synced 2024-12-19 10:45:16 -07:00
vim-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: vim/vim#15193
8159fb18a9
Cherry-pick insexpand.c changes from patch 9.1.0608.
N/A patch:
vim-patch:9.1.0632: MS-Windows: Compiler Warnings
Co-authored-by: glepnir <glephunter@gmail.com>
This commit is contained in:
parent
738320188f
commit
46d89ff9f8
@ -283,6 +283,8 @@ static size_t spell_bad_len = 0; // length of located bad word
|
||||
|
||||
static int compl_selected_item = -1;
|
||||
|
||||
static int *compl_fuzzy_scores;
|
||||
|
||||
// "compl_match_array" points the currently displayed list of entries in the
|
||||
// popup menu. It is NULL when there is no popup menu.
|
||||
static pumitem_T *compl_match_array = NULL;
|
||||
@ -3008,7 +3010,7 @@ enum {
|
||||
/// the "st->e_cpt" option value and process the next matching source.
|
||||
/// INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
|
||||
static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_arg,
|
||||
pos_T *start_match_pos)
|
||||
pos_T *start_match_pos, bool in_fuzzy)
|
||||
{
|
||||
int compl_type = -1;
|
||||
int status = INS_COMPL_CPT_OK;
|
||||
@ -3024,7 +3026,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
|
||||
st->first_match_pos = *start_match_pos;
|
||||
// Move the cursor back one character so that ^N can match the
|
||||
// word immediately after the cursor.
|
||||
if (ctrl_x_mode_normal() && dec(&st->first_match_pos) < 0) {
|
||||
if (ctrl_x_mode_normal() && (!in_fuzzy && dec(&st->first_match_pos) < 0)) {
|
||||
// Move the cursor to after the last character in the
|
||||
// buffer, so that word at start of buffer is found
|
||||
// correctly.
|
||||
@ -3162,11 +3164,30 @@ static void get_next_tag_completion(void)
|
||||
p_ic = save_p_ic;
|
||||
}
|
||||
|
||||
/// Compare function for qsort
|
||||
static int compare_scores(const void *a, const void *b)
|
||||
{
|
||||
int idx_a = *(const int *)a;
|
||||
int idx_b = *(const int *)b;
|
||||
int score_a = compl_fuzzy_scores[idx_a];
|
||||
int score_b = compl_fuzzy_scores[idx_b];
|
||||
return (score_a > score_b) ? -1 : (score_a < score_b) ? 1 : 0;
|
||||
}
|
||||
|
||||
/// Get the next set of filename matching "compl_pattern".
|
||||
static void get_next_filename_completion(void)
|
||||
{
|
||||
char **matches;
|
||||
int num_matches;
|
||||
char *leader = ins_compl_leader();
|
||||
size_t leader_len = strlen(leader);
|
||||
bool in_fuzzy = ((get_cot_flags() & kOptCotFlagFuzzy) != 0 && leader_len > 0);
|
||||
|
||||
if (in_fuzzy) {
|
||||
xfree(compl_pattern);
|
||||
compl_pattern = xstrdup("*");
|
||||
}
|
||||
|
||||
if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
|
||||
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) {
|
||||
return;
|
||||
@ -3189,6 +3210,40 @@ static void get_next_filename_completion(void)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (in_fuzzy) {
|
||||
garray_T fuzzy_indices;
|
||||
ga_init(&fuzzy_indices, sizeof(int), 10);
|
||||
compl_fuzzy_scores = (int *)xmalloc(sizeof(int) * (size_t)num_matches);
|
||||
|
||||
for (int i = 0; i < num_matches; i++) {
|
||||
char *ptr = matches[i];
|
||||
int score = fuzzy_match_str(ptr, leader);
|
||||
if (score > 0) {
|
||||
GA_APPEND(int, &fuzzy_indices, i);
|
||||
compl_fuzzy_scores[i] = score;
|
||||
}
|
||||
}
|
||||
|
||||
// prevent qsort from deref NULL pointer
|
||||
if (fuzzy_indices.ga_len > 0) {
|
||||
int *fuzzy_indices_data = (int *)fuzzy_indices.ga_data;
|
||||
qsort(fuzzy_indices_data, (size_t)fuzzy_indices.ga_len, sizeof(int), compare_scores);
|
||||
|
||||
char **sorted_matches = (char **)xmalloc(sizeof(char *) * (size_t)fuzzy_indices.ga_len);
|
||||
for (int i = 0; i < fuzzy_indices.ga_len; i++) {
|
||||
sorted_matches[i] = xstrdup(matches[fuzzy_indices_data[i]]);
|
||||
}
|
||||
|
||||
FreeWild(num_matches, matches);
|
||||
matches = sorted_matches;
|
||||
num_matches = fuzzy_indices.ga_len;
|
||||
}
|
||||
|
||||
xfree(compl_fuzzy_scores);
|
||||
ga_clear(&fuzzy_indices);
|
||||
}
|
||||
|
||||
ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
|
||||
}
|
||||
|
||||
@ -3310,6 +3365,11 @@ static char *ins_compl_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_po
|
||||
/// @return OK if a new next match is found, otherwise FAIL.
|
||||
static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
|
||||
{
|
||||
char *ptr = NULL;
|
||||
int len = 0;
|
||||
bool in_fuzzy = (get_cot_flags() & kOptCotFlagFuzzy) != 0 && compl_length > 0;
|
||||
char *leader = ins_compl_leader();
|
||||
|
||||
// If 'infercase' is set, don't use 'smartcase' here
|
||||
const int save_p_scs = p_scs;
|
||||
assert(st->ins_buf);
|
||||
@ -3324,7 +3384,7 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
|
||||
const int save_p_ws = p_ws;
|
||||
if (st->ins_buf != curbuf) {
|
||||
p_ws = false;
|
||||
} else if (*st->e_cpt == '.') {
|
||||
} else if (*st->e_cpt == '.' && !in_fuzzy) {
|
||||
p_ws = true;
|
||||
}
|
||||
bool looped_around = false;
|
||||
@ -3335,9 +3395,14 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
|
||||
msg_silent++; // Don't want messages for wrapscan.
|
||||
// ctrl_x_mode_line_or_eval() || word-wise search that
|
||||
// has added a word that was at the beginning of the line.
|
||||
if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL)) {
|
||||
if ((ctrl_x_mode_whole_line() && !in_fuzzy) || ctrl_x_mode_eval()
|
||||
|| (compl_cont_status & CONT_SOL)) {
|
||||
found_new_match = search_for_exact_line(st->ins_buf, st->cur_match_pos,
|
||||
compl_direction, compl_pattern);
|
||||
} else if (in_fuzzy) {
|
||||
found_new_match = search_for_fuzzy_match(st->ins_buf,
|
||||
st->cur_match_pos, leader, compl_direction,
|
||||
start_pos, &len, &ptr, ctrl_x_mode_whole_line());
|
||||
} else {
|
||||
found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
|
||||
NULL, compl_direction, compl_pattern, compl_patternlen,
|
||||
@ -3383,12 +3448,15 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
|
||||
&& start_pos->col == st->cur_match_pos->col) {
|
||||
continue;
|
||||
}
|
||||
int len;
|
||||
char *ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
|
||||
&len, &cont_s_ipos);
|
||||
|
||||
if (!in_fuzzy) {
|
||||
ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
|
||||
&len, &cont_s_ipos);
|
||||
}
|
||||
if (ptr == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ins_compl_add_infercase(ptr, len, p_ic,
|
||||
st->ins_buf == curbuf ? NULL : st->ins_buf->b_sfname,
|
||||
0, cont_s_ipos) != NOTDONE) {
|
||||
@ -3489,6 +3557,7 @@ static int ins_compl_get_exp(pos_T *ini)
|
||||
static bool st_cleared = false;
|
||||
int found_new_match;
|
||||
int type = ctrl_x_mode;
|
||||
bool in_fuzzy = (get_cot_flags() & kOptCotFlagFuzzy) != 0;
|
||||
|
||||
assert(curbuf != NULL);
|
||||
|
||||
@ -3513,7 +3582,11 @@ static int ins_compl_get_exp(pos_T *ini)
|
||||
assert(st.ins_buf != NULL);
|
||||
|
||||
compl_old_match = compl_curr_match; // remember the last current match
|
||||
st.cur_match_pos = compl_dir_forward() ? &st.last_match_pos : &st.first_match_pos;
|
||||
if (in_fuzzy) {
|
||||
st.cur_match_pos = compl_dir_forward() ? &st.last_match_pos : &st.first_match_pos;
|
||||
} else {
|
||||
st.cur_match_pos = &st.last_match_pos;
|
||||
}
|
||||
|
||||
// For ^N/^P loop over all the flags/windows/buffers in 'complete'
|
||||
while (true) {
|
||||
@ -3525,7 +3598,7 @@ static int ins_compl_get_exp(pos_T *ini)
|
||||
// entries from 'complete' that look in loaded buffers.
|
||||
if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
|
||||
&& (!compl_started || st.found_all)) {
|
||||
int status = process_next_cpt_value(&st, &type, ini);
|
||||
int status = process_next_cpt_value(&st, &type, ini, in_fuzzy);
|
||||
if (status == INS_COMPL_CPT_END) {
|
||||
break;
|
||||
}
|
||||
|
@ -3564,6 +3564,141 @@ garray_T *fuzzy_match_str_with_pos(char *const str, const char *const pat)
|
||||
return match_positions;
|
||||
}
|
||||
|
||||
/// This function searches for a fuzzy match of the pattern `pat` within the
|
||||
/// line pointed to by `*ptr`. It splits the line into words, performs fuzzy
|
||||
/// matching on each word, and returns the length and position of the first
|
||||
/// matched word.
|
||||
static bool fuzzy_match_str_in_line(char **ptr, char *pat, int *len, pos_T *current_pos)
|
||||
{
|
||||
char *str = *ptr;
|
||||
char *strBegin = str;
|
||||
char *end = NULL;
|
||||
char *start = NULL;
|
||||
bool found = false;
|
||||
|
||||
if (str == NULL || pat == NULL) {
|
||||
return found;
|
||||
}
|
||||
|
||||
while (*str != NUL) {
|
||||
// Skip non-word characters
|
||||
start = find_word_start(str);
|
||||
if (*start == NUL) {
|
||||
break;
|
||||
}
|
||||
end = find_word_end(start);
|
||||
|
||||
// Extract the word from start to end
|
||||
char save_end = *end;
|
||||
*end = NUL;
|
||||
|
||||
// Perform fuzzy match
|
||||
int result = fuzzy_match_str(start, pat);
|
||||
*end = save_end;
|
||||
|
||||
if (result > 0) {
|
||||
*len = (int)(end - start);
|
||||
current_pos->col += (int)(end - strBegin);
|
||||
found = true;
|
||||
*ptr = start;
|
||||
break;
|
||||
}
|
||||
|
||||
// Move to the end of the current word for the next iteration
|
||||
str = end;
|
||||
// Ensure we continue searching after the current word
|
||||
while (*str != NUL && !vim_iswordp(str)) {
|
||||
MB_PTR_ADV(str);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// Search for the next fuzzy match in the specified buffer.
|
||||
/// This function attempts to find the next occurrence of the given pattern
|
||||
/// in the buffer, starting from the current position. It handles line wrapping
|
||||
/// and direction of search.
|
||||
///
|
||||
/// Return true if a match is found, otherwise false.
|
||||
bool search_for_fuzzy_match(buf_T *buf, pos_T *pos, char *pattern, int dir, pos_T *start_pos,
|
||||
int *len, char **ptr, bool whole_line)
|
||||
{
|
||||
pos_T current_pos = *pos;
|
||||
pos_T circly_end;
|
||||
bool found_new_match = false;
|
||||
bool looped_around = false;
|
||||
|
||||
if (whole_line) {
|
||||
current_pos.lnum += dir;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (buf == curbuf) {
|
||||
circly_end = *start_pos;
|
||||
} else {
|
||||
circly_end.lnum = buf->b_ml.ml_line_count;
|
||||
circly_end.col = 0;
|
||||
circly_end.coladd = 0;
|
||||
}
|
||||
|
||||
// Check if looped around and back to start position
|
||||
if (looped_around && equalpos(current_pos, circly_end)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure current_pos is valid
|
||||
if (current_pos.lnum >= 1 && current_pos.lnum <= buf->b_ml.ml_line_count) {
|
||||
// Get the current line buffer
|
||||
*ptr = ml_get_buf(buf, current_pos.lnum);
|
||||
// If ptr is end of line is reached, move to next line
|
||||
// or previous line based on direction
|
||||
if (**ptr != NUL) {
|
||||
if (!whole_line) {
|
||||
*ptr += current_pos.col;
|
||||
// Try to find a fuzzy match in the current line starting from current position
|
||||
found_new_match = fuzzy_match_str_in_line(ptr, pattern, len, ¤t_pos);
|
||||
if (found_new_match) {
|
||||
*pos = current_pos;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (fuzzy_match_str(*ptr, pattern) > 0) {
|
||||
found_new_match = true;
|
||||
*pos = current_pos;
|
||||
*len = (int)strlen(*ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the next line or previous line based on direction
|
||||
if (dir == FORWARD) {
|
||||
if (++current_pos.lnum > buf->b_ml.ml_line_count) {
|
||||
if (p_ws) {
|
||||
current_pos.lnum = 1;
|
||||
looped_around = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (--current_pos.lnum < 1) {
|
||||
if (p_ws) {
|
||||
current_pos.lnum = buf->b_ml.ml_line_count;
|
||||
looped_around = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
current_pos.col = 0;
|
||||
}
|
||||
|
||||
return found_new_match;
|
||||
}
|
||||
|
||||
/// Copy a list of fuzzy matches into a string list after sorting the matches by
|
||||
/// the fuzzy score. Frees the memory allocated for "fuzmatch".
|
||||
void fuzzymatches_to_strmatches(fuzmatch_str_T *const fuzmatch, char ***const matches,
|
||||
|
@ -5294,9 +5294,9 @@ describe('builtin popupmenu', function()
|
||||
feed('S hello helio hero h<C-X><C-P>')
|
||||
screen:expect([[
|
||||
hello helio hero h^ |
|
||||
{1:~ }{n: }{mn:h}{n:ello }{1: }|
|
||||
{1:~ }{n: }{mn:h}{n:ero }{1: }|
|
||||
{1:~ }{n: }{mn:h}{n:elio }{1: }|
|
||||
{1:~ }{s: }{ms:h}{s:ero }{1: }|
|
||||
{1:~ }{s: }{ms:h}{s:ello }{1: }|
|
||||
{1:~ }|*15
|
||||
{2:-- }{5:match 1 of 3} |
|
||||
]])
|
||||
@ -5304,9 +5304,9 @@ describe('builtin popupmenu', function()
|
||||
feed('<Esc>S hello helio hero h<C-X><C-P><C-P>')
|
||||
screen:expect([[
|
||||
hello helio hero h^ |
|
||||
{1:~ }{n: }{mn:h}{n:ello }{1: }|
|
||||
{1:~ }{s: }{ms:h}{s:elio }{1: }|
|
||||
{1:~ }{n: }{mn:h}{n:ero }{1: }|
|
||||
{1:~ }{s: }{ms:h}{s:elio }{1: }|
|
||||
{1:~ }{n: }{mn:h}{n:ello }{1: }|
|
||||
{1:~ }|*15
|
||||
{2:-- }{5:match 2 of 3} |
|
||||
]])
|
||||
|
@ -2830,6 +2830,68 @@ func Test_complete_fuzzy_match()
|
||||
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('hello help hero h', getline('.'))
|
||||
|
||||
set completeopt-=noinsert
|
||||
call setline(1, ['xyz yxz x'])
|
||||
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('xyz yxz xyz', getline('.'))
|
||||
" can fuzzy get yxz when use Ctrl-N twice
|
||||
call setline(1, ['xyz yxz x'])
|
||||
call feedkeys("A\<C-X>\<C-N>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('xyz yxz yxz', getline('.'))
|
||||
|
||||
call setline(1, ['你好 你'])
|
||||
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('你好 你好', getline('.'))
|
||||
call setline(1, ['你的 我的 的'])
|
||||
call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('你的 我的 你的', getline('.'))
|
||||
" can fuzzy get multiple-byte word when use Ctrl-N twice
|
||||
call setline(1, ['你的 我的 的'])
|
||||
call feedkeys("A\<C-X>\<C-N>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('你的 我的 我的', getline('.'))
|
||||
|
||||
" respect wrapscan
|
||||
set nowrapscan
|
||||
call setline(1, ["xyz", "yxz", ""])
|
||||
call cursor(3, 1)
|
||||
call feedkeys("Sy\<C-X>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('y', getline('.'))
|
||||
set wrapscan
|
||||
call feedkeys("Sy\<C-X>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('xyz', getline('.'))
|
||||
|
||||
" fuzzy on file
|
||||
call writefile([''], 'fobar', 'D')
|
||||
call writefile([''], 'foobar', 'D')
|
||||
call setline(1, ['fob'])
|
||||
call cursor(1, 1)
|
||||
call feedkeys("A\<C-X>\<C-f>\<Esc>0", 'tx!')
|
||||
call assert_equal('fobar', getline('.'))
|
||||
call feedkeys("Sfob\<C-X>\<C-f>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('foobar', getline('.'))
|
||||
|
||||
" can get completion from other buffer
|
||||
set completeopt=fuzzy,menu,menuone
|
||||
vnew
|
||||
call setline(1, ["completeness,", "compatibility", "Composite", "Omnipotent"])
|
||||
wincmd p
|
||||
call feedkeys("Somp\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('completeness', getline('.'))
|
||||
call feedkeys("Somp\<C-N>\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('compatibility', getline('.'))
|
||||
call feedkeys("Somp\<C-P>\<Esc>0", 'tx!')
|
||||
call assert_equal('Omnipotent', getline('.'))
|
||||
call feedkeys("Somp\<C-P>\<C-P>\<Esc>0", 'tx!')
|
||||
call assert_equal('Composite', getline('.'))
|
||||
|
||||
" fuzzy on whole line completion
|
||||
call setline(1, ["world is on fire", "no one can save me but you", 'user can execute', ''])
|
||||
call cursor(4, 1)
|
||||
call feedkeys("Swio\<C-X>\<C-L>\<Esc>0", 'tx!')
|
||||
call assert_equal('world is on fire', getline('.'))
|
||||
call feedkeys("Su\<C-X>\<C-L>\<C-P>\<Esc>0", 'tx!')
|
||||
call assert_equal('no one can save me but you', getline('.'))
|
||||
|
||||
" issue #15526
|
||||
set completeopt=fuzzy,menuone,menu,noselect
|
||||
call setline(1, ['Text', 'ToText', ''])
|
||||
@ -2840,6 +2902,7 @@ func Test_complete_fuzzy_match()
|
||||
" clean up
|
||||
set omnifunc=
|
||||
bw!
|
||||
bw!
|
||||
set complete& completeopt&
|
||||
autocmd! AAAAA_Group
|
||||
augroup! AAAAA_Group
|
||||
|
Loading…
Reference in New Issue
Block a user