diff options
Diffstat (limited to 'src/nvim/insexpand.c')
-rw-r--r-- | src/nvim/insexpand.c | 416 |
1 files changed, 227 insertions, 189 deletions
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 6de3b0a9d0..12543a2d42 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // insexpand.c: functions for Insert mode completion #include <assert.h> @@ -12,7 +9,7 @@ #include <stdlib.h> #include <string.h> -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/change.h" @@ -23,23 +20,21 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" -#include "nvim/ex_cmds_defs.h" -#include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" +#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" -#include "nvim/highlight_defs.h" +#include "nvim/highlight.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/insexpand.h" #include "nvim/keycodes.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -47,24 +42,26 @@ #include "nvim/message.h" #include "nvim/move.h" #include "nvim/option.h" +#include "nvim/option_defs.h" +#include "nvim/option_vars.h" #include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/time.h" #include "nvim/path.h" #include "nvim/popupmenu.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/regexp.h" -#include "nvim/screen.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/tag.h" #include "nvim/textformat.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/undo.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" +#include "nvim/window.h" // Definitions used for CTRL-X submode. // Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] @@ -91,6 +88,7 @@ enum { CTRL_X_LOCAL_MSG = 15, ///< only used in "ctrl_x_msgs" CTRL_X_EVAL = 16, ///< for builtin function complete() CTRL_X_CMDLINE_CTRL_X = 17, ///< CTRL-X typed in CTRL_X_CMDLINE + CTRL_X_BUFNAMES = 18, }; #define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] @@ -162,7 +160,8 @@ struct compl_S { /// state information used for getting the next set of insert completion /// matches. typedef struct { - char *e_cpt; ///< current entry in 'complete' + char *e_cpt_copy; ///< copy of 'complete' + char *e_cpt; ///< current entry in "e_cpt_copy" buf_T *ins_buf; ///< buffer being scanned pos_T *cur_match_pos; ///< current match position pos_T prev_match_pos; ///< previous match position @@ -188,8 +187,8 @@ typedef enum { CP_FAST = 32, ///< use fast_breakcheck instead of os_breakcheck } cp_flags_T; -static char e_hitend[] = N_("Hit end of paragraph"); -static char e_compldel[] = N_("E840: Completion function deleted text"); +static const char e_hitend[] = N_("Hit end of paragraph"); +static const char e_compldel[] = N_("E840: Completion function deleted text"); // All the current matches are stored in a list. // "compl_first_match" points to the start of the list. @@ -466,14 +465,13 @@ bool check_compl_option(bool dict_opt) && *curbuf->b_p_tsrfu == NUL && *p_tsrfu == NUL)) { ctrl_x_mode = CTRL_X_NORMAL; edit_submode = NULL; - msg_attr((dict_opt - ? _("'dictionary' option is empty") - : _("'thesaurus' option is empty")), HL_ATTR(HLF_E)); + msg((dict_opt ? _("'dictionary' option is empty") : _("'thesaurus' option is empty")), + HL_ATTR(HLF_E)); if (emsg_silent == 0 && !in_assert_fails) { vim_beep(BO_COMPL); setcursor(); ui_flush(); - os_delay(2004L, false); + os_delay(2004, false); } return false; } @@ -536,6 +534,8 @@ bool vim_is_ctrl_x_key(int c) return c == Ctrl_S || c == Ctrl_P || c == Ctrl_N; case CTRL_X_EVAL: return (c == Ctrl_P || c == Ctrl_N); + case CTRL_X_BUFNAMES: + return (c == Ctrl_P || c == Ctrl_N); } internal_error("vim_is_ctrl_x_key()"); return false; @@ -909,8 +909,6 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len) /// Reduce the longest common string for match "match". static void ins_compl_longest_match(compl_T *match) { - char *p, *s; - int c1, c2; int had_match; if (compl_leader == NULL) { @@ -933,11 +931,11 @@ static void ins_compl_longest_match(compl_T *match) } // Reduce the text if this match differs from compl_leader. - p = compl_leader; - s = match->cp_str; + char *p = compl_leader; + char *s = match->cp_str; while (*p != NUL) { - c1 = utf_ptr2char(p); - c2 = utf_ptr2char(s); + int c1 = utf_ptr2char(p); + int c2 = utf_ptr2char(s); if ((match->cp_flags & CP_ICASE) ? (mb_tolower(c1) != mb_tolower(c2)) @@ -1068,14 +1066,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 *compl = compl_first_match; + compl_T *comp = compl_first_match; int i = 0; do { - if (compl == NULL || (!match_at_original_text(compl) && ++i == 2)) { + if (comp == NULL || (!match_at_original_text(comp) && ++i == 2)) { break; } - compl = compl->cp_next; - } while (!is_first_match(compl)); + comp = comp->cp_next; + } while (!is_first_match(comp)); if (strstr(p_cot, "menuone") != NULL) { return i >= 1; @@ -1139,7 +1137,7 @@ static int ins_compl_build_pum(void) { // Need to build the popup menu list. compl_match_arraysize = 0; - compl_T *compl = compl_first_match; + compl_T *comp = compl_first_match; // If it's user complete function and refresh_always, // do not use "compl_leader" as prefix filter. @@ -1150,13 +1148,13 @@ static int ins_compl_build_pum(void) const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0; do { - if (!match_at_original_text(compl) + if (!match_at_original_text(comp) && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { + || ins_compl_equal(comp, compl_leader, (size_t)lead_len))) { compl_match_arraysize++; } - compl = compl->cp_next; - } while (compl != NULL && !is_first_match(compl)); + comp = comp->cp_next; + } while (comp != NULL && !is_first_match(comp)); if (compl_match_arraysize == 0) { return -1; @@ -1173,46 +1171,46 @@ static int ins_compl_build_pum(void) bool did_find_shown_match = false; int cur = -1; int i = 0; - compl = compl_first_match; + comp = compl_first_match; do { - if (!match_at_original_text(compl) + if (!match_at_original_text(comp) && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) { + || ins_compl_equal(comp, compl_leader, (size_t)lead_len))) { if (!shown_match_ok) { - if (compl == compl_shown_match || did_find_shown_match) { + if (comp == compl_shown_match || did_find_shown_match) { // This item is the shown match or this is the // first displayed item after the shown match. - compl_shown_match = compl; + compl_shown_match = comp; did_find_shown_match = true; shown_match_ok = true; } else { // Remember this displayed match for when the // shown match is just below it. - shown_compl = compl; + shown_compl = comp; } cur = i; } - if (compl->cp_text[CPT_ABBR] != NULL) { - compl_match_array[i].pum_text = compl->cp_text[CPT_ABBR]; + if (comp->cp_text[CPT_ABBR] != NULL) { + compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR]; } else { - compl_match_array[i].pum_text = compl->cp_str; + compl_match_array[i].pum_text = comp->cp_str; } - compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; - compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; - if (compl->cp_text[CPT_MENU] != NULL) { - compl_match_array[i++].pum_extra = compl->cp_text[CPT_MENU]; + compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND]; + compl_match_array[i].pum_info = comp->cp_text[CPT_INFO]; + if (comp->cp_text[CPT_MENU] != NULL) { + compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU]; } else { - compl_match_array[i++].pum_extra = compl->cp_fname; + compl_match_array[i++].pum_extra = comp->cp_fname; } } - if (compl == compl_shown_match) { + if (comp == compl_shown_match) { did_find_shown_match = true; // When the original text is the shown match don't set // compl_shown_match. - if (match_at_original_text(compl)) { + if (match_at_original_text(comp)) { shown_match_ok = true; } @@ -1223,8 +1221,8 @@ static int ins_compl_build_pum(void) shown_match_ok = true; } } - compl = compl->cp_next; - } while (compl != NULL && !is_first_match(compl)); + comp = comp->cp_next; + } while (comp != NULL && !is_first_match(comp)); if (!shown_match_ok) { // no displayed match at all cur = -1; @@ -1241,9 +1239,6 @@ void ins_compl_show_pum(void) return; } - // Dirty hard-coded hack: remove any matchparen highlighting. - do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif"); - // Update the screen before drawing the popup menu over it. update_screen(); @@ -1298,11 +1293,9 @@ static void ins_compl_dictionaries(char *dict_start, char *pat, int flags, int t { char *dict = dict_start; char *ptr; - char *buf; regmatch_T regmatch; char **files; int count; - int save_p_scs; Direction dir = compl_direction; if (*dict == NUL) { @@ -1315,11 +1308,11 @@ static void ins_compl_dictionaries(char *dict_start, char *pat, int flags, int t } } - buf = xmalloc(LSIZE); + char *buf = xmalloc(LSIZE); regmatch.regprog = NULL; // so that we can goto theend // If 'infercase' is set, don't use 'smartcase' here - save_p_scs = p_scs; + int save_p_scs = p_scs; if (curbuf->b_p_inf) { p_scs = false; } @@ -1415,7 +1408,7 @@ static int thesaurus_add_words_in_line(char *fname, char **buf_arg, int dir, con // different classes, only separate words // with single-byte non-word characters. while (*ptr != NUL) { - const int l = utfc_ptr2len((const char *)ptr); + const int l = utfc_ptr2len(ptr); if (l < 2 && !vim_iswordc((uint8_t)(*ptr))) { break; @@ -1443,18 +1436,13 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r char *buf, Direction *dir) FUNC_ATTR_NONNULL_ARG(2, 7) { - char *ptr; - int i; - FILE *fp; - int add_r; - - for (i = 0; i < count && !got_int && !compl_interrupted; i++) { - fp = os_fopen(files[i], "r"); // open dictionary file + for (int i = 0; i < count && !got_int && !compl_interrupted; i++) { + FILE *fp = os_fopen(files[i], "r"); // open dictionary file if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN)) { - msg_hist_off = true; // reset in msg_trunc_attr() + msg_hist_off = true; // reset in msg_trunc() vim_snprintf(IObuff, IOSIZE, _("Scanning dictionary: %s"), files[i]); - (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); + (void)msg_trunc(IObuff, true, HL_ATTR(HLF_R)); } if (fp == NULL) { @@ -1464,7 +1452,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)) { - ptr = buf; + char *ptr = buf; while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) { ptr = regmatch->startp[0]; if (ctrl_x_mode_line_or_eval()) { @@ -1472,9 +1460,9 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r } else { ptr = find_word_end(ptr); } - add_r = ins_compl_add_infercase(regmatch->startp[0], - (int)(ptr - regmatch->startp[0]), - p_ic, files[i], *dir, false); + int add_r = ins_compl_add_infercase(regmatch->startp[0], + (int)(ptr - regmatch->startp[0]), + p_ic, files[i], *dir, false); if (thesaurus) { // For a thesaurus, add all the words in the line ptr = buf; @@ -1532,9 +1520,7 @@ char *find_word_end(char *ptr) /// @return a pointer to just after the line. static char *find_line_end(char *ptr) { - char *s; - - s = ptr + strlen(ptr); + char *s = ptr + strlen(ptr); while (s > ptr && (s[-1] == CAR || s[-1] == NL)) { s--; } @@ -1544,8 +1530,6 @@ static char *find_line_end(char *ptr) /// Free the list of completions static void ins_compl_free(void) { - compl_T *match; - XFREE_CLEAR(compl_pattern); XFREE_CLEAR(compl_leader); @@ -1558,7 +1542,7 @@ static void ins_compl_free(void) compl_curr_match = compl_first_match; do { - match = compl_curr_match; + compl_T *match = compl_curr_match; compl_curr_match = compl_curr_match->cp_next; xfree(match->cp_str); // several entries may use the same fname, free it just once. @@ -1749,9 +1733,9 @@ void ins_compl_addleader(int c) return; } if ((cc = utf_char2len(c)) > 1) { - char buf[MB_MAXBYTES + 1]; + char buf[MB_MAXCHAR + 1]; - utf_char2bytes(c, (char *)buf); + utf_char2bytes(c, buf); buf[cc] = NUL; ins_char_bytes(buf, (size_t)cc); } else { @@ -1805,12 +1789,9 @@ static void ins_compl_set_original_text(char *str) /// matches. void ins_compl_addfrommatch(void) { - char *p; int len = (int)curwin->w_cursor.col - (int)compl_col; - int c; - compl_T *cp; assert(compl_shown_match != NULL); - p = compl_shown_match->cp_str; + char *p = compl_shown_match->cp_str; if ((int)strlen(p) <= len) { // the match is too short // When still at the original match use the first entry that matches // the leader. @@ -1819,7 +1800,7 @@ void ins_compl_addfrommatch(void) } p = NULL; - for (cp = compl_shown_match->cp_next; cp != NULL + for (compl_T *cp = compl_shown_match->cp_next; cp != NULL && !is_first_match(cp); cp = cp->cp_next) { if (compl_leader == NULL || ins_compl_equal(cp, compl_leader, strlen(compl_leader))) { @@ -1832,7 +1813,7 @@ void ins_compl_addfrommatch(void) } } p += len; - c = utf_ptr2char(p); + int c = utf_ptr2char(p); ins_compl_addleader(c); } @@ -2096,10 +2077,10 @@ bool ins_compl_prep(int c) edit_submode_extra = NULL; } - // Ignore end of Select mode mapping and mouse scroll buttons. + // Ignore end of Select mode mapping and mouse scroll/movement. if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP - || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT - || c == K_COMMAND || c == K_LUA) { + || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_MOUSEMOVE + || c == K_EVENT || c == K_COMMAND || c == K_LUA) { return retval; } @@ -2184,7 +2165,6 @@ bool ins_compl_prep(int c) static void ins_compl_fixRedoBufForLeader(char *ptr_arg) { int len; - char *p; char *ptr = ptr_arg; if (ptr == NULL) { @@ -2195,7 +2175,7 @@ static void ins_compl_fixRedoBufForLeader(char *ptr_arg) } } if (compl_orig_text != NULL) { - p = compl_orig_text; + char *p = compl_orig_text; for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {} if (len > 0) { len -= utf_head_off(p, p + len); @@ -2220,7 +2200,8 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag) static win_T *wp = NULL; if (flag == 'w') { // just windows - if (buf == curbuf || wp == NULL) { // first call for this flag/expansion + if (buf == curbuf || !win_valid(wp)) { + // first call for this flag/expansion or window was closed wp = curwin; } assert(wp); @@ -2258,14 +2239,14 @@ static void copy_global_to_buflocal_cb(Callback *globcb, Callback *bufcb) /// Invoked when the 'completefunc' option is set. The option value can be a /// name of a function (string), or function(<name>) or funcref(<name>) or a /// lambda expression. -void set_completefunc_option(char **errmsg) +const char *did_set_completefunc(optset_T *args FUNC_ATTR_UNUSED) { if (option_set_callback_func(curbuf->b_p_cfu, &cfu_cb) == FAIL) { - *errmsg = e_invarg; - return; + return e_invarg; } set_buflocal_cfu_callback(curbuf); + return NULL; } /// Copy the global 'completefunc' callback function to the buffer-local @@ -2279,13 +2260,14 @@ void set_buflocal_cfu_callback(buf_T *buf) /// Invoked when the 'omnifunc' option is set. The option value can be a /// name of a function (string), or function(<name>) or funcref(<name>) or a /// lambda expression. -void set_omnifunc_option(buf_T *buf, char **errmsg) +const char *did_set_omnifunc(optset_T *args) { + buf_T *buf = (buf_T *)args->os_buf; if (option_set_callback_func(buf->b_p_ofu, &ofu_cb) == FAIL) { - *errmsg = e_invarg; - return; + return e_invarg; } set_buflocal_ofu_callback(buf); + return NULL; } /// Copy the global 'omnifunc' callback function to the buffer-local 'omnifunc' @@ -2299,7 +2281,7 @@ void set_buflocal_ofu_callback(buf_T *buf) /// Invoked when the 'thesaurusfunc' option is set. The option value can be a /// name of a function (string), or function(<name>) or funcref(<name>) or a /// lambda expression. -void set_thesaurusfunc_option(char **errmsg) +const char *did_set_thesaurusfunc(optset_T *args FUNC_ATTR_UNUSED) { int retval; @@ -2311,9 +2293,7 @@ void set_thesaurusfunc_option(char **errmsg) retval = option_set_callback_func(p_tsrfu, &tsrfu_cb); } - if (retval == FAIL) { - *errmsg = e_invarg; - } + return retval == FAIL ? e_invarg : NULL; } /// Mark the global 'completefunc' 'omnifunc' and 'thesaurusfunc' callbacks with @@ -2363,13 +2343,11 @@ static void expand_by_function(int type, char *base) { list_T *matchlist = NULL; dict_T *matchdict = NULL; - char *funcname; - pos_T pos; typval_T rettv; const int save_State = State; assert(curbuf != NULL); - funcname = get_complete_funcname(type); + char *funcname = get_complete_funcname(type); if (*funcname == NUL) { return; } @@ -2382,7 +2360,7 @@ static void expand_by_function(int type, char *base) args[0].vval.v_number = 0; args[1].vval.v_string = base != NULL ? base : ""; - pos = curwin->w_cursor; + pos_T pos = curwin->w_cursor; // Lock the text to avoid weird things from happening. Also disallow // switching to another window, it should not be needed and may end up in // Insert mode in another buffer. @@ -2434,7 +2412,7 @@ theend: } } -/// Add a match to the list of matches from VimL object +/// Add a match to the list of matches from Vimscript object /// /// @param[in] tv Object to get matches from. /// @param[in] dir Completion direction. @@ -2509,14 +2487,11 @@ static void ins_compl_add_list(list_T *const list) /// Add completions from a dict. static void ins_compl_add_dict(dict_T *dict) { - dictitem_T *di_refresh; - dictitem_T *di_words; - // Check for optional "refresh" item. compl_opt_refresh_always = false; - di_refresh = tv_dict_find(dict, S_LEN("refresh")); + dictitem_T *di_refresh = tv_dict_find(dict, S_LEN("refresh")); if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { - const char *v = (const char *)di_refresh->di_tv.vval.v_string; + const char *v = di_refresh->di_tv.vval.v_string; if (v != NULL && strcmp(v, "always") == 0) { compl_opt_refresh_always = true; @@ -2524,7 +2499,7 @@ static void ins_compl_add_dict(dict_T *dict) } // Add completions from a "words" list. - di_words = tv_dict_find(dict, S_LEN("words")); + dictitem_T *di_words = tv_dict_find(dict, S_LEN("words")); if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) { ins_compl_add_list(di_words->di_tv.vval.v_list); } @@ -2652,7 +2627,7 @@ static void ins_compl_update_sequence_numbers(void) compl_T *match; if (compl_dir_forward()) { - // search backwards for the first valid (!= -1) number. + // Search backwards for the first valid (!= -1) number. // This should normally succeed already at the first loop // cycle, so it's fast! for (match = compl_curr_match->cp_prev; @@ -2672,7 +2647,7 @@ static void ins_compl_update_sequence_numbers(void) } } else { // BACKWARD assert(compl_direction == BACKWARD); - // search forwards (upwards) for the first valid (!= -1) + // Search forwards (upwards) for the first valid (!= -1) // number. This should normally succeed already at the // first loop cycle, so it's fast! for (match = compl_curr_match->cp_next; @@ -2683,8 +2658,7 @@ static void ins_compl_update_sequence_numbers(void) } } if (match != NULL) { - // go down and assign all numbers which are not - // assigned yet + // go down and assign all numbers which are not assigned yet for (match = match->cp_prev; match && match->cp_number == -1; match = match->cp_prev) { @@ -2694,6 +2668,70 @@ static void ins_compl_update_sequence_numbers(void) } } +static int info_add_completion_info(list_T *li) +{ + if (compl_first_match == NULL) { + return OK; + } + + bool forward = compl_dir_forward(); + compl_T *match = compl_first_match; + // There are four cases to consider here: + // 1) when just going forward through the menu, + // compl_first_match should point to the initial entry with + // number zero and CP_ORIGINAL_TEXT flag set + // 2) when just going backwards, + // compl-first_match should point to the last entry before + // the entry with the CP_ORIGINAL_TEXT flag set + // 3) when first going forwards and then backwards, e.g. + // pressing C-N, C-P, compl_first_match points to the + // last entry before the entry with the CP_ORIGINAL_TEXT + // flag set and next-entry moves opposite through the list + // compared to case 2, so pretend the direction is forward again + // 4) when first going backwards and then forwards, e.g. + // pressing C-P, C-N, compl_first_match points to the + // first entry with the CP_ORIGINAL_TEXT + // flag set and next-entry moves in opposite direction through the list + // compared to case 1, so pretend the direction is backwards again + // + // But only do this when the 'noselect' option is not active! + + if (!compl_no_select) { + if (forward && !match_at_original_text(match)) { + forward = false; + } else if (!forward && match_at_original_text(match)) { + forward = true; + } + } + + // Skip the element with the CP_ORIGINAL_TEXT flag at the beginning, in case of + // forward completion, or at the end, in case of backward completion. + match = forward ? match->cp_next + : (compl_no_select && match_at_original_text(match) + ? match->cp_prev : match->cp_prev->cp_prev); + + while (match != NULL && !match_at_original_text(match)) { + dict_T *di = tv_dict_alloc(); + + tv_list_append_dict(li, di); + tv_dict_add_str(di, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); + tv_dict_add_str(di, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); + tv_dict_add_str(di, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); + tv_dict_add_str(di, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); + tv_dict_add_str(di, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + if (match->cp_user_data.v_type == VAR_UNKNOWN) { + // Add an empty string for backwards compatibility + tv_dict_add_str(di, S_LEN("user_data"), ""); + } else { + tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); + } + + match = forward ? match->cp_next : match->cp_prev; + } + + return OK; +} + /// Get complete information static void get_complete_info(list_T *what_list, dict_T *retdict) { @@ -2739,29 +2777,9 @@ static void get_complete_info(list_T *what_list, dict_T *retdict) if (ret == OK && (what_flag & CI_WHAT_ITEMS)) { list_T *li = tv_list_alloc(get_compl_len()); - ret = tv_dict_add_list(retdict, S_LEN("items"), li); - if (ret == OK && compl_first_match != NULL) { - compl_T *match = compl_first_match; - do { - if (!match_at_original_text(match)) { - dict_T *di = tv_dict_alloc(); - - tv_list_append_dict(li, di); - tv_dict_add_str(di, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str(di, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str(di, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str(di, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str(di, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); - if (match->cp_user_data.v_type == VAR_UNKNOWN) { - // Add an empty string for backwards compatibility - tv_dict_add_str(di, S_LEN("user_data"), ""); - } else { - tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); - } - } - match = match->cp_next; - } while (match != NULL && !is_first_match(match)); + if (ret == OK) { + ret = info_add_completion_info(li); } } @@ -2878,20 +2896,20 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar st->dict_f = DICT_EXACT; } if (!shortmess(SHM_COMPLETIONSCAN)) { - msg_hist_off = true; // reset in msg_trunc_attr() + msg_hist_off = true; // reset in msg_trunc() vim_snprintf(IObuff, IOSIZE, _("Scanning: %s"), 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(IObuff, true, HL_ATTR(HLF_R)); + (void)msg_trunc(IObuff, true, HL_ATTR(HLF_R)); } } else if (*st->e_cpt == NUL) { status = INS_COMPL_CPT_END; } else { if (ctrl_x_mode_line_or_eval()) { - compl_type = -1; + // compl_type = -1; } else if (*st->e_cpt == 'k' || *st->e_cpt == 's') { if (*st->e_cpt == 'k') { compl_type = CTRL_X_DICTIONARY; @@ -2906,15 +2924,15 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar compl_type = CTRL_X_PATH_PATTERNS; } else if (*st->e_cpt == 'd') { compl_type = CTRL_X_PATH_DEFINES; + } else if (*st->e_cpt == 'f') { + compl_type = CTRL_X_BUFNAMES; } else if (*st->e_cpt == ']' || *st->e_cpt == 't') { compl_type = CTRL_X_TAGS; if (!shortmess(SHM_COMPLETIONSCAN)) { - msg_hist_off = true; // reset in msg_trunc_attr() + msg_hist_off = true; // reset in msg_trunc() vim_snprintf(IObuff, IOSIZE, "%s", _("Scanning tags.")); - (void)msg_trunc_attr(IObuff, true, HL_ATTR(HLF_R)); + (void)msg_trunc(IObuff, true, HL_ATTR(HLF_R)); } - } else { - compl_type = -1; } // in any case e_cpt is advanced to the next entry @@ -2940,7 +2958,7 @@ static void get_next_include_file_completion(int compl_type) ((compl_type == CTRL_X_PATH_DEFINES && !(compl_cont_status & CONT_SOL)) ? FIND_DEFINE : FIND_ANY), - 1L, ACTION_EXPAND, 1, MAXLNUM); + 1, ACTION_EXPAND, 1, MAXLNUM); } /// Get the next set of words matching "compl_pattern" in dictionary or @@ -2950,11 +2968,11 @@ static void get_next_dict_tsr_completion(int compl_type, char *dict, int dict_f) if (thesaurus_func_complete(compl_type)) { expand_by_function(compl_type, 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)), + : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)), compl_pattern, dict != NULL ? dict_f : 0, compl_type == CTRL_X_THESAURUS); @@ -3043,18 +3061,18 @@ static void get_next_spell_completion(linenr_T lnum) /// @param cur_match_pos current match position /// @param match_len /// @param cont_s_ipos next ^X<> will set initial_pos -static char *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_pos, int *match_len, - bool *cont_s_ipos) +static char *ins_compl_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_pos, int *match_len, + bool *cont_s_ipos) { *match_len = 0; - char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, false) + cur_match_pos->col; + char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum) + cur_match_pos->col; int len; if (ctrl_x_mode_line_or_eval()) { if (compl_status_adding()) { if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) { return NULL; } - ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false); + ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1); if (!p_paste) { ptr = skipwhite(ptr); } @@ -3082,7 +3100,7 @@ static char *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_pos // normal command "J" was used. IOSIZE is always greater than // compl_length, so the next strncpy always works -- Acevedo strncpy(IObuff, ptr, (size_t)len); // NOLINT(runtime/printf) - ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false); + ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1); tmp_ptr = ptr = skipwhite(ptr); // Find start of next word. tmp_ptr = find_word_start(tmp_ptr); @@ -3151,7 +3169,7 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_ } bool looped_around = false; int found_new_match = FAIL; - for (;;) { + while (true) { bool cont_s_ipos = false; msg_silent++; // Don't want messages for wrapscan. @@ -3162,7 +3180,7 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_ compl_direction, compl_pattern); } else { found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos, - NULL, compl_direction, compl_pattern, 1L, + NULL, compl_direction, compl_pattern, 1, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL); } msg_silent--; @@ -3206,8 +3224,8 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_ continue; } int len; - char *ptr = ins_comp_get_next_word_or_line(st->ins_buf, st->cur_match_pos, - &len, &cont_s_ipos); + char *ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos, + &len, &cont_s_ipos); if (ptr == NULL) { continue; } @@ -3265,6 +3283,9 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_ case CTRL_X_SPELL: get_next_spell_completion(st->first_match_pos.lnum); break; + case CTRL_X_BUFNAMES: + get_next_bufname_token(); + break; default: // normal ^P/^N and ^X^L found_new_match = get_next_default_completion(st, ini); @@ -3282,6 +3303,19 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_ return found_new_match; } +static void get_next_bufname_token(void) +{ + FOR_ALL_BUFFERS(b) { + if (b->b_p_bl && b->b_sfname != NULL) { + char *tail = path_tail(b->b_sfname); + if (strncmp(tail, compl_orig_text, strlen(compl_orig_text)) == 0) { + ins_compl_add(tail, (int)strlen(tail), NULL, NULL, false, NULL, 0, + p_ic ? CP_ICASE : 0, false); + } + } + } +} + /// Get the next expansion(s), using "compl_pattern". /// The search starts at position "ini" in curbuf and in the direction /// compl_direction. @@ -3292,7 +3326,7 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_ static int ins_compl_get_exp(pos_T *ini) { static ins_compl_next_state_T st; - int i; + static bool st_cleared = false; int found_new_match; int type = ctrl_x_mode; @@ -3302,9 +3336,16 @@ static int ins_compl_get_exp(pos_T *ini) FOR_ALL_BUFFERS(buf) { buf->b_scanned = false; } + if (!st_cleared) { + CLEAR_FIELD(st); + st_cleared = true; + } st.found_all = false; st.ins_buf = curbuf; - st.e_cpt = (compl_cont_status & CONT_LOCAL) ? "." : curbuf->b_p_cpt; + xfree(st.e_cpt_copy); + // Make a copy of 'complete', in case the buffer is wiped out. + st.e_cpt_copy = xstrdup((compl_cont_status & CONT_LOCAL) ? "." : curbuf->b_p_cpt); + st.e_cpt = st.e_cpt_copy; 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. @@ -3315,7 +3356,7 @@ static int ins_compl_get_exp(pos_T *ini) st.cur_match_pos = compl_dir_forward() ? &st.last_match_pos : &st.first_match_pos; // For ^N/^P loop over all the flags/windows/buffers in 'complete' - for (;;) { + while (true) { found_new_match = FAIL; st.set_match_pos = false; @@ -3361,7 +3402,7 @@ static int ins_compl_get_exp(pos_T *ini) compl_started = true; } else { // Mark a buffer scanned when it has been scanned completely - if (type == 0 || type == CTRL_X_PATH_PATTERNS) { + if (buf_valid(st.ins_buf) && (type == 0 || type == CTRL_X_PATH_PATTERNS)) { assert(st.ins_buf); st.ins_buf->b_scanned = true; } @@ -3376,7 +3417,7 @@ static int ins_compl_get_exp(pos_T *ini) found_new_match = FAIL; } - i = -1; // total of matches, unknown + int i = -1; // total of matches, unknown if (found_new_match == FAIL || (ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())) { i = ins_compl_make_cyclic(); @@ -3387,8 +3428,8 @@ static int ins_compl_get_exp(pos_T *ini) // just been made cyclic then we have to move compl_curr_match to the // next or previous entry (if any) -- Acevedo compl_curr_match = compl_dir_forward() - ? compl_old_match->cp_next - : compl_old_match->cp_prev; + ? compl_old_match->cp_next + : compl_old_match->cp_prev; if (compl_curr_match == NULL) { compl_curr_match = compl_old_match; } @@ -3426,11 +3467,9 @@ static void ins_compl_update_shown_match(void) /// Delete the old text being completed. void ins_compl_delete(void) { - int col; - // In insert mode: Delete the typed part. // In replace mode: Put the old characters back, if any. - col = compl_col + (compl_status_adding() ? compl_length : 0); + int col = compl_col + (compl_status_adding() ? compl_length : 0); if ((int)curwin->w_cursor.col > col) { if (stop_arrow() == FAIL) { return; @@ -3440,7 +3479,7 @@ void ins_compl_delete(void) // TODO(vim): is this sufficient for redrawing? Redrawing everything // causes flicker, thus we can't do that. - changed_cline_bef_curs(); + changed_cline_bef_curs(curwin); // clear v:completed_item set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); } @@ -3484,7 +3523,7 @@ static void ins_compl_show_filename(void) msg_hist_off = true; vim_snprintf(IObuff, IOSIZE, "%s %s%s", lead, s > compl_shown_match->cp_fname ? "<" : "", s); - msg(IObuff); + msg(IObuff, 0); msg_hist_off = false; redraw_cmdline = false; // don't overwrite! } @@ -3604,6 +3643,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match int num_matches = -1; int todo = count; const bool started = compl_started; + buf_T *const orig_curbuf = curbuf; // When user complete function return -1 for findstart which is next // time of 'always', compl_shown_match become NULL. @@ -3617,7 +3657,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match } if (allow_get_expansion && insert_match - && (!(compl_get_longest || compl_restarting) || compl_used_match)) { + && (!compl_get_longest || compl_used_match)) { // Delete old text to be replaced ins_compl_delete(); } @@ -3639,6 +3679,12 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match return -1; } + if (curbuf != orig_curbuf) { + // In case some completion function switched buffer, don't want to + // insert the completion elsewhere. + return -1; + } + // Insert the text of the new completion, or the compl_leader. if (compl_no_insert && !started) { ins_bytes(compl_orig_text + get_compl_len()); @@ -3768,15 +3814,13 @@ static bool ins_compl_pum_key(int c) /// Returns 1 for most keys, height of the popup menu for page-up/down keys. static int ins_compl_key2count(int c) { - int h; - if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { int offset = pum_want.item - pum_selected_item; return abs(offset); } if (ins_compl_pum_key(c) && c != K_UP && c != K_DOWN) { - h = pum_get_height(); + int h = pum_get_height(); if (h > 3) { h -= 2; // keep some context } @@ -4304,9 +4348,8 @@ static void ins_compl_show_statusmsg(void) if (edit_submode_extra != NULL) { if (!p_smd) { msg_hist_off = true; - msg_attr((const char *)edit_submode_extra, - (edit_submode_highl < HLF_COUNT - ? HL_ATTR(edit_submode_highl) : 0)); + msg(edit_submode_extra, (edit_submode_highl < HLF_COUNT + ? HL_ATTR(edit_submode_highl) : 0)); msg_hist_off = false; } } else { @@ -4320,13 +4363,8 @@ static void ins_compl_show_statusmsg(void) /// Returns OK if completion was done, FAIL if something failed. int ins_complete(int c, bool enable_pum) { - int n; - int save_w_wrow; - int save_w_leftcol; - int insert_match; - compl_direction = ins_compl_key2dir(c); - insert_match = ins_compl_use_match(c); + int insert_match = ins_compl_use_match(c); if (!compl_started) { if (ins_compl_start() == FAIL) { @@ -4340,9 +4378,9 @@ int ins_complete(int c, bool enable_pum) compl_shows_dir = compl_direction; // Find next match (and following matches). - save_w_wrow = curwin->w_wrow; - save_w_leftcol = curwin->w_leftcol; - n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false); + int save_w_wrow = curwin->w_wrow; + int save_w_leftcol = curwin->w_leftcol; + int n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false); if (n > 1) { // all matches have been found compl_matches = n; |