vim-patch:8.2.3944: insert mode completion functions are too long

Problem:    Insert mode completion functions are too long.
Solution:   Split up into multiple functions. (Yegappan Lakshmanan,
            closes vim/vim#9431)
5d2e007ccb

Cherry-pick can_cindent_get() -> get_can_cindent() from patch 8.1.2062.
This commit is contained in:
zeertzjq
2022-08-24 13:15:38 +08:00
parent ef748af01d
commit 9ac44c7f5d
3 changed files with 704 additions and 563 deletions

View File

@@ -5654,7 +5654,7 @@ static char_u *do_insert_char_pre(int c)
return res; return res;
} }
bool can_cindent_get(void) bool get_can_cindent(void)
{ {
return can_cindent; return can_cindent;
} }

View File

@@ -1650,64 +1650,19 @@ void ins_compl_addfrommatch(void)
ins_compl_addleader(c); ins_compl_addleader(c);
} }
/// Prepare for Insert mode completion, or stop it. /// Set the CTRL-X completion mode based on the key 'c' typed after a CTRL-X.
/// Called just after typing a character in Insert mode. /// Uses the global variables: ctrl_x_mode, edit_submode, edit_submode_pre,
/// compl_cont_mode and compl_cont_status.
/// ///
/// @param c character that was typed /// @return true when the character is not to be inserted.
/// static bool set_ctrl_x_mode(const int c)
/// @return true when the character is not to be inserted;
bool ins_compl_prep(int c)
{ {
char_u *ptr;
bool retval = false; bool retval = false;
const int prev_mode = ctrl_x_mode;
// Forget any previous 'special' messages if this is actually
// a ^X mode key - bar ^R, in which case we wait to see what it gives us.
if (c != Ctrl_R && vim_is_ctrl_x_key(c)) {
edit_submode_extra = NULL;
}
// Ignore end of Select mode mapping and mouse scroll buttons.
if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
|| c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT
|| c == K_COMMAND || c == K_LUA) {
return retval;
}
if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X) {
if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c)
|| !vim_is_ctrl_x_key(c)) {
// Not starting another completion mode.
ctrl_x_mode = CTRL_X_CMDLINE;
// CTRL-X CTRL-Z should stop completion without inserting anything
if (c == Ctrl_Z) {
retval = true;
}
} else {
ctrl_x_mode = CTRL_X_CMDLINE;
// Other CTRL-X keys first stop completion, then start another
// completion mode.
ins_compl_prep(' ');
ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
}
}
// Set "compl_get_longest" when finding the first matches.
if (ctrl_x_mode_not_defined_yet()
|| (ctrl_x_mode_normal() && !compl_started)) {
compl_get_longest = (strstr((char *)p_cot, "longest") != NULL);
compl_used_match = true;
}
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) { switch (c) {
case Ctrl_E: case Ctrl_E:
case Ctrl_Y: case Ctrl_Y:
// scroll the window one line up or down
ctrl_x_mode = CTRL_X_SCROLL; ctrl_x_mode = CTRL_X_SCROLL;
if (!(State & REPLACE_FLAG)) { if (!(State & REPLACE_FLAG)) {
edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
@@ -1718,48 +1673,61 @@ bool ins_compl_prep(int c)
showmode(); showmode();
break; break;
case Ctrl_L: case Ctrl_L:
// complete whole line
ctrl_x_mode = CTRL_X_WHOLE_LINE; ctrl_x_mode = CTRL_X_WHOLE_LINE;
break; break;
case Ctrl_F: case Ctrl_F:
// complete filenames
ctrl_x_mode = CTRL_X_FILES; ctrl_x_mode = CTRL_X_FILES;
break; break;
case Ctrl_K: case Ctrl_K:
// complete words from a dictionary
ctrl_x_mode = CTRL_X_DICTIONARY; ctrl_x_mode = CTRL_X_DICTIONARY;
break; break;
case Ctrl_R: case Ctrl_R:
// Register insertion without exiting CTRL-X mode
// Simply allow ^R to happen without affecting ^X mode // Simply allow ^R to happen without affecting ^X mode
break; break;
case Ctrl_T: case Ctrl_T:
// complete words from a thesaurus
ctrl_x_mode = CTRL_X_THESAURUS; ctrl_x_mode = CTRL_X_THESAURUS;
break; break;
case Ctrl_U: case Ctrl_U:
// user defined completion
ctrl_x_mode = CTRL_X_FUNCTION; ctrl_x_mode = CTRL_X_FUNCTION;
break; break;
case Ctrl_O: case Ctrl_O:
// omni completion
ctrl_x_mode = CTRL_X_OMNI; ctrl_x_mode = CTRL_X_OMNI;
break; break;
case 's': case 's':
case Ctrl_S: case Ctrl_S:
// complete spelling suggestions
ctrl_x_mode = CTRL_X_SPELL; ctrl_x_mode = CTRL_X_SPELL;
emsg_off++; // Avoid getting the E756 error twice. emsg_off++; // Avoid getting the E756 error twice.
spell_back_to_badword(); spell_back_to_badword();
emsg_off--; emsg_off--;
break; break;
case Ctrl_RSB: case Ctrl_RSB:
// complete tag names
ctrl_x_mode = CTRL_X_TAGS; ctrl_x_mode = CTRL_X_TAGS;
break; break;
case Ctrl_I: case Ctrl_I:
case K_S_TAB: case K_S_TAB:
// complete keywords from included files
ctrl_x_mode = CTRL_X_PATH_PATTERNS; ctrl_x_mode = CTRL_X_PATH_PATTERNS;
break; break;
case Ctrl_D: case Ctrl_D:
// complete definitions from included files
ctrl_x_mode = CTRL_X_PATH_DEFINES; ctrl_x_mode = CTRL_X_PATH_DEFINES;
break; break;
case Ctrl_V: case Ctrl_V:
case Ctrl_Q: case Ctrl_Q:
// complete vim commands
ctrl_x_mode = CTRL_X_CMDLINE; ctrl_x_mode = CTRL_X_CMDLINE;
break; break;
case Ctrl_Z: case Ctrl_Z:
// stop completion
ctrl_x_mode = CTRL_X_NORMAL; ctrl_x_mode = CTRL_X_NORMAL;
edit_submode = NULL; edit_submode = NULL;
showmode(); showmode();
@@ -1803,30 +1771,13 @@ bool ins_compl_prep(int c)
showmode(); showmode();
break; break;
} }
} else if (ctrl_x_mode_not_default()) {
// We're already in CTRL-X mode, do we stay in it? return retval;
if (!vim_is_ctrl_x_key(c)) {
if (ctrl_x_mode_scroll()) {
ctrl_x_mode = CTRL_X_NORMAL;
} else {
ctrl_x_mode = CTRL_X_FINISHED;
}
edit_submode = NULL;
}
showmode();
} }
if (compl_started || ctrl_x_mode == CTRL_X_FINISHED) { /// Stop insert completion mode
// Show error message from attempted keyword completion (probably static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// 'Pattern not found') until another key is hit, then go back to {
// showing what mode we are in.
showmode();
if ((ctrl_x_mode_normal()
&& c != Ctrl_N
&& c != Ctrl_P
&& 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 // Get here when we have finished typing a sequence of ^N and
// ^P or other completion characters in CTRL-X mode. Free up // ^P or other completion characters in CTRL-X mode. Free up
// memory that was used, and make sure we can redo the insert. // memory that was used, and make sure we can redo the insert.
@@ -1837,6 +1788,7 @@ bool ins_compl_prep(int c)
// of the original text that has changed. // of the original text that has changed.
// When using the longest match, edited the match or used // When using the longest match, edited the match or used
// CTRL-E then don't use the current match. // CTRL-E then don't use the current match.
char_u *ptr;
if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) { if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
ptr = compl_curr_match->cp_str; ptr = compl_curr_match->cp_str;
} else { } else {
@@ -1845,7 +1797,7 @@ bool ins_compl_prep(int c)
ins_compl_fixRedoBufForLeader(ptr); ins_compl_fixRedoBufForLeader(ptr);
} }
bool want_cindent = (can_cindent_get() && cindent_on()); bool want_cindent = (get_can_cindent() && cindent_on());
// When completing whole lines: fix indent for 'cindent'. // When completing whole lines: fix indent for 'cindent'.
// Otherwise, break line if it's too long. // Otherwise, break line if it's too long.
@@ -1856,13 +1808,14 @@ bool ins_compl_prep(int c)
want_cindent = false; // don't do it again want_cindent = false; // don't do it again
} }
} else { } else {
int prev_col = curwin->w_cursor.col; const int prev_col = curwin->w_cursor.col;
// put the cursor on the last char, for 'tw' formatting // put the cursor on the last char, for 'tw' formatting
if (prev_col > 0) { if (prev_col > 0) {
dec_cursor(); dec_cursor();
} }
// only format when something was inserted
if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) { if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) {
insertchar(NUL, 0, -1); insertchar(NUL, 0, -1);
} }
@@ -1915,9 +1868,9 @@ bool ins_compl_prep(int c)
showmode(); showmode();
} }
if (c == Ctrl_C && cmdwin_type != 0) {
// Avoid the popup menu remains displayed when leaving the // Avoid the popup menu remains displayed when leaving the
// command line window. // command line window.
if (c == Ctrl_C && cmdwin_type != 0) {
update_screen(0); update_screen(0);
} }
@@ -1928,6 +1881,90 @@ bool ins_compl_prep(int c)
// Trigger the CompleteDone event to give scripts a chance to act // Trigger the CompleteDone event to give scripts a chance to act
// upon the end of completion. // upon the end of completion.
ins_apply_autocmds(EVENT_COMPLETEDONE); ins_apply_autocmds(EVENT_COMPLETEDONE);
return retval;
}
/// Prepare for Insert mode completion, or stop it.
/// Called just after typing a character in Insert mode.
///
/// @param c character that was typed
///
/// @return true when the character is not to be inserted;
bool ins_compl_prep(int c)
{
bool retval = false;
const int prev_mode = ctrl_x_mode;
// Forget any previous 'special' messages if this is actually
// a ^X mode key - bar ^R, in which case we wait to see what it gives us.
if (c != Ctrl_R && vim_is_ctrl_x_key(c)) {
edit_submode_extra = NULL;
}
// Ignore end of Select mode mapping and mouse scroll buttons.
if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP
|| c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT
|| c == K_COMMAND || c == K_LUA) {
return retval;
}
if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X) {
if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c)
|| !vim_is_ctrl_x_key(c)) {
// Not starting another completion mode.
ctrl_x_mode = CTRL_X_CMDLINE;
// CTRL-X CTRL-Z should stop completion without inserting anything
if (c == Ctrl_Z) {
retval = true;
}
} else {
ctrl_x_mode = CTRL_X_CMDLINE;
// Other CTRL-X keys first stop completion, then start another
// completion mode.
ins_compl_prep(' ');
ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
}
}
// Set "compl_get_longest" when finding the first matches.
if (ctrl_x_mode_not_defined_yet()
|| (ctrl_x_mode_normal() && !compl_started)) {
compl_get_longest = (strstr((char *)p_cot, "longest") != NULL);
compl_used_match = true;
}
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.
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)) {
if (ctrl_x_mode_scroll()) {
ctrl_x_mode = CTRL_X_NORMAL;
} else {
ctrl_x_mode = CTRL_X_FINISHED;
}
edit_submode = NULL;
}
showmode();
}
if (compl_started || ctrl_x_mode == CTRL_X_FINISHED) {
// Show error message from attempted keyword completion (probably
// 'Pattern not found') until another key is hit, then go back to
// showing what mode we are in.
showmode();
if ((ctrl_x_mode_normal()
&& c != Ctrl_N
&& c != Ctrl_P
&& c != Ctrl_R
&& !ins_compl_pum_key(c))
|| ctrl_x_mode == CTRL_X_FINISHED) {
retval = ins_compl_stop(c, prev_mode, retval);
} }
} else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) { } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) {
// Trigger the CompleteDone event to give scripts a chance to act // Trigger the CompleteDone event to give scripts a chance to act
@@ -2701,6 +2738,91 @@ static void get_next_spell_completion(linenr_T lnum)
} }
} }
/// 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) /// Get the next set of words matching "compl_pattern" for default completion(s)
/// (normal ^P/^N and ^X^L). /// (normal ^P/^N and ^X^L).
/// Search for "compl_pattern" in the buffer "ins_buf" starting from the /// Search for "compl_pattern" in the buffer "ins_buf" starting from the
@@ -2797,76 +2919,12 @@ static int get_next_default_completion(buf_T *ins_buf, pos_T *start_pos, pos_T *
&& start_pos->col == cur_match_pos->col) { && start_pos->col == cur_match_pos->col) {
continue; continue;
} }
char_u *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, false) + cur_match_pos->col;
int len; int len;
if (ctrl_x_mode_line_or_eval()) { char_u *ptr = ins_comp_get_next_word_or_line(ins_buf, cur_match_pos, &len,
if (compl_cont_status & CONT_ADDING) { &cont_s_ipos);
if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) { if (ptr == NULL) {
continue; continue;
} }
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)) {
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); // 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) {
continue;
}
}
}
if (ins_compl_add_infercase(ptr, len, p_ic, if (ins_compl_add_infercase(ptr, len, p_ic,
ins_buf == curbuf ? NULL : (char_u *)ins_buf->b_sfname, ins_buf == curbuf ? NULL : (char_u *)ins_buf->b_sfname,
0, cont_s_ipos) != NOTDONE) { 0, cont_s_ipos) != NOTDONE) {
@@ -3060,6 +3118,31 @@ static int ins_compl_get_exp(pos_T *ini)
return i; 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. /// Delete the old text being completed.
void ins_compl_delete(void) void ins_compl_delete(void)
{ {
@@ -3096,82 +3179,54 @@ void ins_compl_insert(bool in_compl_func)
} }
} }
/// Fill in the next completion in the current direction. /// show the file name for the completion match (if any). Truncate the file
/// If "allow_get_expansion" is true, then we may call ins_compl_get_exp() to /// name to avoid a wait for return.
/// get more completions. If it is false, then we just do nothing when there static void ins_compl_show_filename(void)
/// 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 char *const lead = _("match in file");
/// through the ones found so far. int space = sc_col - vim_strsize(lead) - 2;
/// @return the total number of matches, or -1 if still unknown -- webb. if (space <= 0) {
/// return;
/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use }
/// compl_shown_match here.
/// // We need the tail that fits. With double-byte encoding going
/// Note that this function may be called recursively once only. First with // back from the end is very slow, thus go from the start and keep
/// "allow_get_expansion" true, which calls ins_compl_get_exp(), which in turn // the text that fits in "space" between "s" and "e".
/// calls this function with "allow_get_expansion" false. char *s;
/// char *e;
/// @param count Repeat completion this many times; should be at least 1 for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
/// @param insert_match Insert the newly selected match space -= ptr2cells(e);
/// @param in_compl_func Called from complete_check() while (space < 0) {
static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match, space += ptr2cells(s);
bool in_compl_func) 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!
}
/// 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)
{ {
int num_matches = -1;
int todo = count;
compl_T *found_compl = NULL;
bool found_end = false; bool found_end = false;
const bool started = compl_started; compl_T *found_compl = NULL;
// 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) {
// 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;
}
}
}
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.
while (--todo >= 0) { while (--todo >= 0) {
if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) { if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) {
compl_shown_match = compl_shown_match->cp_next; compl_shown_match = compl_shown_match->cp_next;
@@ -3204,7 +3259,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
} }
// Find matches. // Find matches.
num_matches = ins_compl_get_exp(&compl_startpos); *num_matches = ins_compl_get_exp(&compl_startpos);
// handle any pending completions // handle any pending completions
while (compl_pending != 0 && compl_direction == compl_shows_dir while (compl_pending != 0 && compl_direction == compl_shows_dir
@@ -3242,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. // Insert the text of the new completion, or the compl_leader.
if (compl_no_insert && !started) { if (compl_no_insert && !started) {
ins_bytes(compl_orig_text + get_compl_len()); ins_bytes(compl_orig_text + get_compl_len());
@@ -3277,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) // 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) { if (compl_shown_match->cp_fname != NULL) {
char *lead = _("match in file"); ins_compl_show_filename();
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!
}
} }
return num_matches; return num_matches;
@@ -3711,49 +3806,16 @@ static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *li
return OK; return OK;
} }
/// Do Insert mode completion. /// Continue an interrupted completion mode search in "line".
/// Called when character "c" was typed, which has a meaning for completion. ///
/// Returns OK if completion was done, FAIL if something failed. /// If this same ctrl_x_mode has been interrupted use the text from
int ins_complete(int c, bool enable_pum) /// "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 // it is a continued search
compl_cont_status &= ~CONT_INTRPT; // remove INTRPT compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
if (ctrl_x_mode_normal() if (ctrl_x_mode_normal()
@@ -3798,10 +3860,37 @@ int ins_complete(int c, bool enable_pum)
} else { } else {
compl_cont_status = 0; compl_cont_status = 0;
} }
}
/// start insert mode completion
static int ins_compl_start(void)
{
const bool save_did_ai = did_ai;
// 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;
}
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 { } else {
compl_cont_status &= CONT_LOCAL; compl_cont_status &= CONT_LOCAL;
} }
int startcol = 0; // column where searched text starts
if (!(compl_cont_status & CONT_ADDING)) { // normal expansion if (!(compl_cont_status & CONT_ADDING)) { // normal expansion
compl_cont_mode = ctrl_x_mode; compl_cont_mode = ctrl_x_mode;
if (ctrl_x_mode_not_default()) { if (ctrl_x_mode_not_default()) {
@@ -3815,6 +3904,7 @@ int ins_complete(int c, bool enable_pum)
} }
// Work out completion pattern and original text -- webb // Work out completion pattern and original text -- webb
bool line_invalid = false;
if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) { if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) {
if (ctrl_x_mode_function() || ctrl_x_mode_omni() if (ctrl_x_mode_function() || ctrl_x_mode_omni()
|| thesaurus_func_complete(ctrl_x_mode)) { || thesaurus_func_complete(ctrl_x_mode)) {
@@ -3860,6 +3950,7 @@ int ins_complete(int c, bool enable_pum)
// Always add completion for the original text. // Always add completion for the original text.
xfree(compl_orig_text); xfree(compl_orig_text);
compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length); compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length);
int flags = CP_ORIGINAL_TEXT;
if (p_ic) { if (p_ic) {
flags |= CP_ICASE; flags |= CP_ICASE;
} }
@@ -3878,54 +3969,19 @@ int ins_complete(int c, bool enable_pum)
showmode(); showmode();
edit_submode_extra = NULL; edit_submode_extra = NULL;
ui_flush(); ui_flush();
} else if (insert_match && stop_arrow() == FAIL) {
return FAIL; return OK;
}
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;
} }
/// 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 // we found no match if the list has only the "compl_orig_text"-entry
if (compl_first_match == compl_first_match->cp_next) { if (compl_first_match == compl_first_match->cp_next) {
edit_submode_extra = (compl_cont_status & CONT_ADDING) edit_submode_extra = (compl_cont_status & CONT_ADDING)
&& compl_length > 1 && compl_length > 1
? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf); ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf);
edit_submode_highl = HLF_E; 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) { if (edit_submode_extra == NULL) {
@@ -3985,6 +4041,72 @@ int ins_complete(int c, bool enable_pum)
msg_clr_cmdline(); // necessary for "noshowmode" 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. // Show the popup menu, unless we got interrupted.
if (enable_pum && !compl_interrupted) { if (enable_pum && !compl_interrupted) {

View File

@@ -873,6 +873,25 @@ func Test_complete_stop()
close! close!
endfunc 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() func Test_issue_7021()
CheckMSWindows CheckMSWindows