diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-11-03 10:06:41 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-03 10:06:41 +0800 |
commit | 3075c69ff02faf396e5efbdcb4a255b0b0309649 (patch) | |
tree | 1d02f7488d7a98a518852b202e15897d58252d6f | |
parent | ed3fb1bb9ad97435c50655ee8de71b2d7d67d01c (diff) | |
download | rneovim-3075c69ff02faf396e5efbdcb4a255b0b0309649.tar.gz rneovim-3075c69ff02faf396e5efbdcb4a255b0b0309649.tar.bz2 rneovim-3075c69ff02faf396e5efbdcb4a255b0b0309649.zip |
vim-patch:9.1.0831: 'findexpr' can't be used as lambad or Funcref (#31058)
Problem: 'findexpr' can't be used for lambads
(Justin Keyes)
Solution: Replace the findexpr option with the findfunc option
(Yegappan Lakshmanan)
related: vim/vim#15905
closes: vim/vim#15976
https://github.com/vim/vim/commit/a13f3a4f5de9c150f70298850e34747838904995
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
-rw-r--r-- | runtime/doc/options.txt | 53 | ||||
-rw-r--r-- | runtime/doc/quickref.txt | 2 | ||||
-rw-r--r-- | runtime/doc/vvars.txt | 8 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/options.lua | 52 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/vvars.lua | 8 | ||||
-rw-r--r-- | src/nvim/buffer.c | 3 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 5 | ||||
-rw-r--r-- | src/nvim/cmdexpand.c | 12 | ||||
-rw-r--r-- | src/nvim/cmdexpand_defs.h | 2 | ||||
-rw-r--r-- | src/nvim/errors.h | 2 | ||||
-rw-r--r-- | src/nvim/eval.c | 6 | ||||
-rw-r--r-- | src/nvim/eval.h | 1 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 128 | ||||
-rw-r--r-- | src/nvim/generators/gen_options.lua | 1 | ||||
-rw-r--r-- | src/nvim/option.c | 22 | ||||
-rw-r--r-- | src/nvim/option_vars.h | 2 | ||||
-rw-r--r-- | src/nvim/options.lua | 51 | ||||
-rw-r--r-- | src/nvim/optionstr.c | 7 | ||||
-rw-r--r-- | src/nvim/vvars.lua | 10 | ||||
-rw-r--r-- | test/old/testdir/test_findfile.vim | 497 | ||||
-rw-r--r-- | test/old/testdir/test_ins_complete.vim | 12 | ||||
-rw-r--r-- | test/old/testdir/test_modeline.vim | 2 | ||||
-rw-r--r-- | test/old/testdir/test_normal.vim | 4 | ||||
-rw-r--r-- | test/old/testdir/test_options.vim | 2 | ||||
-rw-r--r-- | test/old/testdir/test_tagfunc.vim | 4 |
25 files changed, 604 insertions, 292 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index feaff3e425..8877054b77 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -370,10 +370,11 @@ Note: In the future more global options can be made |global-local|. Using ":setlocal" on a global option might work differently then. *option-value-function* -Some options ('completefunc', 'omnifunc', 'operatorfunc', 'quickfixtextfunc', -'tagfunc' and 'thesaurusfunc') are set to a function name or a function -reference or a lambda function. When using a lambda it will be converted to -the name, e.g. "<lambda>123". Examples: +Some options ('completefunc', 'findfunc', 'omnifunc', 'operatorfunc', +'quickfixtextfunc', 'tagfunc' and 'thesaurusfunc') are set to a function name +or a function reference or a lambda function. When using a lambda it will be +converted to the name, e.g. "<lambda>123". +Examples: > set opfunc=MyOpFunc set opfunc=function('MyOpFunc') @@ -2598,34 +2599,34 @@ A jump table for the options with a short description can be found at |Q_op|. eob EndOfBuffer |hl-EndOfBuffer| lastline NonText |hl-NonText| - *'findexpr'* *'fexpr'* *E1514* -'findexpr' 'fexpr' string (default "") + *'findfunc'* *'ffu'* *E1514* +'findfunc' 'ffu' string (default "") global or local to buffer |global-local| - Expression that is evaluated to obtain the filename(s) for the |:find| + Function that is called to obtain the filename(s) for the |:find| command. When this option is empty, the internal |file-searching| mechanism is used. - While evaluating the expression, the |v:fname| variable is set to the - argument of the |:find| command. + The value can be the name of a function, a |lambda| or a |Funcref|. + See |option-value-function| for more information. - The expression is evaluated only once per |:find| command invocation. - The expression can process all the directories specified in 'path'. + The function is called with two arguments. The first argument is a + |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 expression may be evaluated for command-line completion as well, - in which case the |v:cmdcomplete| variable will be set to |v:true|, - otherwise it will be set to |v:false|. + The function is called only once per |:find| command invocation. + The function can process all the directories specified in 'path'. - If a match is found, the expression should return a |List| containing - one or more file names. If a match is not found, the expression + If a match is found, the function should return a |List| containing + one or more file names. If a match is not found, the function should return an empty List. - If any errors are encountered during the expression evaluation, an + If any errors are encountered during the function invocation, an empty List is used as the return value. - Using a function call without arguments is faster |expr-option-function| - It is not allowed to change text or jump to another window while - evaluating 'findexpr' |textlock|. + executing the 'findfunc' |textlock|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. @@ -2633,18 +2634,18 @@ A jump table for the options with a short description can be found at |Q_op|. Examples: >vim " Use glob() - func FindExprGlob() - let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname + func FindFuncGlob(cmdarg, cmdcomplete) + let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg return glob(pat, v:false, v:true) endfunc - set findexpr=FindExprGlob() + set findfunc=FindFuncGlob " Use the 'git ls-files' output - func FindGitFiles() + func FindGitFiles(cmdarg, cmdcomplete) let fnames = systemlist('git ls-files') - return fnames->filter('v:val =~? v:fname') + return fnames->filter('v:val =~? a:cmdarg') endfunc - set findexpr=FindGitFiles() + set findfunc=FindGitFiles < *'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'* diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index f64865a031..f43ddb57fb 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -705,7 +705,7 @@ Short explanation of each option: *option-list* 'fileignorecase' 'fic' ignore case when using file names 'filetype' 'ft' type of file, used for autocommands 'fillchars' 'fcs' characters to use for displaying special items -'findexpr' 'fexpr' expression to evaluate for |:find| +'findfunc' 'ffu' function to be called for the |:find| command 'fixendofline' 'fixeol' make sure last line in file has <EOL> 'foldclose' 'fcl' close a fold when the cursor leaves it 'foldcolumn' 'fdc' width of the column used to indicate folds diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt index 1c1d88c29c..15d836a83d 100644 --- a/runtime/doc/vvars.txt +++ b/runtime/doc/vvars.txt @@ -48,11 +48,6 @@ v:cmdbang can only be used in autocommands. For user commands |<bang>| can be used. - *v:cmdcomplete* *cmdcomplete-variable* -v:cmdcomplete - When evaluating 'findexpr': if 'findexpr' is used for cmdline - completion the value is |v:true|, otherwise it is |v:false|. - *v:collate* *collate-variable* v:collate The current locale setting for collation order of the runtime @@ -259,8 +254,7 @@ v:fcs_reason *v:fname* *fname-variable* v:fname When evaluating 'includeexpr': the file name that was - detected. When evaluating 'findexpr': the argument passed to - the |:find| command. Empty otherwise. + detected. Empty otherwise. *v:fname_diff* *fname_diff-variable* v:fname_diff diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 5e5b6b5ed1..f9886957a7 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -2294,31 +2294,31 @@ vim.wo.fcs = vim.wo.fillchars vim.go.fillchars = vim.o.fillchars vim.go.fcs = vim.go.fillchars ---- Expression that is evaluated to obtain the filename(s) for the `:find` +--- Function that is called to obtain the filename(s) for the `:find` --- command. When this option is empty, the internal `file-searching` --- mechanism is used. --- ---- While evaluating the expression, the `v:fname` variable is set to the ---- argument of the `:find` command. +--- The value can be the name of a function, a `lambda` or a `Funcref`. +--- See `option-value-function` for more information. --- ---- The expression is evaluated only once per `:find` command invocation. ---- The expression can process all the directories specified in 'path'. +--- The function is called with two arguments. The first argument is a +--- `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 expression may be evaluated for command-line completion as well, ---- in which case the `v:cmdcomplete` variable will be set to `v:true`, ---- otherwise it will be set to `v:false`. +--- The function is called only once per `:find` command invocation. +--- The function can process all the directories specified in 'path'. --- ---- If a match is found, the expression should return a `List` containing ---- one or more file names. If a match is not found, the expression +--- If a match is found, the function should return a `List` containing +--- one or more file names. If a match is not found, the function --- should return an empty List. --- ---- If any errors are encountered during the expression evaluation, an +--- If any errors are encountered during the function invocation, an --- empty List is used as the return value. --- ---- Using a function call without arguments is faster `expr-option-function` ---- --- It is not allowed to change text or jump to another window while ---- evaluating 'findexpr' `textlock`. +--- executing the 'findfunc' `textlock`. --- --- This option cannot be set from a `modeline` or in the `sandbox`, for --- security reasons. @@ -2327,28 +2327,28 @@ vim.go.fcs = vim.go.fillchars --- --- ```vim --- " Use glob() ---- func FindExprGlob() ---- let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname +--- func FindFuncGlob(cmdarg, cmdcomplete) +--- let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg --- return glob(pat, v:false, v:true) --- endfunc ---- set findexpr=FindExprGlob() +--- set findfunc=FindFuncGlob --- --- " Use the 'git ls-files' output ---- func FindGitFiles() +--- func FindGitFiles(cmdarg, cmdcomplete) --- let fnames = systemlist('git ls-files') ---- return fnames->filter('v:val =~? v:fname') +--- return fnames->filter('v:val =~? a:cmdarg') --- endfunc ---- set findexpr=FindGitFiles() +--- set findfunc=FindGitFiles --- ``` --- --- --- @type string -vim.o.findexpr = "" -vim.o.fexpr = vim.o.findexpr -vim.bo.findexpr = vim.o.findexpr -vim.bo.fexpr = vim.bo.findexpr -vim.go.findexpr = vim.o.findexpr -vim.go.fexpr = vim.go.findexpr +vim.o.findfunc = "" +vim.o.ffu = vim.o.findfunc +vim.bo.findfunc = vim.o.findfunc +vim.bo.ffu = vim.bo.findfunc +vim.go.findfunc = vim.o.findfunc +vim.go.ffu = vim.go.findfunc --- When writing a file and this option is on, <EOL> at the end of file --- will be restored if missing. Turn this option off if you want to diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index b104356334..e00402ab3f 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -44,11 +44,6 @@ vim.v.cmdarg = ... --- @type integer vim.v.cmdbang = ... ---- When evaluating 'findexpr': if 'findexpr' is used for cmdline ---- completion the value is `v:true`, otherwise it is `v:false`. ---- @type boolean -vim.v.cmdcomplete = ... - --- The current locale setting for collation order of the runtime --- environment. This allows Vim scripts to be aware of the --- current locale encoding. Technical: it's the value of @@ -272,8 +267,7 @@ vim.v.fcs_choice = ... vim.v.fcs_reason = ... --- When evaluating 'includeexpr': the file name that was ---- detected. When evaluating 'findexpr': the argument passed to ---- the `:find` command. Empty otherwise. +--- detected. Empty otherwise. --- @type string vim.v.fname = ... diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 42cc745fe6..fe22742a84 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2049,7 +2049,6 @@ void free_buf_options(buf_T *buf, bool free_p_ff) clear_string_option(&buf->b_p_indk); clear_string_option(&buf->b_p_fp); clear_string_option(&buf->b_p_fex); - clear_string_option(&buf->b_p_fexpr); clear_string_option(&buf->b_p_kp); clear_string_option(&buf->b_p_mps); clear_string_option(&buf->b_p_fo); @@ -2098,6 +2097,8 @@ void free_buf_options(buf_T *buf, bool free_p_ff) clear_string_option(&buf->b_p_tc); clear_string_option(&buf->b_p_tfu); callback_free(&buf->b_tfu_cb); + clear_string_option(&buf->b_p_ffu); + callback_free(&buf->b_ffu_cb); clear_string_option(&buf->b_p_dict); clear_string_option(&buf->b_p_tsr); clear_string_option(&buf->b_p_qe); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 88e8d59faa..e6bd63f4f8 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -543,8 +543,10 @@ struct file_buffer { Callback b_cfu_cb; ///< 'completefunc' callback char *b_p_ofu; ///< 'omnifunc' Callback b_ofu_cb; ///< 'omnifunc' callback - char *b_p_tfu; ///< 'tagfunc' + char *b_p_tfu; ///< 'tagfunc' option value Callback b_tfu_cb; ///< 'tagfunc' callback + char *b_p_ffu; ///< 'findfunc' option value + Callback b_ffu_cb; ///< 'findfunc' callback int b_p_eof; ///< 'endoffile' int b_p_eol; ///< 'endofline' int b_p_fixeol; ///< 'fixendofline' @@ -608,7 +610,6 @@ struct file_buffer { char *b_p_mp; ///< 'makeprg' local value char *b_p_efm; ///< 'errorformat' local value char *b_p_ep; ///< 'equalprg' local value - char *b_p_fexpr; ///< 'findexpr' local value char *b_p_path; ///< 'path' local value int b_p_ar; ///< 'autoread' local value char *b_p_tags; ///< 'tags' local value diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index aeaed536fc..8d1f87cbcf 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -109,7 +109,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp) && xp->xp_context != EXPAND_FILES && xp->xp_context != EXPAND_FILES_IN_PATH && xp->xp_context != EXPAND_FILETYPE - && xp->xp_context != EXPAND_FINDEXPR + && xp->xp_context != EXPAND_FINDFUNC && xp->xp_context != EXPAND_HELP && xp->xp_context != EXPAND_KEYMAP && xp->xp_context != EXPAND_LUA @@ -1229,7 +1229,7 @@ char *addstar(char *fname, size_t len, int context) // For help tags the translation is done in find_help_tags(). // For a tag pattern starting with "/" no translation is needed. - if (context == EXPAND_FINDEXPR + if (context == EXPAND_FINDFUNC || context == EXPAND_HELP || context == EXPAND_COLORS || context == EXPAND_COMPILER @@ -1829,7 +1829,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa case CMD_sfind: case CMD_tabfind: if (xp->xp_context == EXPAND_FILES) { - xp->xp_context = *get_findexpr() != NUL ? EXPAND_FINDEXPR : EXPAND_FILES_IN_PATH; + xp->xp_context = *get_findfunc() != NUL ? EXPAND_FINDFUNC : EXPAND_FILES_IN_PATH; } break; case CMD_cd: @@ -2500,8 +2500,8 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int * } int ret = FAIL; - if (xp->xp_context == EXPAND_FINDEXPR) { - ret = expand_findexpr(pat, matches, numMatches); + if (xp->xp_context == EXPAND_FINDFUNC) { + ret = expand_findfunc(pat, matches, numMatches); } else { if (xp->xp_context == EXPAND_FILES) { flags |= EW_FILE; @@ -2722,7 +2722,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM if (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_DIRECTORIES || xp->xp_context == EXPAND_FILES_IN_PATH - || xp->xp_context == EXPAND_FINDEXPR + || xp->xp_context == EXPAND_FINDFUNC || xp->xp_context == EXPAND_DIRS_IN_CDPATH) { return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options); } diff --git a/src/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h index ce51f30bec..dfda9fdaed 100644 --- a/src/nvim/cmdexpand_defs.h +++ b/src/nvim/cmdexpand_defs.h @@ -107,7 +107,7 @@ enum { EXPAND_KEYMAP, EXPAND_DIRS_IN_CDPATH, EXPAND_SHELLCMDLINE, - EXPAND_FINDEXPR, + EXPAND_FINDFUNC, EXPAND_CHECKHEALTH, EXPAND_LUA, }; diff --git a/src/nvim/errors.h b/src/nvim/errors.h index 6682a42d61..7897a71489 100644 --- a/src/nvim/errors.h +++ b/src/nvim/errors.h @@ -186,7 +186,7 @@ INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch") EXTERN const char e_winfixbuf_cannot_go_to_buffer[] INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled")); -EXTERN const char e_invalid_return_type_from_findexpr[] INIT( = N_("E1514: 'findexpr' did not return a List type")); +EXTERN const char e_invalid_return_type_from_findfunc[] INIT( = N_("E1514: 'findfunc' did not return a List type")); EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s")); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bf85ed1646..58c98c42ff 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -270,7 +270,6 @@ static struct vimvar { VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO), - VV(VV_CMDCOMPLETE, "cmdcomplete", VAR_BOOL, VV_RO), // Neovim VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), @@ -462,7 +461,6 @@ void eval_init(void) set_vim_var_nr(VV_HLSEARCH, 1); set_vim_var_nr(VV_COUNT1, 1); set_vim_var_special(VV_EXITING, kSpecialVarNull); - set_vim_var_bool(VV_CMDCOMPLETE, kBoolVarFalse); set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER); set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING); @@ -4793,6 +4791,7 @@ bool garbage_collect(bool testing) ABORTING(set_ref_in_callback)(&buf->b_ofu_cb, copyID, NULL, NULL); ABORTING(set_ref_in_callback)(&buf->b_tsrfu_cb, copyID, NULL, NULL); ABORTING(set_ref_in_callback)(&buf->b_tfu_cb, copyID, NULL, NULL); + ABORTING(set_ref_in_callback)(&buf->b_ffu_cb, copyID, NULL, NULL); } // 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks @@ -4804,6 +4803,9 @@ bool garbage_collect(bool testing) // 'tagfunc' callback ABORTING(set_ref_in_tagfunc)(copyID); + // 'findfunc' callback + ABORTING(set_ref_in_findfunc)(copyID); + FOR_ALL_TAB_WINDOWS(tp, wp) { // window-local variables ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL); diff --git a/src/nvim/eval.h b/src/nvim/eval.h index b5605bb644..bb9b00abc7 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -167,7 +167,6 @@ typedef enum { VV_COLLATE, VV_EXITING, VV_MAXCOL, - VV_CMDCOMPLETE, // Nvim VV_STDERR, VV_MSGPACK_TYPES, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 74ba19b30a..1c215cd3e1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5165,55 +5165,68 @@ static void ex_wrongmodifier(exarg_T *eap) eap->errmsg = _(e_invcmd); } -/// Evaluate the 'findexpr' expression and return the result. When evaluating -/// the expression, v:fname is set to the ":find" command argument. -static list_T *eval_findexpr(const char *pat, bool cmdcomplete) +/// callback function for 'findfunc' +static Callback ffu_cb; + +static Callback *get_findfunc_callback(void) +{ + return *curbuf->b_p_ffu != NUL ? &curbuf->b_ffu_cb : &ffu_cb; +} + +/// Call 'findfunc' to obtain the list of file names. +static list_T *call_findfunc(char *pat, BoolVarValue cmdcomplete) { const sctx_T saved_sctx = current_sctx; - char *findexpr = get_findexpr(); + typval_T args[3]; + args[0].v_type = VAR_STRING; + args[0].vval.v_string = pat; + args[1].v_type = VAR_BOOL; + args[1].vval.v_bool = cmdcomplete; + args[2].v_type = VAR_UNKNOWN; - set_vim_var_string(VV_FNAME, pat, -1); - set_vim_var_bool(VV_CMDCOMPLETE, cmdcomplete ? kBoolVarTrue : kBoolVarFalse); - current_sctx = curbuf->b_p_script_ctx[BV_FEXPR].script_ctx; + // Lock the text to prevent weird things from happening. Also disallow + // switching to another window, it should not be needed and may end up in + // Insert mode in another buffer. + textlock++; - char *arg = skipwhite(findexpr); + sctx_T *ctx = get_option_sctx(kOptFindfunc); + if (ctx != NULL) { + current_sctx = *ctx; + } - textlock++; + Callback *cb = get_findfunc_callback(); + typval_T rettv; + int retval = callback_call(cb, 2, args, &rettv); + + current_sctx = saved_sctx; + + textlock--; - // Evaluate the expression. If the expression is "FuncName()" call the - // function directly. - typval_T tv; list_T *retlist = NULL; - if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { - retlist = NULL; - } else { - if (tv.v_type == VAR_LIST) { - retlist = tv_list_copy(NULL, tv.vval.v_list, true, get_copyID()); + + if (retval == OK) { + if (rettv.v_type == VAR_LIST) { + retlist = tv_list_copy(NULL, rettv.vval.v_list, false, get_copyID()); } else { - emsg(_(e_invalid_return_type_from_findexpr)); + emsg(_(e_invalid_return_type_from_findfunc)); } - tv_clear(&tv); - } - textlock--; - clear_evalarg(&EVALARG_EVALUATE, NULL); - set_vim_var_string(VV_FNAME, NULL, 0); - set_vim_var_bool(VV_CMDCOMPLETE, kBoolVarFalse); - current_sctx = saved_sctx; + tv_clear(&rettv); + } return retlist; } -/// Find file names matching "pat" using 'findexpr' and return it in "files". +/// 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_findexpr(const char *pat, char ***files, int *numMatches) +int expand_findfunc(char *pat, char ***files, int *numMatches) { *numMatches = 0; *files = NULL; - list_T *l = eval_findexpr(pat, true); + list_T *l = call_findfunc(pat, kBoolVarTrue); if (l == NULL) { return FAIL; } @@ -5240,16 +5253,16 @@ int expand_findexpr(const char *pat, char ***files, int *numMatches) return OK; } -/// Use 'findexpr' to find file 'findarg'. The 'count' argument is used to find +/// Use 'findfunc' to find file 'findarg'. The 'count' argument is used to find /// the n'th matching file. -static char *findexpr_find_file(char *findarg, size_t findarg_len, int count) +static char *findfunc_find_file(char *findarg, size_t findarg_len, int count) { char *ret_fname = NULL; const char cc = findarg[findarg_len]; findarg[findarg_len] = NUL; - list_T *fname_list = eval_findexpr(findarg, false); + list_T *fname_list = call_findfunc(findarg, kBoolVarFalse); int fname_count = tv_list_len(fname_list); if (fname_count == 0) { @@ -5274,6 +5287,51 @@ static char *findexpr_find_file(char *findarg, size_t findarg_len, int count) return ret_fname; } +/// Process the 'findfunc' option value. +/// Returns NULL on success and an error message on failure. +const char *did_set_findfunc(optset_T *args) +{ + buf_T *buf = (buf_T *)args->os_buf; + int retval; + + if (*buf->b_p_ffu != NUL) { + // buffer-local option set + retval = option_set_callback_func(buf->b_p_ffu, &buf->b_ffu_cb); + } else { + // global option set + retval = option_set_callback_func(p_ffu, &ffu_cb); + } + + if (retval == FAIL) { + return e_invarg; + } + + // If the option value starts with <SID> or s:, then replace that with + // the script identifier. + char **varp = (char **)args->os_varp; + char *name = get_scriptlocal_funcname(*varp); + if (name != NULL) { + free_string_option(*varp); + *varp = name; + } + + return NULL; +} + +void free_findfunc_option(void) +{ + callback_free(&ffu_cb); +} + +/// Mark the global 'findfunc' callback with "copyID" so that it is not +/// garbage collected. +bool set_ref_in_findfunc(int copyID) +{ + bool abort = false; + abort = set_ref_in_callback(&ffu_cb, copyID, NULL, NULL); + return abort; +} + /// :sview [+command] file split window with new file, read-only /// :split [[+command] file] split window with current or new file /// :vsplit [[+command] file] split window vertically with current or new file @@ -5305,8 +5363,8 @@ void ex_splitview(exarg_T *eap) } if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) { - if (*get_findexpr() != NUL) { - fname = findexpr_find_file(eap->arg, strlen(eap->arg), + if (*get_findfunc() != NUL) { + fname = findfunc_find_file(eap->arg, strlen(eap->arg), eap->addr_count > 0 ? eap->line2 : 1); } else { char *file_to_find = NULL; @@ -5512,8 +5570,8 @@ static void ex_find(exarg_T *eap) } char *fname = NULL; - if (*get_findexpr() != NUL) { - fname = findexpr_find_file(eap->arg, strlen(eap->arg), + if (*get_findfunc() != NUL) { + fname = findfunc_find_file(eap->arg, strlen(eap->arg), eap->addr_count > 0 ? eap->line2 : 1); } else { char *file_to_find = NULL; diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index 0cb5fa8e95..8397a434e4 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -260,6 +260,7 @@ local function dump_option(i, o) end w([[ +#include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/insexpand.h" #include "nvim/mapping.h" diff --git a/src/nvim/option.c b/src/nvim/option.c index a7e56d6d39..5d2e1ce4c6 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -578,6 +578,7 @@ void free_all_options(void) } free_operatorfunc_option(); free_tagfunc_option(); + free_findfunc_option(); XFREE_CLEAR(fenc_default); XFREE_CLEAR(p_term); XFREE_CLEAR(p_ttytype); @@ -4472,8 +4473,8 @@ void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win) switch ((int)p->indir) { case PV_FP: return &(buf->b_p_fp); - case PV_FEXPR: - return &(buf->b_p_fexpr); + case PV_FFU: + return &(buf->b_p_ffu); case PV_EFM: return &(buf->b_p_efm); case PV_GP: @@ -4595,8 +4596,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var; case PV_FP: return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var; - case PV_FEXPR: - return *buf->b_p_fexpr != NUL ? &(buf->b_p_fexpr) : p->var; + case PV_FFU: + return *buf->b_p_ffu != NUL ? &(buf->b_p_ffu) : p->var; case PV_EFM: return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var; case PV_GP: @@ -4868,13 +4869,13 @@ char *get_equalprg(void) return curbuf->b_p_ep; } -/// Get the value of 'findexpr', either the buffer-local one or the global one. -char *get_findexpr(void) +/// Get the value of 'findfunc', either the buffer-local one or the global one. +char *get_findfunc(void) { - if (*curbuf->b_p_fexpr == NUL) { - return p_fexpr; + if (*curbuf->b_p_ffu == NUL) { + return p_ffu; } - return curbuf->b_p_fexpr; + return curbuf->b_p_ffu; } /// Copy options from one window to another. @@ -5275,8 +5276,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_mp = empty_string_option; buf->b_p_efm = empty_string_option; buf->b_p_ep = empty_string_option; - buf->b_p_fexpr = xstrdup(p_fexpr); - COPY_OPT_SCTX(buf, BV_FEXPR); + buf->b_p_ffu = empty_string_option; buf->b_p_kp = empty_string_option; buf->b_p_path = empty_string_option; buf->b_p_tags = empty_string_option; diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index a88b51dae7..d59a4549d1 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -451,7 +451,7 @@ EXTERN char *p_ffs; ///< 'fileformats' EXTERN int p_fic; ///< 'fileignorecase' EXTERN char *p_ft; ///< 'filetype' EXTERN char *p_fcs; ///< 'fillchar' -EXTERN char *p_fexpr; ///< 'findexpr' +EXTERN char *p_ffu; ///< 'findfunc' EXTERN int p_fixeol; ///< 'fixendofline' EXTERN char *p_fcl; ///< 'foldclose' EXTERN OptInt p_fdls; ///< 'foldlevelstart' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 1b8e3ea256..3c21436c3a 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2906,35 +2906,35 @@ return { varname = 'p_fcs', }, { - abbreviation = 'fexpr', - cb = 'did_set_optexpr', + abbreviation = 'ffu', + cb = 'did_set_findfunc', defaults = { if_true = '' }, desc = [=[ - Expression that is evaluated to obtain the filename(s) for the |:find| + Function that is called to obtain the filename(s) for the |:find| command. When this option is empty, the internal |file-searching| mechanism is used. - While evaluating the expression, the |v:fname| variable is set to the - argument of the |:find| command. + The value can be the name of a function, a |lambda| or a |Funcref|. + See |option-value-function| for more information. - The expression is evaluated only once per |:find| command invocation. - The expression can process all the directories specified in 'path'. + The function is called with two arguments. The first argument is a + |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 expression may be evaluated for command-line completion as well, - in which case the |v:cmdcomplete| variable will be set to |v:true|, - otherwise it will be set to |v:false|. + The function is called only once per |:find| command invocation. + The function can process all the directories specified in 'path'. - If a match is found, the expression should return a |List| containing - one or more file names. If a match is not found, the expression + If a match is found, the function should return a |List| containing + one or more file names. If a match is not found, the function should return an empty List. - If any errors are encountered during the expression evaluation, an + If any errors are encountered during the function invocation, an empty List is used as the return value. - Using a function call without arguments is faster |expr-option-function| - It is not allowed to change text or jump to another window while - evaluating 'findexpr' |textlock|. + executing the 'findfunc' |textlock|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. @@ -2942,27 +2942,28 @@ return { Examples: >vim " Use glob() - func FindExprGlob() - let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname + func FindFuncGlob(cmdarg, cmdcomplete) + let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg return glob(pat, v:false, v:true) endfunc - set findexpr=FindExprGlob() + set findfunc=FindFuncGlob " Use the 'git ls-files' output - func FindGitFiles() + func FindGitFiles(cmdarg, cmdcomplete) let fnames = systemlist('git ls-files') - return fnames->filter('v:val =~? v:fname') + return fnames->filter('v:val =~? a:cmdarg') endfunc - set findexpr=FindGitFiles() + set findfunc=FindGitFiles < ]=], - full_name = 'findexpr', + full_name = 'findfunc', + func = true, scope = { 'global', 'buffer' }, secure = true, - short_desc = N_('expression used for :find'), + short_desc = N_('function called for :find'), tags = { 'E1514' }, type = 'string', - varname = 'p_fexpr', + varname = 'p_ffu', }, { abbreviation = 'fixeol', diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index c66849800c..307c4ae79f 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -233,9 +233,9 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_mp); check_string_option(&buf->b_p_efm); check_string_option(&buf->b_p_ep); - check_string_option(&buf->b_p_fexpr); check_string_option(&buf->b_p_path); check_string_option(&buf->b_p_tags); + check_string_option(&buf->b_p_ffu); check_string_option(&buf->b_p_tfu); check_string_option(&buf->b_p_tc); check_string_option(&buf->b_p_dict); @@ -1886,9 +1886,8 @@ int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches) matches); } -/// One of the '*expr' options is changed:, 'diffexpr', 'findexpr', -/// 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', 'indentexpr', -/// 'patchexpr' and 'charconvert'. +/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext', +/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'. const char *did_set_optexpr(optset_T *args) { char **varp = (char **)args->os_varp; diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua index 6c6edd4ee2..ad139bbbfe 100644 --- a/src/nvim/vvars.lua +++ b/src/nvim/vvars.lua @@ -50,13 +50,6 @@ M.vars = { can be used. ]=], }, - cmdcomplete = { - type = 'boolean', - desc = [=[ - When evaluating 'findexpr': if 'findexpr' is used for cmdline - completion the value is |v:true|, otherwise it is |v:false|. - ]=], - }, collate = { type = 'string', desc = [=[ @@ -291,8 +284,7 @@ M.vars = { type = 'string', desc = [=[ When evaluating 'includeexpr': the file name that was - detected. When evaluating 'findexpr': the argument passed to - the |:find| command. Empty otherwise. + detected. Empty otherwise. ]=], }, fname_diff = { diff --git a/test/old/testdir/test_findfile.vim b/test/old/testdir/test_findfile.vim index d3fdcad045..2c049537f0 100644 --- a/test/old/testdir/test_findfile.vim +++ b/test/old/testdir/test_findfile.vim @@ -1,6 +1,7 @@ " Test findfile() and finddir() source check.vim +source vim9.vim let s:files = [ 'Xfinddir1/foo', \ 'Xfinddir1/bar', @@ -288,223 +289,491 @@ func Test_find_non_existing_path() let &path = save_path endfunc -" Test for 'findexpr' -func Test_findexpr() +" Test for 'findfunc' +func Test_findfunc() CheckUnix - call assert_equal('', &findexpr) - call writefile(['aFile'], 'Xfindexpr1.c', 'D') - call writefile(['bFile'], 'Xfindexpr2.c', 'D') - call writefile(['cFile'], 'Xfindexpr3.c', 'D') + call assert_equal('', &findfunc) + call writefile(['aFile'], 'Xfindfunc1.c', 'D') + call writefile(['bFile'], 'Xfindfunc2.c', 'D') + call writefile(['cFile'], 'Xfindfunc3.c', 'D') " basic tests - func FindExpr1() - let fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c'] - return fnames->copy()->filter('v:val =~? v:fname') + func FindFuncBasic(pat, cmdcomplete) + let fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c'] + return fnames->copy()->filter('v:val =~? a:pat') endfunc - set findexpr=FindExpr1() - find Xfindexpr3 - call assert_match('Xfindexpr3.c', @%) + set findfunc=FindFuncBasic + find Xfindfunc3 + call assert_match('Xfindfunc3.c', @%) bw! 2find Xfind - call assert_match('Xfindexpr2.c', @%) + call assert_match('Xfindfunc2.c', @%) bw! call assert_fails('4find Xfind', 'E347: No more file "Xfind" found in path') call assert_fails('find foobar', 'E345: Can''t find file "foobar" in path') - sfind Xfindexpr2.c - call assert_match('Xfindexpr2.c', @%) + sfind Xfindfunc2.c + call assert_match('Xfindfunc2.c', @%) call assert_equal(2, winnr('$')) %bw! call assert_fails('sfind foobar', 'E345: Can''t find file "foobar" in path') - tabfind Xfindexpr3.c - call assert_match('Xfindexpr3.c', @%) + tabfind Xfindfunc3.c + call assert_match('Xfindfunc3.c', @%) call assert_equal(2, tabpagenr()) %bw! call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path') + " Test garbage collection + call test_garbagecollect_now() + find Xfindfunc2 + call assert_match('Xfindfunc2.c', @%) + bw! + delfunc FindFuncBasic + call test_garbagecollect_now() + call assert_fails('find Xfindfunc2', 'E117: Unknown function: FindFuncBasic') + " Buffer-local option - set findexpr=['abc'] + func GlobalFindFunc(pat, cmdcomplete) + return ['global'] + endfunc + func LocalFindFunc(pat, cmdcomplete) + return ['local'] + endfunc + set findfunc=GlobalFindFunc new - setlocal findexpr=['def'] + setlocal findfunc=LocalFindFunc find xxxx - call assert_equal('def', @%) + call assert_equal('local', @%) wincmd w find xxxx - call assert_equal('abc', @%) + call assert_equal('global', @%) aboveleft new - call assert_equal("['abc']", &findexpr) + call assert_equal("GlobalFindFunc", &findfunc) wincmd k aboveleft new - call assert_equal("['abc']", &findexpr) + call assert_equal("GlobalFindFunc", &findfunc) %bw! + delfunc GlobalFindFunc + delfunc LocalFindFunc - " Empty list - set findexpr=[] - call assert_fails('find xxxx', 'E345: Can''t find file "xxxx" in path') + " Assign an expression + set findfunc=[] + call assert_fails('find xxxx', 'E117: Unknown function: []') " Error cases - " Syntax error in the expression - set findexpr=FindExpr1{} - call assert_fails('find Xfindexpr1.c', 'E15: Invalid expression') + " Function that doesn't any argument + func FindFuncNoArg() + endfunc + set findfunc=FindFuncNoArg + call assert_fails('find Xfindfunc1.c', 'E118: Too many arguments for function: FindFuncNoArg') + delfunc FindFuncNoArg - " Find expression throws an error - func FindExpr2() + " Syntax error in the function + func FindFuncSyntaxError(pat, cmdcomplete) + return l + endfunc + set findfunc=FindFuncSyntaxError + call assert_fails('find Xfindfunc1.c', 'E121: Undefined variable: l') + delfunc FindFuncSyntaxError + + " Find function throws an error + func FindFuncWithThrow(pat, cmdcomplete) throw 'find error' endfunc - set findexpr=FindExpr2() - call assert_fails('find Xfindexpr1.c', 'find error') + set findfunc=FindFuncWithThrow + call assert_fails('find Xfindfunc1.c', 'find error') + delfunc FindFuncWithThrow - " Try using a null List as the expression - set findexpr=v:_null_list - call assert_fails('find Xfindexpr1.c', 'E345: Can''t find file "Xfindexpr1.c" in path') + " Try using a null function + "call assert_fails('let &findfunc = test_null_function()', 'E129: Function name required') - " Try to create a new window from the find expression - func FindExpr3() + " Try to create a new window from the find function + func FindFuncNewWindow(pat, cmdexpand) new return ["foo"] endfunc - set findexpr=FindExpr3() - call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window') + set findfunc=FindFuncNewWindow + call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window') + delfunc FindFuncNewWindow - " Try to modify the current buffer from the find expression - func FindExpr4() + " Try to modify the current buffer from the find function + func FindFuncModifyBuf(pat, cmdexpand) call setline(1, ['abc']) return ["foo"] endfunc - set findexpr=FindExpr4() - call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window') - - " Expression returning a string - set findexpr='abc' - call assert_fails('find Xfindexpr1.c', "E1514: 'findexpr' did not return a List type") - - set findexpr& - delfunc! FindExpr1 - delfunc! FindExpr2 - delfunc! FindExpr3 - delfunc! FindExpr4 + set findfunc=FindFuncModifyBuf + call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window') + delfunc FindFuncModifyBuf + + " Return the wrong type from the function + func FindFuncWrongRet(pat, cmdexpand) + return 'foo' + endfunc + set findfunc=FindFuncWrongRet + call assert_fails('find Xfindfunc1.c', "E1514: 'findfunc' did not return a List type") + delfunc FindFuncWrongRet + + set findfunc& endfunc -" Test for using a script-local function for 'findexpr' -func Test_findexpr_scriptlocal_func() - func! s:FindExprScript() - let g:FindExprArg = v:fname +" Test for using a script-local function for 'findfunc' +func Test_findfunc_scriptlocal_func() + func! s:FindFuncScript(pat, cmdexpand) + let g:FindFuncArg = a:pat return ['xxx'] endfunc - set findexpr=s:FindExprScript() - call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + set findfunc=s:FindFuncScript + call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc) + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - set findexpr=<SID>FindExprScript() - call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + set findfunc=<SID>FindFuncScript + call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc) + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - let &findexpr = 's:FindExprScript()' - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + let &findfunc = 's:FindFuncScript' + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - let &findexpr = '<SID>FindExprScript()' - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + let &findfunc = '<SID>FindFuncScript' + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - set findexpr= - setglobal findexpr=s:FindExprScript() - setlocal findexpr= - call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) - call assert_equal('', &l:findexpr) + set findfunc= + setglobal findfunc=s:FindFuncScript + setlocal findfunc= + call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc) + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) + call assert_equal('', &l:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! new | only - set findexpr= - setglobal findexpr= - setlocal findexpr=s:FindExprScript() - call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) - call assert_equal(expand('<SID>') .. 'FindExprScript()', &l:findexpr) - call assert_equal('', &g:findexpr) - let g:FindExprArg = '' + set findfunc= + setglobal findfunc= + setlocal findfunc=s:FindFuncScript + call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc) + call assert_equal(expand('<SID>') .. 'FindFuncScript', &l:findfunc) + call assert_equal('', &g:findfunc) + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - set findexpr= - delfunc s:FindExprScript + set findfunc= + delfunc s:FindFuncScript endfunc -" Test for expanding the argument to the :find command using 'findexpr' -func Test_findexpr_expand_arg() - let s:fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c'] +" Test for expanding the argument to the :find command using 'findfunc' +func Test_findfunc_expand_arg() + let s:fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c'] - " 'findexpr' that accepts a regular expression - func FindExprRegexp() - return s:fnames->copy()->filter('v:val =~? v:fname') + " 'findfunc' that accepts a regular expression + func FindFuncRegexp(pat, cmdcomplete) + return s:fnames->copy()->filter('v:val =~? a:pat') endfunc - " 'findexpr' that accepts a glob - func FindExprGlob() - let pat = glob2regpat(v:cmdcomplete ? $'*{v:fname}*' : v:fname) + " 'findfunc' that accepts a glob + func FindFuncGlob(pat_arg, cmdcomplete) + let pat = glob2regpat(a:cmdcomplete ? $'*{a:pat_arg}*' : a:pat_arg) return s:fnames->copy()->filter('v:val =~? pat') endfunc for regexp in [v:true, v:false] - let &findexpr = regexp ? 'FindExprRegexp()' : 'FindExprGlob()' + let &findfunc = regexp ? 'FindFuncRegexp' : 'FindFuncGlob' call feedkeys(":find \<Tab>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr1.c', @:) + call assert_equal('"find Xfindfunc1.c', @:) call feedkeys(":find Xfind\<Tab>\<Tab>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr2.c', @:) + call assert_equal('"find Xfindfunc2.c', @:) call assert_equal(s:fnames, getcompletion('find ', 'cmdline')) call assert_equal(s:fnames, getcompletion('find Xfind', 'cmdline')) let pat = regexp ? 'X.*1\.c' : 'X*1.c' call feedkeys($":find {pat}\<Tab>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr1.c', @:) - call assert_equal(['Xfindexpr1.c'], getcompletion($'find {pat}', 'cmdline')) + call assert_equal('"find Xfindfunc1.c', @:) + call assert_equal(['Xfindfunc1.c'], getcompletion($'find {pat}', 'cmdline')) call feedkeys(":find 3\<Tab>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr3.c', @:) - call assert_equal(['Xfindexpr3.c'], getcompletion($'find 3', 'cmdline')) + call assert_equal('"find Xfindfunc3.c', @:) + call assert_equal(['Xfindfunc3.c'], getcompletion($'find 3', 'cmdline')) call feedkeys(":find Xfind\<C-A>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr1.c Xfindexpr2.c Xfindexpr3.c', @:) + call assert_equal('"find Xfindfunc1.c Xfindfunc2.c Xfindfunc3.c', @:) call feedkeys(":find abc\<Tab>\<C-B>\"\<CR>", "xt") call assert_equal('"find abc', @:) call assert_equal([], getcompletion('find abc', 'cmdline')) endfor - set findexpr& - delfunc! FindExprRegexp - delfunc! FindExprGlob + set findfunc& + delfunc! FindFuncRegexp + delfunc! FindFuncGlob unlet s:fnames endfunc +" Test for different ways of setting the 'findfunc' option +func Test_findfunc_callback() + new + func FindFunc1(pat, cmdexpand) + let g:FindFunc1Args = [a:pat, a:cmdexpand] + return ['findfunc1'] + endfunc + + let lines =<< trim END + #" Test for using a function name + LET &findfunc = 'g:FindFunc1' + LET g:FindFunc1Args = [] + find abc1 + call assert_equal(['abc1', v:false], g:FindFunc1Args) + + #" Test for using a function() + set findfunc=function('g:FindFunc1') + LET g:FindFunc1Args = [] + find abc2 + call assert_equal(['abc2', v:false], g:FindFunc1Args) + + #" Using a funcref variable to set 'findfunc' + VAR Fn = function('g:FindFunc1') + LET &findfunc = Fn + LET g:FindFunc1Args = [] + find abc3 + call assert_equal(['abc3', v:false], g:FindFunc1Args) + + #" Using a string(funcref_variable) to set 'findfunc' + LET Fn = function('g:FindFunc1') + LET &findfunc = string(Fn) + LET g:FindFunc1Args = [] + find abc4 + call assert_equal(['abc4', v:false], g:FindFunc1Args) + + #" Test for using a funcref() + set findfunc=funcref('g:FindFunc1') + LET g:FindFunc1Args = [] + find abc5 + call assert_equal(['abc5', v:false], g:FindFunc1Args) + + #" Using a funcref variable to set 'findfunc' + LET Fn = funcref('g:FindFunc1') + LET &findfunc = Fn + LET g:FindFunc1Args = [] + find abc6 + call assert_equal(['abc6', v:false], g:FindFunc1Args) + + #" Using a string(funcref_variable) to set 'findfunc' + LET Fn = funcref('g:FindFunc1') + LET &findfunc = string(Fn) + LET g:FindFunc1Args = [] + find abc7 + call assert_equal(['abc7', v:false], g:FindFunc1Args) + + #" Test for using a lambda function using set + VAR optval = "LSTART pat, cmdexpand LMIDDLE FindFunc1(pat, cmdexpand) LEND" + LET optval = substitute(optval, ' ', '\\ ', 'g') + exe "set findfunc=" .. optval + LET g:FindFunc1Args = [] + find abc8 + call assert_equal(['abc8', v:false], g:FindFunc1Args) + + #" Test for using a lambda function using LET + LET &findfunc = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND + LET g:FindFunc1Args = [] + find abc9 + call assert_equal(['abc9', v:false], g:FindFunc1Args) + + #" Set 'findfunc' to a string(lambda expression) + LET &findfunc = 'LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND' + LET g:FindFunc1Args = [] + find abc10 + call assert_equal(['abc10', v:false], g:FindFunc1Args) + + #" Set 'findfunc' to a variable with a lambda expression + VAR Lambda = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND + LET &findfunc = Lambda + LET g:FindFunc1Args = [] + find abc11 + call assert_equal(['abc11', v:false], g:FindFunc1Args) + + #" Set 'findfunc' to a string(variable with a lambda expression) + LET Lambda = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND + LET &findfunc = string(Lambda) + LET g:FindFunc1Args = [] + find abc12 + call assert_equal(['abc12', v:false], g:FindFunc1Args) + + #" Try to use 'findfunc' after the function is deleted + func g:TmpFindFunc(pat, cmdexpand) + let g:TmpFindFunc1Args = [a:pat, a:cmdexpand] + endfunc + LET &findfunc = function('g:TmpFindFunc') + delfunc g:TmpFindFunc + call test_garbagecollect_now() + LET g:TmpFindFunc1Args = [] + call assert_fails('find abc13', 'E117:') + call assert_equal([], g:TmpFindFunc1Args) + + #" Try to use a function with three arguments for 'findfunc' + func g:TmpFindFunc2(x, y, z) + let g:TmpFindFunc2Args = [a:x, a:y, a:z] + endfunc + set findfunc=TmpFindFunc2 + LET g:TmpFindFunc2Args = [] + call assert_fails('find abc14', 'E119:') + call assert_equal([], g:TmpFindFunc2Args) + delfunc TmpFindFunc2 + + #" Try to use a function with zero arguments for 'findfunc' + func g:TmpFindFunc3() + let g:TmpFindFunc3Called = v:true + endfunc + set findfunc=TmpFindFunc3 + LET g:TmpFindFunc3Called = v:false + call assert_fails('find abc15', 'E118:') + call assert_equal(v:false, g:TmpFindFunc3Called) + delfunc TmpFindFunc3 + + #" Try to use a lambda function with three arguments for 'findfunc' + LET &findfunc = LSTART a, b, c LMIDDLE FindFunc1(a, v:false) LEND + LET g:FindFunc1Args = [] + call assert_fails('find abc16', 'E119:') + call assert_equal([], g:FindFunc1Args) + + #" Test for clearing the 'findfunc' option + set findfunc='' + set findfunc& + call assert_fails("set findfunc=function('abc')", "E700:") + call assert_fails("set findfunc=funcref('abc')", "E700:") + + #" set 'findfunc' to a non-existing function + LET &findfunc = function('g:FindFunc1') + call assert_fails("set findfunc=function('NonExistingFunc')", 'E700:') + call assert_fails("LET &findfunc = function('NonExistingFunc')", 'E700:') + LET g:FindFunc1Args = [] + find abc17 + call assert_equal(['abc17', v:false], g:FindFunc1Args) + END + call CheckTransLegacySuccess(lines) + + " Test for using a script-local function name + func s:FindFunc2(pat, cmdexpand) + let g:FindFunc2Args = [a:pat, a:cmdexpand] + return ['findfunc2'] + endfunc + set findfunc=s:FindFunc2 + let g:FindFunc2Args = [] + find abc18 + call assert_equal(['abc18', v:false], g:FindFunc2Args) + + let &findfunc = 's:FindFunc2' + let g:FindFunc2Args = [] + find abc19 + call assert_equal(['abc19', v:false], g:FindFunc2Args) + delfunc s:FindFunc2 + + " Using Vim9 lambda expression in legacy context should fail + set findfunc=(pat,\ cmdexpand)\ =>\ FindFunc1(pat,\ v:false) + let g:FindFunc1Args = [] + call assert_fails('find abc20', 'E117:') + call assert_equal([], g:FindFunc1Args) + + " set 'findfunc' to a partial with dict. + func SetFindFunc() + let operator = {'execute': function('FindFuncExecute')} + let &findfunc = operator.execute + endfunc + func FindFuncExecute(pat, cmdexpand) dict + return ['findfuncexecute'] + endfunc + call SetFindFunc() + call test_garbagecollect_now() + set findfunc= + delfunc SetFindFunc + delfunc FindFuncExecute + + func FindFunc2(pat, cmdexpand) + let g:FindFunc2Args = [a:pat, a:cmdexpand] + return ['findfunc2'] + endfunc + + " Vim9 tests + let lines =<< trim END + vim9script + + def g:Vim9findFunc(pat: string, cmdexpand: bool): list<string> + g:FindFunc1Args = [pat, cmdexpand] + return ['vim9findfunc'] + enddef + + # Test for using a def function with findfunc + set findfunc=function('g:Vim9findFunc') + g:FindFunc1Args = [] + find abc21 + assert_equal(['abc21', false], g:FindFunc1Args) + + # Test for using a global function name + &findfunc = g:FindFunc2 + g:FindFunc2Args = [] + find abc22 + assert_equal(['abc22', false], g:FindFunc2Args) + bw! + + # Test for using a script-local function name + def LocalFindFunc(pat: string, cmdexpand: bool): list<string> + g:LocalFindFuncArgs = [pat, cmdexpand] + return ['localfindfunc'] + enddef + &findfunc = LocalFindFunc + g:LocalFindFuncArgs = [] + find abc23 + assert_equal(['abc23', false], g:LocalFindFuncArgs) + bw! + END + call CheckScriptSuccess(lines) + + " setting 'findfunc' to a script local function outside of a script context + " should fail + let cleanup =<< trim END + call writefile([execute('messages')], 'Xtest.out') + qall + END + call writefile(cleanup, 'Xverify.vim', 'D') + call RunVim([], [], "-c \"set findfunc=s:abc\" -S Xverify.vim") + call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0]) + call delete('Xtest.out') + + " cleanup + set findfunc& + delfunc FindFunc1 + delfunc FindFunc2 + unlet g:FindFunc1Args g:FindFunc2Args + %bw! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim index 48319f5017..8e2a7c4631 100644 --- a/test/old/testdir/test_ins_complete.vim +++ b/test/old/testdir/test_ins_complete.vim @@ -1713,10 +1713,10 @@ func Test_completefunc_callback() call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x') " Using Vim9 lambda expression in legacy context should fail - " set completefunc=(a,\ b)\ =>\ CompleteFunc1(21,\ a,\ b) + set completefunc=(a,\ b)\ =>\ CompleteFunc1(21,\ a,\ b) new | only let g:CompleteFunc1Args = [] - " call assert_fails('call feedkeys("A\<C-X>\<C-U>\<Esc>", "x")', 'E117:') + call assert_fails('call feedkeys("A\<C-X>\<C-U>\<Esc>", "x")', 'E117:') call assert_equal([], g:CompleteFunc1Args) " set 'completefunc' to a partial with dict. This used to cause a crash. @@ -1970,10 +1970,10 @@ func Test_omnifunc_callback() call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x') " Using Vim9 lambda expression in legacy context should fail - " set omnifunc=(a,\ b)\ =>\ OmniFunc1(21,\ a,\ b) + set omnifunc=(a,\ b)\ =>\ OmniFunc1(21,\ a,\ b) new | only let g:OmniFunc1Args = [] - " call assert_fails('call feedkeys("A\<C-X>\<C-O>\<Esc>", "x")', 'E117:') + call assert_fails('call feedkeys("A\<C-X>\<C-O>\<Esc>", "x")', 'E117:') call assert_equal([], g:OmniFunc1Args) " set 'omnifunc' to a partial with dict. This used to cause a crash. @@ -2250,10 +2250,10 @@ func Test_thesaurusfunc_callback() call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x') " Using Vim9 lambda expression in legacy context should fail - " set thesaurusfunc=(a,\ b)\ =>\ TsrFunc1(21,\ a,\ b) + set thesaurusfunc=(a,\ b)\ =>\ TsrFunc1(21,\ a,\ b) new | only let g:TsrFunc1Args = [] - " call assert_fails('call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")', 'E117:') + call assert_fails('call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")', 'E117:') call assert_equal([], g:TsrFunc1Args) bw! diff --git a/test/old/testdir/test_modeline.vim b/test/old/testdir/test_modeline.vim index 7b7163d372..2cd9e49a12 100644 --- a/test/old/testdir/test_modeline.vim +++ b/test/old/testdir/test_modeline.vim @@ -217,7 +217,7 @@ func Test_modeline_fails_always() call s:modeline_fails('equalprg', 'equalprg=Something()', 'E520:') call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:') call s:modeline_fails('exrc', 'exrc=Something()', 'E520:') - call s:modeline_fails('findexpr', 'findexpr=Something()', 'E520:') + call s:modeline_fails('findfunc', 'findfunc=Something', 'E520:') call s:modeline_fails('formatprg', 'formatprg=Something()', 'E520:') call s:modeline_fails('fsync', 'fsync=Something()', 'E520:') call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:') diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim index 46fddd6c1a..faa93c4b61 100644 --- a/test/old/testdir/test_normal.vim +++ b/test/old/testdir/test_normal.vim @@ -692,9 +692,9 @@ func Test_opfunc_callback() delfunc s:OpFunc3 " Using Vim9 lambda expression in legacy context should fail - " set opfunc=(a)\ =>\ OpFunc1(24,\ a) + set opfunc=(a)\ =>\ OpFunc1(24,\ a) let g:OpFunc1Args = [] - " call assert_fails('normal! g@l', 'E117:') + call assert_fails('normal! g@l', 'E117:') call assert_equal([], g:OpFunc1Args) " set 'operatorfunc' to a partial with dict. This used to cause a crash. diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim index 6902560518..540936e7ec 100644 --- a/test/old/testdir/test_options.vim +++ b/test/old/testdir/test_options.vim @@ -1559,7 +1559,7 @@ endfunc " Test for changing options in a sandbox func Test_opt_sandbox() - for opt in ['backupdir', 'cdpath', 'exrc', 'findexpr'] + for opt in ['backupdir', 'cdpath', 'exrc', 'findfunc'] call assert_fails('sandbox set ' .. opt .. '?', 'E48:') call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:') endfor diff --git a/test/old/testdir/test_tagfunc.vim b/test/old/testdir/test_tagfunc.vim index 812603a430..ec1f93e9be 100644 --- a/test/old/testdir/test_tagfunc.vim +++ b/test/old/testdir/test_tagfunc.vim @@ -291,10 +291,10 @@ func Test_tagfunc_callback() call assert_fails("echo taglist('a')", "E987:") " Using Vim9 lambda expression in legacy context should fail - " set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c) + set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c) new let g:TagFunc1Args = [] - " call assert_fails("tag a17", "E117:") + call assert_fails("tag a17", "E117:") call assert_equal([], g:TagFunc1Args) bw! |