diff options
-rw-r--r-- | src/nvim/cmdexpand.c | 11 | ||||
-rw-r--r-- | src/nvim/mapping.c | 81 | ||||
-rw-r--r-- | src/nvim/search.c | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test_cmdline.vim | 53 |
4 files changed, 124 insertions, 23 deletions
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index f6b5127268..899a2a1f62 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -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_FILETYPE && xp->xp_context != EXPAND_HELP - && xp->xp_context != EXPAND_MAPPINGS && xp->xp_context != EXPAND_OLD_SETTING && xp->xp_context != EXPAND_OWNSYNTAX && 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. // Exceptions: - // - the 'k' command can directly be followed by any character, but - // do accept "keepmarks", "keepalt" and "keepjumps". + // - the 'k' command can directly be followed by any character, but do + // 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' - if (*cmd == 'k' && cmd[1] != 'e') { + if (!fuzzy && (*cmd == 'k' && cmd[1] != 'e')) { eap->cmdidx = CMD_k; p = cmd + 1; } else { @@ -2732,7 +2733,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM || xp->xp_context == EXPAND_BOOL_SETTINGS) { ret = ExpandSettings(xp, ®match, pat, numMatches, matches); } else if (xp->xp_context == EXPAND_MAPPINGS) { - ret = ExpandMappings(®match, numMatches, matches); + ret = ExpandMappings(pat, ®match, numMatches, matches); } else if (xp->xp_context == EXPAND_USER_DEFINED) { ret = ExpandUserDefined(xp, ®match, matches, numMatches); } else { diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 2e1563760b..1785f41a37 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -18,6 +18,7 @@ #include "nvim/ascii.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" @@ -39,6 +40,7 @@ #include "nvim/pos.h" #include "nvim/regexp.h" #include "nvim/runtime.h" +#include "nvim/search.h" #include "nvim/strings.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". /// For command line expansion of ":[un]map" and ":[un]abbrev" in all modes. /// @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; int hash; @@ -1277,14 +1279,18 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) char *p; int i; - *num_file = 0; // return values in case of FAIL - *file = NULL; + fuzmatch_str_T *fuzmatch = NULL; + const bool fuzzy = cmdline_fuzzy_complete(pat); + + *numMatches = 0; // return values in case of FAIL + *matches = NULL; // round == 1: Count the matches. // round == 2: Build the array to keep the matches. for (round = 1; round <= 2; round++) { count = 0; + // First search in map modifier arguments for (i = 0; i < 7; i++) { if (i == 0) { p = "<silent>"; @@ -1304,13 +1310,29 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) continue; } - if (vim_regexec(regmatch, p, (colnr_T)0)) { - if (round == 1) { - count++; + bool match; + int score = 0; + 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 { - (*file)[count++] = xstrdup(p); + (*matches)[count] = xstrdup(p); } } + count++; } 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) { if (mp->m_mode & expand_mapmodes) { p = (char *)translate_mapping((char_u *)mp->m_keys, CPO_TO_CPO_FLAGS); - if (p != NULL && vim_regexec(regmatch, p, (colnr_T)0)) { - if (round == 1) { - count++; + if (p != NULL) { + bool match; + int score = 0; + if (!fuzzy) { + match = vim_regexec(regmatch, p, (colnr_T)0); } else { - (*file)[count++] = p; - p = NULL; + score = fuzzy_match_str(p, pat); + 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); @@ -1345,16 +1383,27 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char ***file) } 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) + if (fuzzy) { + fuzzymatches_to_strmatches(fuzmatch, matches, count, false); + } + if (count > 1) { // Sort the matches - sort_strings(*file, count); + // Fuzzy matching already sorts the matches + if (!fuzzy) { + sort_strings(*matches, count); + } // Remove multiple entries - char **ptr1 = *file; + char **ptr1 = *matches; char **ptr2 = ptr1 + 1; 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; } diff --git a/src/nvim/search.c b/src/nvim/search.c index ee99efed5d..205eec8b35 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3472,7 +3472,7 @@ int fuzzy_match_str(char *const str, const char *const pat) int score = 0; 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; } diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 4c59a8ab7c..3895fe27b6 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -2868,11 +2868,52 @@ func Test_wildoptions_fuzzy() call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx') call assert_equal('"mapclear <buffer>', @:) - " map name fuzzy completion - NOT supported + " map name fuzzy completion " test regex completion works set wildoptions=fuzzy call feedkeys(":cnoremap <ex\<Tab> <esc> \<Tab>\<C-B>\"\<CR>", 'tx') 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 if has('gui_running') @@ -3005,6 +3046,16 @@ func Test_wildoptions_fuzzy() call assert_equal('"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& %bw! endfunc |