0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

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 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 only be used in autocommands. For user commands |<bang>|
can be used. 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* *collate-variable*
v:collate The current locale setting for collation order of the runtime v:collate The current locale setting for collation order of the runtime
environment. This allows Vim scripts to be aware of the 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* *fname-variable*
v:fname When evaluating 'includeexpr': the file name that was v:fname When evaluating 'includeexpr': the file name that was
detected. When evaluating 'findexpr': the argument passed to detected. Empty otherwise.
the |:find| command. Empty otherwise.
*v:fname_in* *fname_in-variable* *v:fname_in* *fname_in-variable*
v:fname_in The name of the input file. Valid while evaluating: 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 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. ":setlocal" on a global option might work differently then.
*option-value-function* *option-value-function*
Some options ('completefunc', 'imactivatefunc', 'imstatusfunc', 'omnifunc', Some options ('completefunc', 'findfunc', 'imactivatefunc', 'imstatusfunc',
'operatorfunc', 'quickfixtextfunc', 'tagfunc' and 'thesaurusfunc') are set to 'omnifunc', 'operatorfunc', 'quickfixtextfunc', 'tagfunc' and 'thesaurusfunc')
a function name or a function reference or a lambda function. When using a are set to a function name or a function reference or a lambda function. When
lambda it will be converted to the name, e.g. "<lambda>123". Examples: using a lambda it will be converted to the name, e.g. "<lambda>123".
Examples:
> >
set opfunc=MyOpFunc set opfunc=MyOpFunc
set opfunc=function('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| eob EndOfBuffer |hl-EndOfBuffer|
lastline NonText |hl-NonText| lastline NonText |hl-NonText|
*'findexpr'* *'fexpr'* *E1514* *'findfunc'* *'ffu'* *E1514*
'findexpr' 'fexpr' string (default "") 'findfunc' 'ffu' string (default empty)
global or local to buffer |global-local| global or local to buffer |global-local|
{not available when compiled without the |+eval| {not available when compiled without the |+eval|
feature} 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| command. When this option is empty, the internal |file-searching|
mechanism is used. mechanism is used.
While evaluating the expression, the |v:fname| variable is set to the The value can be the name of a function, a |lambda| or a |Funcref|.
argument of the |:find| command. See |option-value-function| for more information.
The expression is evaluated only once per |:find| command invocation. The function is called with two arguments. The first argument is a
The expression can process all the directories specified in 'path'. |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, The function is called only once per |:find| command invocation.
in which case the |v:cmdcomplete| variable will be set to |v:true|, The function can process all the directories specified in 'path'.
otherwise it will be set to |v:false|.
If a match is found, the expression should return a |List| containing If a match is found, the function should return a |List| containing
one or more file names. If a match is not found, the expression one or more file names. If a match is not found, the function
should return an empty List. 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. 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 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 This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons. security reasons.
@ -3589,18 +3590,18 @@ A jump table for the options with a short description can be found at |Q_op|.
Examples: Examples:
> >
" Use glob() " Use glob()
func FindExprGlob() func FindFuncGlob(cmdarg, cmdcomplete)
let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
return glob(pat, v:false, v:true) return glob(pat, v:false, v:true)
endfunc endfunc
set findexpr=FindExprGlob() set findfunc=FindFuncGlob
" Use the 'git ls-files' output " Use the 'git ls-files' output
func FindGitFiles() func FindGitFiles(cmdarg, cmdcomplete)
let fnames = systemlist('git ls-files') let fnames = systemlist('git ls-files')
return fnames->filter('v:val =~? v:fname') return fnames->filter('v:val =~? a:cmdarg')
endfunc endfunc
set findexpr=FindGitFiles() set findfunc=FindGitFiles
< <
*'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'* *'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'*
'fixendofline' 'fixeol' boolean (default on) '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 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 'fileignorecase' 'fic' ignore case when using file names
'filetype' 'ft' type of file, used for autocommands 'filetype' 'ft' type of file, used for autocommands
'fillchars' 'fcs' characters to use for displaying special items '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> 'fixendofline' 'fixeol' make sure last line in file has <EOL>
'fkmap' 'fk' obsolete option for Farsi 'fkmap' 'fk' obsolete option for Farsi
'foldclose' 'fcl' close a fold when the cursor leaves it 'foldclose' 'fcl' close a fold when the cursor leaves it

View File

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

View File

@ -41652,7 +41652,7 @@ Options: ~
'completeitemalign' Order of |complete-items| in Insert mode completion 'completeitemalign' Order of |complete-items| in Insert mode completion
popup popup
'findexpr' Vim expression to obtain the results for a |:find| 'findfunc' Vim function to obtain the results for a |:find|
command command
'winfixbuf' Keep buffer focused in a window 'winfixbuf' Keep buffer focused in a window
'tabclose' Which tab page to focus after closing a tab page '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); clear_string_option(&buf->b_p_fp);
#if defined(FEAT_EVAL) #if defined(FEAT_EVAL)
clear_string_option(&buf->b_p_fex); clear_string_option(&buf->b_p_fex);
clear_string_option(&buf->b_p_fexpr);
#endif #endif
#ifdef FEAT_CRYPT #ifdef FEAT_CRYPT
# ifdef FEAT_SODIUM # ifdef FEAT_SODIUM
@ -2485,6 +2484,8 @@ free_buf_options(
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
clear_string_option(&buf->b_p_tfu); clear_string_option(&buf->b_p_tfu);
free_callback(&buf->b_tfu_cb); free_callback(&buf->b_tfu_cb);
clear_string_option(&buf->b_p_ffu);
free_callback(&buf->b_ffu_cb);
#endif #endif
clear_string_option(&buf->b_p_dict); clear_string_option(&buf->b_p_dict);
clear_string_option(&buf->b_p_tsr); 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
&& xp->xp_context != EXPAND_FILES_IN_PATH && xp->xp_context != EXPAND_FILES_IN_PATH
&& xp->xp_context != EXPAND_FILETYPE && xp->xp_context != EXPAND_FILETYPE
&& xp->xp_context != EXPAND_FINDEXPR && xp->xp_context != EXPAND_FINDFUNC
&& xp->xp_context != EXPAND_HELP && xp->xp_context != EXPAND_HELP
&& xp->xp_context != EXPAND_KEYMAP && xp->xp_context != EXPAND_KEYMAP
&& xp->xp_context != EXPAND_OLD_SETTING && xp->xp_context != EXPAND_OLD_SETTING
@ -1419,7 +1419,7 @@ addstar(
// For help tags the translation is done in find_help_tags(). // For help tags the translation is done in find_help_tags().
// For a tag pattern starting with "/" no translation is needed. // For a tag pattern starting with "/" no translation is needed.
if (context == EXPAND_FINDEXPR if (context == EXPAND_FINDFUNC
|| context == EXPAND_HELP || context == EXPAND_HELP
|| context == EXPAND_COLORS || context == EXPAND_COLORS
|| context == EXPAND_COMPILER || context == EXPAND_COMPILER
@ -2140,7 +2140,7 @@ set_context_by_cmdname(
case CMD_sfind: case CMD_sfind:
case CMD_tabfind: case CMD_tabfind:
if (xp->xp_context == EXPAND_FILES) 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; : EXPAND_FILES_IN_PATH;
break; break;
case CMD_cd: 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 #ifdef FEAT_EVAL
ret = expand_findexpr(pat, matches, numMatches); ret = expand_findfunc(pat, matches, numMatches);
#endif #endif
} }
else else
@ -3122,7 +3122,7 @@ ExpandFromContext(
if (xp->xp_context == EXPAND_FILES if (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_DIRECTORIES || xp->xp_context == EXPAND_DIRECTORIES
|| xp->xp_context == EXPAND_FILES_IN_PATH || xp->xp_context == EXPAND_FILES_IN_PATH
|| xp->xp_context == EXPAND_FINDEXPR || xp->xp_context == EXPAND_FINDFUNC
|| xp->xp_context == EXPAND_DIRS_IN_CDPATH) || xp->xp_context == EXPAND_DIRS_IN_CDPATH)
return expand_files_and_dirs(xp, pat, matches, numMatches, flags, return expand_files_and_dirs(xp, pat, matches, numMatches, flags,
options); 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\"")); INIT(= N_("E1512: Wrong character width for field \"%s\""));
EXTERN char e_winfixbuf_cannot_go_to_buffer[] EXTERN char e_winfixbuf_cannot_go_to_buffer[]
INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled")); INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled"));
EXTERN char e_invalid_return_type_from_findexpr[] EXTERN char e_invalid_return_type_from_findfunc[]
INIT(= N_("E1514: 'findexpr' did not return a List type")); INIT(= N_("E1514: 'findfunc' did not return a List type"));

View File

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

View File

@ -160,8 +160,7 @@ static struct vimvar
{VV_NAME("python3_version", VAR_NUMBER), NULL, VV_RO}, {VV_NAME("python3_version", VAR_NUMBER), NULL, VV_RO},
{VV_NAME("t_typealias", 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_enum", VAR_NUMBER), NULL, VV_RO},
{VV_NAME("t_enumvalue", VAR_NUMBER), NULL, VV_RO}, {VV_NAME("t_enumvalue", VAR_NUMBER), NULL, VV_RO}
{VV_NAME("cmdcomplete", VAR_BOOL), NULL, VV_RO},
}; };
// shorthand // shorthand
@ -235,7 +234,6 @@ evalvars_init(void)
set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L); set_vim_var_nr(VV_HLSEARCH, 1L);
set_vim_var_nr(VV_EXITING, VVAL_NULL); 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_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED));
set_vim_var_list(VV_ERRORS, list_alloc()); set_vim_var_list(VV_ERRORS, list_alloc());
set_vim_var_dict(VV_EVENT, dict_alloc_lock(VAR_FIXED)); 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) #if defined(FEAT_EVAL) || defined(PROTO)
/*
* Evaluate the 'findexpr' expression and return the result. When evaluating // callback function for 'findfunc'
* the expression, v:fname is set to the ":find" command argument. static callback_T ffu_cb;
*/
static list_T * static callback_T *
eval_findexpr(char_u *pat, int cmdcomplete) 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; sctx_T saved_sctx = current_sctx;
char_u *findexpr; sctx_T *ctx;
char_u *arg;
typval_T tv;
list_T *retlist = NULL;
findexpr = get_findexpr(); // Call 'findfunc' to obtain the list of file names.
args[0].v_type = VAR_STRING;
set_vim_var_string(VV_FNAME, pat, -1); args[0].vval.v_string = pat;
set_vim_var_nr(VV_CMDCOMPLETE, cmdcomplete ? VVAL_TRUE : VVAL_FALSE); args[1].v_type = VAR_BOOL;
current_sctx = curbuf->b_p_script_ctx[BV_FEXPR]; args[1].vval.v_number = cmdcomplete;
args[2].v_type = VAR_UNKNOWN;
arg = skipwhite(findexpr);
// 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; ++textlock;
// Evaluate the expression. If the expression is "FuncName()" call the ctx = get_option_sctx("findfunc");
// function directly. if (ctx != NULL)
if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) current_sctx = *ctx;
retlist = NULL;
else cb = get_findfunc_callback();
{ retval = call_callback(cb, -1, &rettv, 2, args);
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);
set_vim_var_string(VV_FNAME, NULL, 0);
set_vim_var_nr(VV_CMDCOMPLETE, VVAL_FALSE);
current_sctx = saved_sctx; 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; 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. * Used for expanding the :find, :sfind and :tabfind command argument.
* Returns OK on success and FAIL otherwise. * Returns OK on success and FAIL otherwise.
*/ */
int int
expand_findexpr(char_u *pat, char_u ***files, int *numMatches) expand_findfunc(char_u *pat, char_u ***files, int *numMatches)
{ {
list_T *l; list_T *l;
int len; int len;
@ -6983,7 +6996,7 @@ expand_findexpr(char_u *pat, char_u ***files, int *numMatches)
*numMatches = 0; *numMatches = 0;
*files = NULL; *files = NULL;
l = eval_findexpr(pat, TRUE); l = call_findfunc(pat, VVAL_TRUE);
if (l == NULL) if (l == NULL)
return FAIL; 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. * the n'th matching file.
*/ */
static char_u * 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; list_T *fname_list;
char_u *ret_fname = NULL; char_u *ret_fname = NULL;
@ -7029,7 +7042,7 @@ findexpr_find_file(char_u *findarg, int findarg_len, int count)
cc = findarg[findarg_len]; cc = findarg[findarg_len];
findarg[findarg_len] = NUL; findarg[findarg_len] = NUL;
fname_list = eval_findexpr(findarg, FALSE); fname_list = call_findfunc(findarg, VVAL_FALSE);
fname_count = list_len(fname_list); fname_count = list_len(fname_list);
if (fname_count == 0) if (fname_count == 0)
@ -7053,6 +7066,62 @@ findexpr_find_file(char_u *findarg, int findarg_len, int count)
return ret_fname; 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 #endif
/* /*
@ -7105,10 +7174,10 @@ ex_splitview(exarg_T *eap)
char_u *file_to_find = NULL; char_u *file_to_find = NULL;
char *search_ctx = NULL; char *search_ctx = NULL;
if (*get_findexpr() != NUL) if (*get_findfunc() != NUL)
{ {
#ifdef FEAT_EVAL #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); eap->addr_count > 0 ? eap->line2 : 1);
#endif #endif
} }
@ -7389,10 +7458,10 @@ ex_find(exarg_T *eap)
char_u *file_to_find = NULL; char_u *file_to_find = NULL;
char *search_ctx = NULL; char *search_ctx = NULL;
if (*get_findexpr() != NUL) if (*get_findfunc() != NUL)
{ {
#ifdef FEAT_EVAL #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); eap->addr_count > 0 ? eap->line2 : 1);
#endif #endif
} }

View File

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

View File

@ -1014,6 +1014,9 @@ free_all_options(void)
} }
free_operatorfunc_option(); free_operatorfunc_option();
free_tagfunc_option(); free_tagfunc_option();
# if defined(FEAT_EVAL)
free_findfunc_option();
# endif
} }
#endif #endif
@ -6372,8 +6375,8 @@ unset_global_local_option(char_u *name, void *from)
clear_string_option(&buf->b_p_fp); clear_string_option(&buf->b_p_fp);
break; break;
# ifdef FEAT_EVAL # ifdef FEAT_EVAL
case PV_FEXPR: case PV_FFU:
clear_string_option(&buf->b_p_fexpr); clear_string_option(&buf->b_p_ffu);
break; break;
# endif # endif
# ifdef FEAT_QUICKFIX # 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); case PV_FP: return (char_u *)&(curbuf->b_p_fp);
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
case PV_FEXPR: return (char_u *)&(curbuf->b_p_fexpr); case PV_FFU: return (char_u *)&(curbuf->b_p_ffu);
#endif #endif
#ifdef FEAT_QUICKFIX #ifdef FEAT_QUICKFIX
case PV_EFM: return (char_u *)&(curbuf->b_p_efm); 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 case PV_FP: return *curbuf->b_p_fp != NUL
? (char_u *)&(curbuf->b_p_fp) : p->var; ? (char_u *)&(curbuf->b_p_fp) : p->var;
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
case PV_FEXPR: return *curbuf->b_p_fexpr != NUL case PV_FFU: return *curbuf->b_p_ffu != NUL
? (char_u *)&curbuf->b_p_fexpr : p->var; ? (char_u *)&(curbuf->b_p_ffu) : p->var;
#endif #endif
#ifdef FEAT_QUICKFIX #ifdef FEAT_QUICKFIX
case PV_EFM: return *curbuf->b_p_efm != NUL 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 * char_u *
get_findexpr(void) get_findfunc(void)
{ {
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
if (*curbuf->b_p_fexpr == NUL) if (*curbuf->b_p_ffu == NUL)
return p_fexpr; return p_ffu;
return curbuf->b_p_fexpr; return curbuf->b_p_ffu;
#else #else
return (char_u *)""; return (char_u *)"";
#endif #endif
@ -7361,8 +7364,7 @@ buf_copy_options(buf_T *buf, int flags)
#endif #endif
buf->b_p_ep = empty_option; buf->b_p_ep = empty_option;
#if defined(FEAT_EVAL) #if defined(FEAT_EVAL)
buf->b_p_fexpr = vim_strsave(p_fexpr); buf->b_p_ffu = empty_option;
COPY_OPT_SCTX(buf, BV_FEXPR);
#endif #endif
buf->b_p_kp = empty_option; buf->b_p_kp = empty_option;
buf->b_p_path = 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 #ifdef FEAT_EVAL
typval_T *tv; typval_T *tv;
callback_T cb; callback_T cb;
int funcname = FALSE;
if (optval == NULL || *optval == NUL) 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 // Lambda expression or a funcref
tv = eval_expr(optval, NULL); tv = eval_expr(optval, NULL);
else else
{
// treat everything else as a function name string // treat everything else as a function name string
tv = alloc_string_tv(vim_strsave(optval)); tv = alloc_string_tv(vim_strsave(optval));
funcname = TRUE;
}
if (tv == NULL) if (tv == NULL)
return FAIL; return FAIL;
@ -8780,6 +8786,16 @@ option_set_callback_func(char_u *optval UNUSED, callback_T *optcb UNUSED)
vim_free(cb.cb_name); vim_free(cb.cb_name);
free_tv(tv); 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 // when using Vim9 style "import.funcname" it needs to be expanded to
// "import#funcname". // "import#funcname".
expand_autload_callback(optcb); 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_ft; // 'filetype'
EXTERN char_u *p_fcs; // 'fillchar' EXTERN char_u *p_fcs; // 'fillchar'
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
EXTERN char_u *p_fexpr; // 'findexpr' EXTERN char_u *p_ffu; // 'findfunc'
#endif #endif
EXTERN int p_fixeol; // 'fixendofline' EXTERN int p_fixeol; // 'fixendofline'
#ifdef FEAT_FOLDING #ifdef FEAT_FOLDING
@ -1171,11 +1171,11 @@ enum
, BV_EP , BV_EP
, BV_ET , BV_ET
, BV_FENC , BV_FENC
, BV_FEXPR
, BV_FP , BV_FP
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
, BV_BEXPR , BV_BEXPR
, BV_FEX , BV_FEX
, BV_FFU
#endif #endif
, BV_FF , BV_FF
, BV_FLP , BV_FLP

View File

@ -54,6 +54,7 @@
#define PV_CPT OPT_BUF(BV_CPT) #define PV_CPT OPT_BUF(BV_CPT)
#define PV_DICT OPT_BOTH(OPT_BUF(BV_DICT)) #define PV_DICT OPT_BOTH(OPT_BUF(BV_DICT))
#define PV_TSR OPT_BOTH(OPT_BUF(BV_TSR)) #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) #define PV_CSL OPT_BUF(BV_CSL)
#ifdef FEAT_COMPL_FUNC #ifdef FEAT_COMPL_FUNC
# define PV_CFU OPT_BUF(BV_CFU) # define PV_CFU OPT_BUF(BV_CFU)
@ -74,7 +75,6 @@
#define PV_FP OPT_BOTH(OPT_BUF(BV_FP)) #define PV_FP OPT_BOTH(OPT_BUF(BV_FP))
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
# define PV_FEX OPT_BUF(BV_FEX) # define PV_FEX OPT_BUF(BV_FEX)
# define PV_FEXPR OPT_BOTH(OPT_BUF(BV_FEXPR))
#endif #endif
#define PV_FF OPT_BUF(BV_FF) #define PV_FF OPT_BUF(BV_FF)
#define PV_FLP OPT_BUF(BV_FLP) #define PV_FLP OPT_BUF(BV_FLP)
@ -959,9 +959,10 @@ static struct vimoption options[] =
{(char_u *)"vert:|,fold:-,eob:~,lastline:@", {(char_u *)"vert:|,fold:-,eob:~,lastline:@",
(char_u *)0L} (char_u *)0L}
SCTX_INIT}, SCTX_INIT},
{"findexpr", "fexpr", P_STRING|P_ALLOCED|P_VI_DEF|P_VIM|P_SECURE, {"findfunc", "ffu", P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE|P_FUNC,
#if defined(FEAT_EVAL) #ifdef FEAT_EVAL
(char_u *)&p_fexpr, PV_FEXPR, did_set_optexpr, NULL, (char_u *)&p_ffu, PV_FFU,
did_set_findfunc, NULL,
{(char_u *)"", (char_u *)0L} {(char_u *)"", (char_u *)0L}
#else #else
(char_u *)NULL, PV_NONE, NULL, NULL, (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); check_string_option(&buf->b_p_tsrfu);
#endif #endif
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
check_string_option(&buf->b_p_ffu);
check_string_option(&buf->b_p_tfu); check_string_option(&buf->b_p_tfu);
#endif #endif
#ifdef FEAT_KEYMAP #ifdef FEAT_KEYMAP
@ -324,9 +325,6 @@ check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_efm); check_string_option(&buf->b_p_efm);
#endif #endif
check_string_option(&buf->b_p_ep); 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_path);
check_string_option(&buf->b_p_tags); check_string_option(&buf->b_p_tags);
check_string_option(&buf->b_p_tc); 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) #if defined(FEAT_EVAL) || defined(PROTO)
/* /*
* One of the '*expr' options is changed: 'balloonexpr', 'diffexpr', * One of the '*expr' options is changed: 'balloonexpr', 'diffexpr',
* 'findexpr', 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', * 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', 'indentexpr',
* 'indentexpr', 'patchexpr', 'printexpr' and 'charconvert'. * 'patchexpr', 'printexpr' and 'charconvert'.
*
*/ */
char * char *
did_set_optexpr(optset_T *args) 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 ex_stop(exarg_T *eap);
void handle_drop(int filec, char_u **filev, int split, void (*callback)(void *), void *cookie); void handle_drop(int filec, char_u **filev, int split, void (*callback)(void *), void *cookie);
void handle_any_postponed_drop(void); 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 ex_splitview(exarg_T *eap);
void tabpage_new(void); void tabpage_new(void);
void do_exedit(exarg_T *eap, win_T *old_curwin); 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); char_u *get_option_fullname(int opt_idx);
opt_did_set_cb_T get_option_did_set_cb(int opt_idx); opt_did_set_cb_T get_option_did_set_cb(int opt_idx);
char_u *get_equalprg(void); 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 win_copy_options(win_T *wp_from, win_T *wp_to);
void after_copy_winopt(win_T *wp); void after_copy_winopt(win_T *wp);
void copy_winopt(winopt_T *from, winopt_T *to); void copy_winopt(winopt_T *from, winopt_T *to);

View File

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

View File

@ -1,6 +1,7 @@
" Test findfile() and finddir() " Test findfile() and finddir()
source check.vim source check.vim
import './vim9.vim' as v9
let s:files = [ 'Xfinddir1/foo', let s:files = [ 'Xfinddir1/foo',
\ 'Xfinddir1/bar', \ 'Xfinddir1/bar',
@ -283,223 +284,491 @@ func Test_find_non_existing_path()
let &path = save_path let &path = save_path
endfunc endfunc
" Test for 'findexpr' " Test for 'findfunc'
func Test_findexpr() func Test_findfunc()
CheckUnix CheckUnix
call assert_equal('', &findexpr) call assert_equal('', &findfunc)
call writefile(['aFile'], 'Xfindexpr1.c', 'D') call writefile(['aFile'], 'Xfindfunc1.c', 'D')
call writefile(['bFile'], 'Xfindexpr2.c', 'D') call writefile(['bFile'], 'Xfindfunc2.c', 'D')
call writefile(['cFile'], 'Xfindexpr3.c', 'D') call writefile(['cFile'], 'Xfindfunc3.c', 'D')
" basic tests " basic tests
func FindExpr1() func FindFuncBasic(pat, cmdcomplete)
let fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c'] let fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c']
return fnames->copy()->filter('v:val =~? v:fname') return fnames->copy()->filter('v:val =~? a:pat')
endfunc endfunc
set findexpr=FindExpr1() set findfunc=FindFuncBasic
find Xfindexpr3 find Xfindfunc3
call assert_match('Xfindexpr3.c', @%) call assert_match('Xfindfunc3.c', @%)
bw! bw!
2find Xfind 2find Xfind
call assert_match('Xfindexpr2.c', @%) call assert_match('Xfindfunc2.c', @%)
bw! bw!
call assert_fails('4find Xfind', 'E347: No more file "Xfind" found in path') 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') call assert_fails('find foobar', 'E345: Can''t find file "foobar" in path')
sfind Xfindexpr2.c sfind Xfindfunc2.c
call assert_match('Xfindexpr2.c', @%) call assert_match('Xfindfunc2.c', @%)
call assert_equal(2, winnr('$')) call assert_equal(2, winnr('$'))
%bw! %bw!
call assert_fails('sfind foobar', 'E345: Can''t find file "foobar" in path') call assert_fails('sfind foobar', 'E345: Can''t find file "foobar" in path')
tabfind Xfindexpr3.c tabfind Xfindfunc3.c
call assert_match('Xfindexpr3.c', @%) call assert_match('Xfindfunc3.c', @%)
call assert_equal(2, tabpagenr()) call assert_equal(2, tabpagenr())
%bw! %bw!
call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path') 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 " Buffer-local option
set findexpr=['abc'] func GlobalFindFunc(pat, cmdcomplete)
return ['global']
endfunc
func LocalFindFunc(pat, cmdcomplete)
return ['local']
endfunc
set findfunc=GlobalFindFunc
new new
setlocal findexpr=['def'] setlocal findfunc=LocalFindFunc
find xxxx find xxxx
call assert_equal('def', @%) call assert_equal('local', @%)
wincmd w wincmd w
find xxxx find xxxx
call assert_equal('abc', @%) call assert_equal('global', @%)
aboveleft new aboveleft new
call assert_equal("['abc']", &findexpr) call assert_equal("GlobalFindFunc", &findfunc)
wincmd k wincmd k
aboveleft new aboveleft new
call assert_equal("['abc']", &findexpr) call assert_equal("GlobalFindFunc", &findfunc)
%bw! %bw!
delfunc GlobalFindFunc
delfunc LocalFindFunc
" Empty list " Assign an expression
set findexpr=[] set findfunc=[]
call assert_fails('find xxxx', 'E345: Can''t find file "xxxx" in path') call assert_fails('find xxxx', 'E117: Unknown function: []')
" Error cases " Error cases
" Syntax error in the expression " Function that doesn't any argument
set findexpr=FindExpr1{} func FindFuncNoArg()
call assert_fails('find Xfindexpr1.c', 'E15: Invalid expression') endfunc
set findfunc=FindFuncNoArg
call assert_fails('find Xfindfunc1.c', 'E118: Too many arguments for function: FindFuncNoArg')
delfunc FindFuncNoArg
" Find expression throws an error " Syntax error in the function
func FindExpr2() 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' throw 'find error'
endfunc endfunc
set findexpr=FindExpr2() set findfunc=FindFuncWithThrow
call assert_fails('find Xfindexpr1.c', 'find error') call assert_fails('find Xfindfunc1.c', 'find error')
delfunc FindFuncWithThrow
" Try using a null List as the expression " Try using a null function
set findexpr=test_null_list() call assert_fails('let &findfunc = test_null_function()', 'E129: Function name required')
call assert_fails('find Xfindexpr1.c', 'E345: Can''t find file "Xfindexpr1.c" in path')
" Try to create a new window from the find expression " Try to create a new window from the find function
func FindExpr3() func FindFuncNewWindow(pat, cmdexpand)
new new
return ["foo"] return ["foo"]
endfunc endfunc
set findexpr=FindExpr3() set findfunc=FindFuncNewWindow
call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window') 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 " Try to modify the current buffer from the find function
func FindExpr4() func FindFuncModifyBuf(pat, cmdexpand)
call setline(1, ['abc']) call setline(1, ['abc'])
return ["foo"] return ["foo"]
endfunc endfunc
set findexpr=FindExpr4() set findfunc=FindFuncModifyBuf
call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window') call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window')
delfunc FindFuncModifyBuf
" Expression returning a string " Return the wrong type from the function
set findexpr='abc' func FindFuncWrongRet(pat, cmdexpand)
call assert_fails('find Xfindexpr1.c', "E1514: 'findexpr' did not return a List type") return 'foo'
endfunc
set findfunc=FindFuncWrongRet
call assert_fails('find Xfindfunc1.c', "E1514: 'findfunc' did not return a List type")
delfunc FindFuncWrongRet
set findexpr& set findfunc&
delfunc! FindExpr1
delfunc! FindExpr2
delfunc! FindExpr3
delfunc! FindExpr4
endfunc endfunc
" Test for using a script-local function for 'findexpr' " Test for using a script-local function for 'findfunc'
func Test_findexpr_scriptlocal_func() func Test_findfunc_scriptlocal_func()
func! s:FindExprScript() func! s:FindFuncScript(pat, cmdexpand)
let g:FindExprArg = v:fname let g:FindFuncArg = a:pat
return ['xxx'] return ['xxx']
endfunc endfunc
set findexpr=s:FindExprScript() set findfunc=s:FindFuncScript
call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
new | only new | only
let g:FindExprArg = '' let g:FindFuncArg = ''
find abc find abc
call assert_equal('abc', g:FindExprArg) call assert_equal('abc', g:FindFuncArg)
bw! bw!
set findexpr=<SID>FindExprScript() set findfunc=<SID>FindFuncScript
call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
new | only new | only
let g:FindExprArg = '' let g:FindFuncArg = ''
find abc find abc
call assert_equal('abc', g:FindExprArg) call assert_equal('abc', g:FindFuncArg)
bw! bw!
let &findexpr = 's:FindExprScript()' let &findfunc = 's:FindFuncScript'
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
new | only new | only
let g:FindExprArg = '' let g:FindFuncArg = ''
find abc find abc
call assert_equal('abc', g:FindExprArg) call assert_equal('abc', g:FindFuncArg)
bw! bw!
let &findexpr = '<SID>FindExprScript()' let &findfunc = '<SID>FindFuncScript'
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
new | only new | only
let g:FindExprArg = '' let g:FindFuncArg = ''
find abc find abc
call assert_equal('abc', g:FindExprArg) call assert_equal('abc', g:FindFuncArg)
bw! bw!
set findexpr= set findfunc=
setglobal findexpr=s:FindExprScript() setglobal findfunc=s:FindFuncScript
setlocal findexpr= setlocal findfunc=
call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc)
call assert_equal('', &l:findexpr) call assert_equal('', &l:findfunc)
new | only new | only
let g:FindExprArg = '' let g:FindFuncArg = ''
find abc find abc
call assert_equal('abc', g:FindExprArg) call assert_equal('abc', g:FindFuncArg)
bw! bw!
new | only new | only
set findexpr= set findfunc=
setglobal findexpr= setglobal findfunc=
setlocal findexpr=s:FindExprScript() setlocal findfunc=s:FindFuncScript
call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc)
call assert_equal(expand('<SID>') .. 'FindExprScript()', &l:findexpr) call assert_equal(expand('<SID>') .. 'FindFuncScript', &l:findfunc)
call assert_equal('', &g:findexpr) call assert_equal('', &g:findfunc)
let g:FindExprArg = '' let g:FindFuncArg = ''
find abc find abc
call assert_equal('abc', g:FindExprArg) call assert_equal('abc', g:FindFuncArg)
bw! bw!
set findexpr= set findfunc=
delfunc s:FindExprScript delfunc s:FindFuncScript
endfunc endfunc
" Test for expanding the argument to the :find command using 'findexpr' " Test for expanding the argument to the :find command using 'findfunc'
func Test_findexpr_expand_arg() func Test_findfunc_expand_arg()
let s:fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c'] let s:fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c']
" 'findexpr' that accepts a regular expression " 'findfunc' that accepts a regular expression
func FindExprRegexp() func FindFuncRegexp(pat, cmdcomplete)
return s:fnames->copy()->filter('v:val =~? v:fname') return s:fnames->copy()->filter('v:val =~? a:pat')
endfunc endfunc
" 'findexpr' that accepts a glob " 'findfunc' that accepts a glob
func FindExprGlob() func FindFuncGlob(pat_arg, cmdcomplete)
let pat = glob2regpat(v:cmdcomplete ? $'*{v:fname}*' : v:fname) let pat = glob2regpat(a:cmdcomplete ? $'*{a:pat_arg}*' : a:pat_arg)
return s:fnames->copy()->filter('v:val =~? pat') return s:fnames->copy()->filter('v:val =~? pat')
endfunc endfunc
for regexp in [v:true, v:false] 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 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 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 ', 'cmdline'))
call assert_equal(s:fnames, getcompletion('find Xfind', 'cmdline')) call assert_equal(s:fnames, getcompletion('find Xfind', 'cmdline'))
let pat = regexp ? 'X.*1\.c' : 'X*1.c' let pat = regexp ? 'X.*1\.c' : 'X*1.c'
call feedkeys($":find {pat}\<Tab>\<C-B>\"\<CR>", "xt") call feedkeys($":find {pat}\<Tab>\<C-B>\"\<CR>", "xt")
call assert_equal('"find Xfindexpr1.c', @:) call assert_equal('"find Xfindfunc1.c', @:)
call assert_equal(['Xfindexpr1.c'], getcompletion($'find {pat}', 'cmdline')) call assert_equal(['Xfindfunc1.c'], getcompletion($'find {pat}', 'cmdline'))
call feedkeys(":find 3\<Tab>\<C-B>\"\<CR>", "xt") call feedkeys(":find 3\<Tab>\<C-B>\"\<CR>", "xt")
call assert_equal('"find Xfindexpr3.c', @:) call assert_equal('"find Xfindfunc3.c', @:)
call assert_equal(['Xfindexpr3.c'], getcompletion($'find 3', 'cmdline')) call assert_equal(['Xfindfunc3.c'], getcompletion($'find 3', 'cmdline'))
call feedkeys(":find Xfind\<C-A>\<C-B>\"\<CR>", "xt") 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 feedkeys(":find abc\<Tab>\<C-B>\"\<CR>", "xt")
call assert_equal('"find abc', @:) call assert_equal('"find abc', @:)
call assert_equal([], getcompletion('find abc', 'cmdline')) call assert_equal([], getcompletion('find abc', 'cmdline'))
endfor endfor
set findexpr& set findfunc&
delfunc! FindExprRegexp delfunc! FindFuncRegexp
delfunc! FindExprGlob delfunc! FindFuncGlob
unlet s:fnames unlet s:fnames
endfunc 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 " 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('equalprg', 'equalprg=Something()', 'E520:')
call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:') call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:')
call s:modeline_fails('exrc', 'exrc=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('formatprg', 'formatprg=Something()', 'E520:')
call s:modeline_fails('fsync', 'fsync=Something()', 'E520:') call s:modeline_fails('fsync', 'fsync=Something()', 'E520:')
call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:') call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:')

View File

@ -1570,7 +1570,7 @@ endfunc
" Test for changing options in a sandbox " Test for changing options in a sandbox
func Test_opt_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 set ' .. opt .. '?', 'E48:')
call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:') call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:')
endfor endfor

View File

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

View File

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

View File

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