]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0451: 'findfunc' can't return extra info for cmdline completion v9.2.0451
authorzeertzjq <zeertzjq@outlook.com>
Thu, 7 May 2026 19:29:06 +0000 (19:29 +0000)
committerChristian Brabandt <cb@256bit.org>
Thu, 7 May 2026 19:32:23 +0000 (19:32 +0000)
Problem:  'findfunc' can't return extra info for cmdline completion
          (Maxim Kim).
Solution: Handle 'findfunc' return value in cmdline completion like that
          of "customlist" functions (zeertzjq).

fixes:  #20155
closes: #20158

Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/options.txt
src/cmdexpand.c
src/ex_docmd.c
src/proto/cmdexpand.pro
src/proto/ex_docmd.pro
src/testdir/dumps/Test_compl_findfunc_dict_01.dump [new file with mode: 0644]
src/testdir/dumps/Test_compl_findfunc_dict_02.dump [new file with mode: 0644]
src/testdir/test_cmdline.vim
src/version.c

index 2baf8a01a789770956737567b39d0c5d9c67340d..e82cd3055571a165efadec9420c5b79d505d64e5 100644 (file)
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.2.  Last change: 2026 May 04
+*options.txt*  For Vim version 9.2.  Last change: 2026 May 07
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -3983,7 +3983,9 @@ A jump table for the options with a short description can be found at |Q_op|.
        |String| and is the |:find| command argument.  The second argument is
        a |Boolean| and is set to |v:true| when the function is called to get
        a List of command-line completion matches for the |:find| command.
-       The function should return a List of strings.
+       The function should return a List of strings, or, in the command-line
+       completion case, whatever a |:command-completion-customlist| function
+       may return.
 
        The function is called only once per |:find| command invocation.
        The function can process all the directories specified in 'path'.
index eb4f362b2bd209afed7ba7ec6d6768b1acb9de18..a763456a1f9044e6468a7dafd7aac02cbb5b1724 100644 (file)
@@ -3156,7 +3156,7 @@ expand_files_and_dirs(
     if (xp->xp_context == EXPAND_FINDFUNC)
     {
 #ifdef FEAT_EVAL
-       ret = expand_findfunc(pat, matches, numMatches);
+       ret = expand_findfunc(xp, pat, matches, numMatches);
 #endif
     }
     else
@@ -4148,16 +4148,13 @@ ExpandUserDefined(
     return OK;
 }
 
-/*
- * Expand names with a list returned by a function defined by the user.
- */
-    static int
-ExpandUserList(
-    expand_T   *xp,
+    void
+expand_process_user_list(
+    list_T     *retlist,
     char_u     ***matches,
-    int                *numMatches)
+    int                *numMatches,
+    expand_T   *xp)
 {
-    list_T      *retlist;
     listitem_T *li;
     garray_T   ga;
     garray_T   ga_abbr;
@@ -4167,12 +4164,6 @@ ExpandUserList(
     int                have_extra = FALSE;
     int                i;
 
-    *matches = NULL;
-    *numMatches = 0;
-    retlist = call_user_expand_func(call_func_retlist, xp);
-    if (retlist == NULL)
-       return FAIL;
-
     ga_init2(&ga, sizeof(char *), 3);
     ga_init2(&ga_abbr, sizeof(char *), 3);
     ga_init2(&ga_kind, sizeof(char *), 3);
@@ -4190,7 +4181,7 @@ ExpandUserList(
        if (li->li_tv.v_type == VAR_STRING)
        {
            if (li->li_tv.vval.v_string == NULL)
-               continue;  // Skip empty strings
+               continue;  // Skip NULL strings
            p = vim_strsave(li->li_tv.vval.v_string);
        }
        else if (li->li_tv.v_type == VAR_DICT
@@ -4233,7 +4224,6 @@ ExpandUserList(
        ((char_u **)ga_menu.ga_data)[ga_menu.ga_len++] = menu;
        ((char_u **)ga_info.ga_data)[ga_info.ga_len++] = info;
     }
-    list_unref(retlist);
 
     *matches = ga.ga_data;
     *numMatches = ga.ga_len;
@@ -4260,6 +4250,27 @@ ExpandUserList(
            vim_free(((char_u **)ga_info.ga_data)[i]);
        vim_free(ga_info.ga_data);
     }
+}
+
+/*
+ * Expand names with a list returned by a function defined by the user.
+ */
+    static int
+ExpandUserList(
+    expand_T   *xp,
+    char_u     ***matches,
+    int                *numMatches)
+{
+    list_T      *retlist;
+
+    *matches = NULL;
+    *numMatches = 0;
+    retlist = call_user_expand_func(call_func_retlist, xp);
+    if (retlist == NULL)
+       return FAIL;
+
+    expand_process_user_list(retlist, matches, numMatches, xp);
+    list_unref(retlist);
     return OK;
 }
 #endif
index 7ea20b262803c436930b1ff248be5aede87c62bf..7b31bed99a20bf6911f291eaba0df207d6106ef2 100644 (file)
@@ -7066,7 +7066,7 @@ call_findfunc(char_u *pat, int cmdcomplete)
  * Returns OK on success and FAIL otherwise.
  */
     int
-expand_findfunc(char_u *pat, char_u ***files, int *numMatches)
+expand_findfunc(expand_T *xp, char_u *pat, char_u ***files, int *numMatches)
 {
     list_T     *l;
     int                len;
@@ -7086,26 +7086,7 @@ expand_findfunc(char_u *pat, char_u ***files, int *numMatches)
        return FAIL;
     }
 
-    *files = ALLOC_MULT(char_u *, len);
-    if (*files == NULL)
-    {
-       list_free(l);
-       return FAIL;
-    }
-
-    // Copy all the List items
-    listitem_T *li;
-    int idx = 0;
-    FOR_ALL_LIST_ITEMS(l, li)
-    {
-       if (li->li_tv.v_type == VAR_STRING)
-       {
-           (*files)[idx] = vim_strsave(li->li_tv.vval.v_string);
-           idx++;
-       }
-    }
-
-    *numMatches = idx;
+    expand_process_user_list(l, files, numMatches, xp);
     list_free(l);
 
     return OK;
index 67dff09ca41bb0b90e535d8171d8d7837c2a412e..390868a51f5fb9e427cbb30e9ab9feadd209869e 100644 (file)
@@ -20,6 +20,7 @@ void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline
 int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u ***matches);
 int ExpandGeneric(char_u *pat, expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches, char_u *(*func)(expand_T *, int), int escaped);
 int ExpandGenericExt(char_u *pat, expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches, char_u *(*func)(expand_T *, int), int escaped, int sortStartIdx);
+void expand_process_user_list(list_T *retlist, char_u ***matches, int *numMatches, expand_T *xp);
 void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options, int dirs);
 int wildmenu_translate_key(cmdline_info_T *cclp, int key, expand_T *xp, int did_wild_list);
 int wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp);
index afaec22827b3ef58151594c5eef029e4b603938e..df44d02004521e3802790a1c3a16d13c7126b081 100644 (file)
@@ -47,7 +47,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit);
 void ex_stop(exarg_T *eap);
 void handle_drop(int filec, char_u **filev, int split, void (*callback)(void *), void *cookie);
 void handle_any_postponed_drop(void);
-int expand_findfunc(char_u *pat, char_u ***files, int *numMatches);
+int expand_findfunc(expand_T *xp, char_u *pat, char_u ***files, int *numMatches);
 char *did_set_findfunc(optset_T *args);
 void free_findfunc_option(void);
 int set_ref_in_findfunc(int copyID);
diff --git a/src/testdir/dumps/Test_compl_findfunc_dict_01.dump b/src/testdir/dumps/Test_compl_findfunc_dict_01.dump
new file mode 100644 (file)
index 0000000..3c93811
--- /dev/null
@@ -0,0 +1,12 @@
+| +0&#ffffff0@74
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @3| +0#0000001#e0e0e08|X|p|l|a|i|n| @8| +0#4040ff13#ffffff0@53
+|~| @3| +0#0000001#ffd7ff255|X|f|i|l|e|1| |F| |f|i|l|e| @1| +0#4040ff13#ffffff0@53
+|~| @3| +0#0000001#ffd7ff255|X|f|i|l|e|2| |F| |f|i|l|e| @1| +0#4040ff13#ffffff0@53
+|~| @3| +0#0000001#ffd7ff255|X|d|i|r|1| @1|D| |d|i|r| @2| +0#4040ff13#ffffff0@53
+|~| @3| +0#0000001#ffd7ff255|X|d|i|r|2| @1|D| |d|i|r| @2| +0#4040ff13#ffffff0@53
+|:+0#0000000&|f|i|n|d| |X|p|l|a|i|n> @62
diff --git a/src/testdir/dumps/Test_compl_findfunc_dict_02.dump b/src/testdir/dumps/Test_compl_findfunc_dict_02.dump
new file mode 100644 (file)
index 0000000..0e39d04
--- /dev/null
@@ -0,0 +1,12 @@
+| +0&#ffffff0@74
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @3| +0#0000001#ffd7ff255|X|p|l|a|i|n| @8| +0#4040ff13#ffffff0@53
+|~| @3| +0#0000001#ffd7ff255|X|f|i|l|e|1| |F| |f|i|l|e| @1| +0#4040ff13#ffffff0@53
+|~| @3| +0#0000001#ffd7ff255|X|f|i|l|e|2| |F| |f|i|l|e| @1|╔+0&#e0e0e08|═@8|X| +0#4040ff13#ffffff0@42
+|~| @3| +0#0000001#e0e0e08|X|d|i|r|1| @1|D| |d|i|r| @2|║| |1|s|t| |d|i|r| |║| +0#4040ff13#ffffff0@42
+|~| @3| +0#0000001#ffd7ff255|X|d|i|r|2| @1|D| |d|i|r| @2|╚+0&#e0e0e08|═@8|⇲| +0#4040ff13#ffffff0@42
+|:+0#0000000&|f|i|n|d| |X|d|i|r|1> @63
index 7b3449e2bd2fe724a24d14eff78b5c110f07d5c8..6809ef5f78fa1fbcd981eefb8a97208bb525b773 100644 (file)
@@ -4705,6 +4705,38 @@ func Test_customlist_dict_completion_info_popup()
   call StopVimInTerminal(buf)
 endfunc
 
+func Test_cmdline_complete_findfunc_dict()
+  CheckScreendump
+
+  let lines =<< trim END
+    set wildmenu wildoptions=pum completeopt=menu,popup
+    func FindComplete(cmdarg, cmdcomplete)
+      return [
+            \ 'Xplain',
+            \ {'word': 'Xfile1', 'kind': 'F', 'menu': 'file', 'info': '1st file'},
+            \ {'word': 'Xfile2', 'kind': 'F', 'menu': 'file', 'info': '2nd file'},
+            \ {'word': 'Xdir1',  'kind': 'D', 'menu': 'dir',  'info': '1st dir'},
+            \ {'word': 'Xdir2',  'kind': 'D', 'menu': 'dir',  'info': '2nd dir'},
+            \ ]
+    endfunc
+    set findfunc=FindComplete
+  END
+  call writefile(lines, 'XTest_compl_findfunc_dict', 'D')
+  let rows = 12
+  let buf = RunVimInTerminal('-S XTest_compl_findfunc_dict', {'rows': rows})
+
+  call term_sendkeys(buf, ":find \<Tab>")
+  call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':find Xplain') + 1)], g:test_timeout, ((rows - 5), '^\~\s\+Xplain\s\+$'))
+  call VerifyScreenDump(buf, 'Test_compl_findfunc_dict_01', {})
+
+  call term_sendkeys(buf, "\<PageDown>")
+  call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':find Xdir1') + 1)], g:test_timeout, ((rows - 2), '1st dir'))
+  call VerifyScreenDump(buf, 'Test_compl_findfunc_dict_02', {})
+
+  call term_sendkeys(buf, "\<Esc>")
+  call StopVimInTerminal(buf)
+endfunc
+
 func Test_custom_completion_with_glob()
   func TestGlobComplete(A, L, P)
     return split(glob('Xglob*'), "\n")
index ff0960c2277b6f18aaf2cf21dc6e36f035023b7f..8ab3ed1d49b5fcfdc21dcfc900333c790a2cc27c 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    451,
 /**/
     450,
 /**/