Compare commits

...

7 Commits

Author SHA1 Message Date
zeertzjq
7d822a0b2a vim-patch:26e4b00: runtime(doc): Revert outdated comment in completeopt's fuzzy documentation
Originally, `:set completeopt+=fuzzy` did not affect how the candidate
list is collected in default keyword completion. A comment was added to
documentation as part of vim/vim#14912 to clarify it. vim/vim#15193 later changed the
fuzzy behavior to now change the candidate collection behavior as well
so the clarification in docs is now wrong. Remove them here.

closes: vim/vim#15656

26e4b00002

Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
2024-09-11 06:05:28 +08:00
zeertzjq
b5df6c4cb5 vim-patch:9.1.0705: Sorting of fuzzy filename completion is not stable
Problem:  Sorting of fuzzy filename completion is not stable
Solution: Compare indexes when scores are equal.  Fix some typos.
          (zeertzjq)

closes: vim/vim#15593

58d705238c
2024-09-11 06:04:35 +08:00
zeertzjq
e3c63dee79 vim-patch:9.1.0654: completion does not respect completeslash with fuzzy
Problem:  completion does not respect completeslash with fuzzy
          (egesip)
Solution: Change path separator on Windows, depending on 'completeslash'
          option value (glepnir)

fixes: vim/vim#15392
closes: vim/vim#15418

b9de1a057f

Co-authored-by: glepnir <glephunter@gmail.com>
2024-09-11 06:04:35 +08:00
zeertzjq
11607ccb85 vim-patch:9.1.0634: Ctrl-P not working by default
Problem:  Ctrl-P not working by default
          (Jesse Pavel, after v9.1.0598)
Solution: Revert part of v9.1.0598 and set cur_match_pos
          correctly according to compl_dir_forward()

fixes: vim/vim#15370
closes: vim/vim#15379

13032a49b7

Co-authored-by: Christian Brabandt <cb@256bit.org>
2024-09-11 06:04:35 +08:00
zeertzjq
cb7ad2c1f5 vim-patch:9.1.0631: wrong completion list displayed with non-existing dir + fuzzy completion
Problem:  wrong completion list displayed with non-existing dir + fuzzy
          completion (kawarimidoll)
Solution: clear list of matches, if leader did not use fuzzy match
          (glepnir)

fixes: vim/vim#15357
closes: vim/vim#15365

6b6280c4a2

Co-authored-by: glepnir <glephunter@gmail.com>
2024-09-11 06:04:35 +08:00
zeertzjq
a24226eae8 vim-patch:9.1.0605: internal error with fuzzy completion
Problem:  internal error with fuzzy completion
          (techntools)
Solution: only fuzzy complete the pattern after directory separator
          (glepnir)

fixes: vim/vim#15287
closes: vim/vim#15291

0be03e14b9

Co-authored-by: glepnir <glephunter@gmail.com>
2024-09-11 06:04:35 +08:00
zeertzjq
1f79ee1156 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>
2024-09-11 06:04:35 +08:00
8 changed files with 395 additions and 25 deletions

View File

@ -1566,10 +1566,7 @@ A jump table for the options with a short description can be found at |Q_op|.
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).
if the exact sequence is not typed.
*'completeslash'* *'csl'*
'completeslash' 'csl' string (default "")

View File

@ -1090,10 +1090,7 @@ vim.bo.cfu = vim.bo.completefunc
--- 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).
--- if the exact sequence is not typed.
---
--- @type string
vim.o.completeopt = "menu,preview"

View File

@ -288,6 +288,8 @@ static size_t spell_bad_len = 0; // length of located bad word
static int compl_selected_item = -1;
static int *compl_fuzzy_scores;
/// CTRL-X pressed in Insert mode.
void ins_ctrl_x(void)
{
@ -2991,7 +2993,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;
@ -3007,7 +3009,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.
@ -3145,11 +3147,75 @@ 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 ? (idx_a == idx_b ? 0 : (idx_a < idx_b ? -1 : 1))
: (score_a > score_b ? -1 : 1);
}
/// 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() & COT_FUZZY) != 0 && leader_len > 0);
#ifdef BACKSLASH_IN_FILENAME
char pathsep = (curbuf->b_p_csl[0] == 's')
? '/' : (curbuf->b_p_csl[0] == 'b') ? '\\' : PATHSEP;
#else
char pathsep = PATHSEP;
#endif
if (in_fuzzy) {
#ifdef BACKSLASH_IN_FILENAME
if (curbuf->b_p_csl[0] == 's') {
for (size_t i = 0; i < leader_len; i++) {
if (leader[i] == '\\') {
leader[i] = '/';
}
}
} else if (curbuf->b_p_csl[0] == 'b') {
for (size_t i = 0; i < leader_len; i++) {
if (leader[i] == '/') {
leader[i] = '\\';
}
}
}
#endif
char *last_sep = strrchr(leader, pathsep);
if (last_sep == NULL) {
// No path separator or separator is the last character,
// fuzzy match the whole leader
xfree(compl_pattern);
compl_pattern = xstrdup("*");
compl_patternlen = strlen(compl_pattern);
} else if (*(last_sep + 1) == NUL) {
in_fuzzy = false;
} else {
// Split leader into path and file parts
size_t path_len = (size_t)(last_sep - leader) + 1;
size_t path_with_wildcard_len = path_len + 2;
char *path_with_wildcard = xmalloc(path_with_wildcard_len);
xmemcpyz(path_with_wildcard, leader, path_len);
xstrlcat(path_with_wildcard, "*", path_with_wildcard_len);
xfree(compl_pattern);
compl_pattern = path_with_wildcard;
compl_patternlen = strlen(compl_pattern);
// Move leader to the file part
leader = last_sep + 1;
leader_len = strlen(leader);
}
}
if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) {
return;
@ -3172,7 +3238,46 @@ static void get_next_filename_completion(void)
}
}
#endif
ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
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;
} else if (leader_len > 0) {
FreeWild(num_matches, matches);
num_matches = 0;
}
xfree(compl_fuzzy_scores);
ga_clear(&fuzzy_indices);
}
if (num_matches > 0) {
ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
}
}
/// Get the next set of command-line completions matching "compl_pattern".
@ -3293,6 +3398,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() & COT_FUZZY) != 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);
@ -3307,7 +3417,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;
@ -3318,9 +3428,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,
@ -3366,12 +3481,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) {
@ -3472,6 +3590,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() & COT_FUZZY) != 0;
assert(curbuf != NULL);
@ -3508,7 +3627,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;
}

View File

@ -1467,10 +1467,7 @@ return {
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).
if the exact sequence is not typed.
]=],
expand_cb = 'expand_set_completeopt',
full_name = 'completeopt',

View File

@ -3561,6 +3561,143 @@ 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;
}
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;
}
while (true) {
// 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, &current_pos);
if (found_new_match) {
*pos = current_pos;
break;
} else if (looped_around && current_pos.lnum == circly_end.lnum) {
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,

View File

@ -4943,9 +4943,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} |
]])
@ -4953,13 +4953,20 @@ 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} |
]])
feed('<Esc>S/non_existing_folder<C-X><C-F>')
screen:expect([[
/non_existing_folder^ |
{1:~ }|*18
{2:-- }{6:Pattern not found} |
]])
feed('<C-E><Esc>')
end)

View File

@ -2664,6 +2664,74 @@ 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('.'))
call feedkeys("S../\<C-X>\<C-f>\<Esc>0", 'tx!')
call assert_match('../*', getline('.'))
call feedkeys("S../td\<C-X>\<C-f>\<Esc>0", 'tx!')
call assert_match('../testdir', 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('.'))
call feedkeys("S omp\<C-N>\<Esc>0", 'tx!')
call assert_equal(' completeness', 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', ''])
@ -2674,6 +2742,7 @@ func Test_complete_fuzzy_match()
" clean up
set omnifunc=
bw!
bw!
set complete& completeopt&
autocmd! AAAAA_Group
augroup! AAAAA_Group
@ -2684,6 +2753,38 @@ func Test_complete_fuzzy_match()
unlet g:word
endfunc
func Test_complete_fuzzy_with_completeslash()
CheckMSWindows
call writefile([''], 'fobar', 'D')
let orig_shellslash = &shellslash
set cpt&
new
set completeopt+=fuzzy
set noshellslash
" Test with completeslash unset
set completeslash=
call setline(1, ['.\fob'])
call feedkeys("A\<C-X>\<C-F>\<Esc>0", 'tx!')
call assert_equal('.\fobar', getline('.'))
" Test with completeslash=backslash
set completeslash=backslash
call feedkeys("S.\\fob\<C-X>\<C-F>\<Esc>0", 'tx!')
call assert_equal('.\fobar', getline('.'))
" Test with completeslash=slash
set completeslash=slash
call feedkeys("S.\\fob\<C-X>\<C-F>\<Esc>0", 'tx!')
call assert_equal('./fobar', getline('.'))
" Reset and clean up
let &shellslash = orig_shellslash
set completeslash=
%bw!
endfunc
" Check that tie breaking is stable for completeopt+=fuzzy (which should
" behave the same on different platforms).
func Test_complete_fuzzy_match_tie()
@ -2704,4 +2805,14 @@ func Test_complete_fuzzy_match_tie()
set completeopt&
endfunc
func Test_complete_backwards_default()
new
call append(1, ['foobar', 'foobaz'])
new
call feedkeys("i\<c-p>", 'tx')
call assert_equal('foobaz', getline('.'))
bw!
bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab nofoldenable

View File

@ -1500,6 +1500,11 @@ func Test_pum_highlights_match()
call TermWait(buf, 50)
call VerifyScreenDump(buf, 'Test_pum_highlights_11', {})
" issue #15357
call term_sendkeys(buf, "\<ESC>S/non_existing_folder\<C-X>\<C-F>")
call TermWait(buf, 50)
call VerifyScreenDump(buf, 'Test_pum_highlights_15', {})
call term_sendkeys(buf, "\<C-E>\<Esc>")
call TermWait(buf)