aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/edit.c2
-rw-r--r--src/nvim/insexpand.c1831
-rw-r--r--src/nvim/testdir/test_ins_complete.vim67
3 files changed, 1104 insertions, 796 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index a06cef82a2..545f3c0d9a 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -5654,7 +5654,7 @@ static char_u *do_insert_char_pre(int c)
return res;
}
-bool can_cindent_get(void)
+bool get_can_cindent(void)
{
return can_cindent;
}
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 938189f343..d9f0c3419c 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -1650,6 +1650,241 @@ void ins_compl_addfrommatch(void)
ins_compl_addleader(c);
}
+/// Set the CTRL-X completion mode based on the key 'c' typed after a CTRL-X.
+/// Uses the global variables: ctrl_x_mode, edit_submode, edit_submode_pre,
+/// compl_cont_mode and compl_cont_status.
+///
+/// @return true when the character is not to be inserted.
+static bool set_ctrl_x_mode(const int c)
+{
+ bool retval = false;
+
+ switch (c) {
+ case Ctrl_E:
+ case Ctrl_Y:
+ // scroll the window one line up or down
+ ctrl_x_mode = CTRL_X_SCROLL;
+ if (!(State & REPLACE_FLAG)) {
+ edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
+ } else {
+ edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)");
+ }
+ edit_submode_pre = NULL;
+ showmode();
+ break;
+ case Ctrl_L:
+ // complete whole line
+ ctrl_x_mode = CTRL_X_WHOLE_LINE;
+ break;
+ case Ctrl_F:
+ // complete filenames
+ ctrl_x_mode = CTRL_X_FILES;
+ break;
+ case Ctrl_K:
+ // complete words from a dictionary
+ ctrl_x_mode = CTRL_X_DICTIONARY;
+ break;
+ case Ctrl_R:
+ // Register insertion without exiting CTRL-X mode
+ // Simply allow ^R to happen without affecting ^X mode
+ break;
+ case Ctrl_T:
+ // complete words from a thesaurus
+ ctrl_x_mode = CTRL_X_THESAURUS;
+ break;
+ case Ctrl_U:
+ // user defined completion
+ ctrl_x_mode = CTRL_X_FUNCTION;
+ break;
+ case Ctrl_O:
+ // omni completion
+ ctrl_x_mode = CTRL_X_OMNI;
+ break;
+ case 's':
+ case Ctrl_S:
+ // complete spelling suggestions
+ ctrl_x_mode = CTRL_X_SPELL;
+ emsg_off++; // Avoid getting the E756 error twice.
+ spell_back_to_badword();
+ emsg_off--;
+ break;
+ case Ctrl_RSB:
+ // complete tag names
+ ctrl_x_mode = CTRL_X_TAGS;
+ break;
+ case Ctrl_I:
+ case K_S_TAB:
+ // complete keywords from included files
+ ctrl_x_mode = CTRL_X_PATH_PATTERNS;
+ break;
+ case Ctrl_D:
+ // complete definitions from included files
+ ctrl_x_mode = CTRL_X_PATH_DEFINES;
+ break;
+ case Ctrl_V:
+ case Ctrl_Q:
+ // complete vim commands
+ ctrl_x_mode = CTRL_X_CMDLINE;
+ break;
+ case Ctrl_Z:
+ // stop completion
+ ctrl_x_mode = CTRL_X_NORMAL;
+ edit_submode = NULL;
+ showmode();
+ retval = true;
+ break;
+ case Ctrl_P:
+ case Ctrl_N:
+ // ^X^P means LOCAL expansion if nothing interrupted (eg we
+ // just started ^X mode, or there were enough ^X's to cancel
+ // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
+ // do normal expansion when interrupting a different mode (say
+ // ^X^F^X^P or ^P^X^X^P, see below)
+ // nothing changes if interrupting mode 0, (eg, the flag
+ // doesn't change when going to ADDING mode -- Acevedo
+ if (!(compl_cont_status & CONT_INTRPT)) {
+ compl_cont_status |= CONT_LOCAL;
+ } else if (compl_cont_mode != 0) {
+ compl_cont_status &= ~CONT_LOCAL;
+ }
+ FALLTHROUGH;
+ default:
+ // If we have typed at least 2 ^X's... for modes != 0, we set
+ // compl_cont_status = 0 (eg, as if we had just started ^X
+ // mode).
+ // For mode 0, we set "compl_cont_mode" to an impossible
+ // value, in both cases ^X^X can be used to restart the same
+ // mode (avoiding ADDING mode).
+ // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
+ // 'complete' and local ^P expansions respectively.
+ // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
+ // mode -- Acevedo
+ if (c == Ctrl_X) {
+ if (compl_cont_mode != 0) {
+ compl_cont_status = 0;
+ } else {
+ compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
+ }
+ }
+ ctrl_x_mode = CTRL_X_NORMAL;
+ edit_submode = NULL;
+ showmode();
+ break;
+ }
+
+ return retval;
+}
+
+/// Stop insert completion mode
+static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
+{
+ // Get here when we have finished typing a sequence of ^N and
+ // ^P or other completion characters in CTRL-X mode. Free up
+ // memory that was used, and make sure we can redo the insert.
+ if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) {
+ // If any of the original typed text has been changed, eg when
+ // ignorecase is set, we must add back-spaces to the redo
+ // buffer. We add as few as necessary to delete just the part
+ // of the original text that has changed.
+ // When using the longest match, edited the match or used
+ // CTRL-E then don't use the current match.
+ char_u *ptr;
+ if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
+ ptr = compl_curr_match->cp_str;
+ } else {
+ ptr = NULL;
+ }
+ ins_compl_fixRedoBufForLeader(ptr);
+ }
+
+ bool want_cindent = (get_can_cindent() && cindent_on());
+
+ // When completing whole lines: fix indent for 'cindent'.
+ // Otherwise, break line if it's too long.
+ if (compl_cont_mode == CTRL_X_WHOLE_LINE) {
+ // re-indent the current line
+ if (want_cindent) {
+ do_c_expr_indent();
+ want_cindent = false; // don't do it again
+ }
+ } else {
+ const int prev_col = curwin->w_cursor.col;
+
+ // put the cursor on the last char, for 'tw' formatting
+ if (prev_col > 0) {
+ dec_cursor();
+ }
+
+ // only format when something was inserted
+ if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) {
+ insertchar(NUL, 0, -1);
+ }
+
+ if (prev_col > 0
+ && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) {
+ inc_cursor();
+ }
+ }
+
+ // If the popup menu is displayed pressing CTRL-Y means accepting
+ // the selection without inserting anything. When
+ // compl_enter_selects is set the Enter key does the same.
+ if ((c == Ctrl_Y || (compl_enter_selects
+ && (c == CAR || c == K_KENTER || c == NL)))
+ && pum_visible()) {
+ retval = true;
+ }
+
+ // CTRL-E means completion is Ended, go back to the typed text.
+ // but only do this, if the Popup is still visible
+ if (c == Ctrl_E) {
+ ins_compl_delete();
+ if (compl_leader != NULL) {
+ ins_bytes(compl_leader + get_compl_len());
+ } else if (compl_first_match != NULL) {
+ ins_bytes(compl_orig_text + get_compl_len());
+ }
+ retval = true;
+ }
+
+ auto_format(false, true);
+
+ // Trigger the CompleteDonePre event to give scripts a chance to
+ // act upon the completion before clearing the info, and restore
+ // ctrl_x_mode, so that complete_info() can be used.
+ ctrl_x_mode = prev_mode;
+ ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
+
+ ins_compl_free();
+ compl_started = false;
+ compl_matches = 0;
+ if (!shortmess(SHM_COMPLETIONMENU)) {
+ msg_clr_cmdline(); // necessary for "noshowmode"
+ }
+ ctrl_x_mode = CTRL_X_NORMAL;
+ compl_enter_selects = false;
+ if (edit_submode != NULL) {
+ edit_submode = NULL;
+ showmode();
+ }
+
+ if (c == Ctrl_C && cmdwin_type != 0) {
+ // Avoid the popup menu remains displayed when leaving the
+ // command line window.
+ update_screen(0);
+ }
+
+ // Indent now if a key was typed that is in 'cinkeys'.
+ if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) {
+ do_c_expr_indent();
+ }
+ // Trigger the CompleteDone event to give scripts a chance to act
+ // upon the end of completion.
+ ins_apply_autocmds(EVENT_COMPLETEDONE);
+
+ return retval;
+}
+
/// Prepare for Insert mode completion, or stop it.
/// Called just after typing a character in Insert mode.
///
@@ -1658,7 +1893,6 @@ void ins_compl_addfrommatch(void)
/// @return true when the character is not to be inserted;
bool ins_compl_prep(int c)
{
- char_u *ptr;
bool retval = false;
const int prev_mode = ctrl_x_mode;
@@ -1705,104 +1939,7 @@ bool ins_compl_prep(int c)
if (ctrl_x_mode_not_defined_yet()) {
// We have just typed CTRL-X and aren't quite sure which CTRL-X mode
// it will be yet. Now we decide.
- switch (c) {
- case Ctrl_E:
- case Ctrl_Y:
- ctrl_x_mode = CTRL_X_SCROLL;
- if (!(State & REPLACE_FLAG)) {
- edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
- } else {
- edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)");
- }
- edit_submode_pre = NULL;
- showmode();
- break;
- case Ctrl_L:
- ctrl_x_mode = CTRL_X_WHOLE_LINE;
- break;
- case Ctrl_F:
- ctrl_x_mode = CTRL_X_FILES;
- break;
- case Ctrl_K:
- ctrl_x_mode = CTRL_X_DICTIONARY;
- break;
- case Ctrl_R:
- // Simply allow ^R to happen without affecting ^X mode
- break;
- case Ctrl_T:
- ctrl_x_mode = CTRL_X_THESAURUS;
- break;
- case Ctrl_U:
- ctrl_x_mode = CTRL_X_FUNCTION;
- break;
- case Ctrl_O:
- ctrl_x_mode = CTRL_X_OMNI;
- break;
- case 's':
- case Ctrl_S:
- ctrl_x_mode = CTRL_X_SPELL;
- emsg_off++; // Avoid getting the E756 error twice.
- spell_back_to_badword();
- emsg_off--;
- break;
- case Ctrl_RSB:
- ctrl_x_mode = CTRL_X_TAGS;
- break;
- case Ctrl_I:
- case K_S_TAB:
- ctrl_x_mode = CTRL_X_PATH_PATTERNS;
- break;
- case Ctrl_D:
- ctrl_x_mode = CTRL_X_PATH_DEFINES;
- break;
- case Ctrl_V:
- case Ctrl_Q:
- ctrl_x_mode = CTRL_X_CMDLINE;
- break;
- case Ctrl_Z:
- ctrl_x_mode = CTRL_X_NORMAL;
- edit_submode = NULL;
- showmode();
- retval = true;
- break;
- case Ctrl_P:
- case Ctrl_N:
- // ^X^P means LOCAL expansion if nothing interrupted (eg we
- // just started ^X mode, or there were enough ^X's to cancel
- // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
- // do normal expansion when interrupting a different mode (say
- // ^X^F^X^P or ^P^X^X^P, see below)
- // nothing changes if interrupting mode 0, (eg, the flag
- // doesn't change when going to ADDING mode -- Acevedo
- if (!(compl_cont_status & CONT_INTRPT)) {
- compl_cont_status |= CONT_LOCAL;
- } else if (compl_cont_mode != 0) {
- compl_cont_status &= ~CONT_LOCAL;
- }
- FALLTHROUGH;
- default:
- // If we have typed at least 2 ^X's... for modes != 0, we set
- // compl_cont_status = 0 (eg, as if we had just started ^X
- // mode).
- // For mode 0, we set "compl_cont_mode" to an impossible
- // value, in both cases ^X^X can be used to restart the same
- // mode (avoiding ADDING mode).
- // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
- // 'complete' and local ^P expansions respectively.
- // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
- // mode -- Acevedo
- if (c == Ctrl_X) {
- if (compl_cont_mode != 0) {
- compl_cont_status = 0;
- } else {
- compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
- }
- }
- ctrl_x_mode = CTRL_X_NORMAL;
- edit_submode = NULL;
- showmode();
- break;
- }
+ retval = set_ctrl_x_mode(c);
} else if (ctrl_x_mode_not_default()) {
// We're already in CTRL-X mode, do we stay in it?
if (!vim_is_ctrl_x_key(c)) {
@@ -1827,107 +1964,7 @@ bool ins_compl_prep(int c)
&& c != Ctrl_R
&& !ins_compl_pum_key(c))
|| ctrl_x_mode == CTRL_X_FINISHED) {
- // Get here when we have finished typing a sequence of ^N and
- // ^P or other completion characters in CTRL-X mode. Free up
- // memory that was used, and make sure we can redo the insert.
- if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) {
- // If any of the original typed text has been changed, eg when
- // ignorecase is set, we must add back-spaces to the redo
- // buffer. We add as few as necessary to delete just the part
- // of the original text that has changed.
- // When using the longest match, edited the match or used
- // CTRL-E then don't use the current match.
- if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
- ptr = compl_curr_match->cp_str;
- } else {
- ptr = NULL;
- }
- ins_compl_fixRedoBufForLeader(ptr);
- }
-
- bool want_cindent = (can_cindent_get() && cindent_on());
-
- // When completing whole lines: fix indent for 'cindent'.
- // Otherwise, break line if it's too long.
- if (compl_cont_mode == CTRL_X_WHOLE_LINE) {
- // re-indent the current line
- if (want_cindent) {
- do_c_expr_indent();
- want_cindent = false; // don't do it again
- }
- } else {
- int prev_col = curwin->w_cursor.col;
-
- // put the cursor on the last char, for 'tw' formatting
- if (prev_col > 0) {
- dec_cursor();
- }
-
- if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) {
- insertchar(NUL, 0, -1);
- }
-
- if (prev_col > 0
- && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) {
- inc_cursor();
- }
- }
-
- // If the popup menu is displayed pressing CTRL-Y means accepting
- // the selection without inserting anything. When
- // compl_enter_selects is set the Enter key does the same.
- if ((c == Ctrl_Y || (compl_enter_selects
- && (c == CAR || c == K_KENTER || c == NL)))
- && pum_visible()) {
- retval = true;
- }
-
- // CTRL-E means completion is Ended, go back to the typed text.
- // but only do this, if the Popup is still visible
- if (c == Ctrl_E) {
- ins_compl_delete();
- if (compl_leader != NULL) {
- ins_bytes(compl_leader + get_compl_len());
- } else if (compl_first_match != NULL) {
- ins_bytes(compl_orig_text + get_compl_len());
- }
- retval = true;
- }
-
- auto_format(false, true);
-
- // Trigger the CompleteDonePre event to give scripts a chance to
- // act upon the completion before clearing the info, and restore
- // ctrl_x_mode, so that complete_info() can be used.
- ctrl_x_mode = prev_mode;
- ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
-
- ins_compl_free();
- compl_started = false;
- compl_matches = 0;
- if (!shortmess(SHM_COMPLETIONMENU)) {
- msg_clr_cmdline(); // necessary for "noshowmode"
- }
- ctrl_x_mode = CTRL_X_NORMAL;
- compl_enter_selects = false;
- if (edit_submode != NULL) {
- edit_submode = NULL;
- showmode();
- }
-
- // Avoid the popup menu remains displayed when leaving the
- // command line window.
- if (c == Ctrl_C && cmdwin_type != 0) {
- update_screen(0);
- }
-
- // Indent now if a key was typed that is in 'cinkeys'.
- if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) {
- do_c_expr_indent();
- }
- // Trigger the CompleteDone event to give scripts a chance to act
- // upon the end of completion.
- ins_apply_autocmds(EVENT_COMPLETEDONE);
+ retval = ins_compl_stop(c, prev_mode, retval);
}
} else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) {
// Trigger the CompleteDone event to give scripts a chance to act
@@ -2464,6 +2501,443 @@ static bool thesaurus_func_complete(int type)
&& (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL);
}
+/// 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
+///
+/// @return INS_COMPL_CPT_OK if the next value is processed successfully.
+/// INS_COMPL_CPT_CONT to skip the current value and process the next
+/// option value.
+/// INS_COMPL_CPT_END if all the values in "e_cpt" are processed.
+static int process_next_cpt_value(char **e_cpt_arg, int *compl_type_arg, bool *found_all_arg,
+ buf_T **buf_arg, pos_T *start_match_pos, pos_T *first_match_pos,
+ pos_T *last_match_pos, bool *set_match_pos, char_u **dict_arg,
+ int *dict_flag)
+{
+ char *e_cpt = *e_cpt_arg;
+ int compl_type = -1;
+ int status = INS_COMPL_CPT_OK;
+ buf_T *buf = *buf_arg;
+ bool 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_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("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 = (char_u *)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 = (char_u *)e_cpt;
+ dict_f = DICT_FIRST;
+ }
+ } else if (*e_cpt == 'i') {
+ compl_type = CTRL_X_PATH_PATTERNS;
+ } else if (*e_cpt == 'd') {
+ compl_type = CTRL_X_PATH_DEFINES;
+ } 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, "%s", _("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, (char *)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;
+}
+
+/// 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((char_u *)compl_pattern, compl_direction,
+ STRLEN(compl_pattern), false, false,
+ ((compl_type == CTRL_X_PATH_DEFINES
+ && !(compl_cont_status & CONT_SOL))
+ ? FIND_DEFINE : FIND_ANY),
+ 1L, ACTION_EXPAND, 1, MAXLNUM);
+}
+
+/// 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)
+{
+ if (thesaurus_func_complete(compl_type)) {
+ expand_by_function(compl_type, (char_u *)compl_pattern);
+ } else {
+ 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)),
+ (char_u *)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)
+{
+ // set p_ic according to p_ic, p_scs and pat for find_tags().
+ const int save_p_ic = p_ic;
+ p_ic = ignorecase((char_u *)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;
+ char **matches;
+ int num_matches;
+ if (find_tags((char_u *)compl_pattern, &num_matches, &matches,
+ TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
+ | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
+ TAG_MANY, (char_u *)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 **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((char_u *)compl_pattern, num_matches, matches);
+#ifdef BACKSLASH_IN_FILENAME
+ if (curbuf->b_p_csl[0] != NUL) {
+ for (int 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 += utfc_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(void)
+{
+ char **matches;
+ int num_matches;
+ if (expand_cmdline(&compl_xp, (char_u *)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)
+{
+ char **matches;
+ int num_matches = expand_spelling(lnum, (char_u *)compl_pattern, &matches);
+ if (num_matches > 0) {
+ ins_compl_add_matches(num_matches, matches, p_ic);
+ }
+}
+
+/// Return the next word or line from buffer "ins_buf" at position
+/// "cur_match_pos" for completion. The length of the match is set in "len".
+/// @param ins_buf buffer being scanned
+/// @param cur_match_pos current match position
+/// @param match_len
+/// @param cont_s_ipos next ^X<> will set initial_pos
+static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_pos, int *match_len,
+ bool *cont_s_ipos)
+{
+ *match_len = 0;
+ char_u *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, false) + cur_match_pos->col;
+ int len;
+ 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) {
+ return NULL;
+ }
+ ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false);
+ if (!p_paste) {
+ ptr = (char_u *)skipwhite((char *)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)) {
+ return NULL;
+ }
+ // 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); // NOLINT(runtime/printf)
+ ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false);
+ tmp_ptr = ptr = (char_u *)skipwhite((char *)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] == '.'
+ || 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;
+ }
+ STRLCPY(IObuff + len, ptr, IOSIZE - len);
+ len += (int)(tmp_ptr - ptr);
+ *cont_s_ipos = true;
+ }
+ IObuff[len] = NUL;
+ ptr = IObuff;
+ }
+ if (len == compl_length) {
+ return NULL;
+ }
+ }
+ }
+
+ *match_len = len;
+ return ptr;
+}
+
+/// 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".
+///
+/// @param ins_buf buffer being scanned
+/// @param start_pos search start position
+/// @param cur_match_pos current match position
+/// @param prev_match_pos previous match position
+/// @param save_match_pos set first_match_pos/last_match_pos
+/// @param first_match_pos first match position
+/// @param last_match_pos last match position
+/// @param scan_curbuf scan current buffer for completion
+///
+/// @return OK if a new next match is found, otherwise FAIL.
+static int get_next_default_completion(buf_T *ins_buf, pos_T *start_pos, pos_T *cur_match_pos,
+ pos_T *prev_match_pos, bool *save_match_pos,
+ pos_T *first_match_pos, pos_T *last_match_pos,
+ bool scan_curbuf)
+{
+ // If 'infercase' is set, don't use 'smartcase' here
+ const int save_p_scs = p_scs;
+ assert(ins_buf);
+ 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
+ // buffers is a good idea, on the other hand, we always set
+ // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
+ const int save_p_ws = p_ws;
+ if (ins_buf != curbuf) {
+ p_ws = false;
+ } else if (scan_curbuf) {
+ p_ws = true;
+ }
+ bool looped_around = false;
+ int found_new_match = FAIL;
+ for (;;) {
+ bool 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, (char_u *)compl_pattern);
+ } else {
+ found_new_match = searchit(NULL, ins_buf, cur_match_pos, NULL,
+ compl_direction,
+ (char_u *)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;
+ }
+ int len;
+ char_u *ptr = ins_comp_get_next_word_or_line(ins_buf, cur_match_pos, &len,
+ &cont_s_ipos);
+ if (ptr == NULL) {
+ continue;
+ }
+ if (ins_compl_add_infercase(ptr, len, p_ic,
+ ins_buf == curbuf ? NULL : (char_u *)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.
@@ -2481,16 +2955,9 @@ static int ins_compl_get_exp(pos_T *ini)
static buf_T *ins_buf = NULL; // buffer being scanned
pos_T *pos;
- char **matches;
- int save_p_scs;
- bool 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;
bool set_match_pos;
@@ -2524,89 +2991,14 @@ static int ins_compl_get_exp(pos_T *ini)
// entries from 'complete' that look in loaded buffers.
if ((ctrl_x_mode_normal() || 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_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("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 = (char_u *)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) {
+ 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;
- } 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 = (char_u *)e_cpt;
- dict_f = DICT_FIRST;
- }
- } else if (*e_cpt == 'i') {
- type = CTRL_X_PATH_PATTERNS;
- } else if (*e_cpt == 'd') {
- type = CTRL_X_PATH_DEFINES;
- } 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, "%s", _("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, (char *)IObuff, IOSIZE, ",");
-
- found_all = true;
- if (type == -1) {
- continue;
- }
+ }
+ if (status == INS_COMPL_CPT_CONT) {
+ continue;
}
}
@@ -2621,80 +3013,25 @@ static int ins_compl_get_exp(pos_T *ini)
break;
case CTRL_X_PATH_PATTERNS:
case CTRL_X_PATH_DEFINES:
- find_pattern_in_path((char_u *)compl_pattern, compl_direction,
- STRLEN(compl_pattern), false, false,
- ((type == CTRL_X_PATH_DEFINES
- && !(compl_cont_status & CONT_SOL))
- ? FIND_DEFINE
- : FIND_ANY),
- 1L, ACTION_EXPAND, 1, MAXLNUM);
+ get_next_include_file_completion(type);
break;
case CTRL_X_DICTIONARY:
case CTRL_X_THESAURUS:
- if (thesaurus_func_complete(type)) {
- expand_by_function(type, (char_u *)compl_pattern);
- } else {
- 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)),
- (char_u *)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((char_u *)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((char_u *)compl_pattern, &num_matches, &matches,
- TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
- | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
- TAG_MANY, (char_u *)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((char_u *)compl_pattern, num_matches, matches);
-#ifdef BACKSLASH_IN_FILENAME
- if (curbuf->b_p_csl[0] != NUL) {
- for (int 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 += utfc_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, (char_u *)compl_pattern,
- (int)STRLEN(compl_pattern),
- &num_matches, &matches) == EXPAND_OK) {
- ins_compl_add_matches(num_matches, matches, false);
- }
+ get_next_cmdline_completion();
break;
case CTRL_X_FUNCTION:
@@ -2703,172 +3040,16 @@ static int ins_compl_get_exp(pos_T *ini)
break;
case CTRL_X_SPELL:
- num_matches = expand_spelling(first_match_pos.lnum,
- (char_u *)compl_pattern, &matches);
- if (num_matches > 0) {
- ins_compl_add_matches(num_matches, matches, p_ic);
- }
+ 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;
- assert(ins_buf);
- 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
- // buffers 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;
- }
- bool looped_around = false;
- for (;;) {
- bool 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,
- (char_u *)compl_pattern);
- } else {
- found_new_match = searchit(NULL, ins_buf, pos, NULL,
- compl_direction,
- (char_u *)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 = (char_u *)skipwhite((char *)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); // NOLINT(runtime/printf)
- ptr = ml_get_buf(ins_buf, pos->lnum + 1, false);
- tmp_ptr = ptr = (char_u *)skipwhite((char *)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] == '.'
- || 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;
- }
- STRLCPY(IObuff + len, ptr, IOSIZE - len);
- 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 : (char_u *)ins_buf->b_sfname,
- 0, cont_s_ipos) != NOTDONE) {
- found_new_match = OK;
- break;
- }
+ 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;
}
- p_scs = save_p_scs;
- p_ws = save_p_ws;
}
// check if compl_curr_match has changed, (e.g. other type of
@@ -2937,6 +3118,31 @@ static int ins_compl_get_exp(pos_T *ini)
return i;
}
+/// Update "compl_shown_match" to the actually shown match, it may differ when
+/// "compl_leader" is used to omit some of the matches.
+static void ins_compl_update_shown_match(void)
+{
+ while (!ins_compl_equal(compl_shown_match,
+ compl_leader, STRLEN(compl_leader))
+ && compl_shown_match->cp_next != NULL
+ && compl_shown_match->cp_next != compl_first_match) {
+ compl_shown_match = compl_shown_match->cp_next;
+ }
+
+ // If we didn't find it searching forward, and compl_shows_dir is
+ // backward, find the last match.
+ if (compl_shows_dir == BACKWARD
+ && !ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
+ && (compl_shown_match->cp_next == NULL
+ || compl_shown_match->cp_next == compl_first_match)) {
+ while (!ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
+ && compl_shown_match->cp_prev != NULL
+ && compl_shown_match->cp_prev != compl_first_match) {
+ compl_shown_match = compl_shown_match->cp_prev;
+ }
+ }
+}
+
/// Delete the old text being completed.
void ins_compl_delete(void)
{
@@ -2973,82 +3179,54 @@ void ins_compl_insert(bool in_compl_func)
}
}
-/// Fill in the next completion in the current direction.
-/// If "allow_get_expansion" is true, then we may call ins_compl_get_exp() to
-/// get more completions. If it is false, then we just do nothing when there
-/// are no more completions in a given direction. The latter case is used when
-/// we are still in the middle of finding completions, to allow browsing
-/// through the ones found so far.
-/// @return the total number of matches, or -1 if still unknown -- webb.
-///
-/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use
-/// compl_shown_match here.
-///
-/// Note that this function may be called recursively once only. First with
-/// "allow_get_expansion" true, which calls ins_compl_get_exp(), which in turn
-/// calls this function with "allow_get_expansion" false.
-///
-/// @param count Repeat completion this many times; should be at least 1
-/// @param insert_match Insert the newly selected match
-/// @param in_compl_func Called from complete_check()
-static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match,
- bool in_compl_func)
+/// show the file name for the completion match (if any). Truncate the file
+/// name to avoid a wait for return.
+static void ins_compl_show_filename(void)
{
- int num_matches = -1;
- int todo = count;
- compl_T *found_compl = NULL;
- bool found_end = false;
- const bool started = compl_started;
-
- // When user complete function return -1 for findstart which is next
- // time of 'always', compl_shown_match become NULL.
- if (compl_shown_match == NULL) {
- return -1;
+ char *const lead = _("match in file");
+ int space = sc_col - vim_strsize(lead) - 2;
+ if (space <= 0) {
+ return;
}
- if (compl_leader != NULL
- && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) {
- // Set "compl_shown_match" to the actually shown match, it may differ
- // when "compl_leader" is used to omit some of the matches.
- while (!ins_compl_equal(compl_shown_match,
- compl_leader, STRLEN(compl_leader))
- && compl_shown_match->cp_next != NULL
- && compl_shown_match->cp_next != compl_first_match) {
- compl_shown_match = compl_shown_match->cp_next;
- }
-
- // If we didn't find it searching forward, and compl_shows_dir is
- // backward, find the last match.
- if (compl_shows_dir == BACKWARD
- && !ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
- && (compl_shown_match->cp_next == NULL
- || compl_shown_match->cp_next == compl_first_match)) {
- while (!ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
- && compl_shown_match->cp_prev != NULL
- && compl_shown_match->cp_prev != compl_first_match) {
- compl_shown_match = compl_shown_match->cp_prev;
- }
+ // We need the tail that fits. With double-byte encoding going
+ // back from the end is very slow, thus go from the start and keep
+ // the text that fits in "space" between "s" and "e".
+ char *s;
+ char *e;
+ for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
+ space -= ptr2cells(e);
+ while (space < 0) {
+ space += ptr2cells(s);
+ MB_PTR_ADV(s);
}
}
+ msg_hist_off = true;
+ vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
+ (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s);
+ msg((char *)IObuff);
+ msg_hist_off = false;
+ redraw_cmdline = false; // don't overwrite!
+}
- if (allow_get_expansion && insert_match
- && (!(compl_get_longest || compl_restarting) || compl_used_match)) {
- // Delete old text to be replaced
- ins_compl_delete();
- }
-
- // When finding the longest common text we stick at the original text,
- // don't let CTRL-N or CTRL-P move to the first match.
- bool advance = count != 1 || !allow_get_expansion || !compl_get_longest;
-
- // When restarting the search don't insert the first match either.
- if (compl_restarting) {
- advance = false;
- compl_restarting = false;
- }
+/// Find the next set of matches for completion. Repeat the completion 'todo'
+/// times. The number of matches found is returned in 'num_matches'.
+///
+/// @param allow_get_expansion If true, then ins_compl_get_exp() may be called to
+/// get more completions.
+/// If false, then do nothing when there are no more
+/// completions in the given direction.
+/// @param todo repeat completion this many times
+/// @param advance If true, then completion will move to the first match.
+/// Otherwise, the original text will be shown.
+///
+/// @return OK on success and -1 if the number of matches are unknown.
+static int find_next_completion_match(bool allow_get_expansion, int todo, bool advance,
+ int *num_matches)
+{
+ bool found_end = false;
+ compl_T *found_compl = NULL;
- // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
- // around.
while (--todo >= 0) {
if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) {
compl_shown_match = compl_shown_match->cp_next;
@@ -3081,7 +3259,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
// Find matches.
- num_matches = ins_compl_get_exp(&compl_startpos);
+ *num_matches = ins_compl_get_exp(&compl_startpos);
// handle any pending completions
while (compl_pending != 0 && compl_direction == compl_shows_dir
@@ -3119,6 +3297,69 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
}
+ return OK;
+}
+
+/// Fill in the next completion in the current direction.
+/// If "allow_get_expansion" is true, then we may call ins_compl_get_exp() to
+/// get more completions. If it is false, then we just do nothing when there
+/// are no more completions in a given direction. The latter case is used when
+/// we are still in the middle of finding completions, to allow browsing
+/// through the ones found so far.
+/// @return the total number of matches, or -1 if still unknown -- webb.
+///
+/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use
+/// compl_shown_match here.
+///
+/// Note that this function may be called recursively once only. First with
+/// "allow_get_expansion" true, which calls ins_compl_get_exp(), which in turn
+/// calls this function with "allow_get_expansion" false.
+///
+/// @param count Repeat completion this many times; should be at least 1
+/// @param insert_match Insert the newly selected match
+/// @param in_compl_func Called from complete_check()
+static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match,
+ bool in_compl_func)
+{
+ int num_matches = -1;
+ int todo = count;
+ const bool started = compl_started;
+
+ // When user complete function return -1 for findstart which is next
+ // time of 'always', compl_shown_match become NULL.
+ if (compl_shown_match == NULL) {
+ return -1;
+ }
+
+ if (compl_leader != NULL
+ && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) {
+ // Update "compl_shown_match" to the actually shown match
+ ins_compl_update_shown_match();
+ }
+
+ if (allow_get_expansion && insert_match
+ && (!(compl_get_longest || compl_restarting) || compl_used_match)) {
+ // Delete old text to be replaced
+ ins_compl_delete();
+ }
+
+ // When finding the longest common text we stick at the original text,
+ // don't let CTRL-N or CTRL-P move to the first match.
+ bool advance = count != 1 || !allow_get_expansion || !compl_get_longest;
+
+ // When restarting the search don't insert the first match either.
+ if (compl_restarting) {
+ advance = false;
+ compl_restarting = false;
+ }
+
+ // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
+ // around.
+ if (find_next_completion_match(allow_get_expansion, todo, advance,
+ &num_matches) == -1) {
+ return -1;
+ }
+
// Insert the text of the new completion, or the compl_leader.
if (compl_no_insert && !started) {
ins_bytes(compl_orig_text + get_compl_len());
@@ -3154,31 +3395,8 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
// Show the file name for the match (if any)
- // Truncate the file name to avoid a wait for return.
if (compl_shown_match->cp_fname != NULL) {
- char *lead = _("match in file");
- int space = sc_col - vim_strsize(lead) - 2;
- char *s;
- char *e;
-
- if (space > 0) {
- // We need the tail that fits. With double-byte encoding going
- // back from the end is very slow, thus go from the start and keep
- // the text that fits in "space" between "s" and "e".
- for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
- space -= ptr2cells(e);
- while (space < 0) {
- space += ptr2cells(s);
- MB_PTR_ADV(s);
- }
- }
- msg_hist_off = true;
- vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
- (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s);
- msg((char *)IObuff);
- msg_hist_off = false;
- redraw_cmdline = false; // don't overwrite!
- }
+ ins_compl_show_filename();
}
return num_matches;
@@ -3588,221 +3806,182 @@ static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *li
return OK;
}
-/// Do Insert mode completion.
-/// Called when character "c" was typed, which has a meaning for completion.
-/// Returns OK if completion was done, FAIL if something failed.
-int ins_complete(int c, bool enable_pum)
+/// Continue an interrupted completion mode search in "line".
+///
+/// If this same ctrl_x_mode has been interrupted use the text from
+/// "compl_startpos" to the cursor as a pattern to add a new word instead of
+/// expand the one before the cursor, in word-wise if "compl_startpos" is not in
+/// the same line as the cursor then fix it (the line has been split because it
+/// was longer than 'tw'). if SOL is set then skip the previous pattern, a word
+/// at the beginning of the line has been inserted, we'll look for that.
+static void ins_compl_continue_search(char_u *line)
{
- char_u *line;
- int startcol = 0; // column where searched text starts
- colnr_T curs_col; // cursor column
- int n;
- int save_w_wrow;
- int save_w_leftcol;
- int insert_match;
- const bool save_did_ai = did_ai;
- int flags = CP_ORIGINAL_TEXT;
- bool line_invalid = false;
-
- compl_direction = ins_compl_key2dir(c);
- insert_match = ins_compl_use_match(c);
-
- if (!compl_started) {
- // First time we hit ^N or ^P (in a row, I mean)
-
- did_ai = false;
- did_si = false;
- can_si = false;
- can_si_back = false;
- if (stop_arrow() == FAIL) {
- return FAIL;
- }
-
- line = ml_get(curwin->w_cursor.lnum);
- curs_col = curwin->w_cursor.col;
- compl_pending = 0;
-
- // If this same ctrl_x_mode has been interrupted use the text from
- // "compl_startpos" to the cursor as a pattern to add a new word
- // instead of expand the one before the cursor, in word-wise if
- // "compl_startpos" is not in the same line as the cursor then fix it
- // (the line has been split because it was longer than 'tw'). if SOL
- // is set then skip the previous pattern, a word at the beginning of
- // the line has been inserted, we'll look for that -- Acevedo.
- if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
- && compl_cont_mode == ctrl_x_mode) {
- // it is a continued search
- compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
- if (ctrl_x_mode_normal()
- || ctrl_x_mode_path_patterns()
- || ctrl_x_mode_path_defines()) {
- if (compl_startpos.lnum != curwin->w_cursor.lnum) {
- // line (probably) wrapped, set compl_startpos to the
- // first non_blank in the line, if it is not a wordchar
- // include it to get a better pattern, but then we don't
- // want the "\\<" prefix, check it below.
- compl_col = (colnr_T)getwhitecols(line);
- compl_startpos.col = compl_col;
- compl_startpos.lnum = curwin->w_cursor.lnum;
- compl_cont_status &= ~CONT_SOL; // clear SOL if present
- } else {
- // S_IPOS was set when we inserted a word that was at the
- // beginning of the line, which means that we'll go to SOL
- // mode but first we need to redefine compl_startpos
- if (compl_cont_status & CONT_S_IPOS) {
- compl_cont_status |= CONT_SOL;
- compl_startpos.col = (colnr_T)((char_u *)skipwhite((char *)line
- + compl_length
- + compl_startpos.col) - line);
- }
- compl_col = compl_startpos.col;
- }
- compl_length = curwin->w_cursor.col - (int)compl_col;
- // IObuff is used to add a "word from the next line" would we
- // have enough space? just being paranoid
-#define MIN_SPACE 75
- if (compl_length > (IOSIZE - MIN_SPACE)) {
- compl_cont_status &= ~CONT_SOL;
- compl_length = (IOSIZE - MIN_SPACE);
- compl_col = curwin->w_cursor.col - compl_length;
- }
- compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
- if (compl_length < 1) {
- compl_cont_status &= CONT_LOCAL;
- }
- } else if (ctrl_x_mode_line_or_eval()) {
- compl_cont_status = CONT_ADDING | CONT_N_ADDS;
- } else {
- compl_cont_status = 0;
- }
+ // it is a continued search
+ compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
+ if (ctrl_x_mode_normal()
+ || ctrl_x_mode_path_patterns()
+ || ctrl_x_mode_path_defines()) {
+ if (compl_startpos.lnum != curwin->w_cursor.lnum) {
+ // line (probably) wrapped, set compl_startpos to the
+ // first non_blank in the line, if it is not a wordchar
+ // include it to get a better pattern, but then we don't
+ // want the "\\<" prefix, check it below.
+ compl_col = (colnr_T)getwhitecols(line);
+ compl_startpos.col = compl_col;
+ compl_startpos.lnum = curwin->w_cursor.lnum;
+ compl_cont_status &= ~CONT_SOL; // clear SOL if present
} else {
- compl_cont_status &= CONT_LOCAL;
- }
-
- if (!(compl_cont_status & CONT_ADDING)) { // normal expansion
- compl_cont_mode = ctrl_x_mode;
- if (ctrl_x_mode_not_default()) {
- // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
- compl_cont_status = 0;
+ // S_IPOS was set when we inserted a word that was at the
+ // beginning of the line, which means that we'll go to SOL
+ // mode but first we need to redefine compl_startpos
+ if (compl_cont_status & CONT_S_IPOS) {
+ compl_cont_status |= CONT_SOL;
+ compl_startpos.col = (colnr_T)((char_u *)skipwhite((char *)line
+ + compl_length
+ + compl_startpos.col) - line);
}
- compl_cont_status |= CONT_N_ADDS;
- compl_startpos = curwin->w_cursor;
- startcol = (int)curs_col;
- compl_col = 0;
+ compl_col = compl_startpos.col;
}
-
- // Work out completion pattern and original text -- webb
- if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) {
- if (ctrl_x_mode_function() || ctrl_x_mode_omni()
- || thesaurus_func_complete(ctrl_x_mode)) {
- // restore did_ai, so that adding comment leader works
- did_ai = save_did_ai;
- }
- return FAIL;
+ compl_length = curwin->w_cursor.col - (int)compl_col;
+ // IObuff is used to add a "word from the next line" would we
+ // have enough space? just being paranoid
+#define MIN_SPACE 75
+ if (compl_length > (IOSIZE - MIN_SPACE)) {
+ compl_cont_status &= ~CONT_SOL;
+ compl_length = (IOSIZE - MIN_SPACE);
+ compl_col = curwin->w_cursor.col - compl_length;
}
- // If "line" was changed while getting completion info get it again.
- if (line_invalid) {
- line = ml_get(curwin->w_cursor.lnum);
+ compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
+ if (compl_length < 1) {
+ compl_cont_status &= CONT_LOCAL;
}
+ } else if (ctrl_x_mode_line_or_eval()) {
+ compl_cont_status = CONT_ADDING | CONT_N_ADDS;
+ } else {
+ compl_cont_status = 0;
+ }
+}
- if (compl_cont_status & CONT_ADDING) {
- edit_submode_pre = (char_u *)_(" Adding");
- if (ctrl_x_mode_line_or_eval()) {
- // Insert a new line, keep indentation but ignore 'comments'.
- char_u *old = curbuf->b_p_com;
-
- curbuf->b_p_com = (char_u *)"";
- compl_startpos.lnum = curwin->w_cursor.lnum;
- compl_startpos.col = compl_col;
- ins_eol('\r');
- curbuf->b_p_com = old;
- compl_length = 0;
- compl_col = curwin->w_cursor.col;
- }
- } else {
- edit_submode_pre = NULL;
- compl_startpos.col = compl_col;
- }
+/// start insert mode completion
+static int ins_compl_start(void)
+{
+ const bool save_did_ai = did_ai;
- if (compl_cont_status & CONT_LOCAL) {
- edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
- } else {
- edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
- }
+ // First time we hit ^N or ^P (in a row, I mean)
- // If any of the original typed text has been changed we need to fix
- // the redo buffer.
- ins_compl_fixRedoBufForLeader(NULL);
+ did_ai = false;
+ did_si = false;
+ can_si = false;
+ can_si_back = false;
+ if (stop_arrow() == FAIL) {
+ return FAIL;
+ }
- // Always add completion for the original text.
- xfree(compl_orig_text);
- compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length);
- if (p_ic) {
- flags |= CP_ICASE;
- }
- if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
- flags, false) != OK) {
- XFREE_CLEAR(compl_pattern);
- XFREE_CLEAR(compl_orig_text);
- return FAIL;
+ char_u *line = ml_get(curwin->w_cursor.lnum);
+ colnr_T curs_col = curwin->w_cursor.col;
+ compl_pending = 0;
+
+ if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
+ && compl_cont_mode == ctrl_x_mode) {
+ // this same ctrl-x_mode was interrupted previously. Continue the
+ // completion.
+ ins_compl_continue_search(line);
+ } else {
+ compl_cont_status &= CONT_LOCAL;
+ }
+
+ int startcol = 0; // column where searched text starts
+ if (!(compl_cont_status & CONT_ADDING)) { // normal expansion
+ compl_cont_mode = ctrl_x_mode;
+ if (ctrl_x_mode_not_default()) {
+ // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
+ compl_cont_status = 0;
}
+ compl_cont_status |= CONT_N_ADDS;
+ compl_startpos = curwin->w_cursor;
+ startcol = (int)curs_col;
+ compl_col = 0;
+ }
- // showmode might reset the internal line pointers, so it must
- // be called before line = ml_get(), or when this address is no
- // longer needed. -- Acevedo.
- edit_submode_extra = (char_u *)_("-- Searching...");
- edit_submode_highl = HLF_COUNT;
- showmode();
- edit_submode_extra = NULL;
- ui_flush();
- } else if (insert_match && stop_arrow() == FAIL) {
+ // Work out completion pattern and original text -- webb
+ bool line_invalid = false;
+ if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) {
+ if (ctrl_x_mode_function() || ctrl_x_mode_omni()
+ || thesaurus_func_complete(ctrl_x_mode)) {
+ // restore did_ai, so that adding comment leader works
+ did_ai = save_did_ai;
+ }
return FAIL;
}
+ // If "line" was changed while getting completion info get it again.
+ if (line_invalid) {
+ line = ml_get(curwin->w_cursor.lnum);
+ }
- compl_shown_match = compl_curr_match;
- compl_shows_dir = compl_direction;
+ if (compl_cont_status & CONT_ADDING) {
+ edit_submode_pre = (char_u *)_(" Adding");
+ if (ctrl_x_mode_line_or_eval()) {
+ // Insert a new line, keep indentation but ignore 'comments'.
+ char_u *old = curbuf->b_p_com;
- // Find next match (and following matches).
- save_w_wrow = curwin->w_wrow;
- save_w_leftcol = curwin->w_leftcol;
- n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false);
+ curbuf->b_p_com = (char_u *)"";
+ compl_startpos.lnum = curwin->w_cursor.lnum;
+ compl_startpos.col = compl_col;
+ ins_eol('\r');
+ curbuf->b_p_com = old;
+ compl_length = 0;
+ compl_col = curwin->w_cursor.col;
+ }
+ } else {
+ edit_submode_pre = NULL;
+ compl_startpos.col = compl_col;
+ }
- if (n > 1) { // all matches have been found
- compl_matches = n;
+ if (compl_cont_status & CONT_LOCAL) {
+ edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
+ } else {
+ edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
}
- compl_curr_match = compl_shown_match;
- compl_direction = compl_shows_dir;
- // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
- // mode.
- if (got_int && !global_busy) {
- (void)vgetc();
- got_int = false;
+ // If any of the original typed text has been changed we need to fix
+ // the redo buffer.
+ ins_compl_fixRedoBufForLeader(NULL);
+
+ // Always add completion for the original text.
+ xfree(compl_orig_text);
+ compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length);
+ int flags = CP_ORIGINAL_TEXT;
+ if (p_ic) {
+ flags |= CP_ICASE;
+ }
+ if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
+ flags, false) != OK) {
+ XFREE_CLEAR(compl_pattern);
+ XFREE_CLEAR(compl_orig_text);
+ return FAIL;
}
+ // showmode might reset the internal line pointers, so it must
+ // be called before line = ml_get(), or when this address is no
+ // longer needed. -- Acevedo.
+ edit_submode_extra = (char_u *)_("-- Searching...");
+ edit_submode_highl = HLF_COUNT;
+ showmode();
+ edit_submode_extra = NULL;
+ ui_flush();
+
+ return OK;
+}
+
+/// display the completion status message
+static void ins_compl_show_statusmsg(void)
+{
// we found no match if the list has only the "compl_orig_text"-entry
if (compl_first_match == compl_first_match->cp_next) {
edit_submode_extra = (compl_cont_status & CONT_ADDING)
&& compl_length > 1
? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf);
edit_submode_highl = HLF_E;
- // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
- // because we couldn't expand anything at first place, but if we used
- // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
- // (such as M in M'exico) if not tried already. -- Acevedo
- if (compl_length > 1
- || (compl_cont_status & CONT_ADDING)
- || (ctrl_x_mode_not_default()
- && !ctrl_x_mode_path_patterns()
- && !ctrl_x_mode_path_defines())) {
- compl_cont_status &= ~CONT_N_ADDS;
- }
- }
-
- if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) {
- compl_cont_status |= CONT_S_IPOS;
- } else {
- compl_cont_status &= ~CONT_S_IPOS;
}
if (edit_submode_extra == NULL) {
@@ -3862,6 +4041,72 @@ int ins_complete(int c, bool enable_pum)
msg_clr_cmdline(); // necessary for "noshowmode"
}
}
+}
+
+/// Do Insert mode completion.
+/// Called when character "c" was typed, which has a meaning for completion.
+/// Returns OK if completion was done, FAIL if something failed.
+int ins_complete(int c, bool enable_pum)
+{
+ int n;
+ int save_w_wrow;
+ int save_w_leftcol;
+ int insert_match;
+
+ compl_direction = ins_compl_key2dir(c);
+ insert_match = ins_compl_use_match(c);
+
+ if (!compl_started) {
+ if (ins_compl_start() == FAIL) {
+ return FAIL;
+ }
+ } else if (insert_match && stop_arrow() == FAIL) {
+ return FAIL;
+ }
+
+ compl_shown_match = compl_curr_match;
+ compl_shows_dir = compl_direction;
+
+ // Find next match (and following matches).
+ save_w_wrow = curwin->w_wrow;
+ save_w_leftcol = curwin->w_leftcol;
+ n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false);
+
+ if (n > 1) { // all matches have been found
+ compl_matches = n;
+ }
+ compl_curr_match = compl_shown_match;
+ compl_direction = compl_shows_dir;
+
+ // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
+ // mode.
+ if (got_int && !global_busy) {
+ (void)vgetc();
+ got_int = false;
+ }
+
+ // we found no match if the list has only the "compl_orig_text"-entry
+ if (compl_first_match == compl_first_match->cp_next) {
+ // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
+ // because we couldn't expand anything at first place, but if we used
+ // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
+ // (such as M in M'exico) if not tried already. -- Acevedo
+ if (compl_length > 1
+ || (compl_cont_status & CONT_ADDING)
+ || (ctrl_x_mode_not_default()
+ && !ctrl_x_mode_path_patterns()
+ && !ctrl_x_mode_path_defines())) {
+ compl_cont_status &= ~CONT_N_ADDS;
+ }
+ }
+
+ if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) {
+ compl_cont_status |= CONT_S_IPOS;
+ } else {
+ compl_cont_status &= ~CONT_S_IPOS;
+ }
+
+ ins_compl_show_statusmsg();
// Show the popup menu, unless we got interrupted.
if (enable_pum && !compl_interrupted) {
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 179218e48a..6f5f72e1ed 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -44,11 +44,11 @@ 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('.'))
- " add-expands lines (it would end in an empty line if it didn't ignored
+ " add-expands lines (it would end in an empty line if it didn't ignore
" itself)
exe "normal o\<C-X>\<C-L>\<C-X>\<C-L>\<C-P>\<C-P>"
call assert_equal("Makefile\tto\trun3", getline('.'))
@@ -721,6 +721,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
@@ -741,6 +752,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
@@ -829,6 +873,25 @@ func Test_complete_stop()
close!
endfunc
+" Test for typing CTRL-R in insert completion mode to insert a register
+" content.
+func Test_complete_reginsert()
+ new
+ call setline(1, ['a1', 'a12', 'a123', 'a1234'])
+
+ " if a valid CTRL-X mode key is returned from <C-R>=, then it should be
+ " processed. Otherwise, CTRL-X mode should be stopped and the key should be
+ " inserted.
+ exe "normal Goa\<C-P>\<C-R>=\"\\<C-P>\"\<CR>"
+ call assert_equal('a123', getline(5))
+ let @r = "\<C-P>\<C-P>"
+ exe "normal GCa\<C-P>\<C-R>r"
+ call assert_equal('a12', getline(5))
+ exe "normal GCa\<C-P>\<C-R>=\"x\"\<CR>"
+ call assert_equal('a1234x', getline(5))
+ bw!
+endfunc
+
func Test_issue_7021()
CheckMSWindows