vim-patch:8.2.4479: no fuzzy completieon for maps and abbreviations

Problem:    No fuzzy completieon for maps and abbreviations.
Solution:   Fuzzy complete maps and abbreviations. (Yegappan Lakshmanan,
            closes vim/vim#9856)

6caeda2fce

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
zeertzjq
2023-01-17 13:23:38 +08:00
parent 245522db1e
commit 5ce6685119
4 changed files with 124 additions and 23 deletions

View File

@@ -105,7 +105,6 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
&& xp->xp_context != EXPAND_FILES_IN_PATH && xp->xp_context != EXPAND_FILES_IN_PATH
&& xp->xp_context != EXPAND_FILETYPE && xp->xp_context != EXPAND_FILETYPE
&& xp->xp_context != EXPAND_HELP && xp->xp_context != EXPAND_HELP
&& xp->xp_context != EXPAND_MAPPINGS
&& xp->xp_context != EXPAND_OLD_SETTING && xp->xp_context != EXPAND_OLD_SETTING
&& xp->xp_context != EXPAND_OWNSYNTAX && xp->xp_context != EXPAND_OWNSYNTAX
&& xp->xp_context != EXPAND_PACKADD && xp->xp_context != EXPAND_PACKADD
@@ -1377,10 +1376,12 @@ static const char *set_cmd_index(const char *cmd, exarg_T *eap, expand_T *xp, in
// Isolate the command and search for it in the command table. // Isolate the command and search for it in the command table.
// Exceptions: // Exceptions:
// - the 'k' command can directly be followed by any character, but // - the 'k' command can directly be followed by any character, but do
// do accept "keepmarks", "keepalt" and "keepjumps". // accept "keepmarks", "keepalt" and "keepjumps". As fuzzy matching can
// find matches anywhere in the command name, do this only for command
// expansion based on regular expression and not for fuzzy matching.
// - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
if (*cmd == 'k' && cmd[1] != 'e') { if (!fuzzy && (*cmd == 'k' && cmd[1] != 'e')) {
eap->cmdidx = CMD_k; eap->cmdidx = CMD_k;
p = cmd + 1; p = cmd + 1;
} else { } else {
@@ -2732,7 +2733,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
|| xp->xp_context == EXPAND_BOOL_SETTINGS) { || xp->xp_context == EXPAND_BOOL_SETTINGS) {
ret = ExpandSettings(xp, &regmatch, pat, numMatches, matches); ret = ExpandSettings(xp, &regmatch, pat, numMatches, matches);
} else if (xp->xp_context == EXPAND_MAPPINGS) { } else if (xp->xp_context == EXPAND_MAPPINGS) {
ret = ExpandMappings(&regmatch, numMatches, matches); ret = ExpandMappings(pat, &regmatch, numMatches, matches);
} else if (xp->xp_context == EXPAND_USER_DEFINED) { } else if (xp->xp_context == EXPAND_USER_DEFINED) {
ret = ExpandUserDefined(xp, &regmatch, matches, numMatches); ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
} else { } else {

View File

@@ -18,6 +18,7 @@
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/buffer_defs.h" #include "nvim/buffer_defs.h"
#include "nvim/charset.h" #include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/eval.h" #include "nvim/eval.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_defs.h"
@@ -39,6 +40,7 @@
#include "nvim/pos.h" #include "nvim/pos.h"
#include "nvim/regexp.h" #include "nvim/regexp.h"
#include "nvim/runtime.h" #include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/strings.h" #include "nvim/strings.h"
#include "nvim/vim.h" #include "nvim/vim.h"
@@ -1268,7 +1270,7 @@ char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char *arg, bool forceit,
/// Find all mapping/abbreviation names that match regexp "regmatch". /// Find all mapping/abbreviation names that match regexp "regmatch".
/// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes. /// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes.
/// @return OK if matches found, FAIL otherwise. /// @return OK if matches found, FAIL otherwise.
int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) int ExpandMappings(char *pat, regmatch_T *regmatch, int *numMatches, char ***matches)
{ {
mapblock_T *mp; mapblock_T *mp;
int hash; int hash;
@@ -1277,14 +1279,18 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
char *p; char *p;
int i; int i;
*num_file = 0; // return values in case of FAIL fuzmatch_str_T *fuzmatch = NULL;
*file = NULL; const bool fuzzy = cmdline_fuzzy_complete(pat);
*numMatches = 0; // return values in case of FAIL
*matches = NULL;
// round == 1: Count the matches. // round == 1: Count the matches.
// round == 2: Build the array to keep the matches. // round == 2: Build the array to keep the matches.
for (round = 1; round <= 2; round++) { for (round = 1; round <= 2; round++) {
count = 0; count = 0;
// First search in map modifier arguments
for (i = 0; i < 7; i++) { for (i = 0; i < 7; i++) {
if (i == 0) { if (i == 0) {
p = "<silent>"; p = "<silent>";
@@ -1304,13 +1310,29 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
continue; continue;
} }
if (vim_regexec(regmatch, p, (colnr_T)0)) { bool match;
if (round == 1) { int score = 0;
count++; if (!fuzzy) {
match = vim_regexec(regmatch, p, (colnr_T)0);
} else {
score = fuzzy_match_str(p, pat);
match = (score != 0);
}
if (!match) {
continue;
}
if (round == 2) {
if (fuzzy) {
fuzmatch[count].idx = count;
fuzmatch[count].str = xstrdup(p);
fuzmatch[count].score = score;
} else { } else {
(*file)[count++] = xstrdup(p); (*matches)[count] = xstrdup(p);
} }
} }
count++;
} }
for (hash = 0; hash < 256; hash++) { for (hash = 0; hash < 256; hash++) {
@@ -1327,12 +1349,28 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
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); p = (char *)translate_mapping((char_u *)mp->m_keys, CPO_TO_CPO_FLAGS);
if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) { if (p != NULL) {
if (round == 1) { bool match;
count++; int score = 0;
if (!fuzzy) {
match = vim_regexec(regmatch, p, (colnr_T)0);
} else { } else {
(*file)[count++] = p; score = fuzzy_match_str(p, pat);
p = NULL; match = (score != 0);
}
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);
@@ -1345,16 +1383,27 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
} }
if (round == 1) { if (round == 1) {
*file = xmalloc((size_t)count * sizeof(char *)); if (fuzzy) {
fuzmatch = xmalloc((size_t)count * sizeof(fuzmatch_str_T));
} else {
*matches = xmalloc((size_t)count * sizeof(char *));
}
} }
} // for (round) } // for (round)
if (fuzzy) {
fuzzymatches_to_strmatches(fuzmatch, matches, count, false);
}
if (count > 1) { if (count > 1) {
// Sort the matches // Sort the matches
sort_strings(*file, count); // Fuzzy matching already sorts the matches
if (!fuzzy) {
sort_strings(*matches, count);
}
// Remove multiple entries // Remove multiple entries
char **ptr1 = *file; char **ptr1 = *matches;
char **ptr2 = ptr1 + 1; char **ptr2 = ptr1 + 1;
char **ptr3 = ptr1 + count; char **ptr3 = ptr1 + count;
@@ -1368,7 +1417,7 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file)
} }
} }
*num_file = count; *numMatches = count;
return count == 0 ? FAIL : OK; return count == 0 ? FAIL : OK;
} }

View File

@@ -3472,7 +3472,7 @@ int fuzzy_match_str(char *const str, const char *const pat)
int score = 0; int score = 0;
uint32_t matchpos[MAX_FUZZY_MATCHES]; uint32_t matchpos[MAX_FUZZY_MATCHES];
fuzzy_match(str, pat, false, &score, matchpos, sizeof(matchpos) / sizeof(matchpos[0])); fuzzy_match(str, pat, true, &score, matchpos, sizeof(matchpos) / sizeof(matchpos[0]));
return score; return score;
} }

View File

@@ -2868,11 +2868,52 @@ func Test_wildoptions_fuzzy()
call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx') call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal('"mapclear <buffer>', @:) call assert_equal('"mapclear <buffer>', @:)
" map name fuzzy completion - NOT supported " map name fuzzy completion
" test regex completion works " test regex completion works
set wildoptions=fuzzy set wildoptions=fuzzy
call feedkeys(":cnoremap <ex\<Tab> <esc> \<Tab>\<C-B>\"\<CR>", 'tx') call feedkeys(":cnoremap <ex\<Tab> <esc> \<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"cnoremap <expr> <esc> \<Tab>", @:) call assert_equal("\"cnoremap <expr> <esc> \<Tab>", @:)
nmap <plug>MyLongMap :p<CR>
call feedkeys(":nmap MLM\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"nmap <Plug>MyLongMap", @:)
call feedkeys(":nmap MLM \<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"nmap MLM \t", @:)
call feedkeys(":nmap <F2> one two \<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"nmap <F2> one two \t", @:)
" duplicate entries should be removed
vmap <plug>MyLongMap :<C-U>#<CR>
call feedkeys(":nmap MLM\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"nmap <Plug>MyLongMap", @:)
nunmap <plug>MyLongMap
vunmap <plug>MyLongMap
call feedkeys(":nmap ABC\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"nmap ABC\t", @:)
" results should be sorted by best match
nmap <Plug>format :
nmap <Plug>goformat :
nmap <Plug>TestFOrmat :
nmap <Plug>fendoff :
nmap <Plug>state :
nmap <Plug>FendingOff :
call feedkeys(":nmap <Plug>fo\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"nmap <Plug>format <Plug>TestFOrmat <Plug>FendingOff <Plug>goformat <Plug>fendoff", @:)
nunmap <Plug>format
nunmap <Plug>goformat
nunmap <Plug>TestFOrmat
nunmap <Plug>fendoff
nunmap <Plug>state
nunmap <Plug>FendingOff
" abbreviation fuzzy completion
set wildoptions=fuzzy
call feedkeys(":iabbr wait\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"iabbr <nowait>", @:)
iabbr WaitForCompletion WFC
call feedkeys(":iabbr fcl\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"iabbr WaitForCompletion", @:)
call feedkeys(":iabbr a1z\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"iabbr a1z\t", @:)
iunabbrev WaitForCompletion
" menu name fuzzy completion " menu name fuzzy completion
if has('gui_running') if has('gui_running')
@@ -3005,6 +3046,16 @@ func Test_wildoptions_fuzzy()
call assert_equal('"Foo2Bar', @:) call assert_equal('"Foo2Bar', @:)
delcommand Foo2Bar delcommand Foo2Bar
" Test for command completion for a command starting with 'k'
command KillKillKill :
set wildoptions&
call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal("\"killkill\<Tab>", @:)
set wildoptions=fuzzy
call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal('"KillKillKill', @:)
delcom KillKillKill
set wildoptions& set wildoptions&
%bw! %bw!
endfunc endfunc