changeset 27914:9a997de62da2 v8.2.4482

patch 8.2.4482: no fuzzy cmdline completion for user defined completion Commit: https://github.com/vim/vim/commit/afd4ae35d66b2e7732eceb5ad9f6b4ece6b7c64c Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sun Feb 27 21:03:21 2022 +0000 patch 8.2.4482: no fuzzy cmdline completion for user defined completion Problem: No fuzzy cmdline completion for user defined completion. Solution: Add fuzzy completion for user defined completion. (Yegappan Lakshmanan, closes #9858)
author Bram Moolenaar <Bram@vim.org>
date Sun, 27 Feb 2022 22:15:04 +0100
parents 38695d6adf31
children 4058d89b7a48
files src/cmdexpand.c src/testdir/test_cmdline.vim src/version.c
diffstat 3 files changed, 343 insertions(+), 171 deletions(-) [+]
line wrap: on
line diff
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -16,15 +16,14 @@
 static int	cmd_showtail;	// Only show path tail in lists ?
 
 static void	set_expand_context(expand_T *xp);
-static int      ExpandGeneric(expand_T *xp, regmatch_T *regmatch,
+static int      ExpandGeneric(char_u *pat, expand_T *xp, regmatch_T *regmatch,
 			      char_u ***matches, int *numMatches,
-			      char_u *((*func)(expand_T *, int)), int escaped,
-			      char_u *fuzzystr);
+			      char_u *((*func)(expand_T *, int)), int escaped);
 static int	ExpandFromContext(expand_T *xp, char_u *, char_u ***, int *, int);
 static int	expand_showtail(expand_T *xp);
 static int	expand_shellcmd(char_u *filepat, char_u ***matches, int *numMatches, int flagsarg);
 #if defined(FEAT_EVAL)
-static int	ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches);
+static int	ExpandUserDefined(char_u *pat, expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches);
 static int	ExpandUserList(expand_T *xp, char_u ***matches, int *numMatches);
 #endif
 
@@ -62,13 +61,13 @@ cmdline_fuzzy_completion_supported(expan
 	    && xp->xp_context != EXPAND_SHELLCMD
 	    && xp->xp_context != EXPAND_TAGS
 	    && xp->xp_context != EXPAND_TAGS_LISTFILES
-	    && xp->xp_context != EXPAND_USER_DEFINED
 	    && xp->xp_context != EXPAND_USER_LIST);
 }
 
 /*
  * Returns TRUE if fuzzy completion for cmdline completion is enabled and
- * 'fuzzystr' is not empty.
+ * 'fuzzystr' is not empty.  If search pattern is empty, then don't use fuzzy
+ * matching.
  */
     int
 cmdline_fuzzy_complete(char_u *fuzzystr)
@@ -2444,16 +2443,10 @@ ExpandOther(
     {
 	if (xp->xp_context == tab[i].context)
 	{
-	    // Use fuzzy matching if 'wildoptions' has 'fuzzy'.
-	    // If no search pattern is supplied, then don't use fuzzy
-	    // matching and return all the found items.
-	    int fuzzy = cmdline_fuzzy_complete(pat);
-
 	    if (tab[i].ic)
 		rmp->rm_ic = TRUE;
-	    ret = ExpandGeneric(xp, rmp, matches, numMatches,
-						tab[i].func, tab[i].escaped,
-						fuzzy ? pat : NULL);
+	    ret = ExpandGeneric(pat, xp, rmp, matches, numMatches,
+						tab[i].func, tab[i].escaped);
 	    break;
 	}
     }
@@ -2604,7 +2597,7 @@ ExpandFromContext(
 	ret = ExpandMappings(pat, &regmatch, numMatches, matches);
 # if defined(FEAT_EVAL)
     else if (xp->xp_context == EXPAND_USER_DEFINED)
-	ret = ExpandUserDefined(xp, &regmatch, matches, numMatches);
+	ret = ExpandUserDefined(pat, xp, &regmatch, matches, numMatches);
 # endif
     else
 	ret = ExpandOther(pat, xp, &regmatch, matches, numMatches);
@@ -2630,14 +2623,14 @@ ExpandFromContext(
  */
     static int
 ExpandGeneric(
+    char_u	*pat,
     expand_T	*xp,
     regmatch_T	*regmatch,
     char_u	***matches,
     int		*numMatches,
     char_u	*((*func)(expand_T *, int)),
 					  // returns a string from the list
-    int		escaped,
-    char_u	*fuzzystr)
+    int		escaped)
 {
     int		i;
     int		count = 0;
@@ -2645,10 +2638,12 @@ ExpandGeneric(
     char_u	*str;
     fuzmatch_str_T	*fuzmatch = NULL;
     int			score = 0;
-    int		fuzzy = (fuzzystr != NULL);
+    int		fuzzy;
     int		funcsort = FALSE;
     int		match;
 
+    fuzzy = cmdline_fuzzy_complete(pat);
+
     // do this loop twice:
     // round == 0: count the number of matching names
     // round == 1: copy the matching names into allocated memory
@@ -2666,7 +2661,7 @@ ExpandGeneric(
 	       match = vim_regexec(regmatch, str, (colnr_T)0);
 	    else
 	    {
-		score = fuzzy_match_str(str, fuzzystr);
+		score = fuzzy_match_str(str, pat);
 		match = (score != 0);
 	    }
 
@@ -2976,10 +2971,12 @@ call_user_expand_func(
 }
 
 /*
- * Expand names with a function defined by the user.
+ * Expand names with a function defined by the user (EXPAND_USER_DEFINED and
+ * EXPAND_USER_LIST).
  */
     static int
 ExpandUserDefined(
+    char_u	*pat,
     expand_T	*xp,
     regmatch_T	*regmatch,
     char_u	***matches,
@@ -2990,7 +2987,12 @@ ExpandUserDefined(
     char_u	*e;
     int		keep;
     garray_T	ga;
-    int		skip;
+    int		fuzzy;
+    int		match;
+    int		score;
+    int		count = 0;
+
+    fuzzy = cmdline_fuzzy_complete(pat);
 
     *matches = NULL;
     *numMatches = 0;
@@ -2998,7 +3000,11 @@ ExpandUserDefined(
     if (retstr == NULL)
 	return FAIL;
 
-    ga_init2(&ga, sizeof(char *), 3);
+    if (!fuzzy)
+	ga_init2(&ga, sizeof(char *), 3);
+    else
+	ga_init2(&ga, sizeof(fuzmatch_str_T), 3);
+
     for (s = retstr; *s != NUL; s = e)
     {
 	e = vim_strchr(s, '\n');
@@ -3007,23 +3013,56 @@ ExpandUserDefined(
 	keep = *e;
 	*e = NUL;
 
-	skip = xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0;
+	if (xp->xp_pattern[0] || fuzzy)
+	{
+	    if (!fuzzy)
+		match = vim_regexec(regmatch, s, (colnr_T)0);
+	    else
+	    {
+		score = fuzzy_match_str(s, pat);
+		match = (score != 0);
+	    }
+	}
+	else
+	    match = TRUE;		// match everything
+
 	*e = keep;
 
-	if (!skip)
+	if (match)
 	{
 	    if (ga_grow(&ga, 1) == FAIL)
 		break;
-	    ((char_u **)ga.ga_data)[ga.ga_len] = vim_strnsave(s, e - s);
+	    if (!fuzzy)
+		((char_u **)ga.ga_data)[ga.ga_len] = vim_strnsave(s, e - s);
+	    else
+	    {
+		fuzmatch_str_T  *fuzmatch =
+				&((fuzmatch_str_T  *)ga.ga_data)[ga.ga_len];
+		fuzmatch->idx = count;
+		fuzmatch->str = vim_strnsave(s, e - s);
+		fuzmatch->score = score;
+	    }
 	    ++ga.ga_len;
+	    count++;
 	}
 
 	if (*e != NUL)
 	    ++e;
     }
     vim_free(retstr);
-    *matches = ga.ga_data;
-    *numMatches = ga.ga_len;
+
+    if (!fuzzy)
+    {
+	*matches = ga.ga_data;
+	*numMatches = ga.ga_len;
+    }
+    else
+    {
+	if (fuzzymatches_to_strmatches(ga.ga_data, matches, count,
+							FALSE) == FAIL)
+	    return FAIL;
+	*numMatches = count;
+    }
     return OK;
 }
 
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -2455,9 +2455,8 @@ func Test_cmdline_complete_dlist()
   call assert_equal("\"dlist 10 /pat/ | chistory", @:)
 endfunc
 
-" Test for 'fuzzy' in 'wildoptions' (fuzzy completion)
-func Test_wildoptions_fuzzy()
-  " argument list (only for :argdel)
+" argument list (only for :argdel) fuzzy completion
+func Test_fuzzy_completion_arglist()
   argadd change.py count.py charge.py
   set wildoptions&
   call feedkeys(":argdel cge\<C-A>\<C-B>\"\<CR>", 'tx')
@@ -2466,8 +2465,11 @@ func Test_wildoptions_fuzzy()
   call feedkeys(":argdel cge\<C-A>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"argdel change.py charge.py', @:)
   %argdelete
-
-  " autocmd group name fuzzy completion
+  set wildoptions&
+endfunc
+
+" autocmd group name fuzzy completion
+func Test_fuzzy_completion_autocmd()
   set wildoptions&
   augroup MyFuzzyGroup
   augroup END
@@ -2481,8 +2483,11 @@ func Test_wildoptions_fuzzy()
   call feedkeys(":augroup My*p\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"augroup My*p', @:)
   augroup! MyFuzzyGroup
-
-  " buffer name fuzzy completion
+  set wildoptions&
+endfunc
+
+" buffer name fuzzy completion
+func Test_fuzzy_completion_bufname()
   set wildoptions&
   edit SomeFile.txt
   enew
@@ -2496,24 +2501,29 @@ func Test_wildoptions_fuzzy()
   call feedkeys(":b S*File.txt\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"b S*File.txt', @:)
   %bw!
-
-  " buffer name (full path) fuzzy completion
-  if has('unix')
-    set wildoptions&
-    call mkdir('Xcmd/Xstate/Xfile.js', 'p')
-    edit Xcmd/Xstate/Xfile.js
-    cd Xcmd/Xstate
-    enew
-    call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"b CmdStateFile', @:)
-    set wildoptions=fuzzy
-    call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_match('Xcmd/Xstate/Xfile.js$', @:)
-    cd -
-    call delete('Xcmd', 'rf')
-  endif
-
-  " :behave suboptions fuzzy completion
+  set wildoptions&
+endfunc
+
+" buffer name (full path) fuzzy completion
+func Test_fuzzy_completion_bufname_fullpath()
+  CheckUnix
+  set wildoptions&
+  call mkdir('Xcmd/Xstate/Xfile.js', 'p')
+  edit Xcmd/Xstate/Xfile.js
+  cd Xcmd/Xstate
+  enew
+  call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"b CmdStateFile', @:)
+  set wildoptions=fuzzy
+  call feedkeys(":b CmdStateFile\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_match('Xcmd/Xstate/Xfile.js$', @:)
+  cd -
+  call delete('Xcmd', 'rf')
+  set wildoptions&
+endfunc
+
+" :behave suboptions fuzzy completion
+func Test_fuzzy_completion_behave()
   set wildoptions&
   call feedkeys(":behave xm\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"behave xm', @:)
@@ -2528,10 +2538,15 @@ func Test_wildoptions_fuzzy()
   call feedkeys(":behave win\<C-D>\<F4>\<C-B>\"\<CR>", 'tx')
   call assert_equal('mswin', g:Sline)
   call assert_equal('"behave win', @:)
-
-  " colorscheme name fuzzy completion - NOT supported
-
-  " built-in command name fuzzy completion
+  set wildoptions&
+endfunc
+
+" " colorscheme name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_colorscheme()
+" endfunc
+
+" built-in command name fuzzy completion
+func Test_fuzzy_completion_cmdname()
   set wildoptions&
   call feedkeys(":sbwin\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sbwin', @:)
@@ -2542,24 +2557,31 @@ func Test_wildoptions_fuzzy()
   call assert_equal('"sbrewind', @:)
   call feedkeys(":sbr*d\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sbr*d', @:)
-
-  " compiler name fuzzy completion - NOT supported
-
-  " :cscope suboptions fuzzy completion
-  if has('cscope')
-    set wildoptions&
-    call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"cscope ret', @:)
-    call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"cscope reset', @:)
-    set wildoptions=fuzzy
-    call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"cscope reset', @:)
-    call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"cscope re*t', @:)
-  endif
-
-  " :diffget/:diffput buffer name fuzzy completion
+  set wildoptions&
+endfunc
+
+" " compiler name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_compiler()
+" endfunc
+
+" :cscope suboptions fuzzy completion
+func Test_fuzzy_completion_cscope()
+  CheckFeature cscope
+  set wildoptions&
+  call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"cscope ret', @:)
+  call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"cscope reset', @:)
+  set wildoptions=fuzzy
+  call feedkeys(":cscope ret\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"cscope reset', @:)
+  call feedkeys(":cscope re*t\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"cscope re*t', @:)
+  set wildoptions&
+endfunc
+
+" :diffget/:diffput buffer name fuzzy completion
+func Test_fuzzy_completion_diff()
   new SomeBuffer
   diffthis
   new OtherBuffer
@@ -2575,26 +2597,37 @@ func Test_wildoptions_fuzzy()
   call feedkeys(":diffput sbuf\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"diffput SomeBuffer', @:)
   %bw!
-
-  " directory name fuzzy completion - NOT supported
-
-  " environment variable name fuzzy completion
+  set wildoptions&
+endfunc
+
+" " directory name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_dirname()
+" endfunc
+
+" environment variable name fuzzy completion
+func Test_fuzzy_completion_env()
   set wildoptions&
   call feedkeys(":echo $VUT\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"echo $VUT', @:)
   set wildoptions=fuzzy
   call feedkeys(":echo $VUT\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"echo $VIMRUNTIME', @:)
-
-  " autocmd event fuzzy completion
+  set wildoptions&
+endfunc
+
+" autocmd event fuzzy completion
+func Test_fuzzy_completion_autocmd_event()
   set wildoptions&
   call feedkeys(":autocmd BWout\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"autocmd BWout', @:)
   set wildoptions=fuzzy
   call feedkeys(":autocmd BWout\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"autocmd BufWipeout', @:)
-
-  " vim expression fuzzy completion
+  set wildoptions&
+endfunc
+
+" vim expression fuzzy completion
+func Test_fuzzy_completion_expr()
   let g:PerPlaceCount = 10
   set wildoptions&
   call feedkeys(":let c = ppc\<Tab>\<C-B>\"\<CR>", 'tx')
@@ -2602,32 +2635,49 @@ func Test_wildoptions_fuzzy()
   set wildoptions=fuzzy
   call feedkeys(":let c = ppc\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"let c = PerPlaceCount', @:)
-
-  " file name fuzzy completion - NOT supported
-
-  " files in path fuzzy completion - NOT supported
-
-  " filetype name fuzzy completion - NOT supported
-
-  " user defined function name completion
   set wildoptions&
-  call feedkeys(":call Test_w_fuz\<Tab>\<C-B>\"\<CR>", 'tx')
-  call assert_equal('"call Test_w_fuz', @:)
+endfunc
+
+" " file name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_filename()
+" endfunc
+
+" " files in path fuzzy completion - NOT supported
+" func Test_fuzzy_completion_filesinpath()
+" endfunc
+
+" " filetype name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_filetype()
+" endfunc
+
+" user defined function name completion
+func Test_fuzzy_completion_userdefined_func()
+  set wildoptions&
+  call feedkeys(":call Test_f_u_f\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"call Test_f_u_f', @:)
   set wildoptions=fuzzy
-  call feedkeys(":call Test_w_fuz\<Tab>\<C-B>\"\<CR>", 'tx')
-  call assert_equal('"call Test_wildoptions_fuzzy()', @:)
-
-  " user defined command name completion
+  call feedkeys(":call Test_f_u_f\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"call Test_fuzzy_completion_userdefined_func()', @:)
+  set wildoptions&
+endfunc
+
+" user defined command name completion
+func Test_fuzzy_completion_userdefined_cmd()
   set wildoptions&
   call feedkeys(":MsFeat\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"MsFeat', @:)
   set wildoptions=fuzzy
   call feedkeys(":MsFeat\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"MissingFeature', @:)
-
-  " :help tag fuzzy completion - NOT supported
-
-  " highlight group name fuzzy completion
+  set wildoptions&
+endfunc
+
+" " :help tag fuzzy completion - NOT supported
+" func Test_fuzzy_completion_helptag()
+" endfunc
+
+" highlight group name fuzzy completion
+func Test_fuzzy_completion_hlgroup()
   set wildoptions&
   call feedkeys(":highlight SKey\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"highlight SKey', @:)
@@ -2638,8 +2688,11 @@ func Test_wildoptions_fuzzy()
   call assert_equal('"highlight SpecialKey', @:)
   call feedkeys(":highlight Sp*Key\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"highlight Sp*Key', @:)
-
-  " :history suboptions fuzzy completion
+  set wildoptions&
+endfunc
+
+" :history suboptions fuzzy completion
+func Test_fuzzy_completion_history()
   set wildoptions&
   call feedkeys(":history dg\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"history dg', @:)
@@ -2650,26 +2703,34 @@ func Test_wildoptions_fuzzy()
   call assert_equal('"history debug', @:)
   call feedkeys(":history se*h\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"history se*h', @:)
-
-  " :language locale name fuzzy completion
-  if has('unix')
-    set wildoptions&
-    call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"lang psx', @:)
-    set wildoptions=fuzzy
-    call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"lang POSIX', @:)
-  endif
-
-  " :mapclear buffer argument fuzzy completion
+  set wildoptions&
+endfunc
+
+" :language locale name fuzzy completion
+func Test_fuzzy_completion_lang()
+  CheckUnix
+  set wildoptions&
+  call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"lang psx', @:)
+  set wildoptions=fuzzy
+  call feedkeys(":lang psx\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"lang POSIX', @:)
+  set wildoptions&
+endfunc
+
+" :mapclear buffer argument fuzzy completion
+func Test_fuzzy_completion_mapclear()
   set wildoptions&
   call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"mapclear buf', @:)
   set wildoptions=fuzzy
   call feedkeys(":mapclear buf\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"mapclear <buffer>', @:)
-
-  " map name fuzzy completion
+  set wildoptions&
+endfunc
+
+" map name fuzzy completion
+func Test_fuzzy_completion_mapname()
   " test regex completion works
   set wildoptions=fuzzy
   call feedkeys(":cnoremap <ex\<Tab> <esc> \<Tab>\<C-B>\"\<CR>", 'tx')
@@ -2704,8 +2765,11 @@ func Test_wildoptions_fuzzy()
   nunmap <Plug>fendoff
   nunmap <Plug>state
   nunmap <Plug>FendingOff
-
-  " abbreviation fuzzy completion
+  set wildoptions&
+endfunc
+
+" abbreviation fuzzy completion
+func Test_fuzzy_completion_abbr()
   set wildoptions=fuzzy
   call feedkeys(":iabbr wait\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal("\"iabbr <nowait>", @:)
@@ -2715,26 +2779,34 @@ func Test_wildoptions_fuzzy()
   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')
-    set wildoptions&
-    call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"menu pup', @:)
-    set wildoptions=fuzzy
-    call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"menu PopUp.', @:)
-  endif
-
-  " :messages suboptions fuzzy completion
+  set wildoptions&
+endfunc
+
+" menu name fuzzy completion
+func Test_fuzzy_completion_menu()
+  CheckGui
+  set wildoptions&
+  call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"menu pup', @:)
+  set wildoptions=fuzzy
+  call feedkeys(":menu pup\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"menu PopUp.', @:)
+  set wildoptions&
+endfunc
+
+" :messages suboptions fuzzy completion
+func Test_fuzzy_completion_messages()
   set wildoptions&
   call feedkeys(":messages clr\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"messages clr', @:)
   set wildoptions=fuzzy
   call feedkeys(":messages clr\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"messages clear', @:)
-
-  " :set option name fuzzy completion
+  set wildoptions&
+endfunc
+
+" :set option name fuzzy completion
+func Test_fuzzy_completion_option()
   set wildoptions&
   call feedkeys(":set brkopt\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"set brkopt', @:)
@@ -2747,8 +2819,11 @@ func Test_wildoptions_fuzzy()
   set wildoptions=fuzzy
   call feedkeys(":set fixeol\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"set fixendofline', @:)
-
-  " :set <term_option>
+  set wildoptions&
+endfunc
+
+" :set <term_option>
+func Test_fuzzy_completion_term_option()
   set wildoptions&
   call feedkeys(":set t_E\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"set t_EC', @:)
@@ -2759,52 +2834,76 @@ func Test_wildoptions_fuzzy()
   call assert_equal('"set t_EC', @:)
   call feedkeys(":set <t_E\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"set <t_EC>', @:)
-
-  " :packadd directory name fuzzy completion - NOT supported
-
-  " shell command name fuzzy completion - NOT supported
-
-  " :sign suboptions fuzzy completion
+  set wildoptions&
+endfunc
+
+" " :packadd directory name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_packadd()
+" endfunc
+
+" " shell command name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_shellcmd()
+" endfunc
+
+" :sign suboptions fuzzy completion
+func Test_fuzzy_completion_sign()
   set wildoptions&
   call feedkeys(":sign ufe\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sign ufe', @:)
   set wildoptions=fuzzy
   call feedkeys(":sign ufe\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"sign undefine', @:)
-
-  " :syntax suboptions fuzzy completion
+  set wildoptions&
+endfunc
+
+" :syntax suboptions fuzzy completion
+func Test_fuzzy_completion_syntax_cmd()
   set wildoptions&
   call feedkeys(":syntax kwd\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"syntax kwd', @:)
   set wildoptions=fuzzy
   call feedkeys(":syntax kwd\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"syntax keyword', @:)
-
-  " syntax group name fuzzy completion
+  set wildoptions&
+endfunc
+
+" syntax group name fuzzy completion
+func Test_fuzzy_completion_syntax_group()
   set wildoptions&
   call feedkeys(":syntax list mpar\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"syntax list mpar', @:)
   set wildoptions=fuzzy
   call feedkeys(":syntax list mpar\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"syntax list MatchParen', @:)
-
-  " :syntime suboptions fuzzy completion
-  if has('profile')
-    set wildoptions&
-    call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"syntime clr', @:)
-    set wildoptions=fuzzy
-    call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx')
-    call assert_equal('"syntime clear', @:)
-  endif
-
-  " tag name fuzzy completion - NOT supported
-
-  " tag name and file fuzzy completion - NOT supported
-
-  " user names fuzzy completion - how to test this functionality?
-
-  " user defined variable name fuzzy completion
+  set wildoptions&
+endfunc
+
+" :syntime suboptions fuzzy completion
+func Test_fuzzy_completion_syntime()
+  CheckFeature profile
+  set wildoptions&
+  call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"syntime clr', @:)
+  set wildoptions=fuzzy
+  call feedkeys(":syntime clr\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"syntime clear', @:)
+  set wildoptions&
+endfunc
+
+" " tag name fuzzy completion - NOT supported
+" func Test_fuzzy_completion_tagname()
+" endfunc
+
+" " tag name and file fuzzy completion - NOT supported
+" func Test_fuzzy_completion_tagfile()
+" endfunc
+
+" " user names fuzzy completion - how to test this functionality?
+" func Test_fuzzy_completion_username()
+" endfunc
+
+" user defined variable name fuzzy completion
+func Test_fuzzy_completion_userdefined_var()
   let g:SomeVariable=10
   set wildoptions&
   call feedkeys(":let SVar\<Tab>\<C-B>\"\<CR>", 'tx')
@@ -2812,8 +2911,11 @@ func Test_wildoptions_fuzzy()
   set wildoptions=fuzzy
   call feedkeys(":let SVar\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"let SomeVariable', @:)
-
-  " Test for sorting the results by the best match
+  set wildoptions&
+endfunc
+
+" Test for sorting the results by the best match
+func Test_fuzzy_completion_cmd_sort_results()
   %bw!
   command T123format :
   command T123goformat :
@@ -2831,9 +2933,11 @@ func Test_wildoptions_fuzzy()
   delcommand T123state
   delcommand T123FendingOff
   %bw
-
-  " Test for fuzzy completion of a command with lower case letters and a
-  " number
+  set wildoptions&
+endfunc
+
+" Test for fuzzy completion of a command with lower case letters and a number
+func Test_fuzzy_completion_cmd_alnum()
   command Foo2Bar :
   set wildoptions=fuzzy
   call feedkeys(":foo2\<Tab>\<C-B>\"\<CR>", 'tx')
@@ -2843,8 +2947,11 @@ func Test_wildoptions_fuzzy()
   call feedkeys(":bar\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"Foo2Bar', @:)
   delcommand Foo2Bar
-
-  " Test for command completion for a command starting with 'k'
+  set wildoptions&
+endfunc
+
+" Test for command completion for a command starting with 'k'
+func Test_fuzzy_completion_cmd_k()
   command KillKillKill :
   set wildoptions&
   call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx')
@@ -2853,9 +2960,33 @@ func Test_wildoptions_fuzzy()
   call feedkeys(":killkill\<Tab>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"KillKillKill', @:)
   delcom KillKillKill
-
+  set wildoptions&
+endfunc
+
+" Test for fuzzy completion for user defined custom completion function
+func Test_fuzzy_completion_custom_func()
+  func Tcompl(a, c, p)
+    return "format\ngoformat\nTestFOrmat\nfendoff\nstate"
+  endfunc
+  command -nargs=* -complete=custom,Tcompl Fuzzy :
   set wildoptions&
-  %bw!
+  call feedkeys(":Fuzzy fo\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"Fuzzy format", @:)
+  call feedkeys(":Fuzzy xy\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"Fuzzy xy", @:)
+  call feedkeys(":Fuzzy ttt\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"Fuzzy ttt", @:)
+  set wildoptions=fuzzy
+  call feedkeys(":Fuzzy \<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"Fuzzy format goformat TestFOrmat fendoff state", @:)
+  call feedkeys(":Fuzzy fo\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"Fuzzy format TestFOrmat goformat fendoff", @:)
+  call feedkeys(":Fuzzy xy\<Tab>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"Fuzzy xy", @:)
+  call feedkeys(":Fuzzy ttt\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal("\"Fuzzy TestFOrmat", @:)
+  delcom Fuzzy
+  set wildoptions&
 endfunc
 
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4482,
+/**/
     4481,
 /**/
     4480,