diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 2baf8a01a78977..e82cd3055571a1 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -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'. diff --git a/src/cmdexpand.c b/src/cmdexpand.c index eb4f362b2bd209..a763456a1f9044 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -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 diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 7ea20b262803c4..7b31bed99a20bf 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -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; diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro index 67dff09ca41bb0..390868a51f5fb9 100644 --- a/src/proto/cmdexpand.pro +++ b/src/proto/cmdexpand.pro @@ -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); diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index afaec22827b3ef..df44d02004521e 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -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 index 00000000000000..3c93811384bfc1 --- /dev/null +++ b/src/testdir/dumps/Test_compl_findfunc_dict_01.dump @@ -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 index 00000000000000..0e39d0486b4aea --- /dev/null +++ b/src/testdir/dumps/Test_compl_findfunc_dict_02.dump @@ -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 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 7b3449e2bd2fe7..6809ef5f78fa1f 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -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 \") + 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, "\") + 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, "\") + call StopVimInTerminal(buf) +endfunc + func Test_custom_completion_with_glob() func TestGlobComplete(A, L, P) return split(glob('Xglob*'), "\n")