mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
Merge 00cde6144e3d807e7b10b13a4d4d99de66d660ab into a494ce1c64a2637719a5c1339abf19ec7c48089c
This commit is contained in:
commit
399551284a
@ -648,7 +648,7 @@ Completion can be done for:
|
||||
10. User defined completion |i_CTRL-X_CTRL-U|
|
||||
11. omni completion |i_CTRL-X_CTRL-O|
|
||||
12. Spelling suggestions |i_CTRL-X_s|
|
||||
13. keywords in 'complete' |i_CTRL-N| |i_CTRL-P|
|
||||
13. completions from 'complete' |i_CTRL-N| |i_CTRL-P|
|
||||
14. contents from registers |i_CTRL-X_CTRL-R|
|
||||
|
||||
Additionally, |i_CTRL-X_CTRL-Z| stops completion without changing the text.
|
||||
@ -1103,25 +1103,23 @@ CTRL-X s Locate the word in front of the cursor and find the
|
||||
previous one.
|
||||
|
||||
|
||||
Completing keywords from different sources *compl-generic*
|
||||
Completing from different sources *compl-generic*
|
||||
|
||||
*i_CTRL-N*
|
||||
CTRL-N Find next match for words that start with the
|
||||
keyword in front of the cursor, looking in places
|
||||
specified with the 'complete' option. The found
|
||||
keyword is inserted in front of the cursor.
|
||||
CTRL-N Find the next match for a word ending at the cursor,
|
||||
using the sources specified in the 'complete' option.
|
||||
All sources complete from keywords, except functions,
|
||||
which may complete from non-keyword. The matched
|
||||
text is inserted before the cursor.
|
||||
|
||||
*i_CTRL-P*
|
||||
CTRL-P Find previous match for words that start with the
|
||||
keyword in front of the cursor, looking in places
|
||||
specified with the 'complete' option. The found
|
||||
keyword is inserted in front of the cursor.
|
||||
CTRL-P Same as CTRL-N, but find the previous match.
|
||||
|
||||
CTRL-N Search forward for next matching keyword. This
|
||||
keyword replaces the previous matching keyword.
|
||||
CTRL-N Search forward through the matches and insert the
|
||||
next one.
|
||||
|
||||
CTRL-P Search backwards for next matching keyword. This
|
||||
keyword replaces the previous matching keyword.
|
||||
CTRL-P Search backward through the matches and insert the
|
||||
previous one.
|
||||
|
||||
CTRL-X CTRL-N or
|
||||
CTRL-X CTRL-P Further use of CTRL-X CTRL-N or CTRL-X CTRL-P will
|
||||
|
@ -2123,15 +2123,12 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
name of a function or a |Funcref|. For |Funcref| values,
|
||||
spaces must be escaped with a backslash ('\'), and commas with
|
||||
double backslashes ('\\') (see |option-backslash|).
|
||||
Unlike other sources, functions can provide completions starting
|
||||
from a non-keyword character before the cursor, and their
|
||||
start position for replacing text may differ from other sources.
|
||||
If the Dict returned by the {func} includes {"refresh": "always"},
|
||||
the function will be invoked again whenever the leading text
|
||||
changes.
|
||||
Completion matches are always inserted at the keyword
|
||||
boundary, regardless of the column returned by {func} when
|
||||
a:findstart is 1. This ensures compatibility with other
|
||||
completion sources.
|
||||
To make further modifications to the inserted text, {func}
|
||||
can make use of |CompleteDonePre|.
|
||||
If generating matches is potentially slow, |complete_check()|
|
||||
should be used to avoid blocking and preserve editor
|
||||
responsiveness.
|
||||
|
229
src/insexpand.c
229
src/insexpand.c
@ -226,6 +226,7 @@ typedef struct cpt_source_T
|
||||
int cs_max_matches; // Max items to display from this source
|
||||
} cpt_source_T;
|
||||
|
||||
#define STARTCOL_NONE -9
|
||||
static cpt_source_T *cpt_sources_array; // Pointer to the array of completion sources
|
||||
static int cpt_sources_count; // Total number of completion sources specified in the 'cpt' option
|
||||
static int cpt_sources_index = -1; // Index of the current completion source being expanded
|
||||
@ -250,7 +251,7 @@ static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg);
|
||||
static void ins_compl_add_list(list_T *list);
|
||||
static void ins_compl_add_dict(dict_T *dict);
|
||||
static int get_userdefined_compl_info(colnr_T curs_col, callback_T *cb, int *startcol);
|
||||
static void get_cpt_func_completion_matches(callback_T *cb, int restore_leader);
|
||||
static void get_cpt_func_completion_matches(callback_T *cb);
|
||||
static callback_T *get_callback_if_cpt_func(char_u *p);
|
||||
# endif
|
||||
static int setup_cpt_sources(void);
|
||||
@ -1409,6 +1410,76 @@ cp_compare_nearest(const void* a, const void* b)
|
||||
return (score_a > score_b) ? 1 : (score_a < score_b) ? -1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs a new string by prepending text from the current line (from
|
||||
* startcol to compl_col) to the given source string. Stores the result in
|
||||
* dest. Returns OK or FAIL.
|
||||
*/
|
||||
static int
|
||||
prepend_startcol_text(string_T *dest, string_T *src, int startcol)
|
||||
{
|
||||
int prepend_len = compl_col - startcol;
|
||||
int new_length = prepend_len + (int)src->length;
|
||||
|
||||
dest->length = (size_t)new_length;
|
||||
dest->string = alloc(new_length + 1); // +1 for NUL
|
||||
if (dest->string == NULL)
|
||||
{
|
||||
dest->length = 0;
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
char_u *line = ml_get(curwin->w_cursor.lnum);
|
||||
|
||||
mch_memmove(dest->string, line + startcol, prepend_len);
|
||||
mch_memmove(dest->string + prepend_len, src->string, src->length);
|
||||
dest->string[new_length] = NUL;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the completion leader string adjusted for a specific source's
|
||||
* startcol. If the source's startcol is before compl_col, prepends text from
|
||||
* the buffer line to the original compl_leader.
|
||||
*/
|
||||
static string_T *
|
||||
get_leader_for_startcol(compl_T *match, int cached)
|
||||
{
|
||||
static string_T adjusted_leader = {NULL, 0};
|
||||
|
||||
if (match == NULL)
|
||||
{
|
||||
VIM_CLEAR_STRING(adjusted_leader);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cpt_sources_array == NULL || compl_leader.string == NULL)
|
||||
goto theend;
|
||||
|
||||
int cpt_idx = match->cp_cpt_source_idx;
|
||||
if (cpt_idx < 0 || compl_col <= 0)
|
||||
goto theend;
|
||||
int startcol = cpt_sources_array[cpt_idx].cs_startcol;
|
||||
|
||||
if (startcol >= 0 && startcol < compl_col)
|
||||
{
|
||||
int prepend_len = compl_col - startcol;
|
||||
int new_length = prepend_len + (int)compl_leader.length;
|
||||
if (cached && (size_t)new_length == adjusted_leader.length
|
||||
&& adjusted_leader.string != NULL)
|
||||
return &adjusted_leader;
|
||||
|
||||
VIM_CLEAR_STRING(adjusted_leader);
|
||||
if (prepend_startcol_text(&adjusted_leader, &compl_leader,
|
||||
startcol) != OK)
|
||||
goto theend;
|
||||
|
||||
return &adjusted_leader;
|
||||
}
|
||||
theend:
|
||||
return &compl_leader;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set fuzzy score.
|
||||
*/
|
||||
@ -1421,11 +1492,13 @@ set_fuzzy_score(void)
|
||||
|| compl_leader.string == NULL || compl_leader.length == 0)
|
||||
return;
|
||||
|
||||
(void)get_leader_for_startcol(NULL, TRUE); // Clear the cache
|
||||
|
||||
compl = compl_first_match;
|
||||
do
|
||||
{
|
||||
compl->cp_score = fuzzy_match_str(compl->cp_str.string,
|
||||
compl_leader.string);
|
||||
get_leader_for_startcol(compl, TRUE)->string);
|
||||
compl = compl->cp_next;
|
||||
} while (compl != NULL && !is_first_match(compl));
|
||||
}
|
||||
@ -1487,6 +1560,7 @@ ins_compl_build_pum(void)
|
||||
int *match_count = NULL;
|
||||
int is_forward = compl_shows_dir_forward();
|
||||
int is_cpt_completion = (cpt_sources_array != NULL);
|
||||
string_T *leader;
|
||||
|
||||
// Need to build the popup menu list.
|
||||
compl_match_arraysize = 0;
|
||||
@ -1509,6 +1583,8 @@ ins_compl_build_pum(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)get_leader_for_startcol(NULL, TRUE); // Clear the cache
|
||||
|
||||
compl = compl_first_match;
|
||||
do
|
||||
{
|
||||
@ -1519,10 +1595,12 @@ ins_compl_build_pum(void)
|
||||
&& !ignorecase(compl_leader.string) && !fuzzy_filter)
|
||||
compl->cp_flags &= ~CP_ICASE;
|
||||
|
||||
leader = get_leader_for_startcol(compl, TRUE);
|
||||
|
||||
if (!match_at_original_text(compl)
|
||||
&& (compl_leader.string == NULL
|
||||
|| ins_compl_equal(compl, compl_leader.string,
|
||||
(int)compl_leader.length)
|
||||
&& (leader->string == NULL
|
||||
|| ins_compl_equal(compl, leader->string,
|
||||
(int)leader->length)
|
||||
|| (fuzzy_filter && compl->cp_score > 0)))
|
||||
{
|
||||
// Limit number of items from each source if max_items is set.
|
||||
@ -2317,6 +2395,7 @@ ins_compl_need_restart(void)
|
||||
ins_compl_new_leader(void)
|
||||
{
|
||||
int cur_cot_flags = get_cot_flags();
|
||||
|
||||
ins_compl_del_pum();
|
||||
ins_compl_delete();
|
||||
ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
|
||||
@ -4970,7 +5049,7 @@ get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
|
||||
#ifdef FEAT_COMPL_FUNC
|
||||
case CTRL_X_FUNCTION:
|
||||
if (ctrl_x_mode_normal()) // Invoked by a func in 'cpt' option
|
||||
get_cpt_func_completion_matches(st->func_cb, TRUE);
|
||||
get_cpt_func_completion_matches(st->func_cb);
|
||||
else
|
||||
expand_by_function(type, compl_pattern.string, NULL);
|
||||
break;
|
||||
@ -5059,6 +5138,9 @@ prepare_cpt_compl_funcs(void)
|
||||
{
|
||||
while (*p == ',' || *p == ' ') // Skip delimiters
|
||||
p++;
|
||||
if (*p == NUL)
|
||||
break;
|
||||
|
||||
cb = get_callback_if_cpt_func(p);
|
||||
if (cb)
|
||||
{
|
||||
@ -5072,6 +5154,9 @@ prepare_cpt_compl_funcs(void)
|
||||
}
|
||||
cpt_sources_array[idx].cs_startcol = startcol;
|
||||
}
|
||||
else
|
||||
cpt_sources_array[idx].cs_startcol = STARTCOL_NONE;
|
||||
|
||||
(void)copy_option_part(&p, IObuff, IOSIZE, ","); // Advance p
|
||||
idx++;
|
||||
}
|
||||
@ -5268,25 +5353,36 @@ ins_compl_get_exp(pos_T *ini)
|
||||
static void
|
||||
ins_compl_update_shown_match(void)
|
||||
{
|
||||
string_T *leader;
|
||||
|
||||
(void)get_leader_for_startcol(NULL, TRUE); // Clear the cache
|
||||
leader = get_leader_for_startcol(compl_shown_match, TRUE);
|
||||
|
||||
while (!ins_compl_equal(compl_shown_match,
|
||||
compl_leader.string, (int)compl_leader.length)
|
||||
leader->string, (int)leader->length)
|
||||
&& compl_shown_match->cp_next != NULL
|
||||
&& !is_first_match(compl_shown_match->cp_next))
|
||||
{
|
||||
compl_shown_match = compl_shown_match->cp_next;
|
||||
leader = get_leader_for_startcol(compl_shown_match, TRUE);
|
||||
}
|
||||
|
||||
// If we didn't find it searching forward, and compl_shows_dir is
|
||||
// backward, find the last match.
|
||||
if (compl_shows_dir_backward()
|
||||
&& !ins_compl_equal(compl_shown_match,
|
||||
compl_leader.string, (int)compl_leader.length)
|
||||
leader->string, (int)leader->length)
|
||||
&& (compl_shown_match->cp_next == NULL
|
||||
|| is_first_match(compl_shown_match->cp_next)))
|
||||
{
|
||||
while (!ins_compl_equal(compl_shown_match,
|
||||
compl_leader.string, (int)compl_leader.length)
|
||||
leader->string, (int)leader->length)
|
||||
&& compl_shown_match->cp_prev != NULL
|
||||
&& !is_first_match(compl_shown_match->cp_prev))
|
||||
{
|
||||
compl_shown_match = compl_shown_match->cp_prev;
|
||||
leader = get_leader_for_startcol(compl_shown_match, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5409,6 +5505,27 @@ ins_compl_insert(int move_cursor)
|
||||
size_t leader_len = ins_compl_leader_len();
|
||||
char_u *has_multiple = vim_strchr(cp_str, '\n');
|
||||
|
||||
// Since completion sources may provide matches with varying start
|
||||
// positions, insert only the portion of the match that corresponds to the
|
||||
// intended replacement range.
|
||||
if (cpt_sources_array != NULL)
|
||||
{
|
||||
int cpt_idx = compl_shown_match->cp_cpt_source_idx;
|
||||
if (cpt_idx >= 0 && compl_col >= 0)
|
||||
{
|
||||
int startcol = cpt_sources_array[cpt_idx].cs_startcol;
|
||||
if (startcol >= 0 && startcol < (int)compl_col)
|
||||
{
|
||||
int skip = (int)compl_col - startcol;
|
||||
if ((size_t)skip <= cp_str_len)
|
||||
{
|
||||
cp_str_len -= skip;
|
||||
cp_str += skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we don't go over the end of the string, this can happen with
|
||||
// illegal bytes.
|
||||
if (compl_len < (int)cp_str_len)
|
||||
@ -5509,11 +5626,13 @@ find_next_completion_match(
|
||||
int advance,
|
||||
int *num_matches)
|
||||
{
|
||||
int found_end = FALSE;
|
||||
compl_T *found_compl = NULL;
|
||||
int found_end = FALSE;
|
||||
compl_T *found_compl = NULL;
|
||||
unsigned int cur_cot_flags = get_cot_flags();
|
||||
int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
|
||||
int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
|
||||
int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
|
||||
int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
|
||||
string_T *leader;
|
||||
|
||||
|
||||
while (--todo >= 0)
|
||||
{
|
||||
@ -5581,10 +5700,13 @@ find_next_completion_match(
|
||||
}
|
||||
found_end = FALSE;
|
||||
}
|
||||
|
||||
leader = get_leader_for_startcol(compl_shown_match, FALSE);
|
||||
|
||||
if (!match_at_original_text(compl_shown_match)
|
||||
&& compl_leader.string != NULL
|
||||
&& leader->string != NULL
|
||||
&& !ins_compl_equal(compl_shown_match,
|
||||
compl_leader.string, (int)compl_leader.length)
|
||||
leader->string, (int)leader->length)
|
||||
&& !(compl_fuzzy_match && compl_shown_match->cp_score > 0))
|
||||
++todo;
|
||||
else
|
||||
@ -5767,7 +5889,13 @@ ins_compl_check_keys(int frequency, int in_compl_func)
|
||||
// Check for a typed key. Do use mappings, otherwise vim_is_ctrl_x_key()
|
||||
// can't do its work correctly.
|
||||
c = vpeekc_any();
|
||||
if (c != NUL)
|
||||
if (c != NUL
|
||||
#ifdef FEAT_EVAL
|
||||
// If test_override("char_avail", 1) was called, ignore characters
|
||||
// waiting in the typeahead buffer.
|
||||
&& !disable_char_avail_for_testing
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (vim_is_ctrl_x_key(c) && c != Ctrl_X && c != Ctrl_R)
|
||||
{
|
||||
@ -6123,30 +6251,39 @@ set_compl_globals(
|
||||
colnr_T curs_col UNUSED,
|
||||
int is_cpt_compl UNUSED)
|
||||
{
|
||||
char_u *line = NULL;
|
||||
string_T *pattern = NULL;
|
||||
int len;
|
||||
|
||||
if (startcol < 0 || startcol > curs_col)
|
||||
startcol = curs_col;
|
||||
len = curs_col - startcol;
|
||||
|
||||
// Re-obtain line in case it has changed
|
||||
line = ml_get(curwin->w_cursor.lnum);
|
||||
|
||||
pattern = is_cpt_compl ? &cpt_compl_pattern : &compl_pattern;
|
||||
pattern->string = vim_strnsave(line + startcol, (size_t)len);
|
||||
if (pattern->string == NULL)
|
||||
if (is_cpt_compl)
|
||||
{
|
||||
pattern->length = 0;
|
||||
return FAIL;
|
||||
VIM_CLEAR_STRING(cpt_compl_pattern);
|
||||
if (startcol < compl_col)
|
||||
return prepend_startcol_text(&cpt_compl_pattern, &compl_orig_text,
|
||||
startcol);
|
||||
else
|
||||
{
|
||||
cpt_compl_pattern.string = vim_strnsave(compl_orig_text.string,
|
||||
compl_orig_text.length);
|
||||
cpt_compl_pattern.length = compl_orig_text.length;
|
||||
}
|
||||
}
|
||||
pattern->length = (size_t)len;
|
||||
if (!is_cpt_compl)
|
||||
else
|
||||
{
|
||||
if (startcol < 0 || startcol > curs_col)
|
||||
startcol = curs_col;
|
||||
|
||||
// Re-obtain line in case it has changed
|
||||
char_u *line = ml_get(curwin->w_cursor.lnum);
|
||||
int len = curs_col - startcol;
|
||||
|
||||
compl_pattern.string = vim_strnsave(line + startcol, (size_t)len);
|
||||
if (compl_pattern.string == NULL)
|
||||
{
|
||||
compl_pattern.length = 0;
|
||||
return FAIL;
|
||||
}
|
||||
compl_pattern.length = (size_t)len;
|
||||
compl_col = startcol;
|
||||
compl_length = len;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
@ -6301,7 +6438,9 @@ compl_get_info(char_u *line, int startcol, colnr_T curs_col, int *line_invalid)
|
||||
|| (ctrl_x_mode & CTRL_X_WANT_IDENT
|
||||
&& !thesaurus_func_complete(ctrl_x_mode)))
|
||||
{
|
||||
return get_normal_compl_info(line, startcol, curs_col);
|
||||
if (get_normal_compl_info(line, startcol, curs_col) != OK)
|
||||
return FAIL;
|
||||
*line_invalid = TRUE; // 'cpt' func may have invalidated "line"
|
||||
}
|
||||
else if (ctrl_x_mode_line_or_eval())
|
||||
{
|
||||
@ -6975,23 +7114,14 @@ remove_old_matches(void)
|
||||
*/
|
||||
#ifdef FEAT_COMPL_FUNC
|
||||
static void
|
||||
get_cpt_func_completion_matches(callback_T *cb UNUSED, int restore_leader)
|
||||
get_cpt_func_completion_matches(callback_T *cb UNUSED)
|
||||
{
|
||||
int startcol = cpt_sources_array[cpt_sources_index].cs_startcol;
|
||||
int result;
|
||||
|
||||
VIM_CLEAR_STRING(cpt_compl_pattern);
|
||||
|
||||
if (startcol == -2 || startcol == -3)
|
||||
return;
|
||||
|
||||
if (restore_leader) // Re-insert the text removed by ins_compl_delete()
|
||||
ins_compl_insert_bytes(compl_orig_text.string + get_compl_len(), -1);
|
||||
result = set_compl_globals(startcol, curwin->w_cursor.col, TRUE);
|
||||
if (restore_leader)
|
||||
ins_compl_delete(); // Undo insertion
|
||||
|
||||
if (result == OK)
|
||||
if (set_compl_globals(startcol, curwin->w_cursor.col, TRUE) == OK)
|
||||
{
|
||||
expand_by_function(0, cpt_compl_pattern.string, cb);
|
||||
cpt_sources_array[cpt_sources_index].cs_refresh_always =
|
||||
@ -7025,6 +7155,8 @@ cpt_compl_refresh(void)
|
||||
{
|
||||
while (*p == ',' || *p == ' ') // Skip delimiters
|
||||
p++;
|
||||
if (*p == NUL)
|
||||
break;
|
||||
|
||||
if (cpt_sources_array[cpt_sources_index].cs_refresh_always)
|
||||
{
|
||||
@ -7044,8 +7176,11 @@ cpt_compl_refresh(void)
|
||||
}
|
||||
cpt_sources_array[cpt_sources_index].cs_startcol = startcol;
|
||||
if (ret == OK)
|
||||
get_cpt_func_completion_matches(cb, FALSE);
|
||||
get_cpt_func_completion_matches(cb);
|
||||
}
|
||||
else
|
||||
cpt_sources_array[cpt_sources_index].cs_startcol
|
||||
= STARTCOL_NONE;
|
||||
}
|
||||
|
||||
(void)copy_option_part(&p, IObuff, IOSIZE, ","); // Advance p
|
||||
|
@ -133,8 +133,7 @@ func Test_omni_dash()
|
||||
%d
|
||||
set complete=o
|
||||
exe "normal Gofind -\<C-n>"
|
||||
" 'complete' inserts at 'iskeyword' boundary (so you get --help)
|
||||
call assert_equal("find --help", getline('$'))
|
||||
call assert_equal("find -help", getline('$'))
|
||||
|
||||
bwipe!
|
||||
delfunc Omni
|
||||
@ -392,7 +391,7 @@ func Test_CompleteDone_vevent_keys()
|
||||
call assert_equal('spell', g:complete_type)
|
||||
|
||||
bwipe!
|
||||
set completeopt& omnifunc& completefunc& spell& spelllang& dictionary&
|
||||
set completeopt& omnifunc& completefunc& spell& spelllang& dictionary& complete&
|
||||
autocmd! CompleteDone
|
||||
delfunc OnDone
|
||||
delfunc CompleteFunc
|
||||
@ -1037,6 +1036,7 @@ func Test_completefunc_invalid_data()
|
||||
exe "normal i\<C-N>"
|
||||
call assert_equal('moon', getline(1))
|
||||
set completefunc& complete&
|
||||
delfunc! CompleteFunc
|
||||
bw!
|
||||
endfunc
|
||||
|
||||
@ -4861,4 +4861,120 @@ func Test_complete_fuzzy_omnifunc_backspace()
|
||||
unlet g:do_complete
|
||||
endfunc
|
||||
|
||||
" Test 'complete' containing F{func} that complete from nonkeyword
|
||||
func Test_nonkeyword_trigger()
|
||||
|
||||
" Trigger expansion even when another char is waiting in the typehead
|
||||
call test_override("char_avail", 1)
|
||||
|
||||
let g:CallCount = 0
|
||||
func! NonKeywordComplete(findstart, base)
|
||||
let line = getline('.')->strpart(0, col('.') - 1)
|
||||
let nonkeyword2 = len(line) > 1 && match(line[-2:-2], '\k') != 0
|
||||
if a:findstart
|
||||
return nonkeyword2 ? col('.') - 3 : (col('.') - 2)
|
||||
else
|
||||
let g:CallCount += 1
|
||||
return [$"{a:base}foo", $"{a:base}bar"]
|
||||
endif
|
||||
endfunc
|
||||
|
||||
new
|
||||
inoremap <buffer> <F2> <Cmd>let b:matches = complete_info(["matches"]).matches<CR>
|
||||
inoremap <buffer> <F3> <Cmd>let b:selected = complete_info(["selected"]).selected<CR>
|
||||
call setline(1, ['abc', 'abcd', 'fo', 'b', ''])
|
||||
|
||||
" Test 1a: Nonkeyword before cursor lists words with at least two letters
|
||||
call feedkeys("GS=\<C-N>\<F2>\<Esc>0", 'tx!')
|
||||
call assert_equal(['abc', 'abcd', 'fo'], b:matches->mapnew('v:val.word'))
|
||||
call assert_equal('=abc', getline('.'))
|
||||
|
||||
" Test 1b: With F{func} nonkeyword collects matches
|
||||
set complete=.,FNonKeywordComplete
|
||||
for noselect in range(2)
|
||||
if noselect
|
||||
set completeopt+=noselect
|
||||
endif
|
||||
let g:CallCount = 0
|
||||
call feedkeys("S=\<C-N>\<F2>\<Esc>0", 'tx!')
|
||||
call assert_equal(['abc', 'abcd', 'fo', '=foo', '=bar'], b:matches->mapnew('v:val.word'))
|
||||
call assert_equal(1, g:CallCount)
|
||||
call assert_equal(noselect ? '=' : '=abc', getline('.'))
|
||||
let g:CallCount = 0
|
||||
call feedkeys("S->\<C-N>\<F2>\<Esc>0", 'tx!')
|
||||
call assert_equal(['abc', 'abcd', 'fo', '->foo', '->bar'], b:matches->mapnew('v:val.word'))
|
||||
call assert_equal(1, g:CallCount)
|
||||
call assert_equal(noselect ? '->' : '->abc', getline('.'))
|
||||
set completeopt&
|
||||
endfor
|
||||
|
||||
" Test 1c: Keyword collects from {func}
|
||||
let g:CallCount = 0
|
||||
call feedkeys("Sa\<C-N>\<F2>\<Esc>0", 'tx!')
|
||||
call assert_equal(['abc', 'abcd', 'afoo', 'abar'], b:matches->mapnew('v:val.word'))
|
||||
call assert_equal(1, g:CallCount)
|
||||
call assert_equal('abc', getline('.'))
|
||||
|
||||
set completeopt+=noselect
|
||||
let g:CallCount = 0
|
||||
call feedkeys("Sa\<C-N>\<F2>\<Esc>0", 'tx!')
|
||||
call assert_equal(['abc', 'abcd', 'afoo', 'abar'], b:matches->mapnew('v:val.word'))
|
||||
call assert_equal(1, g:CallCount)
|
||||
call assert_equal('a', getline('.'))
|
||||
|
||||
" Test 1d: Nonkeyword after keyword collects items again
|
||||
let g:CallCount = 0
|
||||
call feedkeys("Sa\<C-N>#\<C-N>\<F2>\<Esc>0", 'tx!')
|
||||
call assert_equal(['abc', 'abcd', 'fo', '#foo', '#bar'], b:matches->mapnew('v:val.word'))
|
||||
call assert_equal(2, g:CallCount)
|
||||
call assert_equal('a#', getline('.'))
|
||||
set completeopt&
|
||||
|
||||
" Test 2: Filter nonkeyword and keyword matches with differet startpos
|
||||
set completeopt+=menuone,noselect
|
||||
call feedkeys("S#a\<C-N>b\<F2>\<F3>\<Esc>0", 'tx!')
|
||||
call assert_equal(['abc', 'abcd', '#abar'], b:matches->mapnew('v:val.word'))
|
||||
call assert_equal(-1, b:selected)
|
||||
call assert_equal('#ab', getline('.'))
|
||||
|
||||
set completeopt+=fuzzy
|
||||
call feedkeys("S#a\<C-N>b\<F2>\<F3>\<Esc>0", 'tx!')
|
||||
call assert_equal(['#abar', 'abc', 'abcd'], b:matches->mapnew('v:val.word'))
|
||||
call assert_equal(-1, b:selected)
|
||||
call assert_equal('#ab', getline('.'))
|
||||
set completeopt&
|
||||
|
||||
" Test 3: Navigate menu containing nonkeyword and keyword items
|
||||
call feedkeys("S->\<C-N>\<F2>\<Esc>0", 'tx!')
|
||||
call assert_equal(['abc', 'abcd', 'fo', '->foo', '->bar'], b:matches->mapnew('v:val.word'))
|
||||
call assert_equal('->abc', getline('.'))
|
||||
call feedkeys("S->" . repeat("\<C-N>", 3) . "\<Esc>0", 'tx!')
|
||||
call assert_equal('->fo', getline('.'))
|
||||
call feedkeys("S->" . repeat("\<C-N>", 4) . "\<Esc>0", 'tx!')
|
||||
call assert_equal('->foo', getline('.'))
|
||||
call feedkeys("S->" . repeat("\<C-N>", 4) . "\<C-P>\<Esc>0", 'tx!')
|
||||
call assert_equal('->fo', getline('.'))
|
||||
call feedkeys("S->" . repeat("\<C-N>", 5) . "\<Esc>0", 'tx!')
|
||||
call assert_equal('->bar', getline('.'))
|
||||
call feedkeys("S->" . repeat("\<C-N>", 5) . "\<C-P>\<Esc>0", 'tx!')
|
||||
call assert_equal('->foo', getline('.'))
|
||||
call feedkeys("S->" . repeat("\<C-N>", 6) . "\<Esc>0", 'tx!')
|
||||
call assert_equal('->', getline('.'))
|
||||
call feedkeys("S->" . repeat("\<C-N>", 7) . "\<Esc>0", 'tx!')
|
||||
call assert_equal('->abc', getline('.'))
|
||||
call feedkeys("S->" . repeat("\<C-P>", 7) . "\<Esc>0", 'tx!')
|
||||
call assert_equal('->fo', getline('.'))
|
||||
" Replace
|
||||
call feedkeys("S# x y z\<Esc>0lR\<C-N>\<Esc>0", 'tx!')
|
||||
call assert_equal('#abcy z', getline('.'))
|
||||
call feedkeys("S# x y z\<Esc>0lR" . repeat("\<C-P>", 4) . "\<Esc>0", 'tx!')
|
||||
call assert_equal('#bary z', getline('.'))
|
||||
|
||||
bw!
|
||||
call test_override("char_avail", 0)
|
||||
delfunc NonKeywordComplete
|
||||
set complete&
|
||||
unlet g:CallCount
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
|
||||
|
Loading…
x
Reference in New Issue
Block a user