diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
| commit | d5f194ce780c95821a855aca3c19426576d28ae0 (patch) | |
| tree | d45f461b19f9118ad2bb1f440a7a08973ad18832 /src/nvim/insexpand.c | |
| parent | c5d770d311841ea5230426cc4c868e8db27300a8 (diff) | |
| parent | 44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff) | |
| download | rneovim-rahm.tar.gz rneovim-rahm.tar.bz2 rneovim-rahm.zip | |
Diffstat (limited to 'src/nvim/insexpand.c')
| -rw-r--r-- | src/nvim/insexpand.c | 721 |
1 files changed, 430 insertions, 291 deletions
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index bd7ee55722..e21433f5ec 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -10,6 +10,7 @@ #include <string.h> #include "klib/kvec.h" +#include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/autocmd_defs.h" @@ -37,7 +38,6 @@ #include "nvim/getchar.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" -#include "nvim/highlight.h" #include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" @@ -156,7 +156,8 @@ typedef struct compl_S compl_T; struct compl_S { compl_T *cp_next; compl_T *cp_prev; - char *cp_str; ///< matched text + compl_T *cp_match_next; ///< matched next compl_T + String cp_str; ///< matched text char *(cp_text[CPT_COUNT]); ///< text for the menu typval_T cp_user_data; char *cp_fname; ///< file containing the match, allocated when @@ -164,6 +165,7 @@ struct compl_S { int cp_flags; ///< CP_ values int cp_number; ///< sequence number int cp_score; ///< fuzzy match score + bool cp_in_match_array; ///< collected by compl_match_array int cp_user_abbr_hlattr; ///< highlight attribute for abbr int cp_user_kind_hlattr; ///< highlight attribute for kind }; @@ -219,7 +221,7 @@ static bool compl_enter_selects = false; /// When "compl_leader" is not NULL only matches that start with this string /// are used. -static char *compl_leader = NULL; +static String compl_leader = STRING_INIT; static bool compl_get_longest = false; ///< put longest common string in compl_leader @@ -244,8 +246,7 @@ static bool compl_started = false; static int ctrl_x_mode = CTRL_X_NORMAL; static int compl_matches = 0; ///< number of completion matches -static char *compl_pattern = NULL; -static size_t compl_patternlen = 0; +static String compl_pattern = STRING_INIT; static Direction compl_direction = FORWARD; static Direction compl_shows_dir = FORWARD; static int compl_pending = 0; ///< > 1 for postponed CTRL-N @@ -255,8 +256,9 @@ static pos_T compl_startpos; static int compl_length = 0; static colnr_T compl_col = 0; ///< column where the text starts ///< that is being completed -static char *compl_orig_text = NULL; ///< text as it was before - ///< completion started +static colnr_T compl_ins_end_col = 0; +static String compl_orig_text = STRING_INIT; ///< text as it was before + ///< completion started /// Undo information to restore extmarks for original text. static extmark_undo_vec_t compl_orig_extmarks; static int compl_cont_mode = 0; @@ -281,6 +283,11 @@ static size_t spell_bad_len = 0; // length of located bad word static int compl_selected_item = -1; +// "compl_match_array" points the currently displayed list of entries in the +// popup menu. It is NULL when there is no popup menu. +static pumitem_T *compl_match_array = NULL; +static int compl_match_arraysize; + /// CTRL-X pressed in Insert mode. void ins_ctrl_x(void) { @@ -475,7 +482,7 @@ bool check_compl_option(bool dict_opt) msg((dict_opt ? _("'dictionary' option is empty") : _("'thesaurus' option is empty")), HLF_E); if (emsg_silent == 0 && !in_assert_fails) { - vim_beep(BO_COMPL); + vim_beep(kOptBoFlagComplete); setcursor(); ui_flush(); os_delay(2004, false); @@ -560,11 +567,19 @@ static bool is_first_match(const compl_T *const match) return match == compl_first_match; } -static void do_autocmd_completedone(int c) +static void do_autocmd_completedone(int c, int mode, char *word) { save_v_event_T save_v_event; dict_T *v_event = get_v_event(&save_v_event); + mode = mode & ~CTRL_X_WANT_IDENT; + char *mode_str = NULL; + if (ctrl_x_mode_names[mode]) { + mode_str = ctrl_x_mode_names[mode]; + } + tv_dict_add_str(v_event, S_LEN("complete_word"), word != NULL ? word : ""); + tv_dict_add_str(v_event, S_LEN("complete_type"), mode_str != NULL ? mode_str : ""); + tv_dict_add_str(v_event, S_LEN("reason"), (c == Ctrl_Y ? "accept" : "cancel")); tv_dict_set_keys_readonly(v_event); @@ -624,7 +639,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp // Rule 1: Were any chars converted to lower? { - const char *p = compl_orig_text; + const char *p = compl_orig_text.data; for (int i = 0; i < min_len; i++) { const int c = mb_ptr2char_adv(&p); if (mb_islower(c)) { @@ -644,7 +659,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp // upper case. if (!has_lower) { bool was_letter = false; - const char *p = compl_orig_text; + const char *p = compl_orig_text.data; 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])) { @@ -660,7 +675,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp // Copy the original case of the part we typed. { - const char *p = compl_orig_text; + const char *p = compl_orig_text.data; for (int i = 0; i < min_len; i++) { const int c = mb_ptr2char_adv(&p); if (mb_islower(c)) { @@ -690,7 +705,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp ga_grow(&gap, IOSIZE); *p = NUL; STRCPY(gap.ga_data, IObuff); - gap.ga_len = (int)strlen(IObuff); + gap.ga_len = (int)(p - IObuff); } else { p += utf_char2bytes(wca[i++], p); } @@ -737,7 +752,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir // Find actual length of original text. { - const char *p = compl_orig_text; + const char *p = compl_orig_text.data; compl_char_len = 0; while (*p != NUL) { MB_PTR_ADV(p); @@ -747,7 +762,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir // "char_len" may be smaller than "compl_char_len" when using // thesaurus, only use the minimum when comparing. - int min_len = char_len < compl_char_len ? char_len : compl_char_len; + int min_len = MIN(char_len, compl_char_len); str = ins_compl_infercase_gettext(str, char_len, compl_char_len, min_len, &tofree); } @@ -758,7 +773,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir flags |= CP_ICASE; } - int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false, -1, -1); + int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false, NULL); xfree(tofree); return res; } @@ -789,6 +804,7 @@ static inline void free_cptext(char *const *const cptext) /// @param[in] cdir match direction. If 0, use "compl_direction". /// @param[in] flags_arg match flags (cp_flags) /// @param[in] adup accept this match even if it is already present. +/// @param[in] user_hl list of extra highlight attributes for abbr kind. /// /// If "cdir" is FORWARD, then the match is added after the current match. /// Otherwise, it is added before the current match. @@ -798,7 +814,7 @@ static inline void free_cptext(char *const *const cptext) /// returned in case of error. static int ins_compl_add(char *const str, int len, char *const fname, char *const *const cptext, const bool cptext_allocated, typval_T *user_data, const Direction cdir, - int flags_arg, const bool adup, int user_abbr_hlattr, int user_kind_hlattr) + int flags_arg, const bool adup, const int *user_hl) FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; @@ -825,8 +841,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons match = compl_first_match; do { if (!match_at_original_text(match) - && strncmp(match->cp_str, str, (size_t)len) == 0 - && ((int)strlen(match->cp_str) <= len || match->cp_str[len] == NUL)) { + && strncmp(match->cp_str.data, str, (size_t)len) == 0 + && ((int)match->cp_str.size <= len || match->cp_str.data[len] == NUL)) { if (cptext_allocated) { free_cptext(cptext); } @@ -846,7 +862,7 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons if (flags & CP_ORIGINAL_TEXT) { match->cp_number = 0; } - match->cp_str = xstrnsave(str, (size_t)len); + match->cp_str = cbuf_to_string(str, (size_t)len); // match-fname is: // - compl_curr_match->cp_fname if it is a string equal to fname. @@ -864,8 +880,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons match->cp_fname = NULL; } match->cp_flags = flags; - match->cp_user_abbr_hlattr = user_abbr_hlattr; - match->cp_user_kind_hlattr = user_kind_hlattr; + match->cp_user_abbr_hlattr = user_hl ? user_hl[0] : -1; + match->cp_user_kind_hlattr = user_hl ? user_hl[1] : -1; if (cptext != NULL) { int i; @@ -928,27 +944,55 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len) return true; } if (match->cp_flags & CP_ICASE) { - return STRNICMP(match->cp_str, str, len) == 0; + return STRNICMP(match->cp_str.data, str, len) == 0; + } + return strncmp(match->cp_str.data, str, len) == 0; +} + +/// when len is -1 mean use whole length of p otherwise part of p +static void ins_compl_insert_bytes(char *p, int len) + FUNC_ATTR_NONNULL_ALL +{ + if (len == -1) { + len = (int)strlen(p); } - return strncmp(match->cp_str, str, len) == 0; + assert(len >= 0); + ins_bytes_len(p, (size_t)len); + compl_ins_end_col = curwin->w_cursor.col; +} + +/// Checks if the column is within the currently inserted completion text +/// column range. If it is, it returns a special highlight attribute. +/// -1 mean normal item. +int ins_compl_col_range_attr(int col) +{ + if (get_cot_flags() & kOptCotFlagFuzzy) { + return -1; + } + + if (col >= (compl_col + (int)ins_compl_leader_len()) && col < compl_ins_end_col) { + return syn_name2attr("ComplMatchIns"); + } + + return -1; } /// Reduce the longest common string for match "match". static void ins_compl_longest_match(compl_T *match) { - if (compl_leader == NULL) { + if (compl_leader.data == NULL) { // First match, use it as a whole. - compl_leader = xstrdup(match->cp_str); + compl_leader = copy_string(match->cp_str, NULL); bool had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + get_compl_len()); + ins_compl_delete(false); + ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1); ins_redraw(false); // When the match isn't there (to avoid matching itself) remove it // again after redrawing. if (!had_match) { - ins_compl_delete(); + ins_compl_delete(false); } compl_used_match = false; @@ -956,8 +1000,8 @@ static void ins_compl_longest_match(compl_T *match) } // Reduce the text if this match differs from compl_leader. - char *p = compl_leader; - char *s = match->cp_str; + char *p = compl_leader.data; + char *s = match->cp_str.data; while (*p != NUL) { int c1 = utf_ptr2char(p); int c2 = utf_ptr2char(s); @@ -974,15 +1018,17 @@ static void ins_compl_longest_match(compl_T *match) if (*p != NUL) { // Leader was shortened, need to change the inserted text. *p = NUL; + compl_leader.size = (size_t)(p - compl_leader.data); + bool had_match = (curwin->w_cursor.col > compl_col); - ins_compl_delete(); - ins_bytes(compl_leader + get_compl_len()); + ins_compl_delete(false); + ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1); ins_redraw(false); // When the match isn't there (to avoid matching itself) remove it // again after redrawing. if (!had_match) { - ins_compl_delete(); + ins_compl_delete(false); } } @@ -997,9 +1043,9 @@ static void ins_compl_add_matches(int num_matches, char **matches, int icase) Direction dir = compl_direction; for (int i = 0; i < num_matches && add_r != FAIL; i++) { - if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir, - CP_FAST | (icase ? CP_ICASE : 0), - false, -1, -1)) == OK) { + add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir, + CP_FAST | (icase ? CP_ICASE : 0), false, NULL); + if (add_r == OK) { // If dir was BACKWARD then honor it just once. dir = FORWARD; } @@ -1038,8 +1084,8 @@ bool ins_compl_has_shown_match(void) /// Return whether the shown match is long enough. bool ins_compl_long_shown_match(void) { - return compl_shown_match != NULL && compl_shown_match->cp_str != NULL - && (colnr_T)strlen(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col; + return compl_shown_match != NULL && compl_shown_match->cp_str.data != NULL + && (colnr_T)compl_shown_match->cp_str.size > curwin->w_cursor.col - compl_col; } /// Get the local or global value of 'completeopt' flags. @@ -1048,11 +1094,6 @@ unsigned get_cot_flags(void) return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags; } -/// "compl_match_array" points the currently displayed list of entries in the -/// popup menu. It is NULL when there is no popup menu. -static pumitem_T *compl_match_array = NULL; -static int compl_match_arraysize; - /// Remove any popup menu. static void ins_compl_del_pum(void) { @@ -1069,7 +1110,7 @@ bool pum_wanted(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { // "completeopt" must contain "menu" or "menuone" - return (get_cot_flags() & COT_ANY_MENU) != 0; + return (get_cot_flags() & (kOptCotFlagMenu | kOptCotFlagMenuone)) != 0; } /// Check that there are two or more matches to be shown in the popup menu. @@ -1088,7 +1129,7 @@ static bool pum_enough_matches(void) comp = comp->cp_next; } while (!is_first_match(comp)); - if (get_cot_flags() & COT_MENUONE) { + if (get_cot_flags() & kOptCotFlagMenuone) { return i >= 1; } return i >= 2; @@ -1099,7 +1140,7 @@ static dict_T *ins_compl_dict_alloc(compl_T *match) { // { word, abbr, menu, kind, info } dict_T *dict = tv_dict_alloc_lock(VAR_FIXED); - tv_dict_add_str(dict, S_LEN("word"), match->cp_str); + tv_dict_add_str(dict, S_LEN("word"), match->cp_str.data); tv_dict_add_str(dict, S_LEN("abbr"), match->cp_text[CPT_ABBR]); tv_dict_add_str(dict, S_LEN("menu"), match->cp_text[CPT_MENU]); tv_dict_add_str(dict, S_LEN("kind"), match->cp_text[CPT_KIND]); @@ -1168,54 +1209,47 @@ static int ins_compl_build_pum(void) XFREE_CLEAR(compl_leader); } - const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0; int max_fuzzy_score = 0; unsigned cur_cot_flags = get_cot_flags(); - bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0; - bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0; - - do { - // When 'completeopt' contains "fuzzy" and leader is not NULL or empty, - // set the cp_score for later comparisons. - if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) { - comp->cp_score = fuzzy_match_str(comp->cp_str, compl_leader); - } - - if (!match_at_original_text(comp) - && (compl_leader == NULL - || ins_compl_equal(comp, compl_leader, (size_t)lead_len) - || (compl_fuzzy_match && comp->cp_score > 0))) { - compl_match_arraysize++; - } - comp = comp->cp_next; - } while (comp != NULL && !is_first_match(comp)); - - if (compl_match_arraysize == 0) { - return -1; - } - - assert(compl_match_arraysize >= 0); - compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); + bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0; + bool fuzzy_filter = (cur_cot_flags & kOptCotFlagFuzzy) != 0; + bool fuzzy_sort = fuzzy_filter && !(cur_cot_flags & kOptCotFlagNosort); + compl_T *match_head = NULL, *match_tail = NULL; // If the current match is the original text don't find the first // match after it, don't highlight anything. bool shown_match_ok = match_at_original_text(compl_shown_match); - if (strequal(compl_leader, compl_orig_text) && !shown_match_ok) { + if (strequal(compl_leader.data, compl_orig_text.data) && !shown_match_ok) { compl_shown_match = compl_no_select ? compl_first_match : compl_first_match->cp_next; } - compl_T *shown_compl = NULL; bool did_find_shown_match = false; - int cur = -1; + compl_T *shown_compl = NULL; int i = 0; - comp = compl_first_match; + int cur = -1; + do { + comp->cp_in_match_array = false; + // When 'completeopt' contains "fuzzy" and leader is not NULL or empty, + // set the cp_score for later comparisons. + if (fuzzy_filter && compl_leader.data != NULL && compl_leader.size > 0) { + comp->cp_score = fuzzy_match_str(comp->cp_str.data, compl_leader.data); + } + if (!match_at_original_text(comp) - && (compl_leader == NULL - || ins_compl_equal(comp, compl_leader, (size_t)lead_len) - || (compl_fuzzy_match && comp->cp_score > 0))) { - if (!shown_match_ok && !compl_fuzzy_match) { + && (compl_leader.data == NULL + || ins_compl_equal(comp, compl_leader.data, compl_leader.size) + || (fuzzy_filter && comp->cp_score > 0))) { + compl_match_arraysize++; + comp->cp_in_match_array = true; + if (match_head == NULL) { + match_head = comp; + } else { + match_tail->cp_match_next = comp; + } + match_tail = comp; + if (!shown_match_ok && !fuzzy_filter) { 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. @@ -1228,64 +1262,36 @@ static int ins_compl_build_pum(void) shown_compl = comp; } cur = i; - } else if (compl_fuzzy_match) { + } else if (fuzzy_filter) { if (i == 0) { shown_compl = comp; } // Update the maximum fuzzy score and the shown match // if the current item's score is higher - if (comp->cp_score > max_fuzzy_score) { + if (fuzzy_sort && comp->cp_score > max_fuzzy_score) { did_find_shown_match = true; max_fuzzy_score = comp->cp_score; if (!compl_no_select) { compl_shown_match = comp; } + } else if (!fuzzy_sort && i == 0 && !compl_no_select) { + compl_shown_match = shown_compl; } - if (!shown_match_ok && comp == compl_shown_match && !compl_no_select) { cur = i; shown_match_ok = true; } - - // If there is no "no select" condition and the max fuzzy - // score is positive, or there is no completion leader or the - // leader length is zero, mark the shown match as valid and - // reset the current index. - if (!compl_no_select - && (max_fuzzy_score > 0 - || (compl_leader == NULL || lead_len == 0))) { - if (match_at_original_text(compl_shown_match)) { - compl_shown_match = shown_compl; - } - } - } - - 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 = comp->cp_str; - } - compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND]; - compl_match_array[i].pum_info = comp->cp_text[CPT_INFO]; - compl_match_array[i].pum_score = comp->cp_score; - compl_match_array[i].pum_user_abbr_hlattr = comp->cp_user_abbr_hlattr; - compl_match_array[i].pum_user_kind_hlattr = comp->cp_user_kind_hlattr; - 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 = comp->cp_fname; } + i++; } - if (comp == compl_shown_match && !compl_fuzzy_match) { + if (comp == compl_shown_match && !fuzzy_filter) { did_find_shown_match = true; - // When the original text is the shown match don't set // compl_shown_match. if (match_at_original_text(comp)) { shown_match_ok = true; } - if (!shown_match_ok && shown_compl != NULL) { // The shown match isn't displayed, set it to the // previously displayed match. @@ -1296,7 +1302,31 @@ static int ins_compl_build_pum(void) comp = comp->cp_next; } while (comp != NULL && !is_first_match(comp)); - if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) { + if (compl_match_arraysize == 0) { + return -1; + } + + assert(compl_match_arraysize >= 0); + compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T)); + + i = 0; + comp = match_head; + while (comp != NULL) { + compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR] != NULL + ? comp->cp_text[CPT_ABBR] : comp->cp_str.data; + compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND]; + compl_match_array[i].pum_info = comp->cp_text[CPT_INFO]; + compl_match_array[i].pum_score = comp->cp_score; + compl_match_array[i].pum_user_abbr_hlattr = comp->cp_user_abbr_hlattr; + compl_match_array[i].pum_user_kind_hlattr = comp->cp_user_kind_hlattr; + compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU] != NULL + ? comp->cp_text[CPT_MENU] : comp->cp_fname; + compl_T *match_next = comp->cp_match_next; + comp->cp_match_next = NULL; + comp = match_next; + } + + if (fuzzy_sort && compl_leader.data != NULL && compl_leader.size > 0) { for (i = 0; i < compl_match_arraysize; i++) { compl_match_array[i].pum_idx = i; } @@ -1334,7 +1364,7 @@ void ins_compl_show_pum(void) } else { // popup menu already exists, only need to find the current item. for (int i = 0; i < compl_match_arraysize; i++) { - if (compl_match_array[i].pum_text == compl_shown_match->cp_str + if (compl_match_array[i].pum_text == compl_shown_match->cp_str.data || compl_match_array[i].pum_text == compl_shown_match->cp_text[CPT_ABBR]) { cur = i; break; @@ -1403,7 +1433,13 @@ bool compl_match_curr_select(int selected) /// Get current completion leader char *ins_compl_leader(void) { - return compl_leader != NULL ? compl_leader : compl_orig_text; + return compl_leader.data != NULL ? compl_leader.data : compl_orig_text.data; +} + +/// Get current completion leader length +size_t ins_compl_leader_len(void) +{ + return compl_leader.data != NULL ? compl_leader.size : compl_orig_text.size; } /// Add any identifiers that match the given pattern "pat" in the list of @@ -1562,6 +1598,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags, 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() + msg_ext_set_kind("completion"); vim_snprintf(IObuff, IOSIZE, _("Scanning dictionary: %s"), files[i]); msg_trunc(IObuff, true, HLF_R); @@ -1577,11 +1614,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags, char *ptr = buf; while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) { ptr = regmatch->startp[0]; - if (ctrl_x_mode_line_or_eval()) { - ptr = find_line_end(ptr); - } else { - ptr = find_word_end(ptr); - } + ptr = ctrl_x_mode_line_or_eval() ? find_line_end(ptr) : find_word_end(ptr); int add_r = ins_compl_add_infercase(regmatch->startp[0], (int)(ptr - regmatch->startp[0]), p_ic, files[i], *dir, false); @@ -1652,9 +1685,8 @@ static char *find_line_end(char *ptr) /// Free the list of completions static void ins_compl_free(void) { - XFREE_CLEAR(compl_pattern); - compl_patternlen = 0; - XFREE_CLEAR(compl_leader); + API_CLEAR_STRING(compl_pattern); + API_CLEAR_STRING(compl_leader); if (compl_first_match == NULL) { return; @@ -1667,7 +1699,7 @@ static void ins_compl_free(void) do { compl_T *match = compl_curr_match; compl_curr_match = compl_curr_match->cp_next; - xfree(match->cp_str); + API_CLEAR_STRING(match->cp_str); // several entries may use the same fname, free it just once. if (match->cp_flags & CP_FREE_FNAME) { xfree(match->cp_fname); @@ -1687,12 +1719,12 @@ void ins_compl_clear(void) compl_cont_status = 0; compl_started = false; compl_matches = 0; - XFREE_CLEAR(compl_pattern); - compl_patternlen = 0; - XFREE_CLEAR(compl_leader); + compl_ins_end_col = 0; + API_CLEAR_STRING(compl_pattern); + API_CLEAR_STRING(compl_leader); edit_submode_extra = NULL; kv_destroy(compl_orig_extmarks); - XFREE_CLEAR(compl_orig_text); + API_CLEAR_STRING(compl_orig_text); compl_enter_selects = false; // clear v:completed_item set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); @@ -1705,6 +1737,12 @@ bool ins_compl_active(void) return compl_started; } +/// Return true when wp is the actual completion window +bool ins_compl_win_active(win_T *wp) +{ + return ins_compl_active() && !(wp->w_p_pvw || wp->w_float_is_info); +} + /// Selected one of the matches. When false the match was edited or using the /// longest common string. bool ins_compl_used_match(void) @@ -1743,12 +1781,33 @@ int ins_compl_len(void) return compl_length; } +/// Return true when preinsert is set otherwise FALSE. +static bool ins_compl_has_preinsert(void) +{ + return (get_cot_flags() & (kOptCotFlagFuzzy|kOptCotFlagPreinsert)) == kOptCotFlagPreinsert; +} + +/// Returns true if the pre-insert effect is valid and the cursor is within +/// the `compl_ins_end_col` range. +bool ins_compl_preinsert_effect(void) +{ + if (!ins_compl_has_preinsert()) { + return false; + } + + return curwin->w_cursor.col < compl_ins_end_col; +} + /// Delete one character before the cursor and show the subset of the matches /// that match the word that is now before the cursor. /// Returns the character to be used, NUL if the work is done and another char /// to be got from the user. int ins_compl_bs(void) { + if (ins_compl_preinsert_effect()) { + ins_compl_delete(false); + } + char *line = get_cursor_line_ptr(); char *p = line + curwin->w_cursor.col; MB_PTR_BACK(line, p); @@ -1776,8 +1835,9 @@ int ins_compl_bs(void) // TODO(bfredl): get rid of random update_screen() calls deep inside completion logic line = get_cursor_line_ptr(); - xfree(compl_leader); - compl_leader = xstrnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col)); + API_CLEAR_STRING(compl_leader); + compl_leader = cbuf_to_string(line + compl_col, + (size_t)(p_off - (ptrdiff_t)compl_col)); ins_compl_new_leader(); if (compl_shown_match != NULL) { @@ -1810,12 +1870,12 @@ static bool ins_compl_need_restart(void) static void ins_compl_new_leader(void) { ins_compl_del_pum(); - ins_compl_delete(); - ins_bytes(compl_leader + get_compl_len()); + ins_compl_delete(true); + ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1); compl_used_match = false; if (compl_started) { - ins_compl_set_original_text(compl_leader); + ins_compl_set_original_text(compl_leader.data, compl_leader.size); } else { spell_bad_len = 0; // need to redetect bad word // Matches were cleared, need to search for them now. @@ -1833,8 +1893,13 @@ static void ins_compl_new_leader(void) ins_compl_show_pum(); // Don't let Enter select the original text when there is no popup menu. + if (compl_match_array == NULL) { + compl_enter_selects = false; + } else if (ins_compl_has_preinsert() && compl_leader.size > 0) { + ins_compl_insert(false, true); + } // Don't let Enter select when use user function and refresh_always is set - if (compl_match_array == NULL || ins_compl_refresh_always()) { + if (ins_compl_refresh_always()) { compl_enter_selects = false; } } @@ -1857,6 +1922,10 @@ void ins_compl_addleader(int c) { int cc; + if (ins_compl_preinsert_effect()) { + ins_compl_delete(false); + } + if (stop_arrow() == FAIL) { return; } @@ -1875,9 +1944,9 @@ void ins_compl_addleader(int c) ins_compl_restart(); } - xfree(compl_leader); - compl_leader = xstrnsave(get_cursor_line_ptr() + compl_col, - (size_t)(curwin->w_cursor.col - compl_col)); + API_CLEAR_STRING(compl_leader); + compl_leader = cbuf_to_string(get_cursor_line_ptr() + compl_col, + (size_t)(curwin->w_cursor.col - compl_col)); ins_compl_new_leader(); } @@ -1897,19 +1966,19 @@ static void ins_compl_restart(void) } /// Set the first match, the original text. -static void ins_compl_set_original_text(char *str) +static void ins_compl_set_original_text(char *str, size_t len) 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 (match_at_original_text(compl_first_match)) { // safety check - xfree(compl_first_match->cp_str); - compl_first_match->cp_str = xstrdup(str); + API_CLEAR_STRING(compl_first_match->cp_str); + compl_first_match->cp_str = cbuf_to_string(str, len); } else if (compl_first_match->cp_prev != NULL && match_at_original_text(compl_first_match->cp_prev)) { - xfree(compl_first_match->cp_prev->cp_str); - compl_first_match->cp_prev->cp_str = xstrdup(str); + API_CLEAR_STRING(compl_first_match->cp_prev->cp_str); + compl_first_match->cp_prev->cp_str = cbuf_to_string(str, len); } } @@ -1919,8 +1988,8 @@ void ins_compl_addfrommatch(void) { int len = (int)curwin->w_cursor.col - (int)compl_col; assert(compl_shown_match != NULL); - char *p = compl_shown_match->cp_str; - if ((int)strlen(p) <= len) { // the match is too short + char *p = compl_shown_match->cp_str.data; + if ((int)compl_shown_match->cp_str.size <= len) { // the match is too short // When still at the original match use the first entry that matches // the leader. if (!match_at_original_text(compl_shown_match)) { @@ -1928,15 +1997,17 @@ void ins_compl_addfrommatch(void) } p = NULL; + size_t plen = 0; 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))) { - p = cp->cp_str; + if (compl_leader.data == NULL + || ins_compl_equal(cp, compl_leader.data, compl_leader.size)) { + p = cp->cp_str.data; + plen = cp->cp_str.size; break; } } - if (p == NULL || (int)strlen(p) <= len) { + if (p == NULL || (int)plen <= len) { return; } } @@ -2076,7 +2147,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) // Get here when we have finished typing a sequence of ^N and // ^P or other completion characters in CTRL-X mode. Free up // memory that was used, and make sure we can redo the insert. - if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) { + if (compl_curr_match != NULL || compl_leader.data != NULL || c == Ctrl_E) { // If any of the original typed text has been changed, eg when // ignorecase is set, we must add back-spaces to the redo // buffer. We add as few as necessary to delete just the part @@ -2085,7 +2156,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) // CTRL-E then don't use the current match. char *ptr; if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) { - ptr = compl_curr_match->cp_str; + ptr = compl_curr_match->cp_str.data; } else { ptr = NULL; } @@ -2121,36 +2192,46 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) } } + char *word = NULL; // If the popup menu is displayed pressing CTRL-Y means accepting // the selection without inserting anything. When // compl_enter_selects is set the Enter key does the same. if ((c == Ctrl_Y || (compl_enter_selects && (c == CAR || c == K_KENTER || c == NL))) && pum_visible()) { + word = xstrdup(compl_shown_match->cp_str.data); retval = true; + // May need to remove ComplMatchIns highlight. + redrawWinline(curwin, curwin->w_cursor.lnum); } // CTRL-E means completion is Ended, go back to the typed text. // but only do this, if the Popup is still visible if (c == Ctrl_E) { - ins_compl_delete(); + ins_compl_delete(false); char *p = NULL; - if (compl_leader != NULL) { - p = compl_leader; + size_t plen = 0; + if (compl_leader.data != NULL) { + p = compl_leader.data; + plen = compl_leader.size; } else if (compl_first_match != NULL) { - p = compl_orig_text; + p = compl_orig_text.data; + plen = compl_orig_text.size; } if (p != NULL) { const int compl_len = get_compl_len(); - const int len = (int)strlen(p); - if (len > compl_len) { - ins_bytes_len(p + compl_len, (size_t)(len - compl_len)); + if ((int)plen > compl_len) { + ins_compl_insert_bytes(p + compl_len, (int)plen - compl_len); } } restore_orig_extmarks(); retval = true; } + if ((c == Ctrl_W || c == Ctrl_U) && ins_compl_preinsert_effect()) { + ins_compl_delete(false); + } + auto_format(false, true); // Trigger the CompleteDonePre event to give scripts a chance to @@ -2184,7 +2265,8 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) } // Trigger the CompleteDone event to give scripts a chance to act // upon the end of completion. - do_autocmd_completedone(c); + do_autocmd_completedone(c, prev_mode, word); + xfree(word); return retval; } @@ -2236,7 +2318,7 @@ bool ins_compl_prep(int c) // Set "compl_get_longest" when finding the first matches. if (ctrl_x_mode_not_defined_yet() || (ctrl_x_mode_normal() && !compl_started)) { - compl_get_longest = (get_cot_flags() & COT_LONGEST) != 0; + compl_get_longest = (get_cot_flags() & kOptCotFlagLongest) != 0; compl_used_match = true; } @@ -2273,7 +2355,7 @@ bool ins_compl_prep(int c) } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) { // Trigger the CompleteDone event to give scripts a chance to act // upon the (possibly failed) completion. - do_autocmd_completedone(c); + do_autocmd_completedone(c, ctrl_x_mode, NULL); } may_trigger_modechanged(); @@ -2293,18 +2375,18 @@ bool ins_compl_prep(int c) /// "ptr" is the known leader text or NUL. static void ins_compl_fixRedoBufForLeader(char *ptr_arg) { - int len; + int len = 0; char *ptr = ptr_arg; if (ptr == NULL) { - if (compl_leader != NULL) { - ptr = compl_leader; + if (compl_leader.data != NULL) { + ptr = compl_leader.data; } else { return; // nothing to do } } - if (compl_orig_text != NULL) { - char *p = compl_orig_text; + if (compl_orig_text.data != NULL) { + char *p = compl_orig_text.data; for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {} if (len > 0) { |