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 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 #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "insexpand.c.generated.h" # include "insexpand.c.generated.h"
#endif #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_curr_match" points to the currently selected entry.
// "compl_shown_match" is different from compl_curr_match during // "compl_shown_match" is different from compl_curr_match during
// ins_compl_get_exp(). // ins_compl_get_exp().
// "compl_old_match" points to previous "compl_curr_match".
static compl_T *compl_first_match = NULL; static compl_T *compl_first_match = NULL;
static compl_T *compl_curr_match = NULL; static compl_T *compl_curr_match = NULL;
@@ -445,6 +461,12 @@ bool vim_is_ctrl_x_key(int c)
return false; 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 /// Check that character "c" is part of the item currently being
/// completed. Used to decide whether to abandon complete mode when the menu /// completed. Used to decide whether to abandon complete mode when the menu
/// is visible. /// is visible.
@@ -479,6 +501,88 @@ bool ins_compl_accept_char(int c)
return vim_iswordc(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 /// 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 /// 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 /// 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) FUNC_ATTR_NONNULL_ARG(1)
{ {
char_u *str = str_arg; char_u *str = str_arg;
int i, c;
int actual_len; // Take multi-byte characters int actual_len; // Take multi-byte characters
int actual_compl_length; // into account. int actual_compl_length; // into account.
int min_len; int min_len;
bool has_lower = false;
bool was_letter = false;
int flags = 0; int flags = 0;
if (p_ic && curbuf->b_p_inf && len > 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 min_len = actual_len < actual_compl_length
? actual_len : actual_compl_length; ? actual_len : actual_compl_length;
// Allocate wide character array for the completion and fill it. str = ins_compl_infercase_gettext(str, actual_len, actual_compl_length, min_len);
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;
} }
if (cont_s_ipos) { if (cont_s_ipos) {
flags |= CP_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) { if (compl_first_match != NULL && !adup) {
match = compl_first_match; match = compl_first_match;
do { do {
if (!(match->cp_flags & CP_ORIGINAL_TEXT) if (!match_at_original_text(match)
&& STRNCMP(match->cp_str, str, len) == 0 && STRNCMP(match->cp_str, str, len) == 0
&& match->cp_str[len] == NUL) { && match->cp_str[len] == NUL) {
FREE_CPTEXT(cptext, cptext_allocated); FREE_CPTEXT(cptext, cptext_allocated);
@@ -777,6 +806,7 @@ static void ins_compl_longest_match(compl_T *match)
if (compl_leader == NULL) { if (compl_leader == NULL) {
// First match, use it as a whole. // First match, use it as a whole.
compl_leader = vim_strsave(match->cp_str); compl_leader = vim_strsave(match->cp_str);
had_match = (curwin->w_cursor.col > compl_col); had_match = (curwin->w_cursor.col > compl_col);
ins_compl_delete(); ins_compl_delete();
ins_bytes(compl_leader + get_compl_len()); ins_bytes(compl_leader + get_compl_len());
@@ -788,7 +818,10 @@ static void ins_compl_longest_match(compl_T *match)
ins_compl_delete(); ins_compl_delete();
} }
compl_used_match = false; compl_used_match = false;
} else {
return;
}
// Reduce the text if this match differs from compl_leader. // Reduce the text if this match differs from compl_leader.
p = compl_leader; p = compl_leader;
s = match->cp_str; s = match->cp_str;
@@ -821,7 +854,6 @@ static void ins_compl_longest_match(compl_T *match)
} }
compl_used_match = false; compl_used_match = false;
}
} }
/// Add an array of matches to the list of matches. /// Add an array of matches to the list of 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). /// Return the number of matches (excluding the original).
static int ins_compl_make_cyclic(void) static int ins_compl_make_cyclic(void)
{ {
compl_T *match; if (compl_first_match == NULL) {
int count = 0; return 0;
}
if (compl_first_match != NULL) {
// Find the end of the list. // 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. // 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) { while (match->cp_next != NULL && match->cp_next != compl_first_match) {
match = match->cp_next; match = match->cp_next;
@@ -859,7 +892,7 @@ static int ins_compl_make_cyclic(void)
} }
match->cp_next = compl_first_match; match->cp_next = compl_first_match;
compl_first_match->cp_prev = match; compl_first_match->cp_prev = match;
}
return count; return count;
} }
@@ -898,10 +931,12 @@ static int compl_match_arraysize;
/// Remove any popup menu. /// Remove any popup menu.
static void ins_compl_del_pum(void) static void ins_compl_del_pum(void)
{ {
if (compl_match_array != NULL) { if (compl_match_array == NULL) {
return;
}
pum_undisplay(false); pum_undisplay(false);
XFREE_CLEAR(compl_match_array); XFREE_CLEAR(compl_match_array);
}
} }
/// Check if the popup menu should be displayed. /// Check if the popup menu should be displayed.
@@ -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 // Don't display the popup menu if there are no matches or there is only
// one (ignoring the original text). // one (ignoring the original text).
compl_T *comp = compl_first_match; compl_T *compl = compl_first_match;
int i = 0; int i = 0;
do { do {
if (comp == NULL if (compl == NULL || (!match_at_original_text(compl) && ++i == 2)) {
|| ((comp->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) {
break; break;
} }
comp = comp->cp_next; compl = compl->cp_next;
} while (comp != compl_first_match); } while (compl != compl_first_match);
if (strstr((char *)p_cot, "menuone") != NULL) { if (strstr((char *)p_cot, "menuone") != NULL) {
return i >= 1; return i >= 1;
@@ -1019,7 +1053,7 @@ void ins_compl_show_pum(void)
lead_len = (int)STRLEN(compl_leader); lead_len = (int)STRLEN(compl_leader);
} }
do { do {
if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 if (!match_at_original_text(compl)
&& (compl_leader == NULL && (compl_leader == NULL
|| ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
compl_match_arraysize++; 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)); 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 // If the current match is the original text don't find the first
// match after it, don't highlight anything. // 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; shown_match_ok = true;
} }
i = 0; i = 0;
compl = compl_first_match; compl = compl_first_match;
do { do {
if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0 if (!match_at_original_text(compl)
&& (compl_leader == NULL && (compl_leader == NULL
|| ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
if (!shown_match_ok) { 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 // When the original text is the shown match don't set
// compl_shown_match. // compl_shown_match.
if (compl->cp_flags & CP_ORIGINAL_TEXT) { if (match_at_original_text(compl)) {
shown_match_ok = true; 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. // Read dictionary file line by line.
// Check each line for a match. // Check each line for a match.
while (!got_int && !compl_interrupted while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp)) {
&& !vim_fgets(buf, LSIZE, fp)) {
ptr = buf; ptr = buf;
while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) { while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) {
ptr = regmatch->startp[0]; ptr = regmatch->startp[0];
@@ -1603,13 +1636,13 @@ static void ins_compl_set_original_text(char_u *str)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
// Replace the original text entry. // Replace the original text entry.
// The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
// at the last item for backward completion // be at the last item for backward completion
if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) { // safety check if (match_at_original_text(compl_first_match)) { // safety check
xfree(compl_first_match->cp_str); xfree(compl_first_match->cp_str);
compl_first_match->cp_str = vim_strsave(str); compl_first_match->cp_str = vim_strsave(str);
} else if (compl_first_match->cp_prev != NULL } 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); xfree(compl_first_match->cp_prev->cp_str);
compl_first_match->cp_prev->cp_str = vim_strsave(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 if ((int)STRLEN(p) <= len) { // the match is too short
// When still at the original match use the first entry that matches // When still at the original match use the first entry that matches
// the leader. // the leader.
if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) { if (!match_at_original_text(compl_shown_match)) {
return;
}
p = NULL; p = NULL;
for (cp = compl_shown_match->cp_next; cp != NULL for (cp = compl_shown_match->cp_next; cp != NULL
&& cp != compl_first_match; cp = cp->cp_next) { && cp != compl_first_match; cp = cp->cp_next) {
@@ -1641,9 +1677,6 @@ void ins_compl_addfrommatch(void)
if (p == NULL || (int)STRLEN(p) <= len) { if (p == NULL || (int)STRLEN(p) <= len) {
return; return;
} }
} else {
return;
}
} }
p += len; p += len;
c = utf_ptr2char((char *)p); 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) { if (ret == OK && compl_first_match != NULL) {
compl_T *match = compl_first_match; compl_T *match = compl_first_match;
do { do {
if (!(match->cp_flags & CP_ORIGINAL_TEXT)) { if (!match_at_original_text(match)) {
dict_T *di = tv_dict_alloc(); dict_T *di = tv_dict_alloc();
tv_list_append_dict(li, di); tv_list_append_dict(li, di);
@@ -2516,105 +2549,101 @@ enum {
INS_COMPL_CPT_END, 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: /// 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 /// compl_type_arg - type of insert mode completion to use
/// found_all_arg - all matches of this type are found /// st->found_all - all matches of this type are found
/// buf_arg - search for completions in this buffer /// st->ins_buf - search for completions in this buffer
/// first_match_pos - position of the first completion match /// st->first_match_pos - position of the first completion match
/// last_match_pos - position of the last completion match /// st->last_match_pos - position of the last completion match
/// set_match_pos - true if the first match position should be saved to avoid /// st->set_match_pos - true if the first match position should be saved to
/// loops after the search wraps around. /// avoid loops after the search wraps around.
/// dict - name of the dictionary or thesaurus file to search /// st->dict - name of the dictionary or thesaurus file to search
/// dict_f - flag specifying whether "dict" is an exact file name or not /// 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. /// @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 /// INS_COMPL_CPT_CONT to skip the current value and process the next
/// option value. /// option value.
/// INS_COMPL_CPT_END if all the values in "e_cpt" are processed. /// INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
static int process_next_cpt_value(char **e_cpt_arg, int *compl_type_arg, bool *found_all_arg, static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_arg,
buf_T **buf_arg, pos_T *start_match_pos, pos_T *first_match_pos, pos_T *start_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 compl_type = -1;
int status = INS_COMPL_CPT_OK; 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 == ' ') { st->found_all = false;
e_cpt++;
while (*st->e_cpt == ',' || *st->e_cpt == ' ') {
st->e_cpt++;
} }
if (*e_cpt == '.' && !curbuf->b_scanned) {
buf = curbuf; if (*st->e_cpt == '.' && !curbuf->b_scanned) {
*first_match_pos = *start_match_pos; st->ins_buf = curbuf;
st->first_match_pos = *start_match_pos;
// Move the cursor back one character so that ^N can match the // Move the cursor back one character so that ^N can match the
// word immediately after the cursor. // 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 // Move the cursor to after the last character in the
// buffer, so that word at start of buffer is found // buffer, so that word at start of buffer is found
// correctly. // correctly.
first_match_pos->lnum = buf->b_ml.ml_line_count; st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count;
first_match_pos->col = (colnr_T)STRLEN(ml_get(first_match_pos->lnum)); 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; compl_type = 0;
// Remember the first match so that the loop stops when we // Remember the first match so that the loop stops when we
// wrap and come back there a second time. // wrap and come back there a second time.
*set_match_pos = true; st->set_match_pos = true;
} else if (vim_strchr("buwU", *e_cpt) != NULL } else if (vim_strchr("buwU", *st->e_cpt) != NULL
&& (buf = ins_compl_next_buf(buf, *e_cpt)) != curbuf) { && (st->ins_buf = ins_compl_next_buf(st->ins_buf, *st->e_cpt)) != curbuf) {
// Scan a buffer, but not the current one. // 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; compl_started = true;
first_match_pos->col = last_match_pos->col = 0; st->first_match_pos.col = st->last_match_pos.col = 0;
first_match_pos->lnum = buf->b_ml.ml_line_count + 1; st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count + 1;
last_match_pos->lnum = 0; st->last_match_pos.lnum = 0;
compl_type = 0; compl_type = 0;
} else { // unloaded buffer, scan like dictionary } else { // unloaded buffer, scan like dictionary
found_all = true; st->found_all = true;
if (buf->b_fname == NULL) { if (st->ins_buf->b_fname == NULL) {
status = INS_COMPL_CPT_CONT; status = INS_COMPL_CPT_CONT;
goto done; goto done;
} }
compl_type = CTRL_X_DICTIONARY; compl_type = CTRL_X_DICTIONARY;
dict = (char_u *)buf->b_fname; st->dict = (char_u *)st->ins_buf->b_fname;
dict_f = DICT_EXACT; st->dict_f = DICT_EXACT;
} }
msg_hist_off = true; // reset in msg_trunc_attr() msg_hist_off = true; // reset in msg_trunc_attr()
vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"), vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
buf->b_fname == NULL st->ins_buf->b_fname == NULL
? buf_spname(buf) ? buf_spname(st->ins_buf)
: buf->b_sfname == NULL : st->ins_buf->b_sfname == NULL
? buf->b_fname ? st->ins_buf->b_fname
: buf->b_sfname); : st->ins_buf->b_sfname);
(void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R)); (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; status = INS_COMPL_CPT_END;
} else { } else {
if (ctrl_x_mode_line_or_eval()) { if (ctrl_x_mode_line_or_eval()) {
compl_type = -1; compl_type = -1;
} else if (*e_cpt == 'k' || *e_cpt == 's') { } else if (*st->e_cpt == 'k' || *st->e_cpt == 's') {
if (*e_cpt == 'k') { if (*st->e_cpt == 'k') {
compl_type = CTRL_X_DICTIONARY; compl_type = CTRL_X_DICTIONARY;
} else { } else {
compl_type = CTRL_X_THESAURUS; compl_type = CTRL_X_THESAURUS;
} }
if (*++e_cpt != ',' && *e_cpt != NUL) { if (*++st->e_cpt != ',' && *st->e_cpt != NUL) {
dict = (char_u *)e_cpt; st->dict = (char_u *)st->e_cpt;
dict_f = DICT_FIRST; st->dict_f = DICT_FIRST;
} }
} else if (*e_cpt == 'i') { } else if (*st->e_cpt == 'i') {
compl_type = CTRL_X_PATH_PATTERNS; compl_type = CTRL_X_PATH_PATTERNS;
} else if (*e_cpt == 'd') { } else if (*st->e_cpt == 'd') {
compl_type = CTRL_X_PATH_DEFINES; 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() msg_hist_off = true; // reset in msg_trunc_attr()
compl_type = CTRL_X_TAGS; compl_type = CTRL_X_TAGS;
vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning 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 // 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) { if (compl_type == -1) {
status = INS_COMPL_CPT_CONT; status = INS_COMPL_CPT_CONT;
} }
} }
done: done:
*e_cpt_arg = e_cpt;
*compl_type_arg = compl_type; *compl_type_arg = compl_type;
*found_all_arg = found_all;
*buf_arg = buf;
*dict_arg = dict;
*dict_flag = dict_f;
return status; 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 /// Get the next set of words matching "compl_pattern" in dictionary or
/// thesaurus files. /// 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)) { if (thesaurus_func_complete(compl_type)) {
expand_by_function(compl_type, (char_u *)compl_pattern); expand_by_function(compl_type, (char_u *)compl_pattern);
} else { } else {
ins_compl_dictionaries(*dict != NULL ? *dict ins_compl_dictionaries(dict != NULL ? dict
: (compl_type == CTRL_X_THESAURUS : (compl_type == CTRL_X_THESAURUS
? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr) ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
: (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)), : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
(char_u *)compl_pattern, (char_u *)compl_pattern,
*dict != NULL ? dict_f : 0, dict != NULL ? dict_f : 0,
compl_type == CTRL_X_THESAURUS); compl_type == CTRL_X_THESAURUS);
} }
*dict = NULL;
} }
/// Get the next set of tag names matching "compl_pattern". /// 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) /// 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 "st->ins_buf" starting from the
/// position "start_pos" in the "compl_direction" direction. If "save_match_pos" /// position "st->start_pos" in the "compl_direction" direction. If
/// is true, then set the "first_match_pos" and "last_match_pos". /// "st->set_match_pos" is true, then set the "st->first_match_pos" and
/// /// "st->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. /// @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, static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_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 // If 'infercase' is set, don't use 'smartcase' here
const int save_p_scs = p_scs; const int save_p_scs = p_scs;
assert(ins_buf); assert(st->ins_buf);
if (ins_buf->b_p_inf) { if (st->ins_buf->b_p_inf) {
p_scs = false; 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 // buffers is a good idea, on the other hand, we always set
// wrapscan for curbuf to avoid missing matches -- Acevedo,Webb // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
const int save_p_ws = p_ws; const int save_p_ws = p_ws;
if (ins_buf != curbuf) { if (st->ins_buf != curbuf) {
p_ws = false; p_ws = false;
} else if (scan_curbuf) { } else if (*st->e_cpt == '.') {
p_ws = true; p_ws = true;
} }
bool looped_around = false; 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. // has added a word that was at the beginning of the line.
if (ctrl_x_mode_line_or_eval() if (ctrl_x_mode_line_or_eval()
|| (compl_cont_status & CONT_SOL)) { || (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); compl_direction, (char_u *)compl_pattern);
} else { } else {
found_new_match = searchit(NULL, ins_buf, cur_match_pos, NULL, found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
compl_direction, NULL, compl_direction, (char_u *)compl_pattern, 1L,
(char_u *)compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
RE_LAST, NULL);
} }
msg_silent--; msg_silent--;
if (!compl_started || *save_match_pos) { if (!compl_started || st->set_match_pos) {
// set "compl_started" even on fail // set "compl_started" even on fail
compl_started = true; compl_started = true;
*first_match_pos = *cur_match_pos; st->first_match_pos = *st->cur_match_pos;
*last_match_pos = *cur_match_pos; st->last_match_pos = *st->cur_match_pos;
*save_match_pos = false; st->set_match_pos = false;
} else if (first_match_pos->lnum == last_match_pos->lnum } else if (st->first_match_pos.lnum == st->last_match_pos.lnum
&& first_match_pos->col == last_match_pos->col) { && st->first_match_pos.col == st->last_match_pos.col) {
found_new_match = FAIL; found_new_match = FAIL;
} else if ((compl_direction == FORWARD) } else if ((compl_direction == FORWARD)
&& (prev_match_pos->lnum > cur_match_pos->lnum && (st->prev_match_pos.lnum > st->cur_match_pos->lnum
|| (prev_match_pos->lnum == cur_match_pos->lnum || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
&& prev_match_pos->col >= cur_match_pos->col))) { && st->prev_match_pos.col >= st->cur_match_pos->col))) {
if (looped_around) { if (looped_around) {
found_new_match = FAIL; found_new_match = FAIL;
} else { } else {
looped_around = true; looped_around = true;
} }
} else if ((compl_direction != FORWARD) } else if ((compl_direction != FORWARD)
&& (prev_match_pos->lnum < cur_match_pos->lnum && (st->prev_match_pos.lnum < st->cur_match_pos->lnum
|| (prev_match_pos->lnum == cur_match_pos->lnum || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
&& prev_match_pos->col <= cur_match_pos->col))) { && st->prev_match_pos.col <= st->cur_match_pos->col))) {
if (looped_around) { if (looped_around) {
found_new_match = FAIL; found_new_match = FAIL;
} else { } else {
looped_around = true; looped_around = true;
} }
} }
*prev_match_pos = *cur_match_pos; st->prev_match_pos = *st->cur_match_pos;
if (found_new_match == FAIL) { if (found_new_match == FAIL) {
break; break;
} }
// when ADDING, the text before the cursor matches, skip it // when ADDING, the text before the cursor matches, skip it
if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf if ((compl_cont_status & CONT_ADDING) && st->ins_buf == curbuf
&& start_pos->lnum == cur_match_pos->lnum && start_pos->lnum == st->cur_match_pos->lnum
&& start_pos->col == cur_match_pos->col) { && start_pos->col == st->cur_match_pos->col) {
continue; continue;
} }
int len; int len;
char_u *ptr = ins_comp_get_next_word_or_line(ins_buf, cur_match_pos, &len, char_u *ptr = ins_comp_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
&cont_s_ipos); &len, &cont_s_ipos);
if (ptr == NULL) { if (ptr == NULL) {
continue; 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, st->ins_buf == curbuf ? NULL : (char_u *)st->ins_buf->b_sfname,
0, cont_s_ipos) != NOTDONE) { 0, cont_s_ipos) != NOTDONE) {
found_new_match = OK; found_new_match = OK;
break; 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; return found_new_match;
} }
/// Get the next expansion(s), using "compl_pattern". /// get the next set of completion matches for 'type'.
/// The search starts at position "ini" in curbuf and in the direction /// @return true if a new match is found, otherwise false.
/// compl_direction. static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
/// 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 pos_T first_match_pos; int found_new_match = FAIL;
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;
}
switch (type) { switch (type) {
case -1: case -1:
@@ -3026,7 +2973,8 @@ static int ins_compl_get_exp(pos_T *ini)
case CTRL_X_DICTIONARY: case CTRL_X_DICTIONARY:
case CTRL_X_THESAURUS: 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; break;
case CTRL_X_TAGS: case CTRL_X_TAGS:
@@ -3048,15 +2996,13 @@ static int ins_compl_get_exp(pos_T *ini)
break; break;
case CTRL_X_SPELL: case CTRL_X_SPELL:
get_next_spell_completion(first_match_pos.lnum); get_next_spell_completion(st->first_match_pos.lnum);
break; break;
default: // normal ^P/^N and ^X^L default: // normal ^P/^N and ^X^L
found_new_match = get_next_default_completion(ins_buf, ini, pos, &prev_pos, found_new_match = get_next_default_completion(st, ini);
&set_match_pos, &first_match_pos, if (found_new_match == FAIL && st->ins_buf == curbuf) {
&last_match_pos, (*e_cpt == '.')); st->found_all = true;
if (found_new_match == FAIL && ins_buf == curbuf) {
found_all = true;
} }
} }
@@ -3066,6 +3012,70 @@ static int ins_compl_get_exp(pos_T *ini)
found_new_match = OK; 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 // 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 // generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new match
if ((ctrl_x_mode_not_default() if ((ctrl_x_mode_not_default()
@@ -3088,8 +3098,8 @@ static int ins_compl_get_exp(pos_T *ini)
} else { } else {
// Mark a buffer scanned when it has been scanned completely // Mark a buffer scanned when it has been scanned completely
if (type == 0 || type == CTRL_X_PATH_PATTERNS) { if (type == 0 || type == CTRL_X_PATH_PATTERNS) {
assert(ins_buf); assert(st.ins_buf);
ins_buf->b_scanned = true; st.ins_buf->b_scanned = true;
} }
compl_started = false; compl_started = false;
@@ -3099,7 +3109,7 @@ static int ins_compl_get_exp(pos_T *ini)
if ((ctrl_x_mode_normal() if ((ctrl_x_mode_normal()
|| ctrl_x_mode_line_or_eval()) || 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; found_new_match = FAIL;
} }
@@ -3178,7 +3188,7 @@ void ins_compl_delete(void)
void ins_compl_insert(bool in_compl_func) void ins_compl_insert(bool in_compl_func)
{ {
ins_bytes(compl_shown_match->cp_str + get_compl_len()); 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); dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
set_vim_var_dict(VV_COMPLETED_ITEM, dict); 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; found_end = false;
} }
if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0 if (!match_at_original_text(compl_shown_match)
&& compl_leader != NULL && compl_leader != NULL
&& !ins_compl_equal(compl_shown_match, && !ins_compl_equal(compl_shown_match,
compl_leader, STRLEN(compl_leader))) { 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 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 // Update "compl_shown_match" to the actually shown match
ins_compl_update_shown_match(); ins_compl_update_shown_match();
} }
@@ -3993,7 +4003,7 @@ static void ins_compl_show_statusmsg(void)
} }
if (edit_submode_extra == NULL) { 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_extra = (char_u *)_("Back at original");
edit_submode_highl = HLF_W; edit_submode_highl = HLF_W;
} else if (compl_cont_status & CONT_S_IPOS) { } else if (compl_cont_status & CONT_S_IPOS) {