1
0
forked from aniani/vim

patch 9.1.0831: 'findexpr' can't be used as lambad or Funcref

Problem:  'findexpr' can't be used for lambads
          (Justin Keyes)
Solution: Replace the findexpr option with the findfunc option
          (Yegappan Lakshmanan)

related: #15905
closes: #15976

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Yegappan Lakshmanan 2024-11-02 18:40:10 +01:00 committed by Christian Brabandt
parent 0b8176dff2
commit a13f3a4f5d
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
25 changed files with 629 additions and 256 deletions

View File

@ -1,4 +1,4 @@
*eval.txt* For Vim version 9.1. Last change: 2024 Oct 28
*eval.txt* For Vim version 9.1. Last change: 2024 Nov 02
VIM REFERENCE MANUAL by Bram Moolenaar
@ -2027,10 +2027,6 @@ v:cmdbang Set like v:cmdarg for a file read/write command. When a "!"
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
environment. This allows Vim scripts to be aware of the
@ -2228,8 +2224,7 @@ v:fcs_choice What should happen after a |FileChangedShell| event was
*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_in* *fname_in-variable*
v:fname_in The name of the input file. Valid while evaluating:

View File

@ -1,4 +1,4 @@
*options.txt* For Vim version 9.1. Last change: 2024 Oct 28
*options.txt* For Vim version 9.1. Last change: 2024 Nov 02
VIM REFERENCE MANUAL by Bram Moolenaar
@ -439,10 +439,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', 'imactivatefunc', 'imstatusfunc', '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', 'imactivatefunc', 'imstatusfunc',
'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')
@ -3552,36 +3553,36 @@ 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 empty)
global or local to buffer |global-local|
{not available when compiled without the |+eval|
feature}
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.
@ -3589,18 +3590,18 @@ A jump table for the options with a short description can be found at |Q_op|.
Examples:
>
" 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'*
'fixendofline' 'fixeol' boolean (default on)

View File

@ -1,4 +1,4 @@
*quickref.txt* For Vim version 9.1. Last change: 2024 Oct 22
*quickref.txt* For Vim version 9.1. Last change: 2024 Nov 02
VIM REFERENCE MANUAL by Bram Moolenaar
@ -707,7 +707,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>
'fkmap' 'fk' obsolete option for Farsi
'foldclose' 'fcl' close a fold when the cursor leaves it

View File

@ -267,9 +267,9 @@ $quote eval.txt /*$quote*
'fenc' options.txt /*'fenc'*
'fencs' options.txt /*'fencs'*
'fex' options.txt /*'fex'*
'fexpr' options.txt /*'fexpr'*
'ff' options.txt /*'ff'*
'ffs' options.txt /*'ffs'*
'ffu' options.txt /*'ffu'*
'fic' options.txt /*'fic'*
'fileencoding' options.txt /*'fileencoding'*
'fileencodings' options.txt /*'fileencodings'*
@ -278,7 +278,7 @@ $quote eval.txt /*$quote*
'fileignorecase' options.txt /*'fileignorecase'*
'filetype' options.txt /*'filetype'*
'fillchars' options.txt /*'fillchars'*
'findexpr' options.txt /*'findexpr'*
'findfunc' options.txt /*'findfunc'*
'fixendofline' options.txt /*'fixendofline'*
'fixeol' options.txt /*'fixeol'*
'fk' options.txt /*'fk'*
@ -6510,7 +6510,6 @@ close_cb channel.txt /*close_cb*
closure eval.txt /*closure*
cmdarg-variable eval.txt /*cmdarg-variable*
cmdbang-variable eval.txt /*cmdbang-variable*
cmdcomplete-variable eval.txt /*cmdcomplete-variable*
cmdline-arguments vi_diff.txt /*cmdline-arguments*
cmdline-changed version5.txt /*cmdline-changed*
cmdline-completion cmdline.txt /*cmdline-completion*
@ -10942,7 +10941,6 @@ v:charconvert_from eval.txt /*v:charconvert_from*
v:charconvert_to eval.txt /*v:charconvert_to*
v:cmdarg eval.txt /*v:cmdarg*
v:cmdbang eval.txt /*v:cmdbang*
v:cmdcomplete eval.txt /*v:cmdcomplete*
v:collate eval.txt /*v:collate*
v:colornames eval.txt /*v:colornames*
v:completed_item eval.txt /*v:completed_item*

View File

@ -41652,7 +41652,7 @@ Options: ~
'completeitemalign' Order of |complete-items| in Insert mode completion
popup
'findexpr' Vim expression to obtain the results for a |:find|
'findfunc' Vim function to obtain the results for a |:find|
command
'winfixbuf' Keep buffer focused in a window
'tabclose' Which tab page to focus after closing a tab page

View File

@ -2412,7 +2412,6 @@ free_buf_options(
clear_string_option(&buf->b_p_fp);
#if defined(FEAT_EVAL)
clear_string_option(&buf->b_p_fex);
clear_string_option(&buf->b_p_fexpr);
#endif
#ifdef FEAT_CRYPT
# ifdef FEAT_SODIUM
@ -2485,6 +2484,8 @@ free_buf_options(
#ifdef FEAT_EVAL
clear_string_option(&buf->b_p_tfu);
free_callback(&buf->b_tfu_cb);
clear_string_option(&buf->b_p_ffu);
free_callback(&buf->b_ffu_cb);
#endif
clear_string_option(&buf->b_p_dict);
clear_string_option(&buf->b_p_tsr);

View File

@ -50,7 +50,7 @@ cmdline_fuzzy_completion_supported(expand_T *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_OLD_SETTING
@ -1419,7 +1419,7 @@ addstar(
// 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
@ -2140,7 +2140,7 @@ set_context_by_cmdname(
case CMD_sfind:
case CMD_tabfind:
if (xp->xp_context == EXPAND_FILES)
xp->xp_context = *get_findexpr() != NUL ? EXPAND_FINDEXPR
xp->xp_context = *get_findfunc() != NUL ? EXPAND_FINDFUNC
: EXPAND_FILES_IN_PATH;
break;
case CMD_cd:
@ -2853,10 +2853,10 @@ expand_files_and_dirs(
}
}
if (xp->xp_context == EXPAND_FINDEXPR)
if (xp->xp_context == EXPAND_FINDFUNC)
{
#ifdef FEAT_EVAL
ret = expand_findexpr(pat, matches, numMatches);
ret = expand_findfunc(pat, matches, numMatches);
#endif
}
else
@ -3122,7 +3122,7 @@ ExpandFromContext(
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);

View File

@ -3652,5 +3652,5 @@ EXTERN char e_wrong_character_width_for_field_str[]
INIT(= N_("E1512: Wrong character width for field \"%s\""));
EXTERN char e_winfixbuf_cannot_go_to_buffer[]
INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled"));
EXTERN char e_invalid_return_type_from_findexpr[]
INIT(= N_("E1514: 'findexpr' did not return a List type"));
EXTERN char e_invalid_return_type_from_findfunc[]
INIT(= N_("E1514: 'findfunc' did not return a List type"));

View File

@ -45,6 +45,8 @@ set_ref_in_buffers(int copyID)
#endif
if (!abort)
abort = abort || set_ref_in_callback(&bp->b_tfu_cb, copyID);
if (!abort)
abort = abort || set_ref_in_callback(&bp->b_ffu_cb, copyID);
if (abort)
break;
}

View File

@ -160,8 +160,7 @@ static struct vimvar
{VV_NAME("python3_version", VAR_NUMBER), NULL, VV_RO},
{VV_NAME("t_typealias", VAR_NUMBER), NULL, VV_RO},
{VV_NAME("t_enum", VAR_NUMBER), NULL, VV_RO},
{VV_NAME("t_enumvalue", VAR_NUMBER), NULL, VV_RO},
{VV_NAME("cmdcomplete", VAR_BOOL), NULL, VV_RO},
{VV_NAME("t_enumvalue", VAR_NUMBER), NULL, VV_RO}
};
// shorthand
@ -235,7 +234,6 @@ evalvars_init(void)
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
set_vim_var_nr(VV_EXITING, VVAL_NULL);
set_vim_var_nr(VV_CMDCOMPLETE, VVAL_FALSE);
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
set_vim_var_list(VV_ERRORS, list_alloc());
set_vim_var_dict(VV_EVENT, dict_alloc_lock(VAR_FIXED));

View File

@ -6924,58 +6924,71 @@ ex_wrongmodifier(exarg_T *eap)
}
#if defined(FEAT_EVAL) || defined(PROTO)
/*
* 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(char_u *pat, int cmdcomplete)
// callback function for 'findfunc'
static callback_T ffu_cb;
static callback_T *
get_findfunc_callback(void)
{
return *curbuf->b_p_ffu != NUL ? &curbuf->b_ffu_cb : &ffu_cb;
}
static list_T *
call_findfunc(char_u *pat, int cmdcomplete)
{
typval_T args[3];
callback_T *cb;
typval_T rettv;
int retval;
sctx_T saved_sctx = current_sctx;
char_u *findexpr;
char_u *arg;
typval_T tv;
list_T *retlist = NULL;
sctx_T *ctx;
findexpr = get_findexpr();
set_vim_var_string(VV_FNAME, pat, -1);
set_vim_var_nr(VV_CMDCOMPLETE, cmdcomplete ? VVAL_TRUE : VVAL_FALSE);
current_sctx = curbuf->b_p_script_ctx[BV_FEXPR];
arg = skipwhite(findexpr);
// Call 'findfunc' to obtain the list of file names.
args[0].v_type = VAR_STRING;
args[0].vval.v_string = pat;
args[1].v_type = VAR_BOOL;
args[1].vval.v_number = cmdcomplete;
args[2].v_type = VAR_UNKNOWN;
// 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;
// Evaluate the expression. If the expression is "FuncName()" call the
// function directly.
if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL)
retlist = NULL;
else
{
if (tv.v_type == VAR_LIST)
retlist = list_copy(tv.vval.v_list, TRUE, TRUE, get_copyID());
else
emsg(_(e_invalid_return_type_from_findexpr));
clear_tv(&tv);
}
--textlock;
clear_evalarg(&EVALARG_EVALUATE, NULL);
ctx = get_option_sctx("findfunc");
if (ctx != NULL)
current_sctx = *ctx;
cb = get_findfunc_callback();
retval = call_callback(cb, -1, &rettv, 2, args);
set_vim_var_string(VV_FNAME, NULL, 0);
set_vim_var_nr(VV_CMDCOMPLETE, VVAL_FALSE);
current_sctx = saved_sctx;
--textlock;
list_T *retlist = NULL;
if (retval == OK)
{
if (rettv.v_type == VAR_LIST)
retlist = list_copy(rettv.vval.v_list, FALSE, FALSE, get_copyID());
else
emsg(_(e_invalid_return_type_from_findfunc));
clear_tv(&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(char_u *pat, char_u ***files, int *numMatches)
expand_findfunc(char_u *pat, char_u ***files, int *numMatches)
{
list_T *l;
int len;
@ -6983,7 +6996,7 @@ expand_findexpr(char_u *pat, char_u ***files, int *numMatches)
*numMatches = 0;
*files = NULL;
l = eval_findexpr(pat, TRUE);
l = call_findfunc(pat, VVAL_TRUE);
if (l == NULL)
return FAIL;
@ -7015,11 +7028,11 @@ expand_findexpr(char_u *pat, char_u ***files, int *numMatches)
}
/*
* 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_u *
findexpr_find_file(char_u *findarg, int findarg_len, int count)
findfunc_find_file(char_u *findarg, int findarg_len, int count)
{
list_T *fname_list;
char_u *ret_fname = NULL;
@ -7029,7 +7042,7 @@ findexpr_find_file(char_u *findarg, int findarg_len, int count)
cc = findarg[findarg_len];
findarg[findarg_len] = NUL;
fname_list = eval_findexpr(findarg, FALSE);
fname_list = call_findfunc(findarg, VVAL_FALSE);
fname_count = list_len(fname_list);
if (fname_count == 0)
@ -7053,6 +7066,62 @@ findexpr_find_file(char_u *findarg, int findarg_len, int count)
return ret_fname;
}
/*
* Process the 'findfunc' option value.
* Returns NULL on success and an error message on failure.
*/
char *
did_set_findfunc(optset_T *args UNUSED)
{
int retval;
if (*curbuf->b_p_ffu != NUL)
{
// buffer-local option set
retval = option_set_callback_func(curbuf->b_p_ffu, &curbuf->b_ffu_cb);
}
else
{
// global option set
retval = option_set_callback_func(p_ffu, &ffu_cb);
}
if (retval == FAIL)
return e_invalid_argument;
// If the option value starts with <SID> or s:, then replace that with
// the script identifier.
char_u **varp = (char_u **)args->os_varp;
char_u *name = get_scriptlocal_funcname(*varp);
if (name != NULL)
{
free_string_option(*varp);
*varp = name;
}
return NULL;
}
# if defined(EXITFREE) || defined(PROTO)
void
free_findfunc_option(void)
{
free_callback(&ffu_cb);
}
# endif
/*
* Mark the global 'findfunc' callback with "copyID" so that it is not
* garbage collected.
*/
int
set_ref_in_findfunc(int copyID UNUSED)
{
int abort = FALSE;
abort = set_ref_in_callback(&ffu_cb, copyID);
return abort;
}
#endif
/*
@ -7105,10 +7174,10 @@ ex_splitview(exarg_T *eap)
char_u *file_to_find = NULL;
char *search_ctx = NULL;
if (*get_findexpr() != NUL)
if (*get_findfunc() != NUL)
{
#ifdef FEAT_EVAL
fname = findexpr_find_file(eap->arg, (int)STRLEN(eap->arg),
fname = findfunc_find_file(eap->arg, (int)STRLEN(eap->arg),
eap->addr_count > 0 ? eap->line2 : 1);
#endif
}
@ -7389,10 +7458,10 @@ ex_find(exarg_T *eap)
char_u *file_to_find = NULL;
char *search_ctx = NULL;
if (*get_findexpr() != NUL)
if (*get_findfunc() != NUL)
{
#ifdef FEAT_EVAL
fname = findexpr_find_file(eap->arg, (int)STRLEN(eap->arg),
fname = findfunc_find_file(eap->arg, (int)STRLEN(eap->arg),
eap->addr_count > 0 ? eap->line2 : 1);
#endif
}

View File

@ -183,6 +183,9 @@ garbage_collect(int testing)
// 'imactivatefunc' and 'imstatusfunc' callbacks
abort = abort || set_ref_in_im_funcs(copyID);
// 'findfunc' callback
abort = abort || set_ref_in_findfunc(copyID);
#ifdef FEAT_LUA
abort = abort || set_ref_in_lua(copyID);
#endif

View File

@ -1014,6 +1014,9 @@ free_all_options(void)
}
free_operatorfunc_option();
free_tagfunc_option();
# if defined(FEAT_EVAL)
free_findfunc_option();
# endif
}
#endif
@ -6372,8 +6375,8 @@ unset_global_local_option(char_u *name, void *from)
clear_string_option(&buf->b_p_fp);
break;
# ifdef FEAT_EVAL
case PV_FEXPR:
clear_string_option(&buf->b_p_fexpr);
case PV_FFU:
clear_string_option(&buf->b_p_ffu);
break;
# endif
# ifdef FEAT_QUICKFIX
@ -6455,7 +6458,7 @@ get_varp_scope(struct vimoption *p, int scope)
{
case PV_FP: return (char_u *)&(curbuf->b_p_fp);
#ifdef FEAT_EVAL
case PV_FEXPR: return (char_u *)&(curbuf->b_p_fexpr);
case PV_FFU: return (char_u *)&(curbuf->b_p_ffu);
#endif
#ifdef FEAT_QUICKFIX
case PV_EFM: return (char_u *)&(curbuf->b_p_efm);
@ -6568,8 +6571,8 @@ get_varp(struct vimoption *p)
case PV_FP: return *curbuf->b_p_fp != NUL
? (char_u *)&(curbuf->b_p_fp) : p->var;
#ifdef FEAT_EVAL
case PV_FEXPR: return *curbuf->b_p_fexpr != NUL
? (char_u *)&curbuf->b_p_fexpr : p->var;
case PV_FFU: return *curbuf->b_p_ffu != NUL
? (char_u *)&(curbuf->b_p_ffu) : p->var;
#endif
#ifdef FEAT_QUICKFIX
case PV_EFM: return *curbuf->b_p_efm != NUL
@ -6818,15 +6821,15 @@ get_equalprg(void)
}
/*
* Get the value of 'findexpr', either the buffer-local one or the global one.
* Get the value of 'findfunc', either the buffer-local one or the global one.
*/
char_u *
get_findexpr(void)
get_findfunc(void)
{
#ifdef FEAT_EVAL
if (*curbuf->b_p_fexpr == NUL)
return p_fexpr;
return curbuf->b_p_fexpr;
if (*curbuf->b_p_ffu == NUL)
return p_ffu;
return curbuf->b_p_ffu;
#else
return (char_u *)"";
#endif
@ -7361,8 +7364,7 @@ buf_copy_options(buf_T *buf, int flags)
#endif
buf->b_p_ep = empty_option;
#if defined(FEAT_EVAL)
buf->b_p_fexpr = vim_strsave(p_fexpr);
COPY_OPT_SCTX(buf, BV_FEXPR);
buf->b_p_ffu = empty_option;
#endif
buf->b_p_kp = empty_option;
buf->b_p_path = empty_option;
@ -8749,6 +8751,7 @@ option_set_callback_func(char_u *optval UNUSED, callback_T *optcb UNUSED)
#ifdef FEAT_EVAL
typval_T *tv;
callback_T cb;
int funcname = FALSE;
if (optval == NULL || *optval == NUL)
{
@ -8762,8 +8765,11 @@ option_set_callback_func(char_u *optval UNUSED, callback_T *optcb UNUSED)
// Lambda expression or a funcref
tv = eval_expr(optval, NULL);
else
{
// treat everything else as a function name string
tv = alloc_string_tv(vim_strsave(optval));
funcname = TRUE;
}
if (tv == NULL)
return FAIL;
@ -8780,6 +8786,16 @@ option_set_callback_func(char_u *optval UNUSED, callback_T *optcb UNUSED)
vim_free(cb.cb_name);
free_tv(tv);
if (in_vim9script() && funcname && (vim_strchr(optval, '.') != NULL))
{
// When a Vim9 imported function name is used, it is expanded by the
// call to get_callback() above to <SNR>_funcname. Revert the name to
// back to "import.funcname".
if (optcb->cb_free_name)
vim_free(optcb->cb_name);
optcb->cb_name = vim_strsave(optval);
optcb->cb_free_name = TRUE;
}
// when using Vim9 style "import.funcname" it needs to be expanded to
// "import#funcname".
expand_autload_callback(optcb);

View File

@ -597,7 +597,7 @@ EXTERN int p_fic; // 'fileignorecase'
EXTERN char_u *p_ft; // 'filetype'
EXTERN char_u *p_fcs; // 'fillchar'
#ifdef FEAT_EVAL
EXTERN char_u *p_fexpr; // 'findexpr'
EXTERN char_u *p_ffu; // 'findfunc'
#endif
EXTERN int p_fixeol; // 'fixendofline'
#ifdef FEAT_FOLDING
@ -1171,11 +1171,11 @@ enum
, BV_EP
, BV_ET
, BV_FENC
, BV_FEXPR
, BV_FP
#ifdef FEAT_EVAL
, BV_BEXPR
, BV_FEX
, BV_FFU
#endif
, BV_FF
, BV_FLP

View File

@ -54,6 +54,7 @@
#define PV_CPT OPT_BUF(BV_CPT)
#define PV_DICT OPT_BOTH(OPT_BUF(BV_DICT))
#define PV_TSR OPT_BOTH(OPT_BUF(BV_TSR))
#define PV_FFU OPT_BOTH(OPT_BUF(BV_FFU))
#define PV_CSL OPT_BUF(BV_CSL)
#ifdef FEAT_COMPL_FUNC
# define PV_CFU OPT_BUF(BV_CFU)
@ -74,7 +75,6 @@
#define PV_FP OPT_BOTH(OPT_BUF(BV_FP))
#ifdef FEAT_EVAL
# define PV_FEX OPT_BUF(BV_FEX)
# define PV_FEXPR OPT_BOTH(OPT_BUF(BV_FEXPR))
#endif
#define PV_FF OPT_BUF(BV_FF)
#define PV_FLP OPT_BUF(BV_FLP)
@ -959,9 +959,10 @@ static struct vimoption options[] =
{(char_u *)"vert:|,fold:-,eob:~,lastline:@",
(char_u *)0L}
SCTX_INIT},
{"findexpr", "fexpr", P_STRING|P_ALLOCED|P_VI_DEF|P_VIM|P_SECURE,
#if defined(FEAT_EVAL)
(char_u *)&p_fexpr, PV_FEXPR, did_set_optexpr, NULL,
{"findfunc", "ffu", P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE|P_FUNC,
#ifdef FEAT_EVAL
(char_u *)&p_ffu, PV_FFU,
did_set_findfunc, NULL,
{(char_u *)"", (char_u *)0L}
#else
(char_u *)NULL, PV_NONE, NULL, NULL,

View File

@ -313,6 +313,7 @@ check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_tsrfu);
#endif
#ifdef FEAT_EVAL
check_string_option(&buf->b_p_ffu);
check_string_option(&buf->b_p_tfu);
#endif
#ifdef FEAT_KEYMAP
@ -324,9 +325,6 @@ check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_efm);
#endif
check_string_option(&buf->b_p_ep);
#ifdef FEAT_EVAL
check_string_option(&buf->b_p_fexpr);
#endif
check_string_option(&buf->b_p_path);
check_string_option(&buf->b_p_tags);
check_string_option(&buf->b_p_tc);
@ -3135,9 +3133,8 @@ expand_set_nrformats(optexpand_T *args, int *numMatches, char_u ***matches)
#if defined(FEAT_EVAL) || defined(PROTO)
/*
* One of the '*expr' options is changed: 'balloonexpr', 'diffexpr',
* 'findexpr', 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr',
* 'indentexpr', 'patchexpr', 'printexpr' and 'charconvert'.
*
* 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', 'indentexpr',
* 'patchexpr', 'printexpr' and 'charconvert'.
*/
char *
did_set_optexpr(optset_T *args)

View File

@ -46,7 +46,10 @@ 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_findexpr(char_u *pat, char_u ***files, int *numMatches);
int expand_findfunc(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);
void ex_splitview(exarg_T *eap);
void tabpage_new(void);
void do_exedit(exarg_T *eap, win_T *old_curwin);

View File

@ -120,7 +120,7 @@ char_u *get_option_var(int opt_idx);
char_u *get_option_fullname(int opt_idx);
opt_did_set_cb_T get_option_did_set_cb(int opt_idx);
char_u *get_equalprg(void);
char_u *get_findexpr(void);
char_u *get_findfunc(void);
void win_copy_options(win_T *wp_from, win_T *wp_to);
void after_copy_winopt(win_T *wp);
void copy_winopt(winopt_T *from, winopt_T *to);

View File

@ -3248,6 +3248,8 @@ struct file_buffer
#ifdef FEAT_EVAL
char_u *b_p_tfu; // 'tagfunc' option value
callback_T b_tfu_cb; // 'tagfunc' callback
char_u *b_p_ffu; // 'findfunc' option value
callback_T b_ffu_cb; // 'findfunc' callback
#endif
int b_p_eof; // 'endoffile'
int b_p_eol; // 'endofline'
@ -3334,9 +3336,6 @@ struct file_buffer
char_u *b_p_efm; // 'errorformat' local value
#endif
char_u *b_p_ep; // 'equalprg' local value
#ifdef FEAT_EVAL
char_u *b_p_fexpr; // 'findexpr' local value
#endif
char_u *b_p_path; // 'path' local value
int b_p_ar; // 'autoread' local value
char_u *b_p_tags; // 'tags' local value

View File

@ -1,6 +1,7 @@
" Test findfile() and finddir()
source check.vim
import './vim9.vim' as v9
let s:files = [ 'Xfinddir1/foo',
\ 'Xfinddir1/bar',
@ -283,223 +284,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=test_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')
set findfunc=FindFuncModifyBuf
call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window')
delfunc FindFuncModifyBuf
" Expression returning a string
set findexpr='abc'
call assert_fails('find Xfindexpr1.c', "E1514: 'findexpr' did not return a List type")
" 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 findexpr&
delfunc! FindExpr1
delfunc! FindExpr2
delfunc! FindExpr3
delfunc! FindExpr4
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 v9.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 v9.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

View File

@ -208,7 +208,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:')

View File

@ -1570,7 +1570,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

View File

@ -1560,27 +1560,47 @@ def Run_Test_import_in_printexpr()
set printexpr=
enddef
" Test for using an imported function as 'findexpr'
func Test_import_in_findexpr()
call Run_Test_import_in_findexpr()
" Test for using an imported function as 'findfunc'
func Test_import_in_findfunc()
call Run_Test_import_in_findfunc()
endfunc
def Run_Test_import_in_findexpr()
def Run_Test_import_in_findfunc()
var lines =<< trim END
vim9script
vim9script
export def FindExpr(): list<string>
var fnames = ['Xfile1.c', 'Xfile2.c', 'Xfile3.c']
return fnames->copy()->filter('v:val =~? v:fname')
enddef
export def FindFunc(pat: string, cmdexpand: bool): list<string>
var fnames = ['Xfile1.c', 'Xfile2.c', 'Xfile3.c']
return fnames->filter((_, v) => v =~? pat)
enddef
END
writefile(lines, 'Xfindexpr', 'D')
writefile(lines, 'Xfindfunc', 'D')
# Test using the "set" command
lines =<< trim END
vim9script
import './Xfindexpr' as find
vim9script
import './Xfindfunc' as find1
set findexpr=find.FindExpr()
set findfunc=find1.FindFunc
END
v9.CheckScriptSuccess(lines)
enew!
find Xfile2
assert_equal('Xfile2.c', @%)
bwipe!
botright vert new
find Xfile1
assert_equal('Xfile1.c', @%)
bw!
# Test using the option variable
lines =<< trim END
vim9script
import './Xfindfunc' as find2
&findfunc = find2.FindFunc
END
v9.CheckScriptSuccess(lines)
@ -1593,7 +1613,7 @@ def Run_Test_import_in_findexpr()
find Xfile1
assert_equal('Xfile1.c', @%)
set findexpr=
set findfunc=
bwipe!
enddef

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
831,
/**/
830,
/**/

View File

@ -845,7 +845,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
#define EXPAND_KEYMAP 58
#define EXPAND_DIRS_IN_CDPATH 59
#define EXPAND_SHELLCMDLINE 60
#define EXPAND_FINDEXPR 61
#define EXPAND_FINDFUNC 61
// Values for exmode_active (0 is no exmode)
@ -2189,8 +2189,7 @@ typedef int sock_T;
#define VV_TYPE_TYPEALIAS 107
#define VV_TYPE_ENUM 108
#define VV_TYPE_ENUMVALUE 109
#define VV_CMDCOMPLETE 110
#define VV_LEN 111 // number of v: vars
#define VV_LEN 110 // number of v: vars
// used for v_number in VAR_BOOL and VAR_SPECIAL
#define VVAL_FALSE 0L // VAR_BOOL