vim-patch:partial:8.2.3953: insert completion code is too complicated (#19923)

Problem:    Insert completion code is too complicated.
Solution:   More refactoring.  Move function arguments into a struct.
            (Yegappan Lakshmanan, closes vim/vim#9437)
6ad84ab3e4

Skip most pum-related refactoring.
Cherry-pick rename to match_at_original_text() from patch 8.2.4001.
This commit is contained in:
zeertzjq
2022-08-24 20:36:10 +08:00
committed by GitHub
parent b0569f5813
commit b0fdce55d0

View File

@@ -140,6 +140,21 @@ struct compl_S {
int cp_number; ///< sequence number
};
/// state information used for getting the next set of insert completion
/// matches.
typedef struct {
char *e_cpt; ///< current entry in 'complete'
buf_T *ins_buf; ///< buffer being scanned
pos_T *cur_match_pos; ///< current match position
pos_T prev_match_pos; ///< previous match position
bool set_match_pos; ///< save first_match_pos/last_match_pos
pos_T first_match_pos; ///< first match position
pos_T last_match_pos; ///< last match position
bool found_all; ///< found all matches of a certain type.
char_u *dict; ///< dictionary file to search
int dict_f; ///< "dict" is an exact file name or not
} ins_compl_next_state_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "insexpand.c.generated.h"
#endif
@@ -162,6 +177,7 @@ static char e_compldel[] = N_("E840: Completion function deleted text");
// "compl_curr_match" points to the currently selected entry.
// "compl_shown_match" is different from compl_curr_match during
// ins_compl_get_exp().
// "compl_old_match" points to previous "compl_curr_match".
static compl_T *compl_first_match = NULL;
static compl_T *compl_curr_match = NULL;
@@ -445,6 +461,12 @@ bool vim_is_ctrl_x_key(int c)
return false;
}
/// @return true if "match" is the original text when the completion began.
static bool match_at_original_text(const compl_T *const match)
{
return match->cp_flags & CP_ORIGINAL_TEXT;
}
/// Check that character "c" is part of the item currently being
/// completed. Used to decide whether to abandon complete mode when the menu
/// is visible.
@@ -479,6 +501,88 @@ bool ins_compl_accept_char(int c)
return vim_iswordc(c);
}
/// Get the completed text by inferring the case of the originally typed text.
static char_u *ins_compl_infercase_gettext(char_u *str, int actual_len, int actual_compl_length,
int min_len)
{
bool has_lower = false;
bool was_letter = false;
// Allocate wide character array for the completion and fill it.
int *const wca = xmalloc((size_t)actual_len * sizeof(*wca));
{
const char_u *p = str;
for (int i = 0; i < actual_len; i++) {
wca[i] = mb_ptr2char_adv(&p);
}
}
// Rule 1: Were any chars converted to lower?
{
const char_u *p = compl_orig_text;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
has_lower = true;
if (mb_isupper(wca[i])) {
// Rule 1 is satisfied.
for (i = actual_compl_length; i < actual_len; i++) {
wca[i] = mb_tolower(wca[i]);
}
break;
}
}
}
}
// Rule 2: No lower case, 2nd consecutive letter converted to
// upper case.
if (!has_lower) {
const char_u *p = compl_orig_text;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (was_letter && mb_isupper(c) && mb_islower(wca[i])) {
// Rule 2 is satisfied.
for (i = actual_compl_length; i < actual_len; i++) {
wca[i] = mb_toupper(wca[i]);
}
break;
}
was_letter = mb_islower(c) || mb_isupper(c);
}
}
// Copy the original case of the part we typed.
{
const char_u *p = compl_orig_text;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
wca[i] = mb_tolower(wca[i]);
} else if (mb_isupper(c)) {
wca[i] = mb_toupper(wca[i]);
}
}
}
// Generate encoding specific output from wide character array.
// Multi-byte characters can occupy up to five bytes more than
// ASCII characters, and we also need one byte for NUL, so stay
// six bytes away from the edge of IObuff.
{
char_u *p = IObuff;
int i = 0;
while (i < actual_len && (p - IObuff + 6) < IOSIZE) {
p += utf_char2bytes(wca[i++], (char *)p);
}
*p = NUL;
}
xfree(wca);
return IObuff;
}
/// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
/// case of the originally typed text is used, and the case of the completed
/// text is inferred, ie this tries to work out what case you probably wanted
@@ -490,12 +594,9 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *str = str_arg;
int i, c;
int actual_len; // Take multi-byte characters
int actual_compl_length; // into account.
int min_len;
bool has_lower = false;
bool was_letter = false;
int flags = 0;
if (p_ic && curbuf->b_p_inf && len > 0) {
@@ -526,79 +627,7 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
min_len = actual_len < actual_compl_length
? actual_len : actual_compl_length;
// Allocate wide character array for the completion and fill it.
int *const wca = xmalloc((size_t)actual_len * sizeof(*wca));
{
const char_u *p = str;
for (i = 0; i < actual_len; i++) {
wca[i] = mb_ptr2char_adv(&p);
}
}
// Rule 1: Were any chars converted to lower?
{
const char_u *p = compl_orig_text;
for (i = 0; i < min_len; i++) {
c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
has_lower = true;
if (mb_isupper(wca[i])) {
// Rule 1 is satisfied.
for (i = actual_compl_length; i < actual_len; i++) {
wca[i] = mb_tolower(wca[i]);
}
break;
}
}
}
}
// Rule 2: No lower case, 2nd consecutive letter converted to
// upper case.
if (!has_lower) {
const char_u *p = compl_orig_text;
for (i = 0; i < min_len; i++) {
c = mb_ptr2char_adv(&p);
if (was_letter && mb_isupper(c) && mb_islower(wca[i])) {
// Rule 2 is satisfied.
for (i = actual_compl_length; i < actual_len; i++) {
wca[i] = mb_toupper(wca[i]);
}
break;
}
was_letter = mb_islower(c) || mb_isupper(c);
}
}
// Copy the original case of the part we typed.
{
const char_u *p = compl_orig_text;
for (i = 0; i < min_len; i++) {
c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
wca[i] = mb_tolower(wca[i]);
} else if (mb_isupper(c)) {
wca[i] = mb_toupper(wca[i]);
}
}
}
// Generate encoding specific output from wide character array.
// Multi-byte characters can occupy up to five bytes more than
// ASCII characters, and we also need one byte for NUL, so stay
// six bytes away from the edge of IObuff.
{
char_u *p = IObuff;
i = 0;
while (i < actual_len && (p - IObuff + 6) < IOSIZE) {
p += utf_char2bytes(wca[i++], (char *)p);
}
*p = NUL;
}
xfree(wca);
str = IObuff;
str = ins_compl_infercase_gettext(str, actual_len, actual_compl_length, min_len);
}
if (cont_s_ipos) {
flags |= CP_CONT_S_IPOS;
@@ -661,7 +690,7 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
if (compl_first_match != NULL && !adup) {
match = compl_first_match;
do {
if (!(match->cp_flags & CP_ORIGINAL_TEXT)
if (!match_at_original_text(match)
&& STRNCMP(match->cp_str, str, len) == 0
&& match->cp_str[len] == NUL) {
FREE_CPTEXT(cptext, cptext_allocated);
@@ -777,6 +806,7 @@ static void ins_compl_longest_match(compl_T *match)
if (compl_leader == NULL) {
// First match, use it as a whole.
compl_leader = vim_strsave(match->cp_str);
had_match = (curwin->w_cursor.col > compl_col);
ins_compl_delete();
ins_bytes(compl_leader + get_compl_len());
@@ -788,7 +818,10 @@ static void ins_compl_longest_match(compl_T *match)
ins_compl_delete();
}
compl_used_match = false;
} else {
return;
}
// Reduce the text if this match differs from compl_leader.
p = compl_leader;
s = match->cp_str;
@@ -822,7 +855,6 @@ static void ins_compl_longest_match(compl_T *match)
compl_used_match = false;
}
}
/// Add an array of matches to the list of matches.
/// Frees matches[].
@@ -846,12 +878,13 @@ static void ins_compl_add_matches(int num_matches, char **matches, int icase)
/// Return the number of matches (excluding the original).
static int ins_compl_make_cyclic(void)
{
compl_T *match;
int count = 0;
if (compl_first_match == NULL) {
return 0;
}
if (compl_first_match != NULL) {
// Find the end of the list.
match = compl_first_match;
compl_T *match = compl_first_match;
int count = 0;
// there's always an entry for the compl_orig_text, it doesn't count.
while (match->cp_next != NULL && match->cp_next != compl_first_match) {
match = match->cp_next;
@@ -859,7 +892,7 @@ static int ins_compl_make_cyclic(void)
}
match->cp_next = compl_first_match;
compl_first_match->cp_prev = match;
}
return count;
}
@@ -898,11 +931,13 @@ static int compl_match_arraysize;
/// Remove any popup menu.
static void ins_compl_del_pum(void)
{
if (compl_match_array != NULL) {
if (compl_match_array == NULL) {
return;
}
pum_undisplay(false);
XFREE_CLEAR(compl_match_array);
}
}
/// Check if the popup menu should be displayed.
bool pum_wanted(void)
@@ -919,15 +954,14 @@ static bool pum_enough_matches(void)
{
// Don't display the popup menu if there are no matches or there is only
// one (ignoring the original text).
compl_T *comp = compl_first_match;
compl_T *compl = compl_first_match;
int i = 0;
do {
if (comp == NULL
|| ((comp->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) {
if (compl == NULL || (!match_at_original_text(compl) && ++i == 2)) {
break;
}
comp = comp->cp_next;
} while (comp != compl_first_match);
compl = compl->cp_next;
} while (compl != compl_first_match);
if (strstr((char *)p_cot, "menuone") != NULL) {
return i >= 1;
@@ -1019,7 +1053,7 @@ void ins_compl_show_pum(void)
lead_len = (int)STRLEN(compl_leader);
}
do {
if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
if (!match_at_original_text(compl)
&& (compl_leader == NULL
|| ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
compl_match_arraysize++;
@@ -1034,14 +1068,14 @@ void ins_compl_show_pum(void)
compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T));
// If the current match is the original text don't find the first
// match after it, don't highlight anything.
if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) {
if (match_at_original_text(compl_shown_match)) {
shown_match_ok = true;
}
i = 0;
compl = compl_first_match;
do {
if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
if (!match_at_original_text(compl)
&& (compl_leader == NULL
|| ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
if (!shown_match_ok) {
@@ -1080,7 +1114,7 @@ void ins_compl_show_pum(void)
// When the original text is the shown match don't set
// compl_shown_match.
if (compl->cp_flags & CP_ORIGINAL_TEXT) {
if (match_at_original_text(compl)) {
shown_match_ok = true;
}
@@ -1255,8 +1289,7 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r
// Read dictionary file line by line.
// Check each line for a match.
while (!got_int && !compl_interrupted
&& !vim_fgets(buf, LSIZE, fp)) {
while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp)) {
ptr = buf;
while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) {
ptr = regmatch->startp[0];
@@ -1603,13 +1636,13 @@ static void ins_compl_set_original_text(char_u *str)
FUNC_ATTR_NONNULL_ALL
{
// Replace the original text entry.
// The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be
// at the last item for backward completion
if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) { // safety check
// The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
// be at the last item for backward completion
if (match_at_original_text(compl_first_match)) { // safety check
xfree(compl_first_match->cp_str);
compl_first_match->cp_str = vim_strsave(str);
} else if (compl_first_match->cp_prev != NULL
&& (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) {
&& match_at_original_text(compl_first_match->cp_prev)) {
xfree(compl_first_match->cp_prev->cp_str);
compl_first_match->cp_prev->cp_str = vim_strsave(str);
}
@@ -1628,7 +1661,10 @@ void ins_compl_addfrommatch(void)
if ((int)STRLEN(p) <= len) { // the match is too short
// When still at the original match use the first entry that matches
// the leader.
if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) {
if (!match_at_original_text(compl_shown_match)) {
return;
}
p = NULL;
for (cp = compl_shown_match->cp_next; cp != NULL
&& cp != compl_first_match; cp = cp->cp_next) {
@@ -1641,9 +1677,6 @@ void ins_compl_addfrommatch(void)
if (p == NULL || (int)STRLEN(p) <= len) {
return;
}
} else {
return;
}
}
p += len;
c = utf_ptr2char((char *)p);
@@ -2451,7 +2484,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
if (ret == OK && compl_first_match != NULL) {
compl_T *match = compl_first_match;
do {
if (!(match->cp_flags & CP_ORIGINAL_TEXT)) {
if (!match_at_original_text(match)) {
dict_T *di = tv_dict_alloc();
tv_list_append_dict(li, di);
@@ -2516,105 +2549,101 @@ enum {
INS_COMPL_CPT_END,
};
/// Process the next 'complete' option value in "e_cpt_arg".
/// Process the next 'complete' option value in st->e_cpt.
///
/// If successful, the arguments are set as below:
/// e_cpt_arg - pointer to the next option value in 'e_cpt_arg'
/// st->cpt - pointer to the next option value in "st->cpt"
/// 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
/// st->found_all - all matches of this type are found
/// st->ins_buf - search for completions in this buffer
/// st->first_match_pos - position of the first completion match
/// st->last_match_pos - position of the last completion match
/// st->set_match_pos - true if the first match position should be saved to
/// avoid loops after the search wraps around.
/// st->dict - name of the dictionary or thesaurus file to search
/// st->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)
/// INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_arg,
pos_T *start_match_pos)
{
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++;
st->found_all = false;
while (*st->e_cpt == ',' || *st->e_cpt == ' ') {
st->e_cpt++;
}
if (*e_cpt == '.' && !curbuf->b_scanned) {
buf = curbuf;
*first_match_pos = *start_match_pos;
if (*st->e_cpt == '.' && !curbuf->b_scanned) {
st->ins_buf = curbuf;
st->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) {
if (ctrl_x_mode_normal() && dec(&st->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));
st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count;
st->first_match_pos.col = (colnr_T)STRLEN(ml_get(st->first_match_pos.lnum));
}
*last_match_pos = *first_match_pos;
st->last_match_pos = st->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) {
st->set_match_pos = true;
} else if (vim_strchr("buwU", *st->e_cpt) != NULL
&& (st->ins_buf = ins_compl_next_buf(st->ins_buf, *st->e_cpt)) != curbuf) {
// Scan a buffer, but not the current one.
if (buf->b_ml.ml_mfp != NULL) { // loaded buffer
if (st->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 = buf->b_ml.ml_line_count + 1;
last_match_pos->lnum = 0;
st->first_match_pos.col = st->last_match_pos.col = 0;
st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count + 1;
st->last_match_pos.lnum = 0;
compl_type = 0;
} else { // unloaded buffer, scan like dictionary
found_all = true;
if (buf->b_fname == NULL) {
st->found_all = true;
if (st->ins_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;
st->dict = (char_u *)st->ins_buf->b_fname;
st->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);
st->ins_buf->b_fname == NULL
? buf_spname(st->ins_buf)
: st->ins_buf->b_sfname == NULL
? st->ins_buf->b_fname
: st->ins_buf->b_sfname);
(void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
} else if (*e_cpt == NUL) {
} else if (*st->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') {
} else if (*st->e_cpt == 'k' || *st->e_cpt == 's') {
if (*st->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;
if (*++st->e_cpt != ',' && *st->e_cpt != NUL) {
st->dict = (char_u *)st->e_cpt;
st->dict_f = DICT_FIRST;
}
} else if (*e_cpt == 'i') {
} else if (*st->e_cpt == 'i') {
compl_type = CTRL_X_PATH_PATTERNS;
} else if (*e_cpt == 'd') {
} else if (*st->e_cpt == 'd') {
compl_type = CTRL_X_PATH_DEFINES;
} else if (*e_cpt == ']' || *e_cpt == 't') {
} else if (*st->e_cpt == ']' || *st->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."));
@@ -2624,21 +2653,16 @@ static int process_next_cpt_value(char **e_cpt_arg, int *compl_type_arg, bool *f
}
// in any case e_cpt is advanced to the next entry
(void)copy_option_part(&e_cpt, (char *)IObuff, IOSIZE, ",");
(void)copy_option_part(&st->e_cpt, (char *)IObuff, IOSIZE, ",");
found_all = true;
st->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;
}
@@ -2656,20 +2680,19 @@ static void get_next_include_file_completion(int compl_type)
/// 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)
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
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,
dict != NULL ? dict_f : 0,
compl_type == CTRL_X_THESAURUS);
}
*dict = NULL;
}
/// Get the next set of tag names matching "compl_pattern".
@@ -2833,29 +2856,18 @@ static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_p
/// 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
/// Search for "compl_pattern" in the buffer "st->ins_buf" starting from the
/// position "st->start_pos" in the "compl_direction" direction. If
/// "st->set_match_pos" is true, then set the "st->first_match_pos" and
/// "st->last_match_pos".
///
/// @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)
static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
{
// 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) {
assert(st->ins_buf);
if (st->ins_buf->b_p_inf) {
p_scs = false;
}
@@ -2864,9 +2876,9 @@ static int get_next_default_completion(buf_T *ins_buf, pos_T *start_pos, pos_T *
// 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) {
if (st->ins_buf != curbuf) {
p_ws = false;
} else if (scan_curbuf) {
} else if (*st->e_cpt == '.') {
p_ws = true;
}
bool looped_around = false;
@@ -2879,62 +2891,61 @@ static int get_next_default_completion(buf_T *ins_buf, pos_T *start_pos, pos_T *
// 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,
found_new_match = search_for_exact_line(st->ins_buf, st->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);
found_new_match = searchit(NULL, st->ins_buf, st->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) {
if (!compl_started || st->set_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) {
st->first_match_pos = *st->cur_match_pos;
st->last_match_pos = *st->cur_match_pos;
st->set_match_pos = false;
} else if (st->first_match_pos.lnum == st->last_match_pos.lnum
&& st->first_match_pos.col == st->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))) {
&& (st->prev_match_pos.lnum > st->cur_match_pos->lnum
|| (st->prev_match_pos.lnum == st->cur_match_pos->lnum
&& st->prev_match_pos.col >= st->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))) {
&& (st->prev_match_pos.lnum < st->cur_match_pos->lnum
|| (st->prev_match_pos.lnum == st->cur_match_pos->lnum
&& st->prev_match_pos.col <= st->cur_match_pos->col))) {
if (looped_around) {
found_new_match = FAIL;
} else {
looped_around = true;
}
}
*prev_match_pos = *cur_match_pos;
st->prev_match_pos = *st->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) {
if ((compl_cont_status & CONT_ADDING) && st->ins_buf == curbuf
&& start_pos->lnum == st->cur_match_pos->lnum
&& start_pos->col == st->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);
char_u *ptr = ins_comp_get_next_word_or_line(st->ins_buf, st->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,
st->ins_buf == curbuf ? NULL : (char_u *)st->ins_buf->b_sfname,
0, cont_s_ipos) != NOTDONE) {
found_new_match = OK;
break;
@@ -2946,75 +2957,11 @@ static int get_next_default_completion(buf_T *ins_buf, pos_T *start_pos, pos_T *
return found_new_match;
}
/// Get the next expansion(s), using "compl_pattern".
/// The search starts at position "ini" in curbuf and in the direction
/// compl_direction.
/// When "compl_started" is false start at that position, otherwise continue
/// where we stopped searching before.
/// This may return before finding all the matches.
/// Return the total number of matches or -1 if still unknown -- Acevedo
static int ins_compl_get_exp(pos_T *ini)
/// get the next set of completion matches for 'type'.
/// @return true if a new match is found, otherwise false.
static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
{
static pos_T first_match_pos;
static pos_T last_match_pos;
static char *e_cpt = ""; // curr. entry in 'complete'
static bool found_all = false; // Found all matches of a
// certain type.
static buf_T *ins_buf = NULL; // buffer being scanned
pos_T *pos;
int i;
int found_new_match;
int type = ctrl_x_mode;
char_u *dict = NULL;
int dict_f = 0;
bool set_match_pos;
pos_T prev_pos = { 0, 0, 0 };
assert(curbuf != NULL);
if (!compl_started) {
FOR_ALL_BUFFERS(buf) {
buf->b_scanned = false;
}
found_all = false;
ins_buf = curbuf;
e_cpt = (compl_cont_status & CONT_LOCAL) ? "." : (char *)curbuf->b_p_cpt;
last_match_pos = first_match_pos = *ini;
} else if (ins_buf != curbuf && !buf_valid(ins_buf)) {
ins_buf = curbuf; // In case the buffer was wiped out.
}
assert(ins_buf != NULL);
compl_old_match = compl_curr_match; // remember the last current match
pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos;
// For ^N/^P loop over all the flags/windows/buffers in 'complete'
for (;;) {
found_new_match = FAIL;
set_match_pos = false;
// For ^N/^P pick a new entry from e_cpt if compl_started is off,
// or if found_all says this entry is done. For ^X^L only use the
// entries from 'complete' that look in loaded buffers.
if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
&& (!compl_started || found_all)) {
int status = process_next_cpt_value(&e_cpt, &type, &found_all,
&ins_buf, ini, &first_match_pos, &last_match_pos,
&set_match_pos, &dict, &dict_f);
if (status == INS_COMPL_CPT_END) {
break;
}
if (status == INS_COMPL_CPT_CONT) {
continue;
}
}
// If complete() was called then compl_pattern has been reset.
// The following won't work then, bail out.
if (compl_pattern == NULL) {
break;
}
int found_new_match = FAIL;
switch (type) {
case -1:
@@ -3026,7 +2973,8 @@ static int ins_compl_get_exp(pos_T *ini)
case CTRL_X_DICTIONARY:
case CTRL_X_THESAURUS:
get_next_dict_tsr_completion(type, &dict, dict_f);
get_next_dict_tsr_completion(type, st->dict, st->dict_f);
st->dict = NULL;
break;
case CTRL_X_TAGS:
@@ -3048,15 +2996,13 @@ static int ins_compl_get_exp(pos_T *ini)
break;
case CTRL_X_SPELL:
get_next_spell_completion(first_match_pos.lnum);
get_next_spell_completion(st->first_match_pos.lnum);
break;
default: // normal ^P/^N and ^X^L
found_new_match = get_next_default_completion(ins_buf, ini, pos, &prev_pos,
&set_match_pos, &first_match_pos,
&last_match_pos, (*e_cpt == '.'));
if (found_new_match == FAIL && ins_buf == curbuf) {
found_all = true;
found_new_match = get_next_default_completion(st, ini);
if (found_new_match == FAIL && st->ins_buf == curbuf) {
st->found_all = true;
}
}
@@ -3066,6 +3012,70 @@ static int ins_compl_get_exp(pos_T *ini)
found_new_match = OK;
}
return found_new_match;
}
/// Get the next expansion(s), using "compl_pattern".
/// The search starts at position "ini" in curbuf and in the direction
/// compl_direction.
/// When "compl_started" is false start at that position, otherwise continue
/// where we stopped searching before.
/// This may return before finding all the matches.
/// Return the total number of matches or -1 if still unknown -- Acevedo
static int ins_compl_get_exp(pos_T *ini)
{
static ins_compl_next_state_T st;
int i;
int found_new_match;
int type = ctrl_x_mode;
assert(curbuf != NULL);
if (!compl_started) {
FOR_ALL_BUFFERS(buf) {
buf->b_scanned = false;
}
st.found_all = false;
st.ins_buf = curbuf;
st.e_cpt = (compl_cont_status & CONT_LOCAL) ? "." : (char *)curbuf->b_p_cpt;
st.last_match_pos = st.first_match_pos = *ini;
} else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf)) {
st.ins_buf = curbuf; // In case the buffer was wiped out.
}
assert(st.ins_buf != NULL);
compl_old_match = compl_curr_match; // remember the last current match
st.cur_match_pos = (compl_direction == FORWARD
? &st.last_match_pos : &st.first_match_pos);
// For ^N/^P loop over all the flags/windows/buffers in 'complete'
for (;;) {
found_new_match = FAIL;
st.set_match_pos = false;
// For ^N/^P pick a new entry from e_cpt if compl_started is off,
// or if found_all says this entry is done. For ^X^L only use the
// entries from 'complete' that look in loaded buffers.
if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
&& (!compl_started || st.found_all)) {
int status = process_next_cpt_value(&st, &type, ini);
if (status == INS_COMPL_CPT_END) {
break;
}
if (status == INS_COMPL_CPT_CONT) {
continue;
}
}
// If complete() was called then compl_pattern has been reset.
// The following won't work then, bail out.
if (compl_pattern == NULL) {
break;
}
// get the next set of completion matches
found_new_match = get_next_completion_match(type, &st, ini);
// break the loop for specialized modes (use 'complete' just for the
// generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new match
if ((ctrl_x_mode_not_default()
@@ -3088,8 +3098,8 @@ static int ins_compl_get_exp(pos_T *ini)
} else {
// Mark a buffer scanned when it has been scanned completely
if (type == 0 || type == CTRL_X_PATH_PATTERNS) {
assert(ins_buf);
ins_buf->b_scanned = true;
assert(st.ins_buf);
st.ins_buf->b_scanned = true;
}
compl_started = false;
@@ -3099,7 +3109,7 @@ static int ins_compl_get_exp(pos_T *ini)
if ((ctrl_x_mode_normal()
|| ctrl_x_mode_line_or_eval())
&& *e_cpt == NUL) { // Got to end of 'complete'
&& *st.e_cpt == NUL) { // Got to end of 'complete'
found_new_match = FAIL;
}
@@ -3178,7 +3188,7 @@ void ins_compl_delete(void)
void ins_compl_insert(bool in_compl_func)
{
ins_bytes(compl_shown_match->cp_str + get_compl_len());
compl_used_match = !(compl_shown_match->cp_flags & CP_ORIGINAL_TEXT);
compl_used_match = !match_at_original_text(compl_shown_match);
dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
@@ -3285,7 +3295,7 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
}
found_end = false;
}
if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0
if (!match_at_original_text(compl_shown_match)
&& compl_leader != NULL
&& !ins_compl_equal(compl_shown_match,
compl_leader, STRLEN(compl_leader))) {
@@ -3340,7 +3350,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
if (compl_leader != NULL
&& (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) {
&& !match_at_original_text(compl_shown_match)) {
// Update "compl_shown_match" to the actually shown match
ins_compl_update_shown_match();
}
@@ -3993,7 +4003,7 @@ static void ins_compl_show_statusmsg(void)
}
if (edit_submode_extra == NULL) {
if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) {
if (match_at_original_text(compl_curr_match)) {
edit_submode_extra = (char_u *)_("Back at original");
edit_submode_highl = HLF_W;
} else if (compl_cont_status & CONT_S_IPOS) {