diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 5036ec5e45..27c8886c6c 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -10,6 +10,7 @@ #include #include "klib/kvec.h" +#include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/autocmd_defs.h" @@ -157,7 +158,7 @@ struct compl_S { compl_T *cp_next; compl_T *cp_prev; compl_T *cp_match_next; ///< matched next compl_T - char *cp_str; ///< matched text + 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 @@ -220,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 @@ -245,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 @@ -257,8 +257,8 @@ static int compl_length = 0; static colnr_T compl_col = 0; ///< column where the text starts ///< that is being completed static colnr_T compl_ins_end_col = 0; -static char *compl_orig_text = NULL; ///< text as it was before - ///< completion started +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; @@ -639,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)) { @@ -659,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])) { @@ -675,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)) { @@ -705,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); } @@ -752,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); @@ -841,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); } @@ -862,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. @@ -944,9 +944,9 @@ 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, 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 @@ -976,13 +976,13 @@ int ins_compl_col_range_attr(int col) /// 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(false); - ins_compl_insert_bytes(compl_leader + get_compl_len(), -1); + 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 @@ -996,8 +996,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); @@ -1014,9 +1014,11 @@ 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(false); - ins_compl_insert_bytes(compl_leader + get_compl_len(), -1); + 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 @@ -1078,8 +1080,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. @@ -1134,7 +1136,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]); @@ -1203,7 +1205,6 @@ 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 & kOptCotFlagNoselect) != 0; @@ -1214,7 +1215,7 @@ static int ins_compl_build_pum(void) // 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; } @@ -1226,13 +1227,13 @@ static int ins_compl_build_pum(void) 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 (compl_fuzzy_match && 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_leader.data == NULL + || ins_compl_equal(comp, compl_leader.data, compl_leader.size) || (compl_fuzzy_match && comp->cp_score > 0))) { compl_match_arraysize++; if (match_head == NULL) { @@ -1303,7 +1304,7 @@ static int ins_compl_build_pum(void) 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; + ? 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; @@ -1316,7 +1317,7 @@ static int ins_compl_build_pum(void) comp = match_next; } - if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) { + if (compl_fuzzy_match && compl_leader.data != NULL && compl_leader.size > 0) { for (i = 0; i < compl_match_arraysize; i++) { compl_match_array[i].pum_idx = i; } @@ -1354,7 +1355,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; @@ -1423,7 +1424,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 @@ -1668,9 +1675,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; @@ -1683,7 +1689,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); @@ -1704,12 +1710,11 @@ void ins_compl_clear(void) compl_started = false; compl_matches = 0; compl_ins_end_col = 0; - XFREE_CLEAR(compl_pattern); - compl_patternlen = 0; - XFREE_CLEAR(compl_leader); + 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)); @@ -1793,8 +1798,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) { @@ -1828,11 +1834,11 @@ static void ins_compl_new_leader(void) { ins_compl_del_pum(); ins_compl_delete(true); - ins_compl_insert_bytes(compl_leader + get_compl_len(), -1); + 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. @@ -1892,9 +1898,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(); } @@ -1914,19 +1920,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); } } @@ -1936,8 +1942,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)) { @@ -1945,15 +1951,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; } } @@ -2093,7 +2101,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 @@ -2102,7 +2110,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; } @@ -2145,7 +2153,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) if ((c == Ctrl_Y || (compl_enter_selects && (c == CAR || c == K_KENTER || c == NL))) && pum_visible()) { - word = xstrdup(compl_shown_match->cp_str); + word = xstrdup(compl_shown_match->cp_str.data); retval = true; } @@ -2154,16 +2162,18 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) if (c == Ctrl_E) { 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_compl_insert_bytes(p + compl_len, len - compl_len); + if ((int)plen > compl_len) { + ins_compl_insert_bytes(p + compl_len, (int)plen - compl_len); } } restore_orig_extmarks(); @@ -2313,18 +2323,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) { len -= utf_head_off(p, p + len); @@ -2332,8 +2342,6 @@ static void ins_compl_fixRedoBufForLeader(char *ptr_arg) for (p += len; *p != NUL; MB_PTR_ADV(p)) { AppendCharToRedobuff(K_BS); } - } else { - len = 0; } AppendToRedobuffLit(ptr + len, -1); } @@ -2719,12 +2727,14 @@ static void set_completion(colnr_T startcol, list_T *list) compl_col = startcol; compl_length = curwin->w_cursor.col - startcol; // compl_pattern doesn't need to be set - compl_orig_text = xstrnsave(get_cursor_line_ptr() + compl_col, (size_t)compl_length); + compl_orig_text = cbuf_to_string(get_cursor_line_ptr() + compl_col, + (size_t)compl_length); save_orig_extmarks(); if (p_ic) { flags |= CP_ICASE; } - if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, + if (ins_compl_add(compl_orig_text.data, (int)compl_orig_text.size, + NULL, NULL, false, NULL, 0, flags | CP_FAST, false, NULL) != OK) { return; } @@ -2922,7 +2932,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict) if (what_flag & CI_WHAT_ITEMS) { dict_T *di = tv_dict_alloc(); tv_list_append_dict(li, di); - tv_dict_add_str(di, S_LEN("word"), match->cp_str); + tv_dict_add_str(di, S_LEN("word"), match->cp_str.data); tv_dict_add_str(di, S_LEN("abbr"), match->cp_text[CPT_ABBR]); tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]); tv_dict_add_str(di, S_LEN("kind"), match->cp_text[CPT_KIND]); @@ -3114,8 +3124,8 @@ done: /// included files. static void get_next_include_file_completion(int compl_type) { - find_pattern_in_path(compl_pattern, compl_direction, - compl_patternlen, false, false, + find_pattern_in_path(compl_pattern.data, compl_direction, + compl_pattern.size, false, false, ((compl_type == CTRL_X_PATH_DEFINES && !(compl_cont_status & CONT_SOL)) ? FIND_DEFINE : FIND_ANY), @@ -3127,14 +3137,14 @@ static void get_next_include_file_completion(int compl_type) 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); + expand_by_function(compl_type, compl_pattern.data); } else { 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)), - compl_pattern, + compl_pattern.data, dict != NULL ? dict_f : 0, compl_type == CTRL_X_THESAURUS); } @@ -3145,14 +3155,14 @@ static void get_next_tag_completion(void) { // set p_ic according to p_ic, p_scs and pat for find_tags(). const int save_p_ic = p_ic; - p_ic = ignorecase(compl_pattern); + p_ic = ignorecase(compl_pattern.data); // Find up to TAG_MANY matches. Avoids that an enormous number // of matches is found when compl_pattern is empty g_tag_at_cursor = true; char **matches; int num_matches; - if (find_tags(compl_pattern, &num_matches, &matches, + if (find_tags(compl_pattern.data, &num_matches, &matches, TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0), TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) { @@ -3167,13 +3177,13 @@ static void get_next_filename_completion(void) { char **matches; int num_matches; - if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, + if (expand_wildcards(1, &compl_pattern.data, &num_matches, &matches, EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) { return; } // May change home directory back to "~". - tilde_replace(compl_pattern, num_matches, matches); + tilde_replace(compl_pattern.data, num_matches, matches); #ifdef BACKSLASH_IN_FILENAME if (curbuf->b_p_csl[0] != NUL) { for (int i = 0; i < num_matches; i++) { @@ -3197,8 +3207,8 @@ static void get_next_cmdline_completion(void) { char **matches; int num_matches; - if (expand_cmdline(&compl_xp, compl_pattern, - (int)compl_patternlen, &num_matches, &matches) == EXPAND_OK) { + if (expand_cmdline(&compl_xp, compl_pattern.data, + (int)compl_pattern.size, &num_matches, &matches) == EXPAND_OK) { ins_compl_add_matches(num_matches, matches, false); } } @@ -3207,7 +3217,7 @@ static void get_next_cmdline_completion(void) static void get_next_spell_completion(linenr_T lnum) { char **matches; - int num_matches = expand_spelling(lnum, compl_pattern, &matches); + int num_matches = expand_spelling(lnum, compl_pattern.data, &matches); if (num_matches > 0) { ins_compl_add_matches(num_matches, matches, p_ic); } else { @@ -3226,22 +3236,24 @@ static char *ins_compl_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_po { *match_len = 0; char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum) + cur_match_pos->col; - int len; + int len = ml_get_buf_len(ins_buf, cur_match_pos->lnum) - cur_match_pos->col; 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); + len = ml_get_buf_len(ins_buf, cur_match_pos->lnum + 1); if (!p_paste) { - ptr = skipwhite(ptr); + char *tmp_ptr = ptr; + ptr = skipwhite(tmp_ptr); + len -= (int)(ptr - tmp_ptr); } } - len = (int)strlen(ptr); } else { char *tmp_ptr = ptr; - if (compl_status_adding() && compl_length <= (int)strlen(tmp_ptr)) { + if (compl_status_adding() && compl_length <= len) { tmp_ptr += compl_length; // Skip if already inside a word. if (vim_iswordp(tmp_ptr)) { @@ -3337,10 +3349,11 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_ // 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(st->ins_buf, st->cur_match_pos, - compl_direction, compl_pattern); + compl_direction, compl_pattern.data); } else { found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos, - NULL, compl_direction, compl_pattern, compl_patternlen, + NULL, compl_direction, compl_pattern.data, + compl_pattern.size, 1, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL); } msg_silent--; @@ -3437,7 +3450,7 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_ case CTRL_X_FUNCTION: case CTRL_X_OMNI: - expand_by_function(type, compl_pattern); + expand_by_function(type, compl_pattern.data); break; case CTRL_X_SPELL: @@ -3468,7 +3481,7 @@ 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) { + if (strncmp(tail, compl_orig_text.data, compl_orig_text.size) == 0) { ins_compl_add(tail, (int)strlen(tail), NULL, NULL, false, NULL, 0, p_ic ? CP_ICASE : 0, false, NULL); } @@ -3536,7 +3549,7 @@ static int ins_compl_get_exp(pos_T *ini) // If complete() was called then compl_pattern has been reset. // The following won't work then, bail out. - if (compl_pattern == NULL) { + if (compl_pattern.data == NULL) { break; } @@ -3604,7 +3617,7 @@ static int ins_compl_get_exp(pos_T *ini) static void ins_compl_update_shown_match(void) { while (!ins_compl_equal(compl_shown_match, - compl_leader, strlen(compl_leader)) + compl_leader.data, compl_leader.size) && compl_shown_match->cp_next != NULL && !is_first_match(compl_shown_match->cp_next)) { compl_shown_match = compl_shown_match->cp_next; @@ -3613,10 +3626,10 @@ static void ins_compl_update_shown_match(void) // If we didn't find it searching forward, and compl_shows_dir is // backward, find the last match. if (compl_shows_dir_backward() - && !ins_compl_equal(compl_shown_match, compl_leader, strlen(compl_leader)) + && !ins_compl_equal(compl_shown_match, compl_leader.data, compl_leader.size) && (compl_shown_match->cp_next == NULL || is_first_match(compl_shown_match->cp_next))) { - while (!ins_compl_equal(compl_shown_match, compl_leader, strlen(compl_leader)) + while (!ins_compl_equal(compl_shown_match, compl_leader.data, compl_leader.size) && compl_shown_match->cp_prev != NULL && !is_first_match(compl_shown_match->cp_prev)) { compl_shown_match = compl_shown_match->cp_prev; @@ -3631,13 +3644,13 @@ void ins_compl_delete(bool new_leader) // allows marks present on the original text to shrink/grow appropriately. int orig_col = 0; if (new_leader) { - char *orig = compl_orig_text; + char *orig = compl_orig_text.data; char *leader = ins_compl_leader(); while (*orig != NUL && utf_ptr2char(orig) == utf_ptr2char(leader)) { leader += utf_ptr2len(leader); orig += utf_ptr2len(orig); } - orig_col = (int)(orig - compl_orig_text); + orig_col = (int)(orig - compl_orig_text.data); } // In insert mode: Delete the typed part. @@ -3664,8 +3677,8 @@ void ins_compl_insert(bool in_compl_func) int compl_len = get_compl_len(); // Make sure we don't go over the end of the string, this can happen with // illegal bytes. - if (compl_len < (int)strlen(compl_shown_match->cp_str)) { - ins_compl_insert_bytes(compl_shown_match->cp_str + compl_len, -1); + if (compl_len < (int)compl_shown_match->cp_str.size) { + ins_compl_insert_bytes(compl_shown_match->cp_str.data + compl_len, -1); } compl_used_match = !match_at_original_text(compl_shown_match); @@ -3735,7 +3748,7 @@ static compl_T *find_comp_when_fuzzy(void) comp = compl_first_match; do { if (comp->cp_score == score - && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR])) { + && (str == comp->cp_str.data || str == comp->cp_text[CPT_ABBR])) { return comp; } comp = comp->cp_next; @@ -3818,9 +3831,9 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a found_end = false; } if (!match_at_original_text(compl_shown_match) - && compl_leader != NULL + && compl_leader.data != NULL && !ins_compl_equal(compl_shown_match, - compl_leader, strlen(compl_leader)) + compl_leader.data, compl_leader.size) && !(compl_fuzzy_match && compl_shown_match->cp_score > 0)) { todo++; } else { @@ -3876,7 +3889,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match return -1; } - if (compl_leader != NULL + if (compl_leader.data != NULL && !match_at_original_text(compl_shown_match) && !compl_fuzzy_match) { // Update "compl_shown_match" to the actually shown match @@ -3914,17 +3927,17 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match // Insert the text of the new completion, or the compl_leader. if (compl_no_insert && !started) { - ins_compl_insert_bytes(compl_orig_text + get_compl_len(), -1); + ins_compl_insert_bytes(compl_orig_text.data + get_compl_len(), -1); compl_used_match = false; restore_orig_extmarks(); } else if (insert_match) { if (!compl_get_longest || compl_used_match) { ins_compl_insert(in_compl_func); } else { - assert(compl_leader != NULL); - ins_compl_insert_bytes(compl_leader + get_compl_len(), -1); + assert(compl_leader.data != NULL); + ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1); } - if (!strcmp(compl_curr_match->cp_str, compl_orig_text)) { + if (strequal(compl_curr_match->cp_str.data, compl_orig_text.data)) { restore_orig_extmarks(); } } else { @@ -4088,8 +4101,7 @@ static bool ins_compl_use_match(int c) /// Get the pattern, column and length for normal completion (CTRL-N CTRL-P /// completion) -/// Sets the global variables: compl_col, compl_length, compl_pattern and -/// compl_patternlen. +/// Sets the global variables: compl_col, compl_length and compl_pattern. /// Uses the global variables: compl_cont_status and ctrl_x_mode static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) { @@ -4100,29 +4112,32 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) compl_length = curs_col - startcol; } if (p_ic) { - compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0); + compl_pattern = cstr_as_string(str_foldcase(line + compl_col, + compl_length, NULL, 0)); } else { - compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); + compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length); } } else if (compl_status_adding()) { char *prefix = "\\<"; size_t prefixlen = STRLEN_LITERAL("\\<"); - // we need up to 2 extra chars for the prefix - compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, - compl_length) + prefixlen); if (!vim_iswordp(line + compl_col) || (compl_col > 0 && (vim_iswordp(mb_prevptr(line, line + compl_col))))) { prefix = ""; prefixlen = 0; } - STRCPY(compl_pattern, prefix); - quote_meta(compl_pattern + prefixlen, line + compl_col, compl_length); + + // we need up to 2 extra chars for the prefix + size_t n = quote_meta(NULL, line + compl_col, compl_length) + prefixlen; + compl_pattern.data = xmalloc(n); + STRCPY(compl_pattern.data, prefix); + quote_meta(compl_pattern.data + prefixlen, line + compl_col, compl_length); + compl_pattern.size = n - 1; } else if (--startcol < 0 || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) { // Match any word of at least two chars - compl_pattern = xstrnsave(S_LEN("\\<\\k\\k")); + compl_pattern = cbuf_to_string(S_LEN("\\<\\k\\k")); compl_col += curs_col; compl_length = 0; } else { @@ -4143,19 +4158,20 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) // Only match word with at least two chars -- webb // there's no need to call quote_meta, // xmalloc(7) is enough -- Acevedo - compl_pattern = xmalloc(7); - STRCPY(compl_pattern, "\\<"); - quote_meta(compl_pattern + 2, line + compl_col, 1); - strcat(compl_pattern, "\\k"); + compl_pattern.data = xmalloc(7); + STRCPY(compl_pattern.data, "\\<"); + quote_meta(compl_pattern.data + 2, line + compl_col, 1); + strcat(compl_pattern.data, "\\k"); + compl_pattern.size = strlen(compl_pattern.data); } else { - compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2); - STRCPY(compl_pattern, "\\<"); - quote_meta(compl_pattern + 2, line + compl_col, compl_length); + size_t n = quote_meta(NULL, line + compl_col, compl_length) + 2; + compl_pattern.data = xmalloc(n); + STRCPY(compl_pattern.data, "\\<"); + quote_meta(compl_pattern.data + 2, line + compl_col, compl_length); + compl_pattern.size = n - 1; } } - compl_patternlen = strlen(compl_pattern); - return OK; } @@ -4170,13 +4186,12 @@ static int get_wholeline_compl_info(char *line, colnr_T curs_col) compl_length = 0; } if (p_ic) { - compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0); + compl_pattern = cstr_as_string(str_foldcase(line + compl_col, + compl_length, NULL, 0)); } else { - compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); + compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length); } - compl_patternlen = strlen(compl_pattern); - return OK; } @@ -4201,8 +4216,8 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col) compl_col += startcol; compl_length = (int)curs_col - startcol; - compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES); - compl_patternlen = strlen(compl_pattern); + compl_pattern = cstr_as_string(addstar(line + compl_col, + (size_t)compl_length, EXPAND_FILES)); return OK; } @@ -4211,9 +4226,9 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col) /// Sets the global variables: compl_col, compl_length and compl_pattern. static int get_cmdline_compl_info(char *line, colnr_T curs_col) { - compl_pattern = xstrnsave(line, (size_t)curs_col); - compl_patternlen = (size_t)curs_col; - set_cmd_context(&compl_xp, compl_pattern, (int)compl_patternlen, curs_col, false); + compl_pattern = cbuf_to_string(line, (size_t)curs_col); + set_cmd_context(&compl_xp, compl_pattern.data, + (int)compl_pattern.size, curs_col, false); if (compl_xp.xp_context == EXPAND_LUA) { nlua_expand_pat(&compl_xp); } @@ -4223,7 +4238,7 @@ static int get_cmdline_compl_info(char *line, colnr_T curs_col) // "pattern not found" message. compl_col = curs_col; } else { - compl_col = (int)(compl_xp.xp_pattern - compl_pattern); + compl_col = (int)(compl_xp.xp_pattern - compl_pattern.data); } compl_length = curs_col - compl_col; @@ -4301,8 +4316,7 @@ static int get_userdefined_compl_info(colnr_T curs_col) // it may have become invalid. char *line = ml_get(curwin->w_cursor.lnum); compl_length = curs_col - compl_col; - compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); - compl_patternlen = (size_t)compl_length; + compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length); return OK; } @@ -4327,8 +4341,7 @@ static int get_spell_compl_info(int startcol, colnr_T curs_col) } // Need to obtain "line" again, it may have become invalid. char *line = ml_get(curwin->w_cursor.lnum); - compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); - compl_patternlen = (size_t)compl_length; + compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length); return OK; } @@ -4513,19 +4526,19 @@ static int ins_compl_start(void) ins_compl_fixRedoBufForLeader(NULL); // Always add completion for the original text. - xfree(compl_orig_text); + API_CLEAR_STRING(compl_orig_text); kv_destroy(compl_orig_extmarks); - compl_orig_text = xstrnsave(line + compl_col, (size_t)compl_length); + compl_orig_text = cbuf_to_string(line + compl_col, (size_t)compl_length); save_orig_extmarks(); int flags = CP_ORIGINAL_TEXT; if (p_ic) { flags |= CP_ICASE; } - if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, + if (ins_compl_add(compl_orig_text.data, (int)compl_orig_text.size, + NULL, NULL, false, NULL, 0, flags, false, NULL) != OK) { - XFREE_CLEAR(compl_pattern); - compl_patternlen = 0; - XFREE_CLEAR(compl_orig_text); + API_CLEAR_STRING(compl_pattern); + API_CLEAR_STRING(compl_orig_text); kv_destroy(compl_orig_extmarks); return FAIL; } @@ -4758,7 +4771,7 @@ static unsigned quote_meta(char *dest, char *src, int len) #if defined(EXITFREE) void free_insexpand_stuff(void) { - XFREE_CLEAR(compl_orig_text); + API_CLEAR_STRING(compl_orig_text); kv_destroy(compl_orig_extmarks); callback_free(&cfu_cb); callback_free(&ofu_cb); diff --git a/todo.patch b/todo.patch new file mode 100644 index 0000000000..2a08b0cb43 --- /dev/null +++ b/todo.patch @@ -0,0 +1,101 @@ +diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c +index fb67c489b2..94278946df 100644 +--- a/src/nvim/insexpand.c ++++ b/src/nvim/insexpand.c +@@ -3476,17 +3498,15 @@ static void get_next_filename_completion(void) + char_u *ptr; + garray_T fuzzy_indices; + int i; + int score; + char_u *leader = ins_compl_leader(); +- size_t leader_len = STRLEN(leader); ++ size_t leader_len = ins_compl_leader_len(); + int in_fuzzy = ((get_cot_flags() & COT_FUZZY) != 0 && leader_len > 0); + char_u **sorted_matches; + int *fuzzy_indices_data; + char_u *last_sep = NULL; +- size_t path_with_wildcard_len; +- char_u *path_with_wildcard; + + #ifdef BACKSLASH_IN_FILENAME + char pathsep = (curbuf->b_p_csl[0] == 's') + ? '/' : (curbuf->b_p_csl[0] == 'b') ? '\\' : PATHSEP; + #else +@@ -3494,58 +3514,62 @@ static void get_next_filename_completion(void) + #endif + + if (in_fuzzy) { + #ifdef BACKSLASH_IN_FILENAME + if (curbuf->b_p_csl[0] == 's') { +- for (i = 0; i < leader_len; i++) { ++ for (i = 0; i < (int)leader_len; i++) { + if (leader[i] == '\\') { + leader[i] = '/'; + } + } + } else if (curbuf->b_p_csl[0] == 'b') { +- for (i = 0; i < leader_len; i++) { ++ for (i = 0; i < (int)leader_len; i++) { + if (leader[i] == '/') { + leader[i] = '\\'; + } + } + } + #endif + last_sep = vim_strrchr(leader, pathsep); + if (last_sep == NULL) { + // No path separator or separator is the last character, + // fuzzy match the whole leader +- vim_free(compl_pattern); +- compl_pattern = vim_strsave((char_u *)"*"); +- compl_patternlen = STRLEN(compl_pattern); ++ VIM_CLEAR_STRING(compl_pattern); ++ compl_pattern.string = vim_strnsave((char_u *)"*", 1); ++ if (compl_pattern.string == NULL) { ++ return; ++ } ++ compl_pattern.length = 1; + } else if (*(last_sep + 1) == '\0') { + in_fuzzy = FALSE; + } else { + // Split leader into path and file parts + int path_len = last_sep - leader + 1; +- path_with_wildcard_len = path_len + 2; +- path_with_wildcard = alloc(path_with_wildcard_len); ++ char_u *path_with_wildcard; ++ ++ path_with_wildcard = alloc(path_len + 2); + if (path_with_wildcard != NULL) { +- vim_strncpy(path_with_wildcard, leader, path_len); +- vim_strcat(path_with_wildcard, (char_u *)"*", path_with_wildcard_len); +- vim_free(compl_pattern); +- compl_pattern = path_with_wildcard; +- compl_patternlen = STRLEN(compl_pattern); ++ vim_snprintf((char *)path_with_wildcard, path_len + 2, "%*.*s*", path_len, path_len, ++ leader); ++ VIM_CLEAR_STRING(compl_pattern); ++ compl_pattern.string = path_with_wildcard; ++ compl_pattern.length = path_len + 1; + + // Move leader to the file part + leader = last_sep + 1; +- leader_len = STRLEN(leader); ++ leader_len -= path_len; + } + } + } + +- if (expand_wildcards(1, &compl_pattern, &num_matches, &matches, ++ if (expand_wildcards(1, &compl_pattern.string, &num_matches, &matches, + EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) { + return; + } + + // May change home directory back to "~". +- tilde_replace(compl_pattern, num_matches, matches); ++ tilde_replace(compl_pattern.string, num_matches, matches); + #ifdef BACKSLASH_IN_FILENAME + if (curbuf->b_p_csl[0] != NUL) { + for (i = 0; i < num_matches; ++i) { + ptr = matches[i]; + while (*ptr != NUL) {