int cp_in_match_array; // collected by compl_match_array
int cp_user_abbr_hlattr; // highlight attribute for abbr
int cp_user_kind_hlattr; // highlight attribute for kind
+ int cp_cpt_value_idx; // index of this match's source in 'cpt' option
};
// values for cp_flags
* "compl_first_match" points to the start of the list.
* "compl_curr_match" points to the currently selected entry.
* "compl_shown_match" is different from compl_curr_match during
- * ins_compl_get_exp().
+ * ins_compl_get_exp(), when new matches are added to the list.
* "compl_old_match" points to previous "compl_curr_match".
*/
static compl_T *compl_first_match = NULL;
static int ctrl_x_mode = CTRL_X_NORMAL;
static int compl_matches = 0; // number of completion matches
-static string_T compl_pattern = {NULL, 0};
+static string_T compl_pattern = {NULL, 0}; // search pattern for matching items
+#ifdef FEAT_COMPL_FUNC
+static string_T cpt_compl_pattern = {NULL, 0}; // pattern returned by func in 'cpt'
+#endif
static int compl_direction = FORWARD;
static int compl_shows_dir = FORWARD;
static int compl_pending = 0; // > 1 for postponed CTRL-N
static int *compl_fuzzy_scores;
+static int *cpt_func_refresh_always; // array indicating which 'cpt' functions have 'refresh:always' set
+static int cpt_value_count; // total number of completion sources specified in the 'cpt' option
+static int cpt_value_idx; // index of the current completion source being expanded
+
// "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;
# if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL)
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 callback_T *get_cpt_func_callback(char_u *funcname);
+static void get_cpt_func_completion_matches(callback_T *cb);
# endif
+static int cpt_compl_src_init(char_u *p_cpt);
+static int is_cpt_func_refresh_always(void);
+static void cpt_compl_src_clear(void);
+static void cpt_compl_refresh(void);
static int ins_compl_key2dir(int c);
static int ins_compl_pum_key(int c);
static int ins_compl_key2count(int c);
match->cp_user_abbr_hlattr = user_hl ? user_hl[0] : -1;
match->cp_user_kind_hlattr = user_hl ? user_hl[1] : -1;
match->cp_score = score;
+ match->cp_cpt_value_idx = cpt_value_idx;
if (cptext != NULL)
{
return s;
}
+/*
+ * Free a completion item in the list
+ */
+ static void
+ins_compl_item_free(compl_T *match)
+{
+ int i;
+
+ VIM_CLEAR_STRING(match->cp_str);
+ // several entries may use the same fname, free it just once.
+ if (match->cp_flags & CP_FREE_FNAME)
+ vim_free(match->cp_fname);
+ for (i = 0; i < CPT_COUNT; ++i)
+ vim_free(match->cp_text[i]);
+#ifdef FEAT_EVAL
+ clear_tv(&match->cp_user_data);
+#endif
+ vim_free(match);
+}
+
/*
* Free the list of completions
*/
ins_compl_free(void)
{
compl_T *match;
- int i;
VIM_CLEAR_STRING(compl_pattern);
VIM_CLEAR_STRING(compl_leader);
{
match = compl_curr_match;
compl_curr_match = compl_curr_match->cp_next;
- VIM_CLEAR_STRING(match->cp_str);
- // several entries may use the same fname, free it just once.
- if (match->cp_flags & CP_FREE_FNAME)
- vim_free(match->cp_fname);
- for (i = 0; i < CPT_COUNT; ++i)
- vim_free(match->cp_text[i]);
-#ifdef FEAT_EVAL
- clear_tv(&match->cp_user_data);
-#endif
- vim_free(match);
+ ins_compl_item_free(match);
} while (compl_curr_match != NULL && !is_first_match(compl_curr_match));
compl_first_match = compl_curr_match = NULL;
compl_shown_match = NULL;
edit_submode_extra = NULL;
VIM_CLEAR_STRING(compl_orig_text);
compl_enter_selects = FALSE;
+ cpt_compl_src_clear();
#ifdef FEAT_EVAL
// clear v:completed_item
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
}
/*
- * Selected one of the matches. When FALSE the match was edited or using the
- * longest common string.
+ * Selected one of the matches. When FALSE, the match was either edited or
+ * using the longest common string.
*/
int
ins_compl_used_match(void)
compl_used_match = FALSE;
if (compl_started)
+ {
ins_compl_set_original_text(compl_leader.string, compl_leader.length);
+ if (is_cpt_func_refresh_always())
+ cpt_compl_refresh();
+ }
else
{
#ifdef FEAT_SPELL
compl_matches = 0;
compl_cont_status = 0;
compl_cont_mode = 0;
+ cpt_compl_src_clear();
}
/*
/*
* Execute user defined complete function 'completefunc', 'omnifunc' or
* 'thesaurusfunc', and get matches in "matches".
- * "type" is either CTRL_X_OMNI or CTRL_X_FUNCTION or CTRL_X_THESAURUS.
+ * "type" can be one of CTRL_X_OMNI, CTRL_X_FUNCTION, or CTRL_X_THESAURUS.
+ * Callback function "cb" is set if triggered by a function in the 'cpt'
+ * option; otherwise, it is NULL.
*/
static void
-expand_by_function(int type, char_u *base)
+expand_by_function(int type, char_u *base, callback_T *cb)
{
list_T *matchlist = NULL;
dict_T *matchdict = NULL;
typval_T args[3];
char_u *funcname;
pos_T pos;
- callback_T *cb;
typval_T rettv;
int save_State = State;
int retval;
+ int is_cpt_function = (cb != NULL);
- funcname = get_complete_funcname(type);
- if (*funcname == NUL)
- return;
+ if (!is_cpt_function)
+ {
+ funcname = get_complete_funcname(type);
+ if (*funcname == NUL)
+ return;
+ cb = get_insert_callback(type);
+ }
// Call 'completefunc' to obtain the list of matches.
args[0].v_type = VAR_NUMBER;
// Insert mode in another buffer.
++textlock;
- cb = get_insert_callback(type);
retval = call_callback(cb, 0, &rettv, 2, args);
// Call a function, which returns a list or dict.
int found_all; // found all matches of a certain type.
char_u *dict; // dictionary file to search
int dict_f; // "dict" is an exact file name or not
+ callback_T *func_cb; // callback of function in 'cpt' option
} ins_compl_next_state_T;
/*
st->dict_f = DICT_FIRST;
}
}
+#ifdef FEAT_COMPL_FUNC
+ else if (*st->e_cpt == 'f' || *st->e_cpt == 'o')
+ {
+ compl_type = CTRL_X_FUNCTION;
+ if (*st->e_cpt == 'o')
+ st->func_cb = &curbuf->b_ofu_cb;
+ else
+ st->func_cb = (*++st->e_cpt != ',' && *st->e_cpt != NUL)
+ ? get_cpt_func_callback(st->e_cpt) : &curbuf->b_cfu_cb;
+ if (!st->func_cb)
+ compl_type = -1;
+ }
+#endif
#ifdef FEAT_FIND_ID
else if (*st->e_cpt == 'i')
compl_type = CTRL_X_PATH_PATTERNS;
{
#ifdef FEAT_COMPL_FUNC
if (thesaurus_func_complete(compl_type))
- expand_by_function(compl_type, compl_pattern.string);
+ expand_by_function(compl_type, compl_pattern.string, NULL);
else
#endif
ins_compl_dictionaries(
return found_new_match;
}
+/*
+ * Return the callback function associated with "funcname".
+ */
+#ifdef FEAT_COMPL_FUNC
+ static callback_T *
+get_cpt_func_callback(char_u *funcname)
+{
+ static callback_T cb;
+ char_u buf[LSIZE];
+ int slen;
+
+ slen = copy_option_part(&funcname, buf, LSIZE, ",");
+ if (slen > 0 && option_set_callback_func(buf, &cb))
+ return &cb;
+ return NULL;
+}
+
+/*
+ * Retrieve new completion matches by invoking callback "cb".
+ */
+ static void
+expand_cpt_function(callback_T *cb)
+{
+ // Re-insert the text removed by ins_compl_delete().
+ ins_compl_insert_bytes(compl_orig_text.string + get_compl_len(), -1);
+ // Get matches
+ get_cpt_func_completion_matches(cb);
+ // Undo insertion
+ ins_compl_delete();
+}
+#endif
+
/*
* get the next set of completion matches for "type".
* Returns TRUE if a new match is found. Otherwise returns FALSE.
#ifdef FEAT_COMPL_FUNC
case CTRL_X_FUNCTION:
+ if (ctrl_x_mode_normal()) // Invoked by a func in 'cpt' option
+ expand_cpt_function(st->func_cb);
+ else
+ expand_by_function(type, compl_pattern.string, NULL);
+ break;
case CTRL_X_OMNI:
- expand_by_function(type, compl_pattern.string);
+ expand_by_function(type, compl_pattern.string, NULL);
break;
#endif
? (char_u *)"." : curbuf->b_p_cpt);
st.e_cpt = st.e_cpt_copy == NULL ? (char_u *)"" : st.e_cpt_copy;
st.last_match_pos = st.first_match_pos = *ini;
+
+ if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
+ && !cpt_compl_src_init(st.e_cpt))
+ return FAIL;
}
else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf))
st.ins_buf = curbuf; // In case the buffer was wiped out.
? &st.last_match_pos : &st.first_match_pos;
// For ^N/^P loop over all the flags/windows/buffers in 'complete'.
- for (;;)
+ for (cpt_value_idx = 0;;)
{
found_new_match = FAIL;
st.set_match_pos = FALSE;
if (status == INS_COMPL_CPT_END)
break;
if (status == INS_COMPL_CPT_CONT)
+ {
+ cpt_value_idx++;
continue;
+ }
}
// If complete() was called then compl_pattern has been reset. The
// get the next set of completion matches
found_new_match = get_next_completion_match(type, &st, ini);
+ if (type > 0)
+ cpt_value_idx++;
+
// break the loop for specialized modes (use 'complete' just for the
// generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new
// match
compl_started = FALSE;
}
}
+ cpt_value_idx = -1;
compl_started = TRUE;
if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
* 'completefunc' and 'thesaurusfunc')
* Sets the global variables: compl_col, compl_length and compl_pattern.
* Uses the global variable: spell_bad_len
+ * Callback function "cb" is set if triggered by a function in the 'cpt'
+ * option; otherwise, it is NULL.
+ * "startcol", when not NULL, contains the column returned by function.
*/
static int
-get_userdefined_compl_info(colnr_T curs_col UNUSED)
+get_userdefined_compl_info(colnr_T curs_col UNUSED, callback_T *cb UNUSED,
+ int *startcol UNUSED)
{
int ret = FAIL;
char_u *funcname;
pos_T pos;
int save_State = State;
- callback_T *cb;
+ int len;
+ string_T *compl_pat;
+ int is_cpt_function = (cb != NULL);
- // Call 'completefunc' or 'omnifunc' or 'thesaurusfunc' and get pattern
- // length as a string
- funcname = get_complete_funcname(ctrl_x_mode);
- if (*funcname == NUL)
+ if (!is_cpt_function)
{
- semsg(_(e_option_str_is_not_set), ctrl_x_mode_function()
- ? "completefunc" : "omnifunc");
- return FAIL;
+ // Call 'completefunc' or 'omnifunc' or 'thesaurusfunc' and get pattern
+ // length as a string
+ funcname = get_complete_funcname(ctrl_x_mode);
+ if (*funcname == NUL)
+ {
+ semsg(_(e_option_str_is_not_set), ctrl_x_mode_function()
+ ? "completefunc" : "omnifunc");
+ return FAIL;
+ }
+ cb = get_insert_callback(ctrl_x_mode);
}
args[0].v_type = VAR_NUMBER;
args[2].v_type = VAR_UNKNOWN;
pos = curwin->w_cursor;
++textlock;
- cb = get_insert_callback(ctrl_x_mode);
col = call_callback_retnr(cb, 2, args);
--textlock;
return FAIL;
}
+ if (startcol != NULL)
+ *startcol = col;
+
// Return value -2 means the user complete function wants to cancel the
// complete without an error, do the same if the function did not execute
// successfully.
// Return value -3 does the same as -2 and leaves CTRL-X mode.
if (col == -3)
{
+ if (is_cpt_function)
+ return FAIL;
ctrl_x_mode = CTRL_X_NORMAL;
edit_submode = NULL;
if (!shortmess(SHM_COMPLETIONMENU))
compl_opt_refresh_always = FALSE;
compl_opt_suppress_empty = FALSE;
- if (col < 0)
+ if (col < 0 || col > curs_col)
col = curs_col;
- compl_col = col;
- if (compl_col > curs_col)
- compl_col = curs_col;
// Setup variables for completion. Need to obtain "line" again,
// it may have become invalid.
line = ml_get(curwin->w_cursor.lnum);
- compl_length = curs_col - compl_col;
- compl_pattern.string = vim_strnsave(line + compl_col, (size_t)compl_length);
- if (compl_pattern.string == NULL)
+ len = curs_col - col;
+ compl_pat = is_cpt_function ? &cpt_compl_pattern : &compl_pattern;
+ compl_pat->string = vim_strnsave(line + col, (size_t)len);
+ if (compl_pat->string == NULL)
{
- compl_pattern.length = 0;
+ compl_pat->length = 0;
return FAIL;
}
+ compl_pat->length = (size_t)compl_length;
- compl_pattern.length = (size_t)compl_length;
+ if (!is_cpt_function)
+ {
+ compl_col = col;
+ compl_length = len;
+ }
ret = OK;
#endif
else if (ctrl_x_mode_function() || ctrl_x_mode_omni()
|| thesaurus_func_complete(ctrl_x_mode))
{
- if (get_userdefined_compl_info(curs_col) == FAIL)
+ if (get_userdefined_compl_info(curs_col, NULL, NULL) != OK)
return FAIL;
*line_invalid = TRUE; // "line" may have become invalid
}
start_arrow(&tpos);
}
#endif
+
+/*
+ * Reset the info associated with completion sources.
+ */
+ static void
+cpt_compl_src_clear(void)
+{
+ VIM_CLEAR(cpt_func_refresh_always);
+ cpt_value_idx = -1;
+ cpt_value_count = 0;
+}
+
+/*
+ * Initialize the info associated with completion sources.
+ */
+ static int
+cpt_compl_src_init(char_u *cpt_str)
+{
+ int count = 0;
+ char_u *p = cpt_str;
+
+ while (*p)
+ {
+ while (*p == ',' || *p == ' ') // Skip delimiters
+ p++;
+ if (*p) // If not end of string, count this segment
+ {
+ count++;
+ copy_option_part(&p, IObuff, IOSIZE, ","); // Advance p
+ }
+ }
+ cpt_compl_src_clear();
+ cpt_value_count = count;
+ if (count > 0)
+ {
+ cpt_func_refresh_always = ALLOC_CLEAR_MULT(int, count);
+ if (cpt_func_refresh_always == NULL)
+ {
+ cpt_value_count = 0;
+ return FAIL;
+ }
+ }
+ return OK;
+}
+
+/*
+ * Return TRUE if any of the completion sources have 'refresh' set to 'always'.
+ */
+ static int
+is_cpt_func_refresh_always(void)
+{
+#ifdef FEAT_COMPL_FUNC
+ int i;
+
+ for (i = 0; i < cpt_value_count; i++)
+ if (cpt_func_refresh_always[i])
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+/*
+ * Make the completion list non-cyclic.
+ */
+#ifdef FEAT_COMPL_FUNC
+ static void
+ins_compl_make_linear(void)
+{
+ compl_T *m;
+
+ if (compl_first_match == NULL || compl_first_match->cp_prev == NULL)
+ return;
+ m = compl_first_match->cp_prev;
+ m->cp_next = NULL;
+ compl_first_match->cp_prev = NULL;
+}
+#endif
+
+/*
+ * Remove the matches linked to the current completion source (as indicated by
+ * cpt_value_idx) from the completion list.
+ */
+#ifdef FEAT_COMPL_FUNC
+ static compl_T *
+remove_old_matches(void)
+{
+ compl_T *sublist_start = NULL, *sublist_end = NULL, *insert_at = NULL;
+ compl_T *current, *next;
+ int compl_shown_removed = FALSE;
+ int forward = compl_dir_forward();
+
+ // Identify the sublist of old matches that needs removal
+ for (current = compl_first_match; current != NULL; current = current->cp_next)
+ {
+ if (current->cp_cpt_value_idx < cpt_value_idx && (forward || (!forward && !insert_at)))
+ insert_at = current;
+
+ if (current->cp_cpt_value_idx == cpt_value_idx)
+ {
+ if (!sublist_start)
+ sublist_start = current;
+ sublist_end = current;
+ if (!compl_shown_removed && compl_shown_match == current)
+ compl_shown_removed = TRUE;
+ }
+
+ if ((forward && current->cp_cpt_value_idx > cpt_value_idx) || (!forward && insert_at))
+ break;
+ }
+
+ // Re-assign compl_shown_match if necessary
+ if (compl_shown_removed)
+ {
+ if (forward)
+ compl_shown_match = compl_first_match;
+ else
+ { // Last node will have the prefix that is being completed
+ for (current = compl_first_match; current->cp_next != NULL; current = current->cp_next)
+ ;
+ compl_shown_match = current;
+ }
+ }
+
+ if (!sublist_start) // No nodes to remove
+ return insert_at;
+
+ // Update links to remove sublist
+ if (sublist_start->cp_prev)
+ sublist_start->cp_prev->cp_next = sublist_end->cp_next;
+ else
+ compl_first_match = sublist_end->cp_next;
+
+ if (sublist_end->cp_next)
+ sublist_end->cp_next->cp_prev = sublist_start->cp_prev;
+
+ // Free all nodes in the sublist
+ sublist_end->cp_next = NULL;
+ for (current = sublist_start; current != NULL; current = next)
+ {
+ next = current->cp_next;
+ ins_compl_item_free(current);
+ }
+
+ return insert_at;
+}
+#endif
+
+/*
+ * Retrieve completion matches using the callback function "cb" and store the
+ * 'refresh:always' flag.
+ */
+#ifdef FEAT_COMPL_FUNC
+ static void
+get_cpt_func_completion_matches(callback_T *cb UNUSED)
+{
+ int ret;
+ int startcol;
+
+ VIM_CLEAR_STRING(cpt_compl_pattern);
+ ret = get_userdefined_compl_info(curwin->w_cursor.col, cb, &startcol);
+ if (ret == FAIL && startcol == -3)
+ cpt_func_refresh_always[cpt_value_idx] = FALSE;
+ else if (ret == OK)
+ {
+ expand_by_function(0, cpt_compl_pattern.string, cb);
+ cpt_func_refresh_always[cpt_value_idx] = compl_opt_refresh_always;
+ compl_opt_refresh_always = FALSE;
+ }
+}
+#endif
+
+/*
+ * Retrieve completion matches from functions in the 'cpt' option where the
+ * 'refresh:always' flag is set.
+ */
+ static void
+cpt_compl_refresh(void)
+{
+#ifdef FEAT_COMPL_FUNC
+ char_u *cpt;
+ char_u *p;
+ callback_T *cb;
+
+ // Make the completion list linear (non-cyclic)
+ ins_compl_make_linear();
+ // Make a copy of 'cpt' in case the buffer gets wiped out
+ cpt = vim_strsave(curbuf->b_p_cpt);
+
+ cpt_value_idx = 0;
+ for (p = cpt; *p; cpt_value_idx++)
+ {
+ while (*p == ',' || *p == ' ') // Skip delimiters
+ p++;
+
+ if (cpt_func_refresh_always[cpt_value_idx])
+ {
+ if (*p == 'o')
+ cb = &curbuf->b_ofu_cb;
+ else if (*p == 'f')
+ cb = (*(p + 1) != ',' && *(p + 1) != NUL)
+ ? get_cpt_func_callback(p + 1) : &curbuf->b_cfu_cb;
+ if (cb)
+ {
+ compl_curr_match = remove_old_matches();
+ get_cpt_func_completion_matches(cb);
+ }
+ }
+
+ copy_option_part(&p, IObuff, IOSIZE, ","); // Advance p
+ }
+ cpt_value_idx = -1;
+
+ vim_free(cpt);
+ // Make the list cyclic
+ compl_matches = ins_compl_make_cyclic();
+#endif
+}
new
exe "normal Gofind -\<C-x>\<C-o>"
call assert_equal("find -help", getline('$'))
+ %d
+ set complete=o
+ exe "normal Gofind -\<C-n>"
+ " 'complete' inserts at 'iskeyword' boundary (so you get --help)
+ call assert_equal("find --help", getline('$'))
bwipe!
delfunc Omni
- set omnifunc=
+ set omnifunc= complete&
endfunc
func Test_omni_throw()
call assert_exception('he he he')
call assert_equal(1, g:CallCount)
endtry
+ %d
+ set complete=o
+ let g:CallCount = 0
+ try
+ exe "normal ifoo\<C-n>"
+ call assert_false(v:true, 'command should have failed')
+ catch
+ call assert_exception('he he he')
+ call assert_equal(1, g:CallCount)
+ endtry
bwipe!
delfunc Omni
unlet g:CallCount
- set omnifunc=
+ set omnifunc= complete&
endfunc
func Test_omni_autoload()
call assert_equal(0, s:args[1][0])
set omnifunc=
+ set complete=fCompleteFunc
+ call feedkeys("i\<C-N>\<Esc>", 'x')
+ call assert_equal([1, 1], s:args[0])
+ call assert_equal(0, s:args[1][0])
+ set complete=o
+ call feedkeys("i\<C-N>\<Esc>", 'x')
+ call assert_equal([1, 1], s:args[0])
+ call assert_equal(0, s:args[1][0])
+ set complete&
+
bwipe!
unlet s:args
delfunc CompleteFunc
call assert_equal( ['one', 'two'], v:completed_item[ 'user_data' ] )
if a:pre
- call assert_equal('function', complete_info().mode)
+ call assert_equal(a:pre == 1 ? 'function' : 'keyword', complete_info().mode)
endif
let s:called_completedone = 1
call assert_true(s:called_completedone)
call assert_equal(oldline, newline)
+ let s:called_completedone = 0
+ set complete=f<SID>CompleteDone_CompleteFuncNone
+ execute "normal a\<C-N>\<C-Y>"
+ set complete&
+ let newline = join(map(range(&columns), 'nr2char(screenchar(&lines-1, v:val+1))'), '')
+
+ call assert_true(s:called_completedone)
+ call assert_equal(oldline, newline)
let s:called_completedone = 0
au! CompleteDone
endfunc
endfunc
set omnifunc=CompleteFunc
set completefunc=CompleteFunc
+ set complete=.,fCompleteFunc
set completeopt+=menuone
new
call assert_equal('vim', g:complete_word)
call assert_equal('keyword', g:complete_type)
- call feedkeys("Shello vim visual v\<C-X>\<C-N>\<C-Y>", 'tx')
+ call feedkeys("Shello vim visual v\<C-N>\<ESC>", 'tx')
+ call assert_equal('', g:complete_word)
+ call assert_equal('keyword', g:complete_type)
+
+ call feedkeys("Shello vim visual v\<C-N>\<C-Y>", 'tx')
call assert_equal('vim', g:complete_word)
call assert_equal('keyword', g:complete_type)
call assert_true(s:called_completedone)
let s:called_completedone = 0
+ au! CompleteDonePre
+ au! CompleteDone
+
+ au CompleteDonePre * :call <SID>CompleteDone_CheckCompletedItemDict(2)
+ au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemDict(0)
+
+ set complete=.,f<SID>CompleteDone_CompleteFuncDict
+ execute "normal a\<C-N>\<C-Y>"
+ set complete&
+
+ call assert_equal(['one', 'two'], v:completed_item[ 'user_data' ])
+ call assert_true(s:called_completedone)
+
+ let s:called_completedone = 0
+ au! CompleteDonePre
au! CompleteDone
endfunc
call assert_equal('', v:completed_item[ 'user_data' ])
call assert_true(s:called_completedone)
+ let s:called_completedone = 0
+
+ set complete=.,f<SID>CompleteDone_CompleteFuncDictNoUserData
+ execute "normal a\<C-N>\<C-Y>"
+ set complete&
+
+ call assert_equal('', v:completed_item[ 'user_data' ])
+ call assert_true(s:called_completedone)
+
let s:called_completedone = 0
au! CompleteDone
endfunc
call assert_equal('', v:completed_item[ 'user_data' ])
call assert_true(s:called_completedone)
+ let s:called_completedone = 0
+
+ set complete=.,f<SID>CompleteDone_CompleteFuncList
+ execute "normal a\<C-N>\<C-Y>"
+ set complete&
+
+ call assert_equal('', v:completed_item[ 'user_data' ])
+ call assert_true(s:called_completedone)
+
+ let s:called_completedone = 0
+
+ set complete=.,f
+ execute "normal a\<C-N>\<C-Y>"
+ set complete&
+
+ call assert_equal('', v:completed_item[ 'user_data' ])
+ call assert_true(s:called_completedone)
+
let s:called_completedone = 0
au! CompleteDone
endfunc
set completefunc=CompleteTest
call feedkeys("i\<C-X>\<C-U>\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
call assert_equal("matched{'pum_visible': 1, 'mode': 'function', 'selected': 0, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1))
- bwipe!
+ %d
+ set complete=.,fCompleteTest
+ call feedkeys("i\<C-N>\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
+ call assert_equal("matched{'pum_visible': 1, 'mode': 'keyword', 'selected': 0, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1))
+ %d
+ set complete=.,f
+ call feedkeys("i\<C-N>\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
+ call assert_equal("matched{'pum_visible': 1, 'mode': 'keyword', 'selected': 0, 'items': [{'word': 'matched', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}]}", getline(1))
set completeopt&
+ set complete&
set completefunc&
endfunc
+func Test_cpt_func_cursorcol()
+ func CptColTest(findstart, query)
+ if a:findstart
+ call assert_equal("foo bar", getline(1))
+ call assert_equal(8, col('.'))
+ return col('.')
+ endif
+ call assert_equal("foo bar", getline(1))
+ call assert_equal(8, col('.'))
+ return v:none
+ endfunc
+
+ set complete=fCptColTest
+ new
+ call feedkeys("ifoo bar\<C-N>", "tx")
+ bwipe!
+ new
+ set completeopt=longest
+ call feedkeys("ifoo bar\<C-N>", "tx")
+ bwipe!
+ new
+ set completeopt=menuone
+ call feedkeys("ifoo bar\<C-N>", "tx")
+ bwipe!
+ new
+ set completeopt=menuone,preinsert
+ call feedkeys("ifoo bar\<C-N>", "tx")
+ bwipe!
+ set complete& completeopt&
+ delfunc CptColTest
+endfunc
+
func ScrollInfoWindowUserDefinedFn(findstart, query)
" User defined function (i_CTRL-X_CTRL-U)
if a:findstart
endfunc
func CompleteInfoTestUserDefinedFn(mvmt, idx, noselect)
- new
if a:noselect
set completeopt=menuone,popup,noinsert,noselect
else
set completeopt=menu,preview
endif
- set completefunc=CompleteInfoUserDefinedFn
- call feedkeys("i\<C-X>\<C-U>" . a:mvmt . "\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
- let completed = a:idx != -1 ? ['foo', 'bar', 'baz', 'qux']->get(a:idx) : ''
- call assert_equal(completed. "{'pum_visible': 1, 'mode': 'function', 'selected': " . a:idx . ", 'items': [" .
+ let items = "[" .
\ "{'word': 'foo', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
\ "{'word': 'bar', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
\ "{'word': 'baz', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
\ "{'word': 'qux', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}" .
- \ "]}", getline(1))
+ \ "]"
+ new
+ set completefunc=CompleteInfoUserDefinedFn
+ call feedkeys("i\<C-X>\<C-U>" . a:mvmt . "\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
+ let completed = a:idx != -1 ? ['foo', 'bar', 'baz', 'qux']->get(a:idx) : ''
+ call assert_equal(completed. "{'pum_visible': 1, 'mode': 'function', 'selected': " . a:idx . ", 'items': " . items . "}", getline(1))
+ %d
+ set complete=.,fCompleteInfoUserDefinedFn
+ call feedkeys("i\<C-N>" . a:mvmt . "\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
+ let completed = a:idx != -1 ? ['foo', 'bar', 'baz', 'qux']->get(a:idx) : ''
+ call assert_equal(completed. "{'pum_visible': 1, 'mode': 'keyword', 'selected': " . a:idx . ", 'items': " . items . "}", getline(1))
+ %d
+ set complete=.,f
+ call feedkeys("i\<C-N>" . a:mvmt . "\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
+ let completed = a:idx != -1 ? ['foo', 'bar', 'baz', 'qux']->get(a:idx) : ''
+ call assert_equal(completed. "{'pum_visible': 1, 'mode': 'keyword', 'selected': " . a:idx . ", 'items': " . items . "}", getline(1))
bwipe!
- set completeopt&
- set completefunc&
+ set completeopt& completefunc& complete&
endfunc
func Test_complete_info_user_defined_fn()
set completefunc=CompleteFunc
call setline(1, ['', 'abcd', ''])
call assert_fails('exe "normal 2G$a\<C-X>\<C-U>"', 'E565:')
+ set complete=fCompleteFunc
+ call assert_fails('exe "normal 2G$a\<C-N>"', 'E565:')
+ set complete=f
+ call assert_fails('exe "normal 2G$a\<C-N>"', 'E565:')
" delete text when called for the second time
func CompleteFunc2(findstart, base)
set completefunc=CompleteFunc2
call setline(1, ['', 'abcd', ''])
call assert_fails('exe "normal 2G$a\<C-X>\<C-U>"', 'E565:')
+ set complete=fCompleteFunc2
+ call assert_fails('exe "normal 2G$a\<C-N>"', 'E565:')
+ set complete=f
+ call assert_fails('exe "normal 2G$a\<C-N>"', 'E565:')
" Jump to a different window from the complete function
func CompleteFunc3(findstart, base)
set completefunc=CompleteFunc3
new
call assert_fails('exe "normal a\<C-X>\<C-U>"', 'E565:')
+ %d
+ set complete=fCompleteFunc3
+ call assert_fails('exe "normal a\<C-N>"', 'E565:')
+ %d
+ set complete=f
+ call assert_fails('exe "normal a\<C-N>"', 'E565:')
close!
- set completefunc&
+ set completefunc& complete&
delfunc CompleteFunc
delfunc CompleteFunc2
delfunc CompleteFunc3
set completefunc=CompleteFunc
exe "normal i\<C-X>\<C-U>"
call assert_equal('moon', getline(1))
- set completefunc&
+ %d
+ set complete=fCompleteFunc
+ exe "normal i\<C-N>"
+ call assert_equal('moon', getline(1))
+ %d
+ set complete=f
+ exe "normal i\<C-N>"
+ call assert_equal('moon', getline(1))
+ set completefunc& complete&
close!
endfunc
return #{words: res, refresh: 'always'}
endif
endfunc
- new
set completeopt=menu,longest
set completefunc=Tcomplete
+ new
exe "normal! iup\<C-X>\<C-U>\<BS>\<BS>\<BS>\<BS>\<BS>"
call assert_equal('up', getline(1))
call assert_equal(6, g:CallCount)
+ %d
+ let g:CallCount = 0
+ set complete=fTcomplete
+ exe "normal! iup\<C-N>\<BS>\<BS>\<BS>\<BS>\<BS>"
+ call assert_equal('up', getline(1))
+ call assert_equal(6, g:CallCount)
+ %d
+ let g:CallCount = 0
+ set complete=f
+ exe "normal! iup\<C-N>\<BS>\<BS>\<BS>\<BS>\<BS>"
+ call assert_equal('up', getline(1))
+ call assert_equal(6, g:CallCount)
+ %d
+ let g:CallCount = 0
+ set omnifunc=Tcomplete
+ set complete=o
+ exe "normal! iup\<C-N>\<BS>\<BS>\<BS>\<BS>\<BS>"
+ call assert_equal('up', getline(1))
+ call assert_equal(6, g:CallCount)
+ bw!
set completeopt&
+ set complete&
set completefunc&
- bw!
delfunc Tcomplete
endfunc
+" Test for 'cpt' user func that fails (return -2/-3) when refresh:always
+func Test_cpt_func_refresh_always_fail()
+ func! CompleteFail(retval, findstart, base)
+ if a:findstart
+ return a:retval
+ endif
+ call assert_equal(-999, a:findstart) " Should not reach here
+ endfunc
+ new
+ set complete=ffunction('CompleteFail'\\,\ [-2])
+ exe "normal! ia\<C-N>"
+ %d
+ set complete=ffunction('CompleteFail'\\,\ [-3])
+ exe "normal! ia\<C-N>"
+ bw!
+
+ func! CompleteFailIntermittent(retval, findstart, base)
+ if a:findstart
+ if g:CallCount == 2
+ let g:CallCount += 1
+ return a:retval
+ endif
+ return col('.') - 1
+ endif
+ let g:CallCount += 1
+ let res = [[], ['foo', 'fbar'], ['foo1', 'foo2'], ['foofail'], ['fooo3']]
+ return #{words: res[g:CallCount], refresh: 'always'}
+ endfunc
+ new
+ set completeopt=menuone,noselect
+ set complete=ffunction('CompleteFailIntermittent'\\,\ [-2])
+ let g:CallCount = 0
+ exe "normal! if\<C-N>\<c-r>=complete_info([\"items\"])\<cr>"
+ call assert_match('''word'': ''foo''.*''word'': ''fbar''', getline(1))
+ call assert_equal(1, g:CallCount)
+ %d
+ let g:CallCount = 0
+ exe "normal! if\<C-N>o\<c-r>=complete_info([\"items\", \"selected\"])\<cr>"
+ call assert_match('''selected'': -1.*''word'': ''foo1''.*''word'': ''foo2''', getline(1))
+ call assert_equal(2, g:CallCount)
+ %d
+ set complete=ffunction('CompleteFailIntermittent'\\,\ [-3])
+ let g:CallCount = 0
+ exe "normal! if\<C-N>o\<c-r>=complete_info([\"items\", \"selected\"])\<cr>"
+ call assert_match('''selected'': -1.*''word'': ''foo1''.*''word'': ''foo2''', getline(1))
+ call assert_equal(2, g:CallCount)
+ %d
+ set complete=ffunction('CompleteFailIntermittent'\\,\ [-2])
+ " completion mode is dismissed when there are no matches in list
+ let g:CallCount = 0
+ exe "normal! if\<C-N>oo\<c-r>=complete_info([\"items\"])\<cr>"
+ call assert_equal('foo{''items'': []}', getline(1))
+ call assert_equal(3, g:CallCount)
+ %d
+ let g:CallCount = 0
+ exe "normal! if\<C-N>oo\<bs>\<c-r>=complete_info([\"items\"])\<cr>"
+ call assert_equal('fo{''items'': []}', getline(1))
+ call assert_equal(3, g:CallCount)
+ %d
+ " completion mode continues when matches from other sources present
+ set complete=.,ffunction('CompleteFailIntermittent'\\,\ [-2])
+ call setline(1, 'fooo1')
+ let g:CallCount = 0
+ exe "normal! Gof\<C-N>oo\<c-r>=complete_info([\"items\", \"selected\"])\<cr>"
+ call assert_equal('foo{''selected'': -1, ''items'': [{''word'': ''fooo1'', ''menu'': '''', '
+ \ . '''user_data'': '''', ''info'': '''', ''kind'': '''', ''abbr'': ''''}]}',
+ \ getline(2))
+ call assert_equal(3, g:CallCount)
+ %d
+ call setline(1, 'fooo1')
+ let g:CallCount = 0
+ exe "normal! Gof\<C-N>oo\<bs>\<c-r>=complete_info([\"items\"])\<cr>"
+ call assert_match('''word'': ''fooo1''.*''word'': ''fooo3''', getline(2))
+ call assert_equal(4, g:CallCount)
+ %d
+ " refresh will stop when -3 is returned
+ set complete=.,,\ ffunction('CompleteFailIntermittent'\\,\ [-3])
+ call setline(1, 'fooo1')
+ let g:CallCount = 0
+ exe "normal! Gof\<C-N>o\<bs>\<c-r>=complete_info([\"items\", \"selected\"])\<cr>"
+ call assert_equal('f{''selected'': -1, ''items'': [{''word'': ''fooo1'', ''menu'': '''', '
+ \ . '''user_data'': '''', ''info'': '''', ''kind'': '''', ''abbr'': ''''}]}',
+ \ getline(2))
+ call assert_equal(3, g:CallCount)
+ %d
+ call setline(1, 'fooo1')
+ let g:CallCount = 0
+ exe "normal! Gof\<C-N>oo\<bs>\<c-r>=complete_info([\"items\", \"selected\"])\<cr>"
+ call assert_equal('fo{''selected'': -1, ''items'': [{''word'': ''fooo1'', ''menu'': '''', '
+ \ . '''user_data'': '''', ''info'': '''', ''kind'': '''', ''abbr'': ''''}]}',
+ \ getline(2))
+ call assert_equal(3, g:CallCount)
+ bw!
+
+ set complete& completeopt&
+ delfunc CompleteFail
+ delfunc CompleteFailIntermittent
+endfunc
+
+" Select items before they are removed by refresh:always
+func Test_cpt_select_item_refresh_always()
+
+ func CompleteMenuWords()
+ let info = complete_info(["items", "selected"])
+ call map(info.items, {_, v -> v.word})
+ return info
+ endfunc
+
+ func! CompleteItemsSelect(compl, findstart, base)
+ if a:findstart
+ return col('.') - 1
+ endif
+ let g:CallCount += 1
+ if g:CallCount == 2
+ return #{words: a:compl, refresh: 'always'}
+ endif
+ let res = [[], ['fo', 'foobar'], [], ['foo1', 'foo2']]
+ return #{words: res[g:CallCount], refresh: 'always'}
+ endfunc
+
+ new
+ set complete=.,ffunction('CompleteItemsSelect'\\,\ [[]])
+ call setline(1, "foobarbar")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-n>\<c-n>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('fo{''selected'': 1, ''items'': [''foobarbar'', ''fo'', ''foobar'']}', getline(2))
+ call assert_equal(1, g:CallCount)
+ %d
+ call setline(1, "foobarbar")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-p>\<c-p>\<c-p>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('fo{''selected'': 0, ''items'': [''fo'', ''foobar'', ''foobarbar'']}', getline(2))
+ call assert_equal(1, g:CallCount)
+ %d
+ call setline(1, "foobarbar")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-n>\<c-n>o\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('foo{''selected'': -1, ''items'': []}' , getline(2))
+ call assert_equal(1, g:CallCount)
+ %d
+ call setline(1, "foobarbar")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-n>\<c-n>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('f{''selected'': -1, ''items'': [''foobarbar'']}', getline(2))
+ call assert_equal(2, g:CallCount)
+ %d
+ call setline(1, "foobarbar")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-p>\<c-p>\<c-p>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('f{''selected'': -1, ''items'': [''foobarbar'']}', getline(2))
+ call assert_equal(2, g:CallCount)
+
+ %d
+ set complete=.,ffunction('CompleteItemsSelect'\\,\ [['foonext']])
+ call setline(1, "foobarbar")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-n>\<c-n>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('f{''selected'': -1, ''items'': [''foobarbar'', ''foonext'']}', getline(2))
+ call assert_equal(2, g:CallCount)
+ %d
+ call setline(1, "foobarbar")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-p>\<c-p>\<c-p>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('f{''selected'': -1, ''items'': [''foonext'', ''foobarbar'']}', getline(2))
+ call assert_equal(2, g:CallCount)
+
+ %d
+ call setline(1, "foob")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-n>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('foo{''selected'': 0, ''items'': [''foob'', ''foonext'']}', getline(2))
+ call assert_equal(2, g:CallCount)
+ %d
+ call setline(1, "foob")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-n>\<bs>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('fo{''selected'': 0, ''items'': [''foob'', ''foo1'', ''foo2'']}', getline(2))
+ call assert_equal(3, g:CallCount)
+
+ %d
+ call setline(1, "foob")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-p>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('foo{''selected'': 1, ''items'': [''foonext'', ''foob'']}', getline(2))
+ call assert_equal(2, g:CallCount)
+ %d
+ call setline(1, "foob")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-p>\<bs>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('fo{''selected'': 2, ''items'': [''foo1'', ''foo2'', ''foob'']}', getline(2))
+ call assert_equal(3, g:CallCount)
+
+ %d
+ set complete=.,ffunction('CompleteItemsSelect'\\,\ [['fo'\\,\ 'foonext']])
+ call setline(1, "foobarbar")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-n>\<c-n>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('f{''selected'': -1, ''items'': [''foobarbar'', ''fo'', ''foonext'']}', getline(2))
+ call assert_equal(2, g:CallCount)
+ %d
+ call setline(1, "foobarbar")
+ let g:CallCount = 0
+ exe "normal! Gof\<c-p>\<c-p>\<c-p>\<bs>\<c-r>=CompleteMenuWords()\<cr>"
+ call assert_equal('f{''selected'': -1, ''items'': [''fo'', ''foonext'', ''foobarbar'']}', getline(2))
+ call assert_equal(2, g:CallCount)
+ bw!
+
+ set complete&
+ delfunc CompleteMenuWords
+ delfunc CompleteItemsSelect
+endfunc
+
+" Test two functions together, each returning refresh:always
+func Test_cpt_multi_func_refresh_always()
+
+ func CompleteMenuMatches()
+ let info = complete_info(["matches", "selected"])
+ call map(info.matches, {_, v -> v.word})
+ return info
+ endfunc
+
+ func! CompleteItems1(findstart, base)
+ if a:findstart
+ return col('.') - 1
+ endif
+ let g:CallCount1 += 1
+ let res = [[], [], ['foo1', 'foobar1'], [], ['foo11', 'foo12'], [], ['foo13', 'foo14']]
+ return #{words: res[g:CallCount1], refresh: 'always'}
+ endfunc
+
+ func! CompleteItems2(findstart, base)
+ if a:findstart
+ return col('.') - 1
+ endif
+ let g:CallCount2 += 1
+ let res = [[], [], [], ['foo2', 'foobar2'], ['foo21', 'foo22'], ['foo23'], []]
+ return #{words: res[g:CallCount2], refresh: 'always'}
+ endfunc
+
+ set complete=
+ exe "normal! if\<C-N>\<c-r>=CompleteMenuMatches()\<cr>"
+ " \x0e is <c-n>
+ call assert_equal("f\x0e" . '{''matches'': [], ''selected'': -1}', getline(1))
+
+ set completeopt=menuone,noselect
+ set complete=fCompleteItems1,fCompleteItems2
+
+ new
+ let g:CallCount1 = 0
+ let g:CallCount2 = 0
+ exe "normal! if\<c-n>o\<c-n>o\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('foo{''matches'': [''foo2'', ''foobar2''], ''selected'': -1}', getline(1))
+ call assert_equal(3, g:CallCount1)
+ call assert_equal(3, g:CallCount2)
+ %d
+ let g:CallCount1 = 0
+ let g:CallCount2 = 0
+ exe "normal! if\<c-p>o\<c-p>o\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('foo{''matches'': [''foo2'', ''foobar2''], ''selected'': -1}', getline(1))
+ call assert_equal(3, g:CallCount1)
+ call assert_equal(3, g:CallCount2)
+ %d
+ let g:CallCount1 = 0
+ let g:CallCount2 = 0
+ exe "normal! if\<c-p>\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('f{''matches'': [], ''selected'': -1}', getline(1))
+ call assert_equal(1, g:CallCount1)
+ call assert_equal(1, g:CallCount2)
+ %d
+ let g:CallCount1 = 1
+ let g:CallCount2 = 1
+ exe "normal! if\<c-n>\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('f{''matches'': [''foo1'', ''foobar1''], ''selected'': -1}', getline(1))
+ call assert_equal(2, g:CallCount2)
+ call assert_equal(2, g:CallCount2)
+ %d
+ let g:CallCount1 = 1
+ let g:CallCount2 = 1
+ exe "normal! if\<c-n>o\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('fo{''matches'': [''foo2'', ''foobar2''], ''selected'': -1}', getline(1))
+ call assert_equal(3, g:CallCount2)
+ call assert_equal(3, g:CallCount2)
+ %d
+ let g:CallCount1 = 1
+ let g:CallCount2 = 1
+ exe "normal! if\<c-p>o\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('fo{''matches'': [''foo2'', ''foobar2''], ''selected'': -1}', getline(1))
+ call assert_equal(3, g:CallCount2)
+ call assert_equal(3, g:CallCount2)
+ %d
+ let g:CallCount1 = 1
+ let g:CallCount2 = 1
+ exe "normal! if\<c-n>oo\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('foo{''matches'': [''foo11'', ''foo12'', ''foo21'', ''foo22''], ''selected'': -1}', getline(1))
+ call assert_equal(4, g:CallCount2)
+ call assert_equal(4, g:CallCount2)
+ %d
+ let g:CallCount1 = 1
+ let g:CallCount2 = 1
+ exe "normal! if\<c-n>oo\<bs>\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('fo{''matches'': [''foo23''], ''selected'': -1}', getline(1))
+ call assert_equal(5, g:CallCount2)
+ call assert_equal(5, g:CallCount2)
+ %d
+ let g:CallCount1 = 1
+ let g:CallCount2 = 1
+ exe "normal! if\<c-p>oo\<bs>\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('fo{''matches'': [''foo23''], ''selected'': -1}', getline(1))
+ call assert_equal(5, g:CallCount2)
+ call assert_equal(5, g:CallCount2)
+ %d
+ let g:CallCount1 = 1
+ let g:CallCount2 = 1
+ exe "normal! if\<c-n>oo\<bs>o\<c-r>=CompleteMenuMatches()\<cr>"
+ call assert_equal('foo{''matches'': [''foo13'', ''foo14''], ''selected'': -1}', getline(1))
+ call assert_equal(6, g:CallCount2)
+ call assert_equal(6, g:CallCount2)
+ bw!
+
+ set complete& completeopt&
+ delfunc CompleteMenuMatches
+ delfunc CompleteItems1
+ delfunc CompleteItems2
+endfunc
+
" Test for completing from a thesaurus file without read permission
func Test_complete_unreadable_thesaurus_file()
CheckUnix
bwipe!
endfunc
+" Test for different ways of setting a function in 'complete' option
+func Test_cpt_func_callback()
+ func CompleteFunc1(callnr, findstart, base)
+ call add(g:CompleteFunc1Args, [a:callnr, a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+ func CompleteFunc2(findstart, base)
+ call add(g:CompleteFunc2Args, [a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+
+ let lines =<< trim END
+ #" Test for using a global function name
+ set complete=fg:CompleteFunc2
+ new
+ call setline(1, 'global')
+ LET g:CompleteFunc2Args = []
+ call feedkeys("A\<C-N>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'global']], g:CompleteFunc2Args)
+ set complete&
+ bw!
+
+ #" Test for using a function()
+ set complete=ffunction('g:CompleteFunc1'\\,\ [10])
+ new
+ call setline(1, 'one')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-N>\<Esc>", 'x')
+ call assert_equal([[10, 1, ''], [10, 0, 'one']], g:CompleteFunc1Args)
+ set complete&
+ bw!
+
+ #" Using a funcref variable
+ set complete=ffuncref('g:CompleteFunc1'\\,\ [11])
+ new
+ call setline(1, 'two')
+ LET g:CompleteFunc1Args = []
+ call feedkeys("A\<C-N>\<Esc>", 'x')
+ call assert_equal([[11, 1, ''], [11, 0, 'two']], g:CompleteFunc1Args)
+ set complete&
+ bw!
+
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+
+ " Test for using a script-local function name
+ func s:CompleteFunc3(findstart, base)
+ call add(g:CompleteFunc3Args, [a:findstart, a:base])
+ return a:findstart ? 0 : []
+ endfunc
+ set complete=fs:CompleteFunc3
+ new
+ call setline(1, 'script1')
+ let g:CompleteFunc3Args = []
+ call feedkeys("A\<C-N>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script1']], g:CompleteFunc3Args)
+ set complete&
+ bw!
+
+ let &complete = 'fs:CompleteFunc3'
+ new
+ call setline(1, 'script2')
+ let g:CompleteFunc3Args = []
+ call feedkeys("A\<C-N>\<Esc>", 'x')
+ call assert_equal([[1, ''], [0, 'script2']], g:CompleteFunc3Args)
+ bw!
+ delfunc s:CompleteFunc3
+ set complete&
+
+ " In Vim9 script s: can be omitted
+ let lines =<< trim END
+ vim9script
+ var CompleteFunc4Args = []
+ def CompleteFunc4(findstart: bool, base: string): any
+ add(CompleteFunc4Args, [findstart, base])
+ return findstart ? 0 : []
+ enddef
+ set complete=fCompleteFunc4
+ new
+ setline(1, 'script1')
+ feedkeys("A\<C-N>\<Esc>", 'x')
+ assert_equal([[1, ''], [0, 'script1']], CompleteFunc4Args)
+ set complete&
+ bw!
+ END
+ call v9.CheckScriptSuccess(lines)
+
+ " Vim9 tests
+ let lines =<< trim END
+ vim9script
+
+ def Vim9CompleteFunc(callnr: number, findstart: number, base: string): any
+ add(g:Vim9completeFuncArgs, [callnr, findstart, base])
+ return findstart ? 0 : []
+ enddef
+
+ # Test for using a def function with completefunc
+ set complete=ffunction('Vim9CompleteFunc'\\,\ [60])
+ new | only
+ setline(1, 'one')
+ g:Vim9completeFuncArgs = []
+ feedkeys("A\<C-N>\<Esc>", 'x')
+ assert_equal([[60, 1, ''], [60, 0, 'one']], g:Vim9completeFuncArgs)
+ bw!
+
+ # Test for using a global function name
+ &complete = 'fg:CompleteFunc2'
+ new | only
+ setline(1, 'two')
+ g:CompleteFunc2Args = []
+ feedkeys("A\<C-N>\<Esc>", 'x')
+ assert_equal([[1, ''], [0, 'two']], g:CompleteFunc2Args)
+ bw!
+
+ # Test for using a script-local function name
+ def LocalCompleteFunc(findstart: number, base: string): any
+ add(g:LocalCompleteFuncArgs, [findstart, base])
+ return findstart ? 0 : []
+ enddef
+ &complete = 'fLocalCompleteFunc'
+ new | only
+ setline(1, 'three')
+ g:LocalCompleteFuncArgs = []
+ feedkeys("A\<C-N>\<Esc>", 'x')
+ assert_equal([[1, ''], [0, 'three']], g:LocalCompleteFuncArgs)
+ bw!
+ END
+ call v9.CheckScriptSuccess(lines)
+
+ " cleanup
+ set completefunc& complete&
+ delfunc CompleteFunc1
+ delfunc CompleteFunc2
+ unlet g:CompleteFunc1Args g:CompleteFunc2Args
+ %bw!
+endfunc
+
" Test for different ways of setting the 'completefunc' option
func Test_completefunc_callback()
func CompleteFunc1(callnr, findstart, base)
func Test_complete_smartindent()
new
setlocal smartindent completefunc=FooBarComplete
-
exe "norm! o{\<cr>\<c-x>\<c-u>\<c-p>}\<cr>\<esc>"
let result = getline(1,'$')
call assert_equal(['', '{','}',''], result)
+ %d
+ setlocal complete=fFooBarComplete
+ exe "norm! o{\<cr>\<c-n>\<c-p>}\<cr>\<esc>"
+ let result = getline(1,'$')
+ call assert_equal(['', '{','}',''], result)
+ %d
+ setlocal complete=f
+ exe "norm! o{\<cr>\<c-n>\<c-p>}\<cr>\<esc>"
+ let result = getline(1,'$')
+ call assert_equal(['', '{','}',''], result)
bw!
delfunction! FooBarComplete
endfunc