patch 8.2.3937: Insert mode completion function is too long

Problem:    Insert mode completion function is too long.
Solution:   Refactor into multiple functions. (Yegappan Lakshmanan,
            closes #9423)
This commit is contained in:
Yegappan Lakshmanan
2021-12-29 17:38:46 +00:00
committed by Bram Moolenaar
parent ccc1644f95
commit edc6f10390
3 changed files with 552 additions and 346 deletions

View File

@ -2901,137 +2901,126 @@ thesaurus_func_complete(int type UNUSED)
} }
/* /*
* Get the next expansion(s), using "compl_pattern". * Return value of process_next_cpt_value()
* The search starts at position "ini" in curbuf and in the direction */
* compl_direction. enum
* When "compl_started" is FALSE start at that position, otherwise continue {
* where we stopped searching before. INS_COMPL_CPT_OK = 1,
* This may return before finding all the matches. INS_COMPL_CPT_CONT,
* Return the total number of matches or -1 if still unknown -- Acevedo INS_COMPL_CPT_END
};
/*
* Process the next 'complete' option value in "e_cpt_arg".
*
* If successful, the arguments are set as below:
* e_cpt_arg - pointer to the next option value in 'e_cpt_arg'
* compl_type_arg - type of insert mode completion to use
* found_all_arg - all matches of this type are found
* buf_arg - search for completions in this buffer
* first_match_pos - position of the first completion match
* last_match_pos - position of the last completion match
* set_match_pos - TRUE if the first match position should be saved to avoid
* loops after the search wraps around.
* dict - name of the dictionary or thesaurus file to search
* dict_f - flag specifying whether "dict" is an exact file name or not
*
* Returns INS_COMPL_CPT_OK if the next value is processed successfully.
* Returns INS_COMPL_CPT_CONT to skip the current value and process the next
* option value.
* Returns INS_COMPL_CPT_END if all the values in "e_cpt" are processed.
*/ */
static int static int
ins_compl_get_exp(pos_T *ini) process_next_cpt_value(
char_u **e_cpt_arg,
int *compl_type_arg,
int *found_all_arg,
buf_T **buf_arg,
pos_T *start_match_pos,
pos_T *first_match_pos,
pos_T *last_match_pos,
int *set_match_pos,
char_u **dict_arg,
int *dict_flag)
{ {
static pos_T first_match_pos; char_u *e_cpt = *e_cpt_arg;
static pos_T last_match_pos; int compl_type = -1;
static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete' int status = INS_COMPL_CPT_OK;
static int found_all = FALSE; // Found all matches of a buf_T *buf = *buf_arg;
// certain type. int found_all = FALSE;
static buf_T *ins_buf = NULL; // buffer being scanned
pos_T *pos;
char_u **matches;
int save_p_scs;
int save_p_ws;
int save_p_ic;
int i;
int num_matches;
int len;
int found_new_match;
int type = ctrl_x_mode;
char_u *ptr;
char_u *dict = NULL; char_u *dict = NULL;
int dict_f = 0; int dict_f = 0;
int set_match_pos;
pos_T prev_pos = {0, 0, 0};
int looped_around = FALSE;
if (!compl_started)
{
FOR_ALL_BUFFERS(ins_buf)
ins_buf->b_scanned = 0;
found_all = FALSE;
ins_buf = curbuf;
e_cpt = (compl_cont_status & CONT_LOCAL)
? (char_u *)"." : curbuf->b_p_cpt;
last_match_pos = first_match_pos = *ini;
}
else if (ins_buf != curbuf && !buf_valid(ins_buf))
ins_buf = curbuf; // In case the buffer was wiped out.
compl_old_match = compl_curr_match; // remember the last current match
pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos;
// For ^N/^P loop over all the flags/windows/buffers in 'complete'.
for (;;)
{
found_new_match = FAIL;
set_match_pos = FALSE;
// For ^N/^P pick a new entry from e_cpt if compl_started is off,
// or if found_all says this entry is done. For ^X^L only use the
// entries from 'complete' that look in loaded buffers.
if ((ctrl_x_mode == CTRL_X_NORMAL
|| ctrl_x_mode_line_or_eval())
&& (!compl_started || found_all))
{
found_all = FALSE;
while (*e_cpt == ',' || *e_cpt == ' ') while (*e_cpt == ',' || *e_cpt == ' ')
e_cpt++; e_cpt++;
if (*e_cpt == '.' && !curbuf->b_scanned) if (*e_cpt == '.' && !curbuf->b_scanned)
{ {
ins_buf = curbuf; buf = curbuf;
first_match_pos = *ini; *first_match_pos = *start_match_pos;
// Move the cursor back one character so that ^N can match the // Move the cursor back one character so that ^N can match the
// word immediately after the cursor. // word immediately after the cursor.
if (ctrl_x_mode == CTRL_X_NORMAL && dec(&first_match_pos) < 0) if (ctrl_x_mode == CTRL_X_NORMAL && dec(first_match_pos) < 0)
{ {
// Move the cursor to after the last character in the // Move the cursor to after the last character in the
// buffer, so that word at start of buffer is found // buffer, so that word at start of buffer is found
// correctly. // correctly.
first_match_pos.lnum = ins_buf->b_ml.ml_line_count; first_match_pos->lnum = buf->b_ml.ml_line_count;
first_match_pos.col = first_match_pos->col =
(colnr_T)STRLEN(ml_get(first_match_pos.lnum)); (colnr_T)STRLEN(ml_get(first_match_pos->lnum));
} }
last_match_pos = first_match_pos; *last_match_pos = *first_match_pos;
type = 0; compl_type = 0;
// Remember the first match so that the loop stops when we // Remember the first match so that the loop stops when we
// wrap and come back there a second time. // wrap and come back there a second time.
set_match_pos = TRUE; *set_match_pos = TRUE;
} }
else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL
&& (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) && (buf = ins_compl_next_buf(buf, *e_cpt)) != curbuf)
{ {
// Scan a buffer, but not the current one. // Scan a buffer, but not the current one.
if (ins_buf->b_ml.ml_mfp != NULL) // loaded buffer if (buf->b_ml.ml_mfp != NULL) // loaded buffer
{ {
compl_started = TRUE; compl_started = TRUE;
first_match_pos.col = last_match_pos.col = 0; first_match_pos->col = last_match_pos->col = 0;
first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1; first_match_pos->lnum = buf->b_ml.ml_line_count + 1;
last_match_pos.lnum = 0; last_match_pos->lnum = 0;
type = 0; compl_type = 0;
} }
else // unloaded buffer, scan like dictionary else // unloaded buffer, scan like dictionary
{ {
found_all = TRUE; found_all = TRUE;
if (ins_buf->b_fname == NULL) if (buf->b_fname == NULL)
continue; {
type = CTRL_X_DICTIONARY; status = INS_COMPL_CPT_CONT;
dict = ins_buf->b_fname; goto done;
}
compl_type = CTRL_X_DICTIONARY;
dict = buf->b_fname;
dict_f = DICT_EXACT; dict_f = DICT_EXACT;
} }
msg_hist_off = TRUE; // reset in msg_trunc_attr() msg_hist_off = TRUE; // reset in msg_trunc_attr()
vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
ins_buf->b_fname == NULL buf->b_fname == NULL
? buf_spname(ins_buf) ? buf_spname(buf)
: ins_buf->b_sfname == NULL : buf->b_sfname == NULL
? ins_buf->b_fname ? buf->b_fname
: ins_buf->b_sfname); : buf->b_sfname);
(void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
} }
else if (*e_cpt == NUL) else if (*e_cpt == NUL)
break; status = INS_COMPL_CPT_END;
else else
{ {
if (ctrl_x_mode_line_or_eval()) if (ctrl_x_mode_line_or_eval())
type = -1; compl_type = -1;
else if (*e_cpt == 'k' || *e_cpt == 's') else if (*e_cpt == 'k' || *e_cpt == 's')
{ {
if (*e_cpt == 'k') if (*e_cpt == 'k')
type = CTRL_X_DICTIONARY; compl_type = CTRL_X_DICTIONARY;
else else
type = CTRL_X_THESAURUS; compl_type = CTRL_X_THESAURUS;
if (*++e_cpt != ',' && *e_cpt != NUL) if (*++e_cpt != ',' && *e_cpt != NUL)
{ {
dict = e_cpt; dict = e_cpt;
@ -3040,73 +3029,88 @@ ins_compl_get_exp(pos_T *ini)
} }
#ifdef FEAT_FIND_ID #ifdef FEAT_FIND_ID
else if (*e_cpt == 'i') else if (*e_cpt == 'i')
type = CTRL_X_PATH_PATTERNS; compl_type = CTRL_X_PATH_PATTERNS;
else if (*e_cpt == 'd') else if (*e_cpt == 'd')
type = CTRL_X_PATH_DEFINES; compl_type = CTRL_X_PATH_DEFINES;
#endif #endif
else if (*e_cpt == ']' || *e_cpt == 't') else if (*e_cpt == ']' || *e_cpt == 't')
{ {
msg_hist_off = TRUE; // reset in msg_trunc_attr() msg_hist_off = TRUE; // reset in msg_trunc_attr()
type = CTRL_X_TAGS; compl_type = CTRL_X_TAGS;
vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags.")); vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags."));
(void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R)); (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
} }
else else
type = -1; compl_type = -1;
// in any case e_cpt is advanced to the next entry // in any case e_cpt is advanced to the next entry
(void)copy_option_part(&e_cpt, IObuff, IOSIZE, ","); (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ",");
found_all = TRUE; found_all = TRUE;
if (type == -1) if (compl_type == -1)
continue; status = INS_COMPL_CPT_CONT;
}
} }
// If complete() was called then compl_pattern has been reset. The done:
// following won't work then, bail out. *e_cpt_arg = e_cpt;
if (compl_pattern == NULL) *compl_type_arg = compl_type;
break; *found_all_arg = found_all;
*buf_arg = buf;
*dict_arg = dict;
*dict_flag = dict_f;
return status;
}
switch (type)
{
case -1:
break;
#ifdef FEAT_FIND_ID #ifdef FEAT_FIND_ID
case CTRL_X_PATH_PATTERNS: /*
case CTRL_X_PATH_DEFINES: * Get the next set of identifiers or defines matching "compl_pattern" in
* included files.
*/
static void
get_next_include_file_completion(int compl_type)
{
find_pattern_in_path(compl_pattern, compl_direction, find_pattern_in_path(compl_pattern, compl_direction,
(int)STRLEN(compl_pattern), FALSE, FALSE, (int)STRLEN(compl_pattern), FALSE, FALSE,
(type == CTRL_X_PATH_DEFINES (compl_type == CTRL_X_PATH_DEFINES
&& !(compl_cont_status & CONT_SOL)) && !(compl_cont_status & CONT_SOL))
? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND, ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND,
(linenr_T)1, (linenr_T)MAXLNUM); (linenr_T)1, (linenr_T)MAXLNUM);
break; }
#endif #endif
case CTRL_X_DICTIONARY: /*
case CTRL_X_THESAURUS: * Get the next set of words matching "compl_pattern" in dictionary or
* thesaurus files.
*/
static void
get_next_dict_tsr_completion(int compl_type, char_u **dict, int dict_f)
{
#ifdef FEAT_COMPL_FUNC #ifdef FEAT_COMPL_FUNC
if (thesaurus_func_complete(type)) if (thesaurus_func_complete(compl_type))
expand_by_function(type, compl_pattern); expand_by_function(compl_type, compl_pattern);
else else
#endif #endif
ins_compl_dictionaries( ins_compl_dictionaries(
dict != NULL ? dict *dict != NULL ? *dict
: (type == CTRL_X_THESAURUS : (compl_type == CTRL_X_THESAURUS
? (*curbuf->b_p_tsr == NUL ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
? p_tsr : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
: curbuf->b_p_tsr)
: (*curbuf->b_p_dict == NUL
? p_dict
: curbuf->b_p_dict)),
compl_pattern, compl_pattern,
dict != NULL ? dict_f *dict != NULL ? dict_f : 0,
: 0, type == CTRL_X_THESAURUS); compl_type == CTRL_X_THESAURUS);
dict = NULL; *dict = NULL;
break; }
/*
* Get the next set of tag names matching "compl_pattern".
*/
static void
get_next_tag_completion(void)
{
int save_p_ic;
char_u **matches;
int num_matches;
case CTRL_X_TAGS:
// set p_ic according to p_ic, p_scs and pat for find_tags(). // set p_ic according to p_ic, p_scs and pat for find_tags().
save_p_ic = p_ic; save_p_ic = p_ic;
p_ic = ignorecase(compl_pattern); p_ic = ignorecase(compl_pattern);
@ -3121,12 +3125,20 @@ ins_compl_get_exp(pos_T *ini)
ins_compl_add_matches(num_matches, matches, p_ic); ins_compl_add_matches(num_matches, matches, p_ic);
g_tag_at_cursor = FALSE; g_tag_at_cursor = FALSE;
p_ic = save_p_ic; p_ic = save_p_ic;
break; }
/*
* Get the next set of filename matching "compl_pattern".
*/
static void
get_next_filename_completion(void)
{
char_u **matches;
int num_matches;
case CTRL_X_FILES:
if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK)
{ return;
// May change home directory back to "~". // May change home directory back to "~".
tilde_replace(compl_pattern, num_matches, matches); tilde_replace(compl_pattern, num_matches, matches);
@ -3151,34 +3163,65 @@ ins_compl_get_exp(pos_T *ini)
} }
#endif #endif
ins_compl_add_matches(num_matches, matches, p_fic || p_wic); ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
} }
break;
/*
* Get the next set of command-line completions matching "compl_pattern".
*/
static void
get_next_cmdline_completion()
{
char_u **matches;
int num_matches;
case CTRL_X_CMDLINE:
case CTRL_X_CMDLINE_CTRL_X:
if (expand_cmdline(&compl_xp, compl_pattern, if (expand_cmdline(&compl_xp, compl_pattern,
(int)STRLEN(compl_pattern), (int)STRLEN(compl_pattern),
&num_matches, &matches) == EXPAND_OK) &num_matches, &matches) == EXPAND_OK)
ins_compl_add_matches(num_matches, matches, FALSE); ins_compl_add_matches(num_matches, matches, FALSE);
break; }
#ifdef FEAT_COMPL_FUNC /*
case CTRL_X_FUNCTION: * Get the next set of spell suggestions matching "compl_pattern".
case CTRL_X_OMNI: */
expand_by_function(type, compl_pattern); static void
break; get_next_spell_completion(linenr_T lnum UNUSED)
#endif {
case CTRL_X_SPELL:
#ifdef FEAT_SPELL #ifdef FEAT_SPELL
num_matches = expand_spelling(first_match_pos.lnum, char_u **matches;
compl_pattern, &matches); int num_matches;
num_matches = expand_spelling(lnum, compl_pattern, &matches);
if (num_matches > 0) if (num_matches > 0)
ins_compl_add_matches(num_matches, matches, p_ic); ins_compl_add_matches(num_matches, matches, p_ic);
#endif #endif
break; }
/*
* Get the next set of words matching "compl_pattern" for default completion(s)
* (normal ^P/^N and ^X^L).
* Search for "compl_pattern" in the buffer "ins_buf" starting from the
* position "start_pos" in the "compl_direction" direction. If "save_match_pos"
* is TRUE, then set the "first_match_pos" and "last_match_pos".
* Returns OK if a new next match is found, otherwise returns FAIL.
*/
static int
get_next_default_completion(
buf_T *ins_buf, // buffer being scanned
pos_T *start_pos, // search start position
pos_T *cur_match_pos, // current match position
pos_T *prev_match_pos, // previous match position
int *save_match_pos, // set first_match_pos/last_match_pos
pos_T *first_match_pos, // first match position
pos_T *last_match_pos, // last match position
int scan_curbuf) // scan current buffer for completion
{
int found_new_match = FAIL;
int save_p_scs;
int save_p_ws;
int looped_around = FALSE;
char_u *ptr;
int len;
default: // normal ^P/^N and ^X^L
// If 'infercase' is set, don't use 'smartcase' here // If 'infercase' is set, don't use 'smartcase' here
save_p_scs = p_scs; save_p_scs = p_scs;
if (ins_buf->b_p_inf) if (ins_buf->b_p_inf)
@ -3191,7 +3234,7 @@ ins_compl_get_exp(pos_T *ini)
save_p_ws = p_ws; save_p_ws = p_ws;
if (ins_buf != curbuf) if (ins_buf != curbuf)
p_ws = FALSE; p_ws = FALSE;
else if (*e_cpt == '.') else if (scan_curbuf)
p_ws = TRUE; p_ws = TRUE;
looped_around = FALSE; looped_around = FALSE;
for (;;) for (;;)
@ -3204,31 +3247,31 @@ ins_compl_get_exp(pos_T *ini)
// has added a word that was at the beginning of the line // has added a word that was at the beginning of the line
if (ctrl_x_mode_line_or_eval() if (ctrl_x_mode_line_or_eval()
|| (compl_cont_status & CONT_SOL)) || (compl_cont_status & CONT_SOL))
found_new_match = search_for_exact_line(ins_buf, pos, found_new_match = search_for_exact_line(ins_buf, cur_match_pos,
compl_direction, compl_pattern); compl_direction, compl_pattern);
else else
found_new_match = searchit(NULL, ins_buf, pos, NULL, found_new_match = searchit(NULL, ins_buf, cur_match_pos, NULL,
compl_direction, compl_direction,
compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG,
RE_LAST, NULL); RE_LAST, NULL);
--msg_silent; --msg_silent;
if (!compl_started || set_match_pos) if (!compl_started || *save_match_pos)
{ {
// set "compl_started" even on fail // set "compl_started" even on fail
compl_started = TRUE; compl_started = TRUE;
first_match_pos = *pos; *first_match_pos = *cur_match_pos;
last_match_pos = *pos; *last_match_pos = *cur_match_pos;
set_match_pos = FALSE; *save_match_pos = FALSE;
} }
else if (first_match_pos.lnum == last_match_pos.lnum else if (first_match_pos->lnum == last_match_pos->lnum
&& first_match_pos.col == last_match_pos.col) && first_match_pos->col == last_match_pos->col)
{ {
found_new_match = FAIL; found_new_match = FAIL;
} }
else if ((compl_direction == FORWARD) else if ((compl_direction == FORWARD)
&& (prev_pos.lnum > pos->lnum && (prev_match_pos->lnum > cur_match_pos->lnum
|| (prev_pos.lnum == pos->lnum || (prev_match_pos->lnum == cur_match_pos->lnum
&& prev_pos.col >= pos->col))) && prev_match_pos->col >= cur_match_pos->col)))
{ {
if (looped_around) if (looped_around)
found_new_match = FAIL; found_new_match = FAIL;
@ -3236,36 +3279,33 @@ ins_compl_get_exp(pos_T *ini)
looped_around = TRUE; looped_around = TRUE;
} }
else if ((compl_direction != FORWARD) else if ((compl_direction != FORWARD)
&& (prev_pos.lnum < pos->lnum && (prev_match_pos->lnum < cur_match_pos->lnum
|| (prev_pos.lnum == pos->lnum || (prev_match_pos->lnum == cur_match_pos->lnum
&& prev_pos.col <= pos->col))) && prev_match_pos->col <= cur_match_pos->col)))
{ {
if (looped_around) if (looped_around)
found_new_match = FAIL; found_new_match = FAIL;
else else
looped_around = TRUE; looped_around = TRUE;
} }
prev_pos = *pos; *prev_match_pos = *cur_match_pos;
if (found_new_match == FAIL) if (found_new_match == FAIL)
{
if (ins_buf == curbuf)
found_all = TRUE;
break; break;
}
// when ADDING, the text before the cursor matches, skip it // when ADDING, the text before the cursor matches, skip it
if ( (compl_cont_status & CONT_ADDING) && ins_buf == curbuf if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf
&& ini->lnum == pos->lnum && start_pos->lnum == cur_match_pos->lnum
&& ini->col == pos->col) && start_pos->col == cur_match_pos->col)
continue; continue;
ptr = ml_get_buf(ins_buf, pos->lnum, FALSE) + pos->col; ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) +
cur_match_pos->col;
if (ctrl_x_mode_line_or_eval()) if (ctrl_x_mode_line_or_eval())
{ {
if (compl_cont_status & CONT_ADDING) if (compl_cont_status & CONT_ADDING)
{ {
if (pos->lnum >= ins_buf->b_ml.ml_line_count) if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count)
continue; continue;
ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE); ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
if (!p_paste) if (!p_paste)
ptr = skipwhite(ptr); ptr = skipwhite(ptr);
} }
@ -3288,10 +3328,9 @@ ins_compl_get_exp(pos_T *ini)
tmp_ptr = find_word_end(tmp_ptr); tmp_ptr = find_word_end(tmp_ptr);
len = (int)(tmp_ptr - ptr); len = (int)(tmp_ptr - ptr);
if ((compl_cont_status & CONT_ADDING) if ((compl_cont_status & CONT_ADDING) && len == compl_length)
&& len == compl_length)
{ {
if (pos->lnum < ins_buf->b_ml.ml_line_count) if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count)
{ {
// Try next line, if any. the new word will be // Try next line, if any. the new word will be
// "join" as if the normal command "J" was used. // "join" as if the normal command "J" was used.
@ -3299,7 +3338,7 @@ ins_compl_get_exp(pos_T *ini)
// compl_length, so the next STRNCPY always // compl_length, so the next STRNCPY always
// works -- Acevedo // works -- Acevedo
STRNCPY(IObuff, ptr, len); STRNCPY(IObuff, ptr, len);
ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE); ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
tmp_ptr = ptr = skipwhite(ptr); tmp_ptr = ptr = skipwhite(ptr);
// Find start of next word. // Find start of next word.
tmp_ptr = find_word_start(tmp_ptr); tmp_ptr = find_word_start(tmp_ptr);
@ -3344,6 +3383,128 @@ ins_compl_get_exp(pos_T *ini)
} }
p_scs = save_p_scs; p_scs = save_p_scs;
p_ws = save_p_ws; p_ws = save_p_ws;
return found_new_match;
}
/*
* Get the next expansion(s), using "compl_pattern".
* The search starts at position "ini" in curbuf and in the direction
* compl_direction.
* When "compl_started" is FALSE start at that position, otherwise continue
* where we stopped searching before.
* This may return before finding all the matches.
* Return the total number of matches or -1 if still unknown -- Acevedo
*/
static int
ins_compl_get_exp(pos_T *ini)
{
static pos_T first_match_pos;
static pos_T last_match_pos;
static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete'
static int found_all = FALSE; // Found all matches of a
// certain type.
static buf_T *ins_buf = NULL; // buffer being scanned
pos_T *pos;
int i;
int found_new_match;
int type = ctrl_x_mode;
char_u *dict = NULL;
int dict_f = 0;
int set_match_pos;
pos_T prev_pos = {0, 0, 0};
if (!compl_started)
{
FOR_ALL_BUFFERS(ins_buf)
ins_buf->b_scanned = 0;
found_all = FALSE;
ins_buf = curbuf;
e_cpt = (compl_cont_status & CONT_LOCAL)
? (char_u *)"." : curbuf->b_p_cpt;
last_match_pos = first_match_pos = *ini;
}
else if (ins_buf != curbuf && !buf_valid(ins_buf))
ins_buf = curbuf; // In case the buffer was wiped out.
compl_old_match = compl_curr_match; // remember the last current match
pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos;
// For ^N/^P loop over all the flags/windows/buffers in 'complete'.
for (;;)
{
found_new_match = FAIL;
set_match_pos = FALSE;
// For ^N/^P pick a new entry from e_cpt if compl_started is off,
// or if found_all says this entry is done. For ^X^L only use the
// entries from 'complete' that look in loaded buffers.
if ((ctrl_x_mode == CTRL_X_NORMAL
|| ctrl_x_mode_line_or_eval())
&& (!compl_started || found_all))
{
int status = process_next_cpt_value(&e_cpt, &type, &found_all,
&ins_buf, ini, &first_match_pos, &last_match_pos,
&set_match_pos, &dict, &dict_f);
if (status == INS_COMPL_CPT_END)
break;
if (status == INS_COMPL_CPT_CONT)
continue;
}
// If complete() was called then compl_pattern has been reset. The
// following won't work then, bail out.
if (compl_pattern == NULL)
break;
switch (type)
{
case -1:
break;
#ifdef FEAT_FIND_ID
case CTRL_X_PATH_PATTERNS:
case CTRL_X_PATH_DEFINES:
get_next_include_file_completion(type);
break;
#endif
case CTRL_X_DICTIONARY:
case CTRL_X_THESAURUS:
get_next_dict_tsr_completion(type, &dict, dict_f);
break;
case CTRL_X_TAGS:
get_next_tag_completion();
break;
case CTRL_X_FILES:
get_next_filename_completion();
break;
case CTRL_X_CMDLINE:
case CTRL_X_CMDLINE_CTRL_X:
get_next_cmdline_completion();
break;
#ifdef FEAT_COMPL_FUNC
case CTRL_X_FUNCTION:
case CTRL_X_OMNI:
expand_by_function(type, compl_pattern);
break;
#endif
case CTRL_X_SPELL:
get_next_spell_completion(first_match_pos.lnum);
break;
default: // normal ^P/^N and ^X^L
found_new_match = get_next_default_completion(ins_buf, ini, pos,
&prev_pos, &set_match_pos, &first_match_pos,
&last_match_pos, (*e_cpt == '.'));
if (found_new_match == FAIL && ins_buf == curbuf)
found_all = TRUE;
} }
// check if compl_curr_match has changed, (e.g. other type of // check if compl_curr_match has changed, (e.g. other type of
@ -3845,8 +4006,7 @@ ins_compl_use_match(int c)
static int static int
get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col) get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
{ {
if ((compl_cont_status & CONT_SOL) if ((compl_cont_status & CONT_SOL) || ctrl_x_mode == CTRL_X_PATH_DEFINES)
|| ctrl_x_mode == CTRL_X_PATH_DEFINES)
{ {
if (!(compl_cont_status & CONT_ADDING)) if (!(compl_cont_status & CONT_ADDING))
{ {

View File

@ -47,7 +47,7 @@ func Test_ins_complete()
exe "normal o\<C-X>\<C-P>\<C-P>\<C-X>\<C-X>\<C-N>\<C-X>\<C-N>\<C-N>" exe "normal o\<C-X>\<C-P>\<C-P>\<C-X>\<C-X>\<C-N>\<C-X>\<C-N>\<C-N>"
call assert_equal('run1 run2', getline('.')) call assert_equal('run1 run2', getline('.'))
set cpt=.,w,i set cpt=.,\ ,w,i
" i-add-expands and switches to local " i-add-expands and switches to local
exe "normal OM\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-X>\<C-X>\<C-P>" exe "normal OM\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-X>\<C-X>\<C-P>"
call assert_equal("Makefile\tto\trun3", getline('.')) call assert_equal("Makefile\tto\trun3", getline('.'))
@ -748,6 +748,17 @@ func Test_complete_across_line()
close! close!
endfunc endfunc
" Test for completing words with a '.' at the end of a word.
func Test_complete_joinspaces()
new
call setline(1, ['one two.', 'three. four'])
set joinspaces
exe "normal Goon\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>"
call assert_equal("one two. three. four", getline(3))
set joinspaces&
bw!
endfunc
" Test for using CTRL-L to add one character when completing matching " Test for using CTRL-L to add one character when completing matching
func Test_complete_add_onechar() func Test_complete_add_onechar()
new new
@ -768,6 +779,39 @@ func Test_complete_add_onechar()
close! close!
endfunc endfunc
" Test for using CTRL-X CTRL-L to complete whole lines lines
func Test_complete_wholeline()
new
" complete one-line
call setline(1, ['a1', 'a2'])
exe "normal ggoa\<C-X>\<C-L>"
call assert_equal(['a1', 'a1', 'a2'], getline(1, '$'))
" go to the next match (wrapping around the buffer)
exe "normal 2GCa\<C-X>\<C-L>\<C-N>"
call assert_equal(['a1', 'a', 'a2'], getline(1, '$'))
" go to the next match
exe "normal 2GCa\<C-X>\<C-L>\<C-N>\<C-N>"
call assert_equal(['a1', 'a2', 'a2'], getline(1, '$'))
exe "normal 2GCa\<C-X>\<C-L>\<C-N>\<C-N>\<C-N>"
call assert_equal(['a1', 'a1', 'a2'], getline(1, '$'))
" repeat the test using CTRL-L
" go to the next match (wrapping around the buffer)
exe "normal 2GCa\<C-X>\<C-L>\<C-L>"
call assert_equal(['a1', 'a2', 'a2'], getline(1, '$'))
" go to the next match
exe "normal 2GCa\<C-X>\<C-L>\<C-L>\<C-L>"
call assert_equal(['a1', 'a', 'a2'], getline(1, '$'))
exe "normal 2GCa\<C-X>\<C-L>\<C-L>\<C-L>\<C-L>"
call assert_equal(['a1', 'a1', 'a2'], getline(1, '$'))
%d
" use CTRL-X CTRL-L to add one more line
call setline(1, ['a1', 'b1'])
setlocal complete=.
exe "normal ggOa\<C-X>\<C-L>\<C-X>\<C-L>\<C-X>\<C-L>"
call assert_equal(['a1', 'b1', '', 'a1', 'b1'], getline(1, '$'))
bw!
endfunc
" Test insert completion with 'cindent' (adjust the indent) " Test insert completion with 'cindent' (adjust the indent)
func Test_complete_with_cindent() func Test_complete_with_cindent()
new new

View File

@ -749,6 +749,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
3937,
/**/ /**/
3936, 3936,
/**/ /**/