vim-patch:9.1.0882: too many strlen() calls in insexpand.c

Problem:  too many strlen() calls in insexpand.c
Solution: Refactor insexpand.c and reduce number of calls to STRLEN(),
          fix a warning get_next_filename_completion(), add new function
          ins_compl_leader_len() (John Marriott)

closes: vim/vim#16095

5e6ea92b2c

Co-authored-by: John Marriott <basilisk@internode.on.net>
This commit is contained in:
zeertzjq 2024-11-23 22:11:00 +08:00
parent 738320188f
commit a1c14f8d81
2 changed files with 269 additions and 155 deletions

View File

@ -10,6 +10,7 @@
#include <string.h> #include <string.h>
#include "klib/kvec.h" #include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h" #include "nvim/ascii_defs.h"
#include "nvim/autocmd.h" #include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h" #include "nvim/autocmd_defs.h"
@ -157,7 +158,7 @@ struct compl_S {
compl_T *cp_next; compl_T *cp_next;
compl_T *cp_prev; compl_T *cp_prev;
compl_T *cp_match_next; ///< matched next compl_T 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 char *(cp_text[CPT_COUNT]); ///< text for the menu
typval_T cp_user_data; typval_T cp_user_data;
char *cp_fname; ///< file containing the match, allocated when 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 /// When "compl_leader" is not NULL only matches that start with this string
/// are used. /// 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 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 ctrl_x_mode = CTRL_X_NORMAL;
static int compl_matches = 0; ///< number of completion matches static int compl_matches = 0; ///< number of completion matches
static char *compl_pattern = NULL; static String compl_pattern = STRING_INIT;
static size_t compl_patternlen = 0;
static Direction compl_direction = FORWARD; static Direction compl_direction = FORWARD;
static Direction compl_shows_dir = FORWARD; static Direction compl_shows_dir = FORWARD;
static int compl_pending = 0; ///< > 1 for postponed CTRL-N static int compl_pending = 0; ///< > 1 for postponed CTRL-N
@ -257,7 +257,7 @@ static int compl_length = 0;
static colnr_T compl_col = 0; ///< column where the text starts static colnr_T compl_col = 0; ///< column where the text starts
///< that is being completed ///< that is being completed
static colnr_T compl_ins_end_col = 0; static colnr_T compl_ins_end_col = 0;
static char *compl_orig_text = NULL; ///< text as it was before static String compl_orig_text = STRING_INIT; ///< text as it was before
///< completion started ///< completion started
/// Undo information to restore extmarks for original text. /// Undo information to restore extmarks for original text.
static extmark_undo_vec_t compl_orig_extmarks; static extmark_undo_vec_t compl_orig_extmarks;
@ -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? // 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++) { for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p); const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) { if (mb_islower(c)) {
@ -659,7 +659,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// upper case. // upper case.
if (!has_lower) { if (!has_lower) {
bool was_letter = false; 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++) { for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p); const int c = mb_ptr2char_adv(&p);
if (was_letter && mb_isupper(c) && mb_islower(wca[i])) { 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. // 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++) { for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p); const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) { 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); ga_grow(&gap, IOSIZE);
*p = NUL; *p = NUL;
STRCPY(gap.ga_data, IObuff); STRCPY(gap.ga_data, IObuff);
gap.ga_len = (int)strlen(IObuff); gap.ga_len = (int)(p - IObuff);
} else { } else {
p += utf_char2bytes(wca[i++], p); 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. // Find actual length of original text.
{ {
const char *p = compl_orig_text; const char *p = compl_orig_text.data;
compl_char_len = 0; compl_char_len = 0;
while (*p != NUL) { while (*p != NUL) {
MB_PTR_ADV(p); 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; match = compl_first_match;
do { do {
if (!match_at_original_text(match) if (!match_at_original_text(match)
&& strncmp(match->cp_str, str, (size_t)len) == 0 && strncmp(match->cp_str.data, str, (size_t)len) == 0
&& ((int)strlen(match->cp_str) <= len || match->cp_str[len] == NUL)) { && ((int)match->cp_str.size <= len || match->cp_str.data[len] == NUL)) {
if (cptext_allocated) { if (cptext_allocated) {
free_cptext(cptext); 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) { if (flags & CP_ORIGINAL_TEXT) {
match->cp_number = 0; 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: // match-fname is:
// - compl_curr_match->cp_fname if it is a string equal to fname. // - 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; return true;
} }
if (match->cp_flags & CP_ICASE) { 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 /// 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". /// Reduce the longest common string for match "match".
static void ins_compl_longest_match(compl_T *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. // 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); bool had_match = (curwin->w_cursor.col > compl_col);
ins_compl_delete(false); 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); ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it // 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. // Reduce the text if this match differs from compl_leader.
char *p = compl_leader; char *p = compl_leader.data;
char *s = match->cp_str; char *s = match->cp_str.data;
while (*p != NUL) { while (*p != NUL) {
int c1 = utf_ptr2char(p); int c1 = utf_ptr2char(p);
int c2 = utf_ptr2char(s); int c2 = utf_ptr2char(s);
@ -1014,9 +1014,11 @@ static void ins_compl_longest_match(compl_T *match)
if (*p != NUL) { if (*p != NUL) {
// Leader was shortened, need to change the inserted text. // Leader was shortened, need to change the inserted text.
*p = NUL; *p = NUL;
compl_leader.size = (size_t)(p - compl_leader.data);
bool had_match = (curwin->w_cursor.col > compl_col); bool had_match = (curwin->w_cursor.col > compl_col);
ins_compl_delete(false); 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); ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it // 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. /// Return whether the shown match is long enough.
bool ins_compl_long_shown_match(void) bool ins_compl_long_shown_match(void)
{ {
return compl_shown_match != NULL && compl_shown_match->cp_str != NULL return compl_shown_match != NULL && compl_shown_match->cp_str.data != NULL
&& (colnr_T)strlen(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col; && (colnr_T)compl_shown_match->cp_str.size > curwin->w_cursor.col - compl_col;
} }
/// Get the local or global value of 'completeopt' flags. /// 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 } // { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc_lock(VAR_FIXED); 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("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("menu"), match->cp_text[CPT_MENU]);
tv_dict_add_str(dict, S_LEN("kind"), match->cp_text[CPT_KIND]); 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); XFREE_CLEAR(compl_leader);
} }
const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0;
int max_fuzzy_score = 0; int max_fuzzy_score = 0;
unsigned cur_cot_flags = get_cot_flags(); unsigned cur_cot_flags = get_cot_flags();
bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0; 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. // match after it, don't highlight anything.
bool shown_match_ok = match_at_original_text(compl_shown_match); 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_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 { do {
// When 'completeopt' contains "fuzzy" and leader is not NULL or empty, // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
// set the cp_score for later comparisons. // set the cp_score for later comparisons.
if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) { if (compl_fuzzy_match && compl_leader.data != NULL && compl_leader.size > 0) {
comp->cp_score = fuzzy_match_str(comp->cp_str, compl_leader); comp->cp_score = fuzzy_match_str(comp->cp_str.data, compl_leader.data);
} }
if (!match_at_original_text(comp) if (!match_at_original_text(comp)
&& (compl_leader == NULL && (compl_leader.data == NULL
|| ins_compl_equal(comp, compl_leader, (size_t)lead_len) || ins_compl_equal(comp, compl_leader.data, compl_leader.size)
|| (compl_fuzzy_match && comp->cp_score > 0))) { || (compl_fuzzy_match && comp->cp_score > 0))) {
compl_match_arraysize++; compl_match_arraysize++;
if (match_head == NULL) { if (match_head == NULL) {
@ -1303,7 +1304,7 @@ static int ins_compl_build_pum(void)
comp = match_head; comp = match_head;
while (comp != NULL) { while (comp != NULL) {
compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR] != 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_kind = comp->cp_text[CPT_KIND];
compl_match_array[i].pum_info = comp->cp_text[CPT_INFO]; 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_score = comp->cp_score;
@ -1316,7 +1317,7 @@ static int ins_compl_build_pum(void)
comp = match_next; 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++) { for (i = 0; i < compl_match_arraysize; i++) {
compl_match_array[i].pum_idx = i; compl_match_array[i].pum_idx = i;
} }
@ -1354,7 +1355,7 @@ void ins_compl_show_pum(void)
} else { } else {
// popup menu already exists, only need to find the current item. // popup menu already exists, only need to find the current item.
for (int i = 0; i < compl_match_arraysize; i++) { 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]) { || compl_match_array[i].pum_text == compl_shown_match->cp_text[CPT_ABBR]) {
cur = i; cur = i;
break; break;
@ -1423,7 +1424,13 @@ bool compl_match_curr_select(int selected)
/// Get current completion leader /// Get current completion leader
char *ins_compl_leader(void) 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 /// 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 /// Free the list of completions
static void ins_compl_free(void) static void ins_compl_free(void)
{ {
XFREE_CLEAR(compl_pattern); API_CLEAR_STRING(compl_pattern);
compl_patternlen = 0; API_CLEAR_STRING(compl_leader);
XFREE_CLEAR(compl_leader);
if (compl_first_match == NULL) { if (compl_first_match == NULL) {
return; return;
@ -1683,7 +1689,7 @@ static void ins_compl_free(void)
do { do {
compl_T *match = compl_curr_match; compl_T *match = compl_curr_match;
compl_curr_match = compl_curr_match->cp_next; 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. // several entries may use the same fname, free it just once.
if (match->cp_flags & CP_FREE_FNAME) { if (match->cp_flags & CP_FREE_FNAME) {
xfree(match->cp_fname); xfree(match->cp_fname);
@ -1704,12 +1710,11 @@ void ins_compl_clear(void)
compl_started = false; compl_started = false;
compl_matches = 0; compl_matches = 0;
compl_ins_end_col = 0; compl_ins_end_col = 0;
XFREE_CLEAR(compl_pattern); API_CLEAR_STRING(compl_pattern);
compl_patternlen = 0; API_CLEAR_STRING(compl_leader);
XFREE_CLEAR(compl_leader);
edit_submode_extra = NULL; edit_submode_extra = NULL;
kv_destroy(compl_orig_extmarks); kv_destroy(compl_orig_extmarks);
XFREE_CLEAR(compl_orig_text); API_CLEAR_STRING(compl_orig_text);
compl_enter_selects = false; compl_enter_selects = false;
// clear v:completed_item // clear v:completed_item
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED)); 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 // TODO(bfredl): get rid of random update_screen() calls deep inside completion logic
line = get_cursor_line_ptr(); line = get_cursor_line_ptr();
xfree(compl_leader); API_CLEAR_STRING(compl_leader);
compl_leader = xstrnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col)); compl_leader = cbuf_to_string(line + compl_col,
(size_t)(p_off - (ptrdiff_t)compl_col));
ins_compl_new_leader(); ins_compl_new_leader();
if (compl_shown_match != NULL) { if (compl_shown_match != NULL) {
@ -1828,11 +1834,11 @@ static void ins_compl_new_leader(void)
{ {
ins_compl_del_pum(); ins_compl_del_pum();
ins_compl_delete(true); 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; compl_used_match = false;
if (compl_started) { if (compl_started) {
ins_compl_set_original_text(compl_leader); ins_compl_set_original_text(compl_leader.data, compl_leader.size);
} else { } else {
spell_bad_len = 0; // need to redetect bad word spell_bad_len = 0; // need to redetect bad word
// Matches were cleared, need to search for them now. // Matches were cleared, need to search for them now.
@ -1892,8 +1898,8 @@ void ins_compl_addleader(int c)
ins_compl_restart(); ins_compl_restart();
} }
xfree(compl_leader); API_CLEAR_STRING(compl_leader);
compl_leader = xstrnsave(get_cursor_line_ptr() + compl_col, compl_leader = cbuf_to_string(get_cursor_line_ptr() + compl_col,
(size_t)(curwin->w_cursor.col - compl_col)); (size_t)(curwin->w_cursor.col - compl_col));
ins_compl_new_leader(); ins_compl_new_leader();
} }
@ -1914,19 +1920,19 @@ static void ins_compl_restart(void)
} }
/// Set the first match, the original text. /// 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 FUNC_ATTR_NONNULL_ALL
{ {
// Replace the original text entry. // Replace the original text entry.
// The CP_ORIGINAL_TEXT flag is either at the first item or might possibly // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
// be at the last item for backward completion // be at the last item for backward completion
if (match_at_original_text(compl_first_match)) { // safety check if (match_at_original_text(compl_first_match)) { // safety check
xfree(compl_first_match->cp_str); API_CLEAR_STRING(compl_first_match->cp_str);
compl_first_match->cp_str = xstrdup(str); compl_first_match->cp_str = cbuf_to_string(str, len);
} else if (compl_first_match->cp_prev != NULL } else if (compl_first_match->cp_prev != NULL
&& match_at_original_text(compl_first_match->cp_prev)) { && match_at_original_text(compl_first_match->cp_prev)) {
xfree(compl_first_match->cp_prev->cp_str); API_CLEAR_STRING(compl_first_match->cp_prev->cp_str);
compl_first_match->cp_prev->cp_str = xstrdup(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; int len = (int)curwin->w_cursor.col - (int)compl_col;
assert(compl_shown_match != NULL); assert(compl_shown_match != NULL);
char *p = compl_shown_match->cp_str; char *p = compl_shown_match->cp_str.data;
if ((int)strlen(p) <= len) { // the match is too short 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 // When still at the original match use the first entry that matches
// the leader. // the leader.
if (!match_at_original_text(compl_shown_match)) { if (!match_at_original_text(compl_shown_match)) {
@ -1945,15 +1951,17 @@ void ins_compl_addfrommatch(void)
} }
p = NULL; p = NULL;
size_t plen = 0;
for (compl_T *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) { && !is_first_match(cp); cp = cp->cp_next) {
if (compl_leader == NULL if (compl_leader.data == NULL
|| ins_compl_equal(cp, compl_leader, strlen(compl_leader))) { || ins_compl_equal(cp, compl_leader.data, compl_leader.size)) {
p = cp->cp_str; p = cp->cp_str.data;
plen = cp->cp_str.size;
break; break;
} }
} }
if (p == NULL || (int)strlen(p) <= len) { if (p == NULL || (int)plen <= len) {
return; 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 // Get here when we have finished typing a sequence of ^N and
// ^P or other completion characters in CTRL-X mode. Free up // ^P or other completion characters in CTRL-X mode. Free up
// memory that was used, and make sure we can redo the insert. // 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 // If any of the original typed text has been changed, eg when
// ignorecase is set, we must add back-spaces to the redo // ignorecase is set, we must add back-spaces to the redo
// buffer. We add as few as necessary to delete just the part // 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. // CTRL-E then don't use the current match.
char *ptr; char *ptr;
if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) { 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 { } else {
ptr = NULL; 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 if ((c == Ctrl_Y || (compl_enter_selects
&& (c == CAR || c == K_KENTER || c == NL))) && (c == CAR || c == K_KENTER || c == NL)))
&& pum_visible()) { && pum_visible()) {
word = xstrdup(compl_shown_match->cp_str); word = xstrdup(compl_shown_match->cp_str.data);
retval = true; retval = true;
} }
@ -2154,16 +2162,18 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
if (c == Ctrl_E) { if (c == Ctrl_E) {
ins_compl_delete(false); ins_compl_delete(false);
char *p = NULL; char *p = NULL;
if (compl_leader != NULL) { size_t plen = 0;
p = compl_leader; if (compl_leader.data != NULL) {
p = compl_leader.data;
plen = compl_leader.size;
} else if (compl_first_match != NULL) { } else if (compl_first_match != NULL) {
p = compl_orig_text; p = compl_orig_text.data;
plen = compl_orig_text.size;
} }
if (p != NULL) { if (p != NULL) {
const int compl_len = get_compl_len(); const int compl_len = get_compl_len();
const int len = (int)strlen(p); if ((int)plen > compl_len) {
if (len > compl_len) { ins_compl_insert_bytes(p + compl_len, (int)plen - compl_len);
ins_compl_insert_bytes(p + compl_len, len - compl_len);
} }
} }
restore_orig_extmarks(); restore_orig_extmarks();
@ -2313,18 +2323,18 @@ bool ins_compl_prep(int c)
/// "ptr" is the known leader text or NUL. /// "ptr" is the known leader text or NUL.
static void ins_compl_fixRedoBufForLeader(char *ptr_arg) static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
{ {
int len; int len = 0;
char *ptr = ptr_arg; char *ptr = ptr_arg;
if (ptr == NULL) { if (ptr == NULL) {
if (compl_leader != NULL) { if (compl_leader.data != NULL) {
ptr = compl_leader; ptr = compl_leader.data;
} else { } else {
return; // nothing to do return; // nothing to do
} }
} }
if (compl_orig_text != NULL) { if (compl_orig_text.data != NULL) {
char *p = compl_orig_text; char *p = compl_orig_text.data;
for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {} for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {}
if (len > 0) { if (len > 0) {
len -= utf_head_off(p, p + len); 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)) { for (p += len; *p != NUL; MB_PTR_ADV(p)) {
AppendCharToRedobuff(K_BS); AppendCharToRedobuff(K_BS);
} }
} else {
len = 0;
} }
AppendToRedobuffLit(ptr + len, -1); AppendToRedobuffLit(ptr + len, -1);
} }
@ -2719,12 +2727,14 @@ static void set_completion(colnr_T startcol, list_T *list)
compl_col = startcol; compl_col = startcol;
compl_length = curwin->w_cursor.col - startcol; compl_length = curwin->w_cursor.col - startcol;
// compl_pattern doesn't need to be set // 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(); save_orig_extmarks();
if (p_ic) { if (p_ic) {
flags |= CP_ICASE; 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) { flags | CP_FAST, false, NULL) != OK) {
return; return;
} }
@ -2922,7 +2932,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
if (what_flag & CI_WHAT_ITEMS) { if (what_flag & CI_WHAT_ITEMS) {
dict_T *di = tv_dict_alloc(); dict_T *di = tv_dict_alloc();
tv_list_append_dict(li, di); 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("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("menu"), match->cp_text[CPT_MENU]);
tv_dict_add_str(di, S_LEN("kind"), match->cp_text[CPT_KIND]); tv_dict_add_str(di, S_LEN("kind"), match->cp_text[CPT_KIND]);
@ -3114,8 +3124,8 @@ done:
/// included files. /// included files.
static void get_next_include_file_completion(int compl_type) static void get_next_include_file_completion(int compl_type)
{ {
find_pattern_in_path(compl_pattern, compl_direction, find_pattern_in_path(compl_pattern.data, compl_direction,
compl_patternlen, false, false, compl_pattern.size, false, false,
((compl_type == CTRL_X_PATH_DEFINES ((compl_type == CTRL_X_PATH_DEFINES
&& !(compl_cont_status & CONT_SOL)) && !(compl_cont_status & CONT_SOL))
? FIND_DEFINE : FIND_ANY), ? 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) static void get_next_dict_tsr_completion(int compl_type, char *dict, int dict_f)
{ {
if (thesaurus_func_complete(compl_type)) { if (thesaurus_func_complete(compl_type)) {
expand_by_function(compl_type, compl_pattern); expand_by_function(compl_type, compl_pattern.data);
} else { } else {
ins_compl_dictionaries(dict != NULL ins_compl_dictionaries(dict != NULL
? dict ? dict
: (compl_type == CTRL_X_THESAURUS : (compl_type == CTRL_X_THESAURUS
? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr) ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
: (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)), : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)),
compl_pattern, compl_pattern.data,
dict != NULL ? dict_f : 0, dict != NULL ? dict_f : 0,
compl_type == CTRL_X_THESAURUS); 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(). // set p_ic according to p_ic, p_scs and pat for find_tags().
const int save_p_ic = p_ic; 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 // Find up to TAG_MANY matches. Avoids that an enormous number
// of matches is found when compl_pattern is empty // of matches is found when compl_pattern is empty
g_tag_at_cursor = true; g_tag_at_cursor = true;
char **matches; char **matches;
int num_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 TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
| (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0), | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) { TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) {
@ -3167,13 +3177,13 @@ static void get_next_filename_completion(void)
{ {
char **matches; char **matches;
int num_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) { EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) {
return; return;
} }
// May change home directory back to "~". // 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 #ifdef BACKSLASH_IN_FILENAME
if (curbuf->b_p_csl[0] != NUL) { if (curbuf->b_p_csl[0] != NUL) {
for (int i = 0; i < num_matches; i++) { for (int i = 0; i < num_matches; i++) {
@ -3197,8 +3207,8 @@ static void get_next_cmdline_completion(void)
{ {
char **matches; char **matches;
int num_matches; int num_matches;
if (expand_cmdline(&compl_xp, compl_pattern, if (expand_cmdline(&compl_xp, compl_pattern.data,
(int)compl_patternlen, &num_matches, &matches) == EXPAND_OK) { (int)compl_pattern.size, &num_matches, &matches) == EXPAND_OK) {
ins_compl_add_matches(num_matches, matches, false); 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) static void get_next_spell_completion(linenr_T lnum)
{ {
char **matches; 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) { if (num_matches > 0) {
ins_compl_add_matches(num_matches, matches, p_ic); ins_compl_add_matches(num_matches, matches, p_ic);
} else { } 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; *match_len = 0;
char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum) + cur_match_pos->col; 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 (ctrl_x_mode_line_or_eval()) {
if (compl_status_adding()) { if (compl_status_adding()) {
if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) { if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) {
return NULL; return NULL;
} }
ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1); 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) { if (!p_paste) {
ptr = skipwhite(ptr); char *tmp_ptr = ptr;
ptr = skipwhite(tmp_ptr);
len -= (int)(ptr - tmp_ptr);
} }
} }
len = (int)strlen(ptr);
} else { } else {
char *tmp_ptr = ptr; 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; tmp_ptr += compl_length;
// Skip if already inside a word. // Skip if already inside a word.
if (vim_iswordp(tmp_ptr)) { 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. // has added a word that was at the beginning of the line.
if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL)) { 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, found_new_match = search_for_exact_line(st->ins_buf, st->cur_match_pos,
compl_direction, compl_pattern); compl_direction, compl_pattern.data);
} else { } else {
found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos, 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); 1, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
} }
msg_silent--; 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_FUNCTION:
case CTRL_X_OMNI: case CTRL_X_OMNI:
expand_by_function(type, compl_pattern); expand_by_function(type, compl_pattern.data);
break; break;
case CTRL_X_SPELL: case CTRL_X_SPELL:
@ -3468,7 +3481,7 @@ static void get_next_bufname_token(void)
FOR_ALL_BUFFERS(b) { FOR_ALL_BUFFERS(b) {
if (b->b_p_bl && b->b_sfname != NULL) { if (b->b_p_bl && b->b_sfname != NULL) {
char *tail = path_tail(b->b_sfname); 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, ins_compl_add(tail, (int)strlen(tail), NULL, NULL, false, NULL, 0,
p_ic ? CP_ICASE : 0, false, NULL); 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. // If complete() was called then compl_pattern has been reset.
// The following won't work then, bail out. // The following won't work then, bail out.
if (compl_pattern == NULL) { if (compl_pattern.data == NULL) {
break; break;
} }
@ -3604,7 +3617,7 @@ static int ins_compl_get_exp(pos_T *ini)
static void ins_compl_update_shown_match(void) static void ins_compl_update_shown_match(void)
{ {
while (!ins_compl_equal(compl_shown_match, while (!ins_compl_equal(compl_shown_match,
compl_leader, strlen(compl_leader)) compl_leader.data, compl_leader.size)
&& compl_shown_match->cp_next != NULL && compl_shown_match->cp_next != NULL
&& !is_first_match(compl_shown_match->cp_next)) { && !is_first_match(compl_shown_match->cp_next)) {
compl_shown_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 // If we didn't find it searching forward, and compl_shows_dir is
// backward, find the last match. // backward, find the last match.
if (compl_shows_dir_backward() 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 && (compl_shown_match->cp_next == NULL
|| is_first_match(compl_shown_match->cp_next))) { || 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 && compl_shown_match->cp_prev != NULL
&& !is_first_match(compl_shown_match->cp_prev)) { && !is_first_match(compl_shown_match->cp_prev)) {
compl_shown_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. // allows marks present on the original text to shrink/grow appropriately.
int orig_col = 0; int orig_col = 0;
if (new_leader) { if (new_leader) {
char *orig = compl_orig_text; char *orig = compl_orig_text.data;
char *leader = ins_compl_leader(); char *leader = ins_compl_leader();
while (*orig != NUL && utf_ptr2char(orig) == utf_ptr2char(leader)) { while (*orig != NUL && utf_ptr2char(orig) == utf_ptr2char(leader)) {
leader += utf_ptr2len(leader); leader += utf_ptr2len(leader);
orig += utf_ptr2len(orig); 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. // 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(); int compl_len = get_compl_len();
// Make sure we don't go over the end of the string, this can happen with // Make sure we don't go over the end of the string, this can happen with
// illegal bytes. // illegal bytes.
if (compl_len < (int)strlen(compl_shown_match->cp_str)) { if (compl_len < (int)compl_shown_match->cp_str.size) {
ins_compl_insert_bytes(compl_shown_match->cp_str + compl_len, -1); ins_compl_insert_bytes(compl_shown_match->cp_str.data + compl_len, -1);
} }
compl_used_match = !match_at_original_text(compl_shown_match); 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; comp = compl_first_match;
do { do {
if (comp->cp_score == score 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; return comp;
} }
comp = comp->cp_next; 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; found_end = false;
} }
if (!match_at_original_text(compl_shown_match) if (!match_at_original_text(compl_shown_match)
&& compl_leader != NULL && compl_leader.data != NULL
&& !ins_compl_equal(compl_shown_match, && !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)) { && !(compl_fuzzy_match && compl_shown_match->cp_score > 0)) {
todo++; todo++;
} else { } else {
@ -3876,7 +3889,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
return -1; return -1;
} }
if (compl_leader != NULL if (compl_leader.data != NULL
&& !match_at_original_text(compl_shown_match) && !match_at_original_text(compl_shown_match)
&& !compl_fuzzy_match) { && !compl_fuzzy_match) {
// Update "compl_shown_match" to the actually shown 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. // Insert the text of the new completion, or the compl_leader.
if (compl_no_insert && !started) { 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; compl_used_match = false;
restore_orig_extmarks(); restore_orig_extmarks();
} else if (insert_match) { } else if (insert_match) {
if (!compl_get_longest || compl_used_match) { if (!compl_get_longest || compl_used_match) {
ins_compl_insert(in_compl_func); ins_compl_insert(in_compl_func);
} else { } else {
assert(compl_leader != NULL); assert(compl_leader.data != NULL);
ins_compl_insert_bytes(compl_leader + get_compl_len(), -1); 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(); restore_orig_extmarks();
} }
} else { } 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 /// Get the pattern, column and length for normal completion (CTRL-N CTRL-P
/// completion) /// completion)
/// Sets the global variables: compl_col, compl_length, compl_pattern and /// Sets the global variables: compl_col, compl_length and compl_pattern.
/// compl_patternlen.
/// Uses the global variables: compl_cont_status and ctrl_x_mode /// 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) 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; compl_length = curs_col - startcol;
} }
if (p_ic) { 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 { } 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()) { } else if (compl_status_adding()) {
char *prefix = "\\<"; char *prefix = "\\<";
size_t prefixlen = STRLEN_LITERAL("\\<"); 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) if (!vim_iswordp(line + compl_col)
|| (compl_col > 0 || (compl_col > 0
&& (vim_iswordp(mb_prevptr(line, line + compl_col))))) { && (vim_iswordp(mb_prevptr(line, line + compl_col))))) {
prefix = ""; prefix = "";
prefixlen = 0; 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 } else if (--startcol < 0
|| !vim_iswordp(mb_prevptr(line, line + startcol + 1))) { || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) {
// Match any word of at least two chars // 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_col += curs_col;
compl_length = 0; compl_length = 0;
} else { } 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 // Only match word with at least two chars -- webb
// there's no need to call quote_meta, // there's no need to call quote_meta,
// xmalloc(7) is enough -- Acevedo // xmalloc(7) is enough -- Acevedo
compl_pattern = xmalloc(7); compl_pattern.data = xmalloc(7);
STRCPY(compl_pattern, "\\<"); STRCPY(compl_pattern.data, "\\<");
quote_meta(compl_pattern + 2, line + compl_col, 1); quote_meta(compl_pattern.data + 2, line + compl_col, 1);
strcat(compl_pattern, "\\k"); strcat(compl_pattern.data, "\\k");
compl_pattern.size = strlen(compl_pattern.data);
} else { } else {
compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2); size_t n = quote_meta(NULL, line + compl_col, compl_length) + 2;
STRCPY(compl_pattern, "\\<"); compl_pattern.data = xmalloc(n);
quote_meta(compl_pattern + 2, line + compl_col, compl_length); 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; return OK;
} }
@ -4170,13 +4186,12 @@ static int get_wholeline_compl_info(char *line, colnr_T curs_col)
compl_length = 0; compl_length = 0;
} }
if (p_ic) { 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 { } 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; return OK;
} }
@ -4201,8 +4216,8 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col)
compl_col += startcol; compl_col += startcol;
compl_length = (int)curs_col - startcol; compl_length = (int)curs_col - startcol;
compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES); compl_pattern = cstr_as_string(addstar(line + compl_col,
compl_patternlen = strlen(compl_pattern); (size_t)compl_length, EXPAND_FILES));
return OK; 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. /// Sets the global variables: compl_col, compl_length and compl_pattern.
static int get_cmdline_compl_info(char *line, colnr_T curs_col) static int get_cmdline_compl_info(char *line, colnr_T curs_col)
{ {
compl_pattern = xstrnsave(line, (size_t)curs_col); compl_pattern = cbuf_to_string(line, (size_t)curs_col);
compl_patternlen = (size_t)curs_col; set_cmd_context(&compl_xp, compl_pattern.data,
set_cmd_context(&compl_xp, compl_pattern, (int)compl_patternlen, curs_col, false); (int)compl_pattern.size, curs_col, false);
if (compl_xp.xp_context == EXPAND_LUA) { if (compl_xp.xp_context == EXPAND_LUA) {
nlua_expand_pat(&compl_xp); 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. // "pattern not found" message.
compl_col = curs_col; compl_col = curs_col;
} else { } 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; 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. // it may have become invalid.
char *line = ml_get(curwin->w_cursor.lnum); char *line = ml_get(curwin->w_cursor.lnum);
compl_length = curs_col - compl_col; compl_length = curs_col - compl_col;
compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
compl_patternlen = (size_t)compl_length;
return OK; 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. // Need to obtain "line" again, it may have become invalid.
char *line = ml_get(curwin->w_cursor.lnum); char *line = ml_get(curwin->w_cursor.lnum);
compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
compl_patternlen = (size_t)compl_length;
return OK; return OK;
} }
@ -4513,19 +4526,19 @@ static int ins_compl_start(void)
ins_compl_fixRedoBufForLeader(NULL); ins_compl_fixRedoBufForLeader(NULL);
// Always add completion for the original text. // Always add completion for the original text.
xfree(compl_orig_text); API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks); 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(); save_orig_extmarks();
int flags = CP_ORIGINAL_TEXT; int flags = CP_ORIGINAL_TEXT;
if (p_ic) { if (p_ic) {
flags |= CP_ICASE; 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) { flags, false, NULL) != OK) {
XFREE_CLEAR(compl_pattern); API_CLEAR_STRING(compl_pattern);
compl_patternlen = 0; API_CLEAR_STRING(compl_orig_text);
XFREE_CLEAR(compl_orig_text);
kv_destroy(compl_orig_extmarks); kv_destroy(compl_orig_extmarks);
return FAIL; return FAIL;
} }
@ -4758,7 +4771,7 @@ static unsigned quote_meta(char *dest, char *src, int len)
#if defined(EXITFREE) #if defined(EXITFREE)
void free_insexpand_stuff(void) void free_insexpand_stuff(void)
{ {
XFREE_CLEAR(compl_orig_text); API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks); kv_destroy(compl_orig_extmarks);
callback_free(&cfu_cb); callback_free(&cfu_cb);
callback_free(&ofu_cb); callback_free(&ofu_cb);

101
todo.patch Normal file
View File

@ -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) {