mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:8.2.4483: command completion makes two rounds to collect matches (#21857)
Problem: Command completion makes two rounds to collect matches.
Solution: Use a growarray to collect matches. (Yegappan Lakshmanan,
closes vim/vim#9860)
5de4c4372d
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
parent
ddd69a6c81
commit
132f001ce8
@ -2415,10 +2415,15 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p != NULL) {
|
if (p == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (round == 1) {
|
if (round == 1) {
|
||||||
count++;
|
count++;
|
||||||
} else {
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (options & WILD_HOME_REPLACE) {
|
if (options & WILD_HOME_REPLACE) {
|
||||||
p = home_replace_save(buf, p);
|
p = home_replace_save(buf, p);
|
||||||
} else {
|
} else {
|
||||||
@ -2440,8 +2445,6 @@ int ExpandBufnames(char *pat, int *num_file, char ***file, int options)
|
|||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count == 0) { // no match found, break here
|
if (count == 0) { // no match found, break here
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2757,48 +2757,19 @@ static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regma
|
|||||||
char ***matches, int *numMatches, CompleteListItemGetter func,
|
char ***matches, int *numMatches, CompleteListItemGetter func,
|
||||||
int escaped)
|
int escaped)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
size_t count = 0;
|
|
||||||
char *str;
|
|
||||||
|
|
||||||
const bool fuzzy = cmdline_fuzzy_complete(pat);
|
const bool fuzzy = cmdline_fuzzy_complete(pat);
|
||||||
|
*matches = NULL;
|
||||||
|
*numMatches = 0;
|
||||||
|
|
||||||
// count the number of matching names
|
garray_T ga;
|
||||||
for (i = 0;; i++) {
|
|
||||||
str = (*func)(xp, i);
|
|
||||||
if (str == NULL) { // end of list
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (*str == NUL) { // skip empty strings
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool match;
|
|
||||||
if (!fuzzy) {
|
if (!fuzzy) {
|
||||||
match = vim_regexec(regmatch, str, (colnr_T)0);
|
ga_init(&ga, sizeof(char *), 30);
|
||||||
} else {
|
} else {
|
||||||
match = fuzzy_match_str(str, pat) != 0;
|
ga_init(&ga, sizeof(fuzmatch_str_T), 30);
|
||||||
}
|
|
||||||
if (match) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
assert(count < INT_MAX);
|
|
||||||
*numMatches = (int)count;
|
|
||||||
fuzmatch_str_T *fuzmatch = NULL;
|
|
||||||
if (fuzzy) {
|
|
||||||
fuzmatch = xmalloc(count * sizeof(fuzmatch_str_T));
|
|
||||||
} else {
|
|
||||||
*matches = xmalloc(count * sizeof(char *));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the matching names into allocated memory
|
for (int i = 0;; i++) {
|
||||||
count = 0;
|
char *str = (*func)(xp, i);
|
||||||
for (i = 0;; i++) {
|
|
||||||
str = (*func)(xp, i);
|
|
||||||
if (str == NULL) { // End of list.
|
if (str == NULL) { // End of list.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2808,12 +2779,16 @@ static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regma
|
|||||||
|
|
||||||
bool match;
|
bool match;
|
||||||
int score = 0;
|
int score = 0;
|
||||||
|
if (xp->xp_pattern[0] != NUL) {
|
||||||
if (!fuzzy) {
|
if (!fuzzy) {
|
||||||
match = vim_regexec(regmatch, str, (colnr_T)0);
|
match = vim_regexec(regmatch, str, (colnr_T)0);
|
||||||
} else {
|
} else {
|
||||||
score = fuzzy_match_str(str, pat);
|
score = fuzzy_match_str(str, pat);
|
||||||
match = (score != 0);
|
match = (score != 0);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
continue;
|
continue;
|
||||||
@ -2824,14 +2799,17 @@ static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regma
|
|||||||
} else {
|
} else {
|
||||||
str = xstrdup(str);
|
str = xstrdup(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fuzzy) {
|
if (fuzzy) {
|
||||||
fuzmatch[count].idx = (int)count;
|
GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
|
||||||
fuzmatch[count].str = str;
|
.idx = ga.ga_len,
|
||||||
fuzmatch[count].score = score;
|
.str = str,
|
||||||
|
.score = score,
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
(*matches)[count] = str;
|
GA_APPEND(char *, &ga, str);
|
||||||
}
|
}
|
||||||
count++;
|
|
||||||
if (func == get_menu_names) {
|
if (func == get_menu_names) {
|
||||||
// Test for separator added by get_menu_names().
|
// Test for separator added by get_menu_names().
|
||||||
str += strlen(str) - 1;
|
str += strlen(str) - 1;
|
||||||
@ -2841,31 +2819,42 @@ static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ga.ga_len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Sort the results. Keep menu's in the specified order.
|
// Sort the results. Keep menu's in the specified order.
|
||||||
|
if (!fuzzy && xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) {
|
||||||
|
if (xp->xp_context == EXPAND_EXPRESSION
|
||||||
|
|| xp->xp_context == EXPAND_FUNCTIONS
|
||||||
|
|| xp->xp_context == EXPAND_USER_FUNC) {
|
||||||
|
// <SNR> functions should be sorted to the end.
|
||||||
|
qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(char *), sort_func_compare);
|
||||||
|
} else {
|
||||||
|
sort_strings(ga.ga_data, ga.ga_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fuzzy) {
|
||||||
|
*matches = ga.ga_data;
|
||||||
|
*numMatches = ga.ga_len;
|
||||||
|
} else {
|
||||||
bool funcsort = false;
|
bool funcsort = false;
|
||||||
if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) {
|
|
||||||
if (xp->xp_context == EXPAND_EXPRESSION
|
if (xp->xp_context == EXPAND_EXPRESSION
|
||||||
|| xp->xp_context == EXPAND_FUNCTIONS
|
|| xp->xp_context == EXPAND_FUNCTIONS
|
||||||
|| xp->xp_context == EXPAND_USER_FUNC) {
|
|| xp->xp_context == EXPAND_USER_FUNC) {
|
||||||
// <SNR> functions should be sorted to the end.
|
// <SNR> functions should be sorted to the end.
|
||||||
funcsort = true;
|
funcsort = true;
|
||||||
if (!fuzzy) {
|
|
||||||
qsort(*matches, (size_t)(*numMatches), sizeof(char *), sort_func_compare);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!fuzzy) {
|
|
||||||
sort_strings(*matches, *numMatches);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, funcsort);
|
||||||
|
*numMatches = ga.ga_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the variables used for special highlight names expansion, so that
|
// Reset the variables used for special highlight names expansion, so that
|
||||||
// they don't show up when getting normal highlight names by ID.
|
// they don't show up when getting normal highlight names by ID.
|
||||||
reset_expand_highlight();
|
reset_expand_highlight();
|
||||||
|
|
||||||
if (fuzzy) {
|
|
||||||
fuzzymatches_to_strmatches(fuzmatch, matches, (int)count, funcsort);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expand shell command matches in one directory of $PATH.
|
/// Expand shell command matches in one directory of $PATH.
|
||||||
@ -3047,27 +3036,23 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T
|
|||||||
static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *regmatch,
|
static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *regmatch,
|
||||||
char ***matches, int *numMatches)
|
char ***matches, int *numMatches)
|
||||||
{
|
{
|
||||||
char *e;
|
|
||||||
garray_T ga;
|
|
||||||
|
|
||||||
const bool fuzzy = cmdline_fuzzy_complete(pat);
|
const bool fuzzy = cmdline_fuzzy_complete(pat);
|
||||||
|
|
||||||
*matches = NULL;
|
*matches = NULL;
|
||||||
*numMatches = 0;
|
*numMatches = 0;
|
||||||
char *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp);
|
|
||||||
|
|
||||||
|
char *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp);
|
||||||
if (retstr == NULL) {
|
if (retstr == NULL) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
garray_T ga;
|
||||||
if (!fuzzy) {
|
if (!fuzzy) {
|
||||||
ga_init(&ga, (int)sizeof(char *), 3);
|
ga_init(&ga, (int)sizeof(char *), 3);
|
||||||
} else {
|
} else {
|
||||||
ga_init(&ga, (int)sizeof(fuzmatch_str_T), 3);
|
ga_init(&ga, (int)sizeof(fuzmatch_str_T), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = 0;
|
for (char *s = retstr, *e; *s != NUL; s = e) {
|
||||||
for (char *s = retstr; *s != NUL; s = e) {
|
|
||||||
e = vim_strchr(s, '\n');
|
e = vim_strchr(s, '\n');
|
||||||
if (e == NULL) {
|
if (e == NULL) {
|
||||||
e = s + strlen(s);
|
e = s + strlen(s);
|
||||||
@ -3077,7 +3062,7 @@ static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *re
|
|||||||
|
|
||||||
bool match;
|
bool match;
|
||||||
int score = 0;
|
int score = 0;
|
||||||
if (xp->xp_pattern[0] || fuzzy) {
|
if (xp->xp_pattern[0] != NUL) {
|
||||||
if (!fuzzy) {
|
if (!fuzzy) {
|
||||||
match = vim_regexec(regmatch, s, (colnr_T)0);
|
match = vim_regexec(regmatch, s, (colnr_T)0);
|
||||||
} else {
|
} else {
|
||||||
@ -3095,12 +3080,11 @@ static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *re
|
|||||||
GA_APPEND(char *, &ga, xstrnsave(s, (size_t)(e - s)));
|
GA_APPEND(char *, &ga, xstrnsave(s, (size_t)(e - s)));
|
||||||
} else {
|
} else {
|
||||||
GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
|
GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
|
||||||
.idx = count,
|
.idx = ga.ga_len,
|
||||||
.str = xstrnsave(s, (size_t)(e - s)),
|
.str = xstrnsave(s, (size_t)(e - s)),
|
||||||
.score = score,
|
.score = score,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*e != NUL) {
|
if (*e != NUL) {
|
||||||
@ -3109,12 +3093,16 @@ static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *re
|
|||||||
}
|
}
|
||||||
xfree(retstr);
|
xfree(retstr);
|
||||||
|
|
||||||
|
if (ga.ga_len == 0) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (!fuzzy) {
|
if (!fuzzy) {
|
||||||
*matches = ga.ga_data;
|
*matches = ga.ga_data;
|
||||||
*numMatches = ga.ga_len;
|
*numMatches = ga.ga_len;
|
||||||
} else {
|
} else {
|
||||||
fuzzymatches_to_strmatches(ga.ga_data, matches, count, false);
|
fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, false);
|
||||||
*numMatches = count;
|
*numMatches = ga.ga_len;
|
||||||
}
|
}
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,8 @@ typedef struct growarray {
|
|||||||
#define GA_APPEND(item_type, gap, item) \
|
#define GA_APPEND(item_type, gap, item) \
|
||||||
do { \
|
do { \
|
||||||
ga_grow(gap, 1); \
|
ga_grow(gap, 1); \
|
||||||
((item_type *)(gap)->ga_data)[(gap)->ga_len++] = (item); \
|
((item_type *)(gap)->ga_data)[(gap)->ga_len] = (item); \
|
||||||
|
(gap)->ga_len++; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define GA_APPEND_VIA_PTR(item_type, gap) \
|
#define GA_APPEND_VIA_PTR(item_type, gap) \
|
||||||
|
@ -1272,26 +1272,21 @@ char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char *arg, bool forceit,
|
|||||||
/// @return OK if matches found, FAIL otherwise.
|
/// @return OK if matches found, FAIL otherwise.
|
||||||
int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***matches)
|
int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***matches)
|
||||||
{
|
{
|
||||||
mapblock_T *mp;
|
|
||||||
int hash;
|
|
||||||
int count;
|
|
||||||
int round;
|
|
||||||
char *p;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
fuzmatch_str_T *fuzmatch = NULL;
|
|
||||||
const bool fuzzy = cmdline_fuzzy_complete(pat);
|
const bool fuzzy = cmdline_fuzzy_complete(pat);
|
||||||
|
|
||||||
*numMatches = 0; // return values in case of FAIL
|
*numMatches = 0; // return values in case of FAIL
|
||||||
*matches = NULL;
|
*matches = NULL;
|
||||||
|
|
||||||
// round == 1: Count the matches.
|
garray_T ga;
|
||||||
// round == 2: Build the array to keep the matches.
|
if (!fuzzy) {
|
||||||
for (round = 1; round <= 2; round++) {
|
ga_init(&ga, sizeof(char *), 3);
|
||||||
count = 0;
|
} else {
|
||||||
|
ga_init(&ga, sizeof(fuzmatch_str_T), 3);
|
||||||
|
}
|
||||||
|
|
||||||
// First search in map modifier arguments
|
// First search in map modifier arguments
|
||||||
for (i = 0; i < 7; i++) {
|
for (int i = 0; i < 7; i++) {
|
||||||
|
char *p;
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
p = "<silent>";
|
p = "<silent>";
|
||||||
} else if (i == 1) {
|
} else if (i == 1) {
|
||||||
@ -1323,19 +1318,19 @@ int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***mat
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (round == 2) {
|
|
||||||
if (fuzzy) {
|
if (fuzzy) {
|
||||||
fuzmatch[count].idx = count;
|
GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
|
||||||
fuzmatch[count].str = xstrdup(p);
|
.idx = ga.ga_len,
|
||||||
fuzmatch[count].score = score;
|
.str = xstrdup(p),
|
||||||
|
.score = score,
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
(*matches)[count] = xstrdup(p);
|
GA_APPEND(char *, &ga, xstrdup(p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (hash = 0; hash < 256; hash++) {
|
for (int hash = 0; hash < 256; hash++) {
|
||||||
|
mapblock_T *mp;
|
||||||
if (expand_isabbrev) {
|
if (expand_isabbrev) {
|
||||||
if (hash > 0) { // only one abbrev list
|
if (hash > 0) { // only one abbrev list
|
||||||
break; // for (hash)
|
break; // for (hash)
|
||||||
@ -1347,9 +1342,15 @@ int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***mat
|
|||||||
mp = maphash[hash];
|
mp = maphash[hash];
|
||||||
}
|
}
|
||||||
for (; mp; mp = mp->m_next) {
|
for (; mp; mp = mp->m_next) {
|
||||||
if (mp->m_mode & expand_mapmodes) {
|
if (!(mp->m_mode & expand_mapmodes)) {
|
||||||
p = (char *)translate_mapping((char_u *)mp->m_keys, CPO_TO_CPO_FLAGS);
|
continue;
|
||||||
if (p != NULL) {
|
}
|
||||||
|
|
||||||
|
char *p = (char *)translate_mapping((char_u *)mp->m_keys, CPO_TO_CPO_FLAGS);
|
||||||
|
if (p == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool match;
|
bool match;
|
||||||
int score = 0;
|
int score = 0;
|
||||||
if (!fuzzy) {
|
if (!fuzzy) {
|
||||||
@ -1359,42 +1360,36 @@ int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***mat
|
|||||||
match = (score != 0);
|
match = (score != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match) {
|
if (!match) {
|
||||||
if (round == 2) {
|
|
||||||
if (fuzzy) {
|
|
||||||
fuzmatch[count].idx = count;
|
|
||||||
fuzmatch[count].str = p;
|
|
||||||
fuzmatch[count].score = score;
|
|
||||||
} else {
|
|
||||||
(*matches)[count] = p;
|
|
||||||
}
|
|
||||||
p = NULL;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xfree(p);
|
xfree(p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fuzzy) {
|
||||||
|
GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){
|
||||||
|
.idx = ga.ga_len,
|
||||||
|
.str = p,
|
||||||
|
.score = score,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
GA_APPEND(char *, &ga, p);
|
||||||
}
|
}
|
||||||
} // for (mp)
|
} // for (mp)
|
||||||
} // for (hash)
|
} // for (hash)
|
||||||
|
|
||||||
if (count == 0) { // no match found
|
if (ga.ga_len == 0) {
|
||||||
break; // for (round)
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (round == 1) {
|
if (!fuzzy) {
|
||||||
if (fuzzy) {
|
*matches = ga.ga_data;
|
||||||
fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T));
|
*numMatches = ga.ga_len;
|
||||||
} else {
|
} else {
|
||||||
*matches = xmalloc((size_t)count * sizeof(char *));
|
fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len, false);
|
||||||
}
|
*numMatches = ga.ga_len;
|
||||||
}
|
|
||||||
} // for (round)
|
|
||||||
|
|
||||||
if (fuzzy) {
|
|
||||||
fuzzymatches_to_strmatches(fuzmatch, matches, count, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int count = *numMatches;
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
// Sort the matches
|
// Sort the matches
|
||||||
// Fuzzy matching already sorts the matches
|
// Fuzzy matching already sorts the matches
|
||||||
|
@ -2876,6 +2876,24 @@ func Test_fuzzy_completion_userdefined_func()
|
|||||||
set wildoptions&
|
set wildoptions&
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" <SNR> functions should be sorted to the end
|
||||||
|
func Test_fuzzy_completion_userdefined_snr_func()
|
||||||
|
func s:Sendmail()
|
||||||
|
endfunc
|
||||||
|
func SendSomemail()
|
||||||
|
endfunc
|
||||||
|
func S1e2n3dmail()
|
||||||
|
endfunc
|
||||||
|
set wildoptions=fuzzy
|
||||||
|
call feedkeys(":call sendmail\<C-A>\<C-B>\"\<CR>", 'tx')
|
||||||
|
call assert_equal('"call SendSomemail() S1e2n3dmail() '
|
||||||
|
\ .. expand("<SID>") .. 'Sendmail()', @:)
|
||||||
|
set wildoptions&
|
||||||
|
delfunc s:Sendmail
|
||||||
|
delfunc SendSomemail
|
||||||
|
delfunc S1e2n3dmail
|
||||||
|
endfunc
|
||||||
|
|
||||||
" user defined command name completion
|
" user defined command name completion
|
||||||
func Test_fuzzy_completion_userdefined_cmd()
|
func Test_fuzzy_completion_userdefined_cmd()
|
||||||
set wildoptions&
|
set wildoptions&
|
||||||
|
Loading…
Reference in New Issue
Block a user