diff options
author | zeertzjq <zeertzjq@outlook.com> | 2022-08-24 20:36:10 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-24 20:36:10 +0800 |
commit | b0fdce55d02c1da7da3257b33b91e974f89eceef (patch) | |
tree | 928b2736fc8e6ee19513d880c7a9cd010c631f89 /src | |
parent | b0569f5813a019d5fe7a921b9d01f26eb1854b8d (diff) | |
download | rneovim-b0fdce55d02c1da7da3257b33b91e974f89eceef.tar.gz rneovim-b0fdce55d02c1da7da3257b33b91e974f89eceef.tar.bz2 rneovim-b0fdce55d02c1da7da3257b33b91e974f89eceef.zip |
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)
https://github.com/vim/vim/commit/6ad84ab3e48d9490e4139df04f2c55b136f5501d
Skip most pum-related refactoring.
Cherry-pick rename to match_at_original_text() from patch 8.2.4001.
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/insexpand.c | 690 |
1 files changed, 350 insertions, 340 deletions
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 5655b31df1..4b0613229b 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -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,40 +818,42 @@ static void ins_compl_longest_match(compl_T *match) ins_compl_delete(); } compl_used_match = false; - } else { - // Reduce the text if this match differs from compl_leader. - p = compl_leader; - s = match->cp_str; - while (*p != NUL) { - c1 = utf_ptr2char((char *)p); - c2 = utf_ptr2char((char *)s); - - if ((match->cp_flags & CP_ICASE) - ? (mb_tolower(c1) != mb_tolower(c2)) - : (c1 != c2)) { - break; - } - MB_PTR_ADV(p); - MB_PTR_ADV(s); - } - if (*p != NUL) { - // Leader was shortened, need to change the inserted text. - *p = NUL; - had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + get_compl_len()); - ins_redraw(false); + return; + } - // When the match isn't there (to avoid matching itself) remove it - // again after redrawing. - if (!had_match) { - ins_compl_delete(); - } + // Reduce the text if this match differs from compl_leader. + p = compl_leader; + s = match->cp_str; + while (*p != NUL) { + c1 = utf_ptr2char((char *)p); + c2 = utf_ptr2char((char *)s); + + if ((match->cp_flags & CP_ICASE) + ? (mb_tolower(c1) != mb_tolower(c2)) + : (c1 != c2)) { + break; } + MB_PTR_ADV(p); + MB_PTR_ADV(s); + } - compl_used_match = false; + if (*p != NUL) { + // Leader was shortened, need to change the inserted text. + *p = NUL; + had_match = (curwin->w_cursor.col > compl_col); + ins_compl_delete(); + ins_bytes(compl_leader + get_compl_len()); + ins_redraw(false); + + // When the match isn't there (to avoid matching itself) remove it + // again after redrawing. + if (!had_match) { + ins_compl_delete(); + } } + + compl_used_match = false; } /// Add an array of matches to the list of matches. @@ -846,20 +878,21 @@ 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; - // 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; - count++; - } - match->cp_next = compl_first_match; - compl_first_match->cp_prev = match; + // Find the end of the list. + 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; + count++; } + match->cp_next = compl_first_match; + compl_first_match->cp_prev = match; + return count; } @@ -898,10 +931,12 @@ static int compl_match_arraysize; /// Remove any popup menu. static void ins_compl_del_pum(void) { - if (compl_match_array != NULL) { - pum_undisplay(false); - XFREE_CLEAR(compl_match_array); + if (compl_match_array == NULL) { + return; } + + pum_undisplay(false); + XFREE_CLEAR(compl_match_array); } /// 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 // 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,20 +1661,20 @@ 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) { - p = NULL; - for (cp = compl_shown_match->cp_next; cp != NULL - && cp != compl_first_match; cp = cp->cp_next) { - if (compl_leader == NULL - || ins_compl_equal(cp, compl_leader, STRLEN(compl_leader))) { - p = cp->cp_str; - break; - } - } - if (p == NULL || (int)STRLEN(p) <= len) { - return; + 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) { + if (compl_leader == NULL + || ins_compl_equal(cp, compl_leader, STRLEN(compl_leader))) { + p = cp->cp_str; + break; } - } else { + } + if (p == NULL || (int)STRLEN(p) <= len) { return; } } @@ -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,6 +2957,64 @@ static int get_next_default_completion(buf_T *ins_buf, pos_T *start_pos, pos_T * return found_new_match; } +/// 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) +{ + int found_new_match = FAIL; + + switch (type) { + case -1: + break; + case CTRL_X_PATH_PATTERNS: + case CTRL_X_PATH_DEFINES: + get_next_include_file_completion(type); + break; + + case CTRL_X_DICTIONARY: + case CTRL_X_THESAURUS: + get_next_dict_tsr_completion(type, st->dict, st->dict_f); + st->dict = NULL; + break; + + case CTRL_X_TAGS: + get_next_tag_completion(); + break; + + case CTRL_X_FILES: + get_next_filename_completion(); + break; + + case CTRL_X_CMDLINE: + case CTRL_X_CMDLINE_CTRL_X: + get_next_cmdline_completion(); + break; + + case CTRL_X_FUNCTION: + case CTRL_X_OMNI: + expand_by_function(type, (char_u *)compl_pattern); + break; + + case CTRL_X_SPELL: + get_next_spell_completion(st->first_match_pos.lnum); + break; + + default: // normal ^P/^N and ^X^L + found_new_match = get_next_default_completion(st, ini); + if (found_new_match == FAIL && st->ins_buf == curbuf) { + st->found_all = true; + } + } + + // check if compl_curr_match has changed, (e.g. other type of + // expansion added something) + if (type != 0 && compl_curr_match != compl_old_match) { + 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. @@ -2955,21 +3024,10 @@ static int get_next_default_completion(buf_T *ins_buf, pos_T *start_pos, pos_T * /// 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; - 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; + static ins_compl_next_state_T st; 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); @@ -2977,31 +3035,30 @@ static int ins_compl_get_exp(pos_T *ini) 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. + 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(ins_buf != NULL); + assert(st.ins_buf != NULL); compl_old_match = compl_curr_match; // remember the last current match - pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos; + 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; - set_match_pos = false; + 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 || 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); + && (!compl_started || st.found_all)) { + int status = process_next_cpt_value(&st, &type, ini); if (status == INS_COMPL_CPT_END) { break; } @@ -3016,55 +3073,8 @@ static int ins_compl_get_exp(pos_T *ini) break; } - switch (type) { - case -1: - break; - case CTRL_X_PATH_PATTERNS: - case CTRL_X_PATH_DEFINES: - get_next_include_file_completion(type); - break; - - case CTRL_X_DICTIONARY: - case CTRL_X_THESAURUS: - get_next_dict_tsr_completion(type, &dict, dict_f); - break; - - case CTRL_X_TAGS: - get_next_tag_completion(); - break; - - case CTRL_X_FILES: - get_next_filename_completion(); - break; - - case CTRL_X_CMDLINE: - case CTRL_X_CMDLINE_CTRL_X: - get_next_cmdline_completion(); - break; - - case CTRL_X_FUNCTION: - case CTRL_X_OMNI: - expand_by_function(type, (char_u *)compl_pattern); - break; - - case CTRL_X_SPELL: - get_next_spell_completion(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; - } - } - - // check if compl_curr_match has changed, (e.g. other type of - // expansion added something) - if (type != 0 && compl_curr_match != compl_old_match) { - found_new_match = OK; - } + // 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 @@ -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) { |