Merge pull request #29200 from zeertzjq/vim-9.1.0463

vim-patch:9.1.{0463,0466}
This commit is contained in:
zeertzjq
2024-06-05 15:26:24 +08:00
committed by GitHub
8 changed files with 201 additions and 27 deletions

View File

@ -1517,6 +1517,10 @@ A jump table for the options with a short description can be found at |Q_op|.
completion in the preview window. Only works in
combination with "menu" or "menuone".
popup Show extra information about the currently selected
completion in a popup window. Only works in combination
with "menu" or "menuone". Overrides "preview".
noinsert Do not insert any text for a match until the user selects
a match from the menu. Only works in combination with
"menu" or "menuone". No effect if "longest" is present.
@ -1525,9 +1529,13 @@ A jump table for the options with a short description can be found at |Q_op|.
select one from the menu. Only works in combination with
"menu" or "menuone".
popup Show extra information about the currently selected
completion in a popup window. Only works in combination
with "menu" or "menuone". Overrides "preview".
fuzzy Enable |fuzzy-matching| for completion candidates. This
allows for more flexible and intuitive matching, where
characters can be skipped and matches can be found even
if the exact sequence is not typed. Only makes a
difference how completion candidates are reduced from the
list of alternatives, but not how the candidates are
collected (using different completion types).
*'completeslash'* *'csl'*
'completeslash' 'csl' string (default "")

View File

@ -1494,5 +1494,7 @@ the matching positions and the fuzzy match scores.
The "f" flag of `:vimgrep` enables fuzzy matching.
To enable fuzzy matching for |ins-completion|, add the "fuzzy" value to the
'completeopt' option.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -1061,6 +1061,10 @@ vim.bo.cfu = vim.bo.completefunc
--- completion in the preview window. Only works in
--- combination with "menu" or "menuone".
---
--- popup Show extra information about the currently selected
--- completion in a popup window. Only works in combination
--- with "menu" or "menuone". Overrides "preview".
---
--- noinsert Do not insert any text for a match until the user selects
--- a match from the menu. Only works in combination with
--- "menu" or "menuone". No effect if "longest" is present.
@ -1069,9 +1073,13 @@ vim.bo.cfu = vim.bo.completefunc
--- select one from the menu. Only works in combination with
--- "menu" or "menuone".
---
--- popup Show extra information about the currently selected
--- completion in a popup window. Only works in combination
--- with "menu" or "menuone". Overrides "preview".
--- fuzzy Enable `fuzzy-matching` for completion candidates. This
--- allows for more flexible and intuitive matching, where
--- characters can be skipped and matches can be found even
--- if the exact sequence is not typed. Only makes a
--- difference how completion candidates are reduced from the
--- list of alternatives, but not how the candidates are
--- collected (using different completion types).
---
--- @type string
vim.o.completeopt = "menu,preview"

View File

@ -168,6 +168,7 @@ struct compl_S {
///< cp_flags has CP_FREE_FNAME
int cp_flags; ///< CP_ values
int cp_number; ///< sequence number
int cp_score; ///< fuzzy match score
};
/// state information used for getting the next set of insert completion
@ -231,6 +232,7 @@ static bool compl_no_select = false; ///< false: select & insert
///< true: noselect
static bool compl_longest = false; ///< false: insert full match
///< true: insert longest prefix
static bool compl_fuzzy_match = false; ///< true: fuzzy match enabled
/// Selected one of the matches. When false the match was edited or using the
/// longest common string.
@ -288,7 +290,7 @@ static bool compl_opt_refresh_always = false;
static size_t spell_bad_len = 0; // length of located bad word
static int pum_selected_item = -1;
static int compl_selected_item = -1;
/// CTRL-X pressed in Insert mode.
void ins_ctrl_x(void)
@ -1056,6 +1058,7 @@ void completeopt_was_set(void)
compl_no_insert = false;
compl_no_select = false;
compl_longest = false;
compl_fuzzy_match = false;
if (strstr(p_cot, "noselect") != NULL) {
compl_no_select = true;
}
@ -1065,6 +1068,9 @@ void completeopt_was_set(void)
if (strstr(p_cot, "longest") != NULL) {
compl_longest = true;
}
if (strstr(p_cot, "fuzzy") != NULL) {
compl_fuzzy_match = true;
}
}
/// "compl_match_array" points the currently displayed list of entries in the
@ -1161,6 +1167,14 @@ static void trigger_complete_changed_event(int cur)
restore_v_event(v_event, &save_v_event);
}
/// pumitem qsort compare func
static int ins_compl_fuzzy_sort(const void *a, const void *b)
{
const int sa = (*(pumitem_T *)a).pum_score;
const int sb = (*(pumitem_T *)b).pum_score;
return sa == sb ? 0 : sa < sb ? 1 : -1;
}
/// Build a popup menu to show the completion matches.
///
/// @return the popup menu entry that should be selected,
@ -1178,11 +1192,19 @@ static int ins_compl_build_pum(void)
}
const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0;
int max_fuzzy_score = 0;
do {
// when completeopt include fuzzy option and leader is not null or empty
// set the cp_score for after compare.
if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) {
comp->cp_score = fuzzy_match_str(comp->cp_str, compl_leader);
}
if (!match_at_original_text(comp)
&& (compl_leader == NULL
|| ins_compl_equal(comp, compl_leader, (size_t)lead_len))) {
|| ins_compl_equal(comp, compl_leader, (size_t)lead_len)
|| (compl_fuzzy_match && comp->cp_score > 0))) {
compl_match_arraysize++;
}
comp = comp->cp_next;
@ -1211,8 +1233,9 @@ static int ins_compl_build_pum(void)
do {
if (!match_at_original_text(comp)
&& (compl_leader == NULL
|| ins_compl_equal(comp, compl_leader, (size_t)lead_len))) {
if (!shown_match_ok) {
|| ins_compl_equal(comp, compl_leader, (size_t)lead_len)
|| (compl_fuzzy_match && comp->cp_score > 0))) {
if (!shown_match_ok && !compl_fuzzy_match) {
if (comp == compl_shown_match || did_find_shown_match) {
// This item is the shown match or this is the
// first displayed item after the shown match.
@ -1225,6 +1248,26 @@ static int ins_compl_build_pum(void)
shown_compl = comp;
}
cur = i;
} else if (compl_fuzzy_match) {
// Update the maximum fuzzy score and the shown match
// if the current item's score is higher
if (comp->cp_score > max_fuzzy_score) {
did_find_shown_match = true;
max_fuzzy_score = comp->cp_score;
compl_shown_match = comp;
shown_match_ok = true;
}
// If there is no "no select" condition and the max fuzzy
// score is positive, or there is no completion leader or the
// leader length is zero, mark the shown match as valid and
// reset the current index.
if (!compl_no_select
&& (max_fuzzy_score > 0
|| (compl_leader == NULL || lead_len == 0))) {
shown_match_ok = true;
cur = 0;
}
}
if (comp->cp_text[CPT_ABBR] != NULL) {
@ -1234,6 +1277,7 @@ static int ins_compl_build_pum(void)
}
compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND];
compl_match_array[i].pum_info = comp->cp_text[CPT_INFO];
compl_match_array[i].pum_score = comp->cp_score;
if (comp->cp_text[CPT_MENU] != NULL) {
compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU];
} else {
@ -1241,7 +1285,7 @@ static int ins_compl_build_pum(void)
}
}
if (comp == compl_shown_match) {
if (comp == compl_shown_match && !compl_fuzzy_match) {
did_find_shown_match = true;
// When the original text is the shown match don't set
@ -1260,6 +1304,12 @@ static int ins_compl_build_pum(void)
comp = comp->cp_next;
} while (comp != NULL && !is_first_match(comp));
if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) {
// sort by the largest score of fuzzy match
qsort(compl_match_array, (size_t)compl_match_arraysize, sizeof(pumitem_T),
ins_compl_fuzzy_sort);
}
if (!shown_match_ok) { // no displayed match at all
cur = -1;
}
@ -1311,7 +1361,7 @@ void ins_compl_show_pum(void)
// Use the cursor to get all wrapping and other settings right.
const colnr_T col = curwin->w_cursor.col;
curwin->w_cursor.col = compl_col;
pum_selected_item = cur;
compl_selected_item = cur;
pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0);
curwin->w_cursor.col = col;
@ -3589,6 +3639,42 @@ static void ins_compl_show_filename(void)
redraw_cmdline = false; // don't overwrite!
}
/// find a completion item in when completeopt include fuzzy option
static compl_T *find_comp_when_fuzzy(void)
{
int target_idx = -1;
const bool is_forward = compl_shows_dir_forward();
const bool is_backward = compl_shows_dir_backward();
compl_T *comp = NULL;
if (compl_match_array == NULL
|| (is_forward && compl_selected_item == compl_match_arraysize - 1)
|| (is_backward && compl_selected_item == 0)) {
return compl_first_match;
}
if (is_forward) {
target_idx = compl_selected_item + 1;
} else if (is_backward) {
target_idx = compl_selected_item == -1 ? compl_match_arraysize - 1
: compl_selected_item - 1;
}
const int score = compl_match_array[target_idx].pum_score;
char *const str = compl_match_array[target_idx].pum_text;
comp = compl_first_match;
do {
if (comp->cp_score == score
&& (str == comp->cp_str || str == comp->cp_text[CPT_ABBR])) {
return comp;
}
comp = comp->cp_next;
} while (comp != NULL && !is_first_match(comp));
return NULL;
}
/// Find the next set of matches for completion. Repeat the completion "todo"
/// times. The number of matches found is returned in 'num_matches'.
///
@ -3609,14 +3695,16 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
while (--todo >= 0) {
if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) {
compl_shown_match = compl_shown_match->cp_next;
compl_shown_match = !compl_fuzzy_match ? compl_shown_match->cp_next
: find_comp_when_fuzzy();
found_end = (compl_first_match != NULL
&& (is_first_match(compl_shown_match->cp_next)
|| is_first_match(compl_shown_match)));
} else if (compl_shows_dir_backward()
&& compl_shown_match->cp_prev != NULL) {
found_end = is_first_match(compl_shown_match);
compl_shown_match = compl_shown_match->cp_prev;
compl_shown_match = !compl_fuzzy_match ? compl_shown_match->cp_prev
: find_comp_when_fuzzy();
found_end |= is_first_match(compl_shown_match);
} else {
if (!allow_get_expansion) {
@ -3660,7 +3748,8 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
if (!match_at_original_text(compl_shown_match)
&& compl_leader != NULL
&& !ins_compl_equal(compl_shown_match,
compl_leader, strlen(compl_leader))) {
compl_leader, strlen(compl_leader))
&& !(compl_fuzzy_match && compl_shown_match->cp_score > 0)) {
todo++;
} else {
// Remember a matching item.
@ -3712,7 +3801,9 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
return -1;
}
if (compl_leader != NULL && !match_at_original_text(compl_shown_match)) {
if (compl_leader != NULL
&& !match_at_original_text(compl_shown_match)
&& !compl_fuzzy_match) {
// Update "compl_shown_match" to the actually shown match
ins_compl_update_shown_match();
}
@ -3854,7 +3945,7 @@ void ins_compl_check_keys(int frequency, bool in_compl_func)
static int ins_compl_key2dir(int c)
{
if (c == K_EVENT || c == K_COMMAND || c == K_LUA) {
return pum_want.item < pum_selected_item ? BACKWARD : FORWARD;
return pum_want.item < compl_selected_item ? BACKWARD : FORWARD;
}
if (c == Ctrl_P || c == Ctrl_L
|| c == K_PAGEUP || c == K_KPAGEUP
@ -3880,7 +3971,7 @@ static bool ins_compl_pum_key(int c)
static int ins_compl_key2count(int c)
{
if (c == K_EVENT || c == K_COMMAND || c == K_LUA) {
int offset = pum_want.item - pum_selected_item;
int offset = pum_want.item - compl_selected_item;
return abs(offset);
}

View File

@ -1443,6 +1443,10 @@ return {
completion in the preview window. Only works in
combination with "menu" or "menuone".
popup Show extra information about the currently selected
completion in a popup window. Only works in combination
with "menu" or "menuone". Overrides "preview".
noinsert Do not insert any text for a match until the user selects
a match from the menu. Only works in combination with
"menu" or "menuone". No effect if "longest" is present.
@ -1451,9 +1455,13 @@ return {
select one from the menu. Only works in combination with
"menu" or "menuone".
popup Show extra information about the currently selected
completion in a popup window. Only works in combination
with "menu" or "menuone". Overrides "preview".
fuzzy Enable |fuzzy-matching| for completion candidates. This
allows for more flexible and intuitive matching, where
characters can be skipped and matches can be found even
if the exact sequence is not typed. Only makes a
difference how completion candidates are reduced from the
list of alternatives, but not how the candidates are
collected (using different completion types).
]=],
expand_cb = 'expand_set_completeopt',
full_name = 'completeopt',

View File

@ -122,8 +122,8 @@ static char *(p_bs_values[]) = { "indent", "eol", "start", "nostop", NULL };
static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent",
"syntax", "diff", NULL };
static char *(p_fcl_values[]) = { "all", NULL };
static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "noinsert", "noselect",
"popup", NULL };
static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "popup",
"noinsert", "noselect", "fuzzy", NULL };
#ifdef BACKSLASH_IN_FILENAME
static char *(p_csl_values[]) = { "slash", "backslash", NULL };
#endif

View File

@ -10,10 +10,11 @@
/// Used for popup menu items.
typedef struct {
char *pum_text; // main menu text
char *pum_kind; // extra kind text (may be truncated)
char *pum_extra; // extra menu text (may be truncated)
char *pum_info; // extra info
char *pum_text; ///< main menu text
char *pum_kind; ///< extra kind text (may be truncated)
char *pum_extra; ///< extra menu text (may be truncated)
char *pum_info; ///< extra info
int pum_score; ///< fuzzy match score
} pumitem_T;
EXTERN ScreenGrid pum_grid INIT( = SCREEN_GRID_INIT);

View File

@ -2512,4 +2512,60 @@ func Test_completefunc_first_call_complete_add()
bwipe!
endfunc
func Test_complete_fuzzy_match()
func OnPumChange()
let g:item = get(v:event, 'completed_item', {})
let g:word = get(g:item, 'word', v:null)
endfunction
augroup AAAAA_Group
au!
autocmd CompleteChanged * :call OnPumChange()
augroup END
func Omni_test(findstart, base)
if a:findstart
return col(".")
endif
return [#{word: "foo"}, #{word: "foobar"}, #{word: "fooBaz"}, #{word: "foobala"}]
endfunc
new
set omnifunc=Omni_test
set completeopt+=noinsert,fuzzy
call feedkeys("Gi\<C-x>\<C-o>", 'tx')
call assert_equal('foo', g:word)
call feedkeys("S\<C-x>\<C-o>fb", 'tx')
call assert_equal('fooBaz', g:word)
call feedkeys("S\<C-x>\<C-o>fa", 'tx')
call assert_equal('foobar', g:word)
" select next
call feedkeys("S\<C-x>\<C-o>fb\<C-n>", 'tx')
call assert_equal('foobar', g:word)
" can circly select next
call feedkeys("S\<C-x>\<C-o>fb\<C-n>\<C-n>\<C-n>", 'tx')
call assert_equal(v:null, g:word)
" select prev
call feedkeys("S\<C-x>\<C-o>fb\<C-p>", 'tx')
call assert_equal(v:null, g:word)
" can circly select prev
call feedkeys("S\<C-x>\<C-o>fb\<C-p>\<C-p>\<C-p>\<C-p>", 'tx')
call assert_equal('fooBaz', g:word)
" respect noselect
set completeopt+=noselect
call feedkeys("S\<C-x>\<C-o>fb", 'tx')
call assert_equal(v:null, g:word)
call feedkeys("S\<C-x>\<C-o>fb\<C-n>", 'tx')
call assert_equal('fooBaz', g:word)
" clean up
set omnifunc=
bw!
set complete& completeopt&
autocmd! AAAAA_Group
augroup! AAAAA_Group
delfunc OnPumChange
delfunc Omni_test
endfunc
" vim: shiftwidth=2 sts=2 expandtab nofoldenable