diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index db49a7c1cc..793b200c7e 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2935,7 +2935,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/runtime/lua/vim/_meta/options.gen.lua b/runtime/lua/vim/_meta/options.gen.lua index 97f4dab4f3..e2d5b82957 100644 --- a/runtime/lua/vim/_meta/options.gen.lua +++ b/runtime/lua/vim/_meta/options.gen.lua @@ -2625,7 +2625,9 @@ vim.go.fcs = vim.go.fillchars --- `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/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index e4d1b09cdb..ac0d71218f 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -2735,7 +2735,7 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int * int ret = FAIL; if (xp->xp_context == EXPAND_FINDFUNC) { - ret = expand_findfunc(pat, matches, numMatches); + ret = expand_findfunc(xp, pat, matches, numMatches); } else { if (xp->xp_context == EXPAND_FILES) { flags |= EW_FILE; @@ -3560,7 +3560,7 @@ static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *re return OK; } -static void process_user_list(list_T *retlist, char ***matches, int *numMatches, expand_T *xp) +void expand_process_user_list(list_T *retlist, char ***matches, int *numMatches, expand_T *xp) { garray_T ga; garray_T ga_abbr; @@ -3583,7 +3583,7 @@ static void process_user_list(list_T *retlist, char ***matches, int *numMatches, if (TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) { if (TV_LIST_ITEM_TV(li)->vval.v_string == NULL) { - continue; // Skip empty strings + continue; // Skip NULL strings } p = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string); } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT @@ -3612,7 +3612,6 @@ static void process_user_list(list_T *retlist, char ***matches, int *numMatches, GA_APPEND(char *, &ga_menu, menu); GA_APPEND(char *, &ga_info, info); }); - tv_list_unref(retlist); *matches = ga.ga_data; *numMatches = ga.ga_len; @@ -3652,7 +3651,8 @@ static int ExpandUserList(expand_T *xp, char ***matches, int *numMatches) return FAIL; } - process_user_list(retlist, matches, numMatches, xp); + expand_process_user_list(retlist, matches, numMatches, xp); + tv_list_unref(retlist); return OK; } @@ -3667,7 +3667,8 @@ static int ExpandUserLua(expand_T *xp, int *numMatches, char ***matches) list_T *const retlist = rettv.vval.v_list; - process_user_list(retlist, matches, numMatches, xp); + expand_process_user_list(retlist, matches, numMatches, xp); + tv_list_unref(retlist); return OK; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4cef3e0521..0fcf52ba58 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5654,7 +5654,7 @@ static list_T *call_findfunc(char *pat, BoolVarValue cmdcomplete) /// Find file names matching "pat" using 'findfunc' and return it in "files". /// Used for expanding the :find, :sfind and :tabfind command argument. /// Returns OK on success and FAIL otherwise. -int expand_findfunc(char *pat, char ***files, int *numMatches) +int expand_findfunc(expand_T *xp, char *pat, char ***files, int *numMatches) { *numMatches = 0; *files = NULL; @@ -5670,18 +5670,7 @@ int expand_findfunc(char *pat, char ***files, int *numMatches) return FAIL; } - *files = xmalloc(sizeof(char *) * (size_t)len); - - // Copy all the List items - int idx = 0; - TV_LIST_ITER_CONST(l, li, { - if (TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) { - (*files)[idx] = TO_SLASH_SAVE(TV_LIST_ITEM_TV(li)->vval.v_string); - idx++; - } - }); - - *numMatches = idx; + expand_process_user_list(l, files, numMatches, xp); tv_list_free(l); return OK; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index a8ce41c39e..62fd3b2cae 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -3297,7 +3297,9 @@ local options = { |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/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 2d2224b3e8..5df51d502e 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -5690,6 +5690,99 @@ describe('builtin popupmenu', function() end end) + -- oldtest: Test_cmdline_complete_findfunc_dict() + it("'findfunc' can return extra info for cmdline completion", function() + screen:try_resize(55, 12) + exec([[ + 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 + ]]) + + feed(':find ') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:-------------------------------------------------------]|*11 + [3:-------------------------------------------------------]| + ## grid 2 + | + {1:~ }|*10 + ## grid 3 + :find Xplain^ | + ## grid 4 + {12: Xplain }| + {n: Xfile1 F file }| + {n: Xfile2 F file }| + {n: Xdir1 D dir }| + {n: Xdir2 D dir }| + ]], + float_pos = { + [4] = { -1, 'SW', 1, 11, 5, false, 250, 2, 6, 5 }, + }, + }) + else + screen:expect([[ + | + {1:~ }|*5 + {1:~ }{12: Xplain }{1: }| + {1:~ }{n: Xfile1 F file }{1: }| + {1:~ }{n: Xfile2 F file }{1: }| + {1:~ }{n: Xdir1 D dir }{1: }| + {1:~ }{n: Xdir2 D dir }{1: }| + :find Xplain^ | + ]]) + end + + feed('') + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:-------------------------------------------------------]|*11 + [3:-------------------------------------------------------]| + ## grid 2 + | + {1:~ }|*10 + ## grid 3 + :find Xdir1^ | + ## grid 4 + {n: Xplain }| + {n: Xfile1 F file }| + {n: Xfile2 F file }| + {12: Xdir1 D dir }| + {n: Xdir2 D dir }| + ## grid 5 + {n:1st dir}| + ]], + float_pos = { + [4] = { -1, 'SW', 1, 11, 5, false, 250, 3, 6, 5 }, + [5] = { 1001, 'NW', 1, 6, 21, true, 50, 1, 6, 21 }, + }, + }) + else + screen:expect([[ + | + {1:~ }|*5 + {1:~ }{n: Xplain 1st dir}{1: }| + {1:~ }{n: Xfile1 F file }{1: }| + {1:~ }{n: Xfile2 F file }{1: }| + {1:~ }{12: Xdir1 D dir }{1: }| + {1:~ }{n: Xdir2 D dir }{1: }| + :find Xdir1^ | + ]]) + end + end) + it("'pumheight'", function() screen:try_resize(32, 8) feed('isome long prefix before the ') diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index a280265902..52743a8283 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -4688,6 +4688,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")