summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2021-12-29 17:38:46 +0000
committerBram Moolenaar <Bram@vim.org>2021-12-29 17:38:46 +0000
commitedc6f103907a004b9e2265e232dc8be8bc594601 (patch)
tree75106955f3c054d14125f45ee8afca013bc5e619
parentccc1644f95e7833c23fa0d440e42293c1622fdcb (diff)
downloadvim-git-edc6f103907a004b9e2265e232dc8be8bc594601.tar.gz
patch 8.2.3937: Insert mode completion function is too longv8.2.3937
Problem: Insert mode completion function is too long. Solution: Refactor into multiple functions. (Yegappan Lakshmanan, closes #9423)
-rw-r--r--src/insexpand.c850
-rw-r--r--src/testdir/test_ins_complete.vim46
-rw-r--r--src/version.c2
3 files changed, 552 insertions, 346 deletions
diff --git a/src/insexpand.c b/src/insexpand.c
index 9fb7fb726..c5478920d 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -2901,6 +2901,493 @@ thesaurus_func_complete(int type UNUSED)
}
/*
+ * 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,21 +3407,13 @@ ins_compl_get_exp(pos_T *ini)
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)
{
@@ -2965,102 +3444,14 @@ ins_compl_get_exp(pos_T *ini)
|| 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;
+ 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);
- // 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)
+ if (status == INS_COMPL_CPT_END)
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 (status == INS_COMPL_CPT_CONT)
+ continue;
}
// If complete() was called then compl_pattern has been reset. The
@@ -3075,91 +3466,26 @@ ins_compl_get_exp(pos_T *ini)
#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);
+ get_next_include_file_completion(type);
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;
+ get_next_dict_tsr_completion(type, &dict, dict_f);
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;
+ get_next_tag_completion();
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);
- }
+ get_next_filename_completion();
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);
+ get_next_cmdline_completion();
break;
#ifdef FEAT_COMPL_FUNC
@@ -3170,180 +3496,15 @@ ins_compl_get_exp(pos_T *ini)
#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
+ get_next_spell_completion(first_match_pos.lnum);
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;
+ 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,8 +4006,7 @@ ins_compl_use_match(int c)
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_SOL) || ctrl_x_mode == CTRL_X_PATH_DEFINES)
{
if (!(compl_cont_status & CONT_ADDING))
{
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index f775c1c45..638a774d3 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -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>"
call assert_equal('run1 run2', getline('.'))
- set cpt=.,w,i
+ 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,6 +748,17 @@ func Test_complete_across_line()
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,6 +779,39 @@ func Test_complete_add_onechar()
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
diff --git a/src/version.c b/src/version.c
index 8aebc2c80..68ce1c594 100644
--- a/src/version.c
+++ b/src/version.c
@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3937,
+/**/
3936,
/**/
3935,