Patch 8.2.3937
Problem: Insert mode completion function is too long.
Solution: Refactor into multiple functions. (Yegappan Lakshmanan,
closes #9423)
Files: src/insexpand.c, src/testdir/test_ins_complete.vim
*** ../vim-8.2.3936/src/insexpand.c 2021-12-27 19:28:34.005419704 +0000
--- src/insexpand.c 2021-12-29 17:35:37.517730719 +0000
***************
*** 2901,2906 ****
--- 2901,3393 ----
}
/*
+ * Return value of process_next_cpt_value()
+ */
+ enum
+ {
+ INS_COMPL_CPT_OK = 1,
+ INS_COMPL_CPT_CONT,
+ 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
+ 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)
+ {
+ char_u *e_cpt = *e_cpt_arg;
+ int compl_type = -1;
+ int status = INS_COMPL_CPT_OK;
+ buf_T *buf = *buf_arg;
+ int found_all = FALSE;
+ char_u *dict = NULL;
+ int dict_f = 0;
+
+ while (*e_cpt == ',' || *e_cpt == ' ')
+ e_cpt++;
+
+ if (*e_cpt == '.' && !curbuf->b_scanned)
+ {
+ buf = curbuf;
+ *first_match_pos = *start_match_pos;
+ // Move the cursor back one character so that ^N can match the
+ // word immediately after the cursor.
+ if (ctrl_x_mode == CTRL_X_NORMAL && dec(first_match_pos) < 0)
+ {
+ // Move the cursor to after the last character in the
+ // buffer, so that word at start of buffer is found
+ // correctly.
+ first_match_pos->lnum = buf->b_ml.ml_line_count;
+ first_match_pos->col =
+ (colnr_T)STRLEN(ml_get(first_match_pos->lnum));
+ }
+ *last_match_pos = *first_match_pos;
+ compl_type = 0;
+
+ // Remember the first match so that the loop stops when we
+ // wrap and come back there a second time.
+ *set_match_pos = TRUE;
+ }
+ else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL
+ && (buf = ins_compl_next_buf(buf, *e_cpt)) != curbuf)
+ {
+ // Scan a buffer, but not the current one.
+ if (buf->b_ml.ml_mfp != NULL) // loaded buffer
+ {
+ compl_started = TRUE;
+ first_match_pos->col = last_match_pos->col = 0;
+ first_match_pos->lnum = buf->b_ml.ml_line_count + 1;
+ last_match_pos->lnum = 0;
+ compl_type = 0;
+ }
+ else // unloaded buffer, scan like dictionary
+ {
+ found_all = TRUE;
+ if (buf->b_fname == NULL)
+ {
+ status = INS_COMPL_CPT_CONT;
+ goto done;
+ }
+ compl_type = CTRL_X_DICTIONARY;
+ dict = buf->b_fname;
+ dict_f = DICT_EXACT;
+ }
+ msg_hist_off = TRUE; // reset in msg_trunc_attr()
+ vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
+ buf->b_fname == NULL
+ ? buf_spname(buf)
+ : buf->b_sfname == NULL
+ ? buf->b_fname
+ : buf->b_sfname);
+ (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
+ }
+ else if (*e_cpt == NUL)
+ status = INS_COMPL_CPT_END;
+ else
+ {
+ if (ctrl_x_mode_line_or_eval())
+ compl_type = -1;
+ else if (*e_cpt == 'k' || *e_cpt == 's')
+ {
+ if (*e_cpt == 'k')
+ compl_type = CTRL_X_DICTIONARY;
+ else
+ compl_type = CTRL_X_THESAURUS;
+ if (*++e_cpt != ',' && *e_cpt != NUL)
+ {
+ dict = e_cpt;
+ dict_f = DICT_FIRST;
+ }
+ }
+ #ifdef FEAT_FIND_ID
+ else if (*e_cpt == 'i')
+ compl_type = CTRL_X_PATH_PATTERNS;
+ else if (*e_cpt == 'd')
+ compl_type = CTRL_X_PATH_DEFINES;
+ #endif
+ else if (*e_cpt == ']' || *e_cpt == 't')
+ {
+ msg_hist_off = TRUE; // reset in msg_trunc_attr()
+ compl_type = CTRL_X_TAGS;
+ vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags."));
+ (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
+ }
+ else
+ compl_type = -1;
+
+ // in any case e_cpt is advanced to the next entry
+ (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ",");
+
+ found_all = TRUE;
+ if (compl_type == -1)
+ status = INS_COMPL_CPT_CONT;
+ }
+
+ done:
+ *e_cpt_arg = e_cpt;
+ *compl_type_arg = compl_type;
+ *found_all_arg = found_all;
+ *buf_arg = buf;
+ *dict_arg = dict;
+ *dict_flag = dict_f;
+ return status;
+ }
+
+ #ifdef FEAT_FIND_ID
+ /*
+ * 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,
+ (int)STRLEN(compl_pattern), FALSE, FALSE,
+ (compl_type == CTRL_X_PATH_DEFINES
+ && !(compl_cont_status & CONT_SOL))
+ ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND,
+ (linenr_T)1, (linenr_T)MAXLNUM);
+ }
+ #endif
+
+ /*
+ * 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
+ if (thesaurus_func_complete(compl_type))
+ expand_by_function(compl_type, compl_pattern);
+ else
+ #endif
+ ins_compl_dictionaries(
+ *dict != NULL ? *dict
+ : (compl_type == CTRL_X_THESAURUS
+ ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
+ : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
+ compl_pattern,
+ *dict != NULL ? dict_f : 0,
+ compl_type == CTRL_X_THESAURUS);
+ *dict = NULL;
+ }
+
+ /*
+ * 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;
+
+ // set p_ic according to p_ic, p_scs and pat for find_tags().
+ save_p_ic = p_ic;
+ p_ic = ignorecase(compl_pattern);
+
+ // Find up to TAG_MANY matches. Avoids that an enormous number
+ // of matches is found when compl_pattern is empty
+ g_tag_at_cursor = TRUE;
+ if (find_tags(compl_pattern, &num_matches, &matches,
+ TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
+ | (ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0),
+ TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0)
+ ins_compl_add_matches(num_matches, matches, p_ic);
+ g_tag_at_cursor = FALSE;
+ p_ic = save_p_ic;
+ }
+
+ /*
+ * Get the next set of filename matching "compl_pattern".
+ */
+ static void
+ get_next_filename_completion(void)
+ {
+ char_u **matches;
+ int num_matches;
+
+ if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
+ EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK)
+ return;
+
+ // May change home directory back to "~".
+ tilde_replace(compl_pattern, num_matches, matches);
+ #ifdef BACKSLASH_IN_FILENAME
+ if (curbuf->b_p_csl[0] != NUL)
+ {
+ int i;
+
+ for (i = 0; i < num_matches; ++i)
+ {
+ char_u *ptr = matches[i];
+
+ while (*ptr != NUL)
+ {
+ if (curbuf->b_p_csl[0] == 's' && *ptr == '\\')
+ *ptr = '/';
+ else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/')
+ *ptr = '\\';
+ ptr += (*mb_ptr2len)(ptr);
+ }
+ }
+ }
+ #endif
+ ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
+ }
+
+ /*
+ * Get the next set of command-line completions matching "compl_pattern".
+ */
+ static void
+ get_next_cmdline_completion()
+ {
+ char_u **matches;
+ int num_matches;
+
+ if (expand_cmdline(&compl_xp, compl_pattern,
+ (int)STRLEN(compl_pattern),
+ &num_matches, &matches) == EXPAND_OK)
+ ins_compl_add_matches(num_matches, matches, FALSE);
+ }
+
+ /*
+ * Get the next set of spell suggestions matching "compl_pattern".
+ */
+ static void
+ get_next_spell_completion(linenr_T lnum UNUSED)
+ {
+ #ifdef FEAT_SPELL
+ char_u **matches;
+ int num_matches;
+
+ num_matches = expand_spelling(lnum, compl_pattern, &matches);
+ if (num_matches > 0)
+ ins_compl_add_matches(num_matches, matches, p_ic);
+ #endif
+ }
+
+ /*
+ * 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;
+
+ // If 'infercase' is set, don't use 'smartcase' here
+ save_p_scs = p_scs;
+ if (ins_buf->b_p_inf)
+ p_scs = FALSE;
+
+ // Buffers other than curbuf are scanned from the beginning or the
+ // end but never from the middle, thus setting nowrapscan in this
+ // buffer is a good idea, on the other hand, we always set
+ // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
+ save_p_ws = p_ws;
+ if (ins_buf != curbuf)
+ p_ws = FALSE;
+ else if (scan_curbuf)
+ p_ws = TRUE;
+ looped_around = FALSE;
+ for (;;)
+ {
+ int cont_s_ipos = FALSE;
+
+ ++msg_silent; // Don't want messages for wrapscan.
+
+ // ctrl_x_mode_line_or_eval() || word-wise search that
+ // has added a word that was at the beginning of the line
+ if (ctrl_x_mode_line_or_eval()
+ || (compl_cont_status & CONT_SOL))
+ found_new_match = search_for_exact_line(ins_buf, cur_match_pos,
+ compl_direction, compl_pattern);
+ else
+ found_new_match = searchit(NULL, ins_buf, cur_match_pos, NULL,
+ compl_direction,
+ compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG,
+ RE_LAST, NULL);
+ --msg_silent;
+ if (!compl_started || *save_match_pos)
+ {
+ // set "compl_started" even on fail
+ compl_started = TRUE;
+ *first_match_pos = *cur_match_pos;
+ *last_match_pos = *cur_match_pos;
+ *save_match_pos = FALSE;
+ }
+ else if (first_match_pos->lnum == last_match_pos->lnum
+ && first_match_pos->col == last_match_pos->col)
+ {
+ found_new_match = FAIL;
+ }
+ else if ((compl_direction == FORWARD)
+ && (prev_match_pos->lnum > cur_match_pos->lnum
+ || (prev_match_pos->lnum == cur_match_pos->lnum
+ && prev_match_pos->col >= cur_match_pos->col)))
+ {
+ if (looped_around)
+ found_new_match = FAIL;
+ else
+ looped_around = TRUE;
+ }
+ else if ((compl_direction != FORWARD)
+ && (prev_match_pos->lnum < cur_match_pos->lnum
+ || (prev_match_pos->lnum == cur_match_pos->lnum
+ && prev_match_pos->col <= cur_match_pos->col)))
+ {
+ if (looped_around)
+ found_new_match = FAIL;
+ else
+ looped_around = TRUE;
+ }
+ *prev_match_pos = *cur_match_pos;
+ if (found_new_match == FAIL)
+ break;
+
+ // when ADDING, the text before the cursor matches, skip it
+ if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf
+ && start_pos->lnum == cur_match_pos->lnum
+ && start_pos->col == cur_match_pos->col)
+ continue;
+ ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, FALSE) +
+ cur_match_pos->col;
+ if (ctrl_x_mode_line_or_eval())
+ {
+ if (compl_cont_status & CONT_ADDING)
+ {
+ if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count)
+ continue;
+ ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
+ if (!p_paste)
+ ptr = skipwhite(ptr);
+ }
+ len = (int)STRLEN(ptr);
+ }
+ else
+ {
+ char_u *tmp_ptr = ptr;
+
+ if (compl_cont_status & CONT_ADDING)
+ {
+ tmp_ptr += compl_length;
+ // Skip if already inside a word.
+ if (vim_iswordp(tmp_ptr))
+ continue;
+ // Find start of next word.
+ tmp_ptr = find_word_start(tmp_ptr);
+ }
+ // Find end of this word.
+ tmp_ptr = find_word_end(tmp_ptr);
+ len = (int)(tmp_ptr - ptr);
+
+ if ((compl_cont_status & CONT_ADDING) && len == compl_length)
+ {
+ if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count)
+ {
+ // Try next line, if any. the new word will be
+ // "join" as if the normal command "J" was used.
+ // IOSIZE is always greater than
+ // compl_length, so the next STRNCPY always
+ // works -- Acevedo
+ STRNCPY(IObuff, ptr, len);
+ ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, FALSE);
+ tmp_ptr = ptr = skipwhite(ptr);
+ // Find start of next word.
+ tmp_ptr = find_word_start(tmp_ptr);
+ // Find end of next word.
+ tmp_ptr = find_word_end(tmp_ptr);
+ if (tmp_ptr > ptr)
+ {
+ if (*ptr != ')' && IObuff[len - 1] != TAB)
+ {
+ if (IObuff[len - 1] != ' ')
+ IObuff[len++] = ' ';
+ // IObuf =~ "\k.* ", thus len >= 2
+ if (p_js
+ && (IObuff[len - 2] == '.'
+ || (vim_strchr(p_cpo, CPO_JOINSP)
+ == NULL
+ && (IObuff[len - 2] == '?'
+ || IObuff[len - 2] == '!'))))
+ IObuff[len++] = ' ';
+ }
+ // copy as much as possible of the new word
+ if (tmp_ptr - ptr >= IOSIZE - len)
+ tmp_ptr = ptr + IOSIZE - len - 1;
+ STRNCPY(IObuff + len, ptr, tmp_ptr - ptr);
+ len += (int)(tmp_ptr - ptr);
+ cont_s_ipos = TRUE;
+ }
+ IObuff[len] = NUL;
+ ptr = IObuff;
+ }
+ if (len == compl_length)
+ continue;
+ }
+ }
+ if (ins_compl_add_infercase(ptr, len, p_ic,
+ ins_buf == curbuf ? NULL : ins_buf->b_sfname,
+ 0, cont_s_ipos) != NOTDONE)
+ {
+ found_new_match = OK;
+ break;
+ }
+ }
+ p_scs = save_p_scs;
+ 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.
***************
*** 2920,2940 ****
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;
int dict_f = 0;
int set_match_pos;
pos_T prev_pos = {0, 0, 0};
- int looped_around = FALSE;
if (!compl_started)
{
--- 3407,3419 ----
***************
*** 2965,3066 ****
|| ctrl_x_mode_line_or_eval())
&& (!compl_started || found_all))
{
! found_all = FALSE;
! while (*e_cpt == ',' || *e_cpt == ' ')
! e_cpt++;
! if (*e_cpt == '.' && !curbuf->b_scanned)
! {
! ins_buf = curbuf;
! first_match_pos = *ini;
! // Move the cursor back one character so that ^N can match the
! // word immediately after the cursor.
! if (ctrl_x_mode == CTRL_X_NORMAL && dec(&first_match_pos) < 0)
! {
! // Move the cursor to after the last character in the
! // buffer, so that word at start of buffer is found
! // correctly.
! first_match_pos.lnum = ins_buf->b_ml.ml_line_count;
! first_match_pos.col =
! (colnr_T)STRLEN(ml_get(first_match_pos.lnum));
! }
! last_match_pos = first_match_pos;
! type = 0;
! // Remember the first match so that the loop stops when we
! // wrap and come back there a second time.
! set_match_pos = TRUE;
! }
! else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL
! && (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf)
! {
! // Scan a buffer, but not the current one.
! if (ins_buf->b_ml.ml_mfp != NULL) // loaded buffer
! {
! compl_started = TRUE;
! first_match_pos.col = last_match_pos.col = 0;
! first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1;
! last_match_pos.lnum = 0;
! type = 0;
! }
! else // unloaded buffer, scan like dictionary
! {
! found_all = TRUE;
! if (ins_buf->b_fname == NULL)
! continue;
! type = CTRL_X_DICTIONARY;
! dict = ins_buf->b_fname;
! dict_f = DICT_EXACT;
! }
! msg_hist_off = TRUE; // reset in msg_trunc_attr()
! vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
! ins_buf->b_fname == NULL
! ? buf_spname(ins_buf)
! : ins_buf->b_sfname == NULL
! ? ins_buf->b_fname
! : ins_buf->b_sfname);
! (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
! }
! else if (*e_cpt == NUL)
break;
! else
! {
! if (ctrl_x_mode_line_or_eval())
! type = -1;
! else if (*e_cpt == 'k' || *e_cpt == 's')
! {
! if (*e_cpt == 'k')
! type = CTRL_X_DICTIONARY;
! else
! type = CTRL_X_THESAURUS;
! if (*++e_cpt != ',' && *e_cpt != NUL)
! {
! dict = e_cpt;
! dict_f = DICT_FIRST;
! }
! }
! #ifdef FEAT_FIND_ID
! else if (*e_cpt == 'i')
! type = CTRL_X_PATH_PATTERNS;
! else if (*e_cpt == 'd')
! type = CTRL_X_PATH_DEFINES;
! #endif
! else if (*e_cpt == ']' || *e_cpt == 't')
! {
! msg_hist_off = TRUE; // reset in msg_trunc_attr()
! type = CTRL_X_TAGS;
! vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags."));
! (void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
! }
! else
! type = -1;
!
! // in any case e_cpt is advanced to the next entry
! (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ",");
!
! found_all = TRUE;
! if (type == -1)
! continue;
! }
}
// If complete() was called then compl_pattern has been reset. The
--- 3444,3457 ----
|| 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
***************
*** 3075,3165 ****
#ifdef FEAT_FIND_ID
case CTRL_X_PATH_PATTERNS:
case CTRL_X_PATH_DEFINES:
! find_pattern_in_path(compl_pattern, compl_direction,
! (int)STRLEN(compl_pattern), FALSE, FALSE,
! (type == CTRL_X_PATH_DEFINES
! && !(compl_cont_status & CONT_SOL))
! ? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND,
! (linenr_T)1, (linenr_T)MAXLNUM);
break;
#endif
case CTRL_X_DICTIONARY:
case CTRL_X_THESAURUS:
! #ifdef FEAT_COMPL_FUNC
! if (thesaurus_func_complete(type))
! expand_by_function(type, compl_pattern);
! else
! #endif
! ins_compl_dictionaries(
! dict != NULL ? dict
! : (type == CTRL_X_THESAURUS
! ? (*curbuf->b_p_tsr == NUL
! ? p_tsr
! : curbuf->b_p_tsr)
! : (*curbuf->b_p_dict == NUL
! ? p_dict
! : curbuf->b_p_dict)),
! compl_pattern,
! dict != NULL ? dict_f
! : 0, type == CTRL_X_THESAURUS);
! dict = NULL;
break;
case CTRL_X_TAGS:
! // set p_ic according to p_ic, p_scs and pat for find_tags().
! save_p_ic = p_ic;
! p_ic = ignorecase(compl_pattern);
!
! // Find up to TAG_MANY matches. Avoids that an enormous number
! // of matches is found when compl_pattern is empty
! g_tag_at_cursor = TRUE;
! if (find_tags(compl_pattern, &num_matches, &matches,
! TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
! | (ctrl_x_mode != CTRL_X_NORMAL ? TAG_VERBOSE : 0),
! TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0)
! ins_compl_add_matches(num_matches, matches, p_ic);
! g_tag_at_cursor = FALSE;
! p_ic = save_p_ic;
break;
case CTRL_X_FILES:
! if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
! EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK)
! {
!
! // May change home directory back to "~".
! tilde_replace(compl_pattern, num_matches, matches);
! #ifdef BACKSLASH_IN_FILENAME
! if (curbuf->b_p_csl[0] != NUL)
! {
! int i;
!
! for (i = 0; i < num_matches; ++i)
! {
! char_u *ptr = matches[i];
!
! while (*ptr != NUL)
! {
! if (curbuf->b_p_csl[0] == 's' && *ptr == '\\')
! *ptr = '/';
! else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/')
! *ptr = '\\';
! ptr += (*mb_ptr2len)(ptr);
! }
! }
! }
! #endif
! ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
! }
break;
case CTRL_X_CMDLINE:
case CTRL_X_CMDLINE_CTRL_X:
! if (expand_cmdline(&compl_xp, compl_pattern,
! (int)STRLEN(compl_pattern),
! &num_matches, &matches) == EXPAND_OK)
! ins_compl_add_matches(num_matches, matches, FALSE);
break;
#ifdef FEAT_COMPL_FUNC
--- 3466,3491 ----
#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
***************
*** 3170,3349 ****
#endif
case CTRL_X_SPELL:
! #ifdef FEAT_SPELL
! num_matches = expand_spelling(first_match_pos.lnum,
! compl_pattern, &matches);
! if (num_matches > 0)
! ins_compl_add_matches(num_matches, matches, p_ic);
! #endif
break;
default: // normal ^P/^N and ^X^L
! // If 'infercase' is set, don't use 'smartcase' here
! save_p_scs = p_scs;
! if (ins_buf->b_p_inf)
! p_scs = FALSE;
!
! // Buffers other than curbuf are scanned from the beginning or the
! // end but never from the middle, thus setting nowrapscan in this
! // buffer is a good idea, on the other hand, we always set
! // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
! save_p_ws = p_ws;
! if (ins_buf != curbuf)
! p_ws = FALSE;
! else if (*e_cpt == '.')
! p_ws = TRUE;
! looped_around = FALSE;
! for (;;)
! {
! int cont_s_ipos = FALSE;
!
! ++msg_silent; // Don't want messages for wrapscan.
!
! // ctrl_x_mode_line_or_eval() || word-wise search that
! // has added a word that was at the beginning of the line
! if (ctrl_x_mode_line_or_eval()
! || (compl_cont_status & CONT_SOL))
! found_new_match = search_for_exact_line(ins_buf, pos,
! compl_direction, compl_pattern);
! else
! found_new_match = searchit(NULL, ins_buf, pos, NULL,
! compl_direction,
! compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG,
! RE_LAST, NULL);
! --msg_silent;
! if (!compl_started || set_match_pos)
! {
! // set "compl_started" even on fail
! compl_started = TRUE;
! first_match_pos = *pos;
! last_match_pos = *pos;
! set_match_pos = FALSE;
! }
! else if (first_match_pos.lnum == last_match_pos.lnum
! && first_match_pos.col == last_match_pos.col)
! {
! found_new_match = FAIL;
! }
! else if ((compl_direction == FORWARD)
! && (prev_pos.lnum > pos->lnum
! || (prev_pos.lnum == pos->lnum
! && prev_pos.col >= pos->col)))
! {
! if (looped_around)
! found_new_match = FAIL;
! else
! looped_around = TRUE;
! }
! else if ((compl_direction != FORWARD)
! && (prev_pos.lnum < pos->lnum
! || (prev_pos.lnum == pos->lnum
! && prev_pos.col <= pos->col)))
! {
! if (looped_around)
! found_new_match = FAIL;
! else
! looped_around = TRUE;
! }
! prev_pos = *pos;
! if (found_new_match == FAIL)
! {
! if (ins_buf == curbuf)
! found_all = TRUE;
! break;
! }
!
! // when ADDING, the text before the cursor matches, skip it
! if ( (compl_cont_status & CONT_ADDING) && ins_buf == curbuf
! && ini->lnum == pos->lnum
! && ini->col == pos->col)
! continue;
! ptr = ml_get_buf(ins_buf, pos->lnum, FALSE) + pos->col;
! if (ctrl_x_mode_line_or_eval())
! {
! if (compl_cont_status & CONT_ADDING)
! {
! if (pos->lnum >= ins_buf->b_ml.ml_line_count)
! continue;
! ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE);
! if (!p_paste)
! ptr = skipwhite(ptr);
! }
! len = (int)STRLEN(ptr);
! }
! else
! {
! char_u *tmp_ptr = ptr;
!
! if (compl_cont_status & CONT_ADDING)
! {
! tmp_ptr += compl_length;
! // Skip if already inside a word.
! if (vim_iswordp(tmp_ptr))
! continue;
! // Find start of next word.
! tmp_ptr = find_word_start(tmp_ptr);
! }
! // Find end of this word.
! tmp_ptr = find_word_end(tmp_ptr);
! len = (int)(tmp_ptr - ptr);
!
! if ((compl_cont_status & CONT_ADDING)
! && len == compl_length)
! {
! if (pos->lnum < ins_buf->b_ml.ml_line_count)
! {
! // Try next line, if any. the new word will be
! // "join" as if the normal command "J" was used.
! // IOSIZE is always greater than
! // compl_length, so the next STRNCPY always
! // works -- Acevedo
! STRNCPY(IObuff, ptr, len);
! ptr = ml_get_buf(ins_buf, pos->lnum + 1, FALSE);
! tmp_ptr = ptr = skipwhite(ptr);
! // Find start of next word.
! tmp_ptr = find_word_start(tmp_ptr);
! // Find end of next word.
! tmp_ptr = find_word_end(tmp_ptr);
! if (tmp_ptr > ptr)
! {
! if (*ptr != ')' && IObuff[len - 1] != TAB)
! {
! if (IObuff[len - 1] != ' ')
! IObuff[len++] = ' ';
! // IObuf =~ "\k.* ", thus len >= 2
! if (p_js
! && (IObuff[len - 2] == '.'
! || (vim_strchr(p_cpo, CPO_JOINSP)
! == NULL
! && (IObuff[len - 2] == '?'
! || IObuff[len - 2] == '!'))))
! IObuff[len++] = ' ';
! }
! // copy as much as possible of the new word
! if (tmp_ptr - ptr >= IOSIZE - len)
! tmp_ptr = ptr + IOSIZE - len - 1;
! STRNCPY(IObuff + len, ptr, tmp_ptr - ptr);
! len += (int)(tmp_ptr - ptr);
! cont_s_ipos = TRUE;
! }
! IObuff[len] = NUL;
! ptr = IObuff;
! }
! if (len == compl_length)
! continue;
! }
! }
! if (ins_compl_add_infercase(ptr, len, p_ic,
! ins_buf == curbuf ? NULL : ins_buf->b_sfname,
! 0, cont_s_ipos) != NOTDONE)
! {
! found_new_match = OK;
! break;
! }
! }
! p_scs = save_p_scs;
! p_ws = save_p_ws;
}
// check if compl_curr_match has changed, (e.g. other type of
--- 3496,3510 ----
#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
***************
*** 3845,3852 ****
static int
get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
{
! if ((compl_cont_status & CONT_SOL)
! || ctrl_x_mode == CTRL_X_PATH_DEFINES)
{
if (!(compl_cont_status & CONT_ADDING))
{
--- 4006,4012 ----
static int
get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
{
! if ((compl_cont_status & CONT_SOL) || ctrl_x_mode == CTRL_X_PATH_DEFINES)
{
if (!(compl_cont_status & CONT_ADDING))
{
*** ../vim-8.2.3936/src/testdir/test_ins_complete.vim 2021-12-27 19:28:34.005419704 +0000
--- src/testdir/test_ins_complete.vim 2021-12-29 17:32:09.761953444 +0000
***************
*** 47,53 ****
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('.'))
! set cpt=.,w,i
" 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>"
call assert_equal("Makefile\tto\trun3", getline('.'))
--- 47,53 ----
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('.'))
! set cpt=.,\ ,w,i
" 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>"
call assert_equal("Makefile\tto\trun3", getline('.'))
***************
*** 748,753 ****
--- 748,764 ----
close!
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
func Test_complete_add_onechar()
new
***************
*** 768,773 ****
--- 779,817 ----
close!
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)
func Test_complete_with_cindent()
new
*** ../vim-8.2.3936/src/version.c 2021-12-29 16:44:44.709780642 +0000
--- src/version.c 2021-12-29 17:38:20.885542880 +0000
***************
*** 751,752 ****
--- 751,754 ----
{ /* Add new patch number below this line */
+ /**/
+ 3937,
/**/
--
hundred-and-one symptoms of being an internet addict:
143. You dream in pallettes of 216 websafe colors.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///