vim-patch:9.1.0831: 'findexpr' can't be used as lambad or Funcref (#31058)

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

related: vim/vim#15905
closes: vim/vim#15976

a13f3a4f5d

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
zeertzjq
2024-11-03 10:06:41 +08:00
committed by GitHub
parent ed3fb1bb9a
commit 3075c69ff0
25 changed files with 604 additions and 292 deletions

View File

@ -370,10 +370,11 @@ Note: In the future more global options can be made |global-local|. Using
":setlocal" on a global option might work differently then.
*option-value-function*
Some options ('completefunc', 'omnifunc', 'operatorfunc', 'quickfixtextfunc',
'tagfunc' and 'thesaurusfunc') are set to a function name or a function
reference or a lambda function. When using a lambda it will be converted to
the name, e.g. "<lambda>123". Examples:
Some options ('completefunc', 'findfunc', 'omnifunc', 'operatorfunc',
'quickfixtextfunc', 'tagfunc' and 'thesaurusfunc') are set to a function name
or a function reference or a lambda function. When using a lambda it will be
converted to the name, e.g. "<lambda>123".
Examples:
>
set opfunc=MyOpFunc
set opfunc=function('MyOpFunc')
@ -2598,34 +2599,34 @@ A jump table for the options with a short description can be found at |Q_op|.
eob EndOfBuffer |hl-EndOfBuffer|
lastline NonText |hl-NonText|
*'findexpr'* *'fexpr'* *E1514*
'findexpr' 'fexpr' string (default "")
*'findfunc'* *'ffu'* *E1514*
'findfunc' 'ffu' string (default "")
global or local to buffer |global-local|
Expression that is evaluated to obtain the filename(s) for the |:find|
Function that is called to obtain the filename(s) for the |:find|
command. When this option is empty, the internal |file-searching|
mechanism is used.
While evaluating the expression, the |v:fname| variable is set to the
argument of the |:find| command.
The value can be the name of a function, a |lambda| or a |Funcref|.
See |option-value-function| for more information.
The expression is evaluated only once per |:find| command invocation.
The expression can process all the directories specified in 'path'.
The function is called with two arguments. The first argument is a
|String| and is the |:find| command argument. The second argument is
a |Boolean| and is set to |v:true| when the function is called to get
a List of command-line completion matches for the |:find| command.
The function should return a List of strings.
The expression may be evaluated for command-line completion as well,
in which case the |v:cmdcomplete| variable will be set to |v:true|,
otherwise it will be set to |v:false|.
The function is called only once per |:find| command invocation.
The function can process all the directories specified in 'path'.
If a match is found, the expression should return a |List| containing
one or more file names. If a match is not found, the expression
If a match is found, the function should return a |List| containing
one or more file names. If a match is not found, the function
should return an empty List.
If any errors are encountered during the expression evaluation, an
If any errors are encountered during the function invocation, an
empty List is used as the return value.
Using a function call without arguments is faster |expr-option-function|
It is not allowed to change text or jump to another window while
evaluating 'findexpr' |textlock|.
executing the 'findfunc' |textlock|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
@ -2633,18 +2634,18 @@ A jump table for the options with a short description can be found at |Q_op|.
Examples:
>vim
" Use glob()
func FindExprGlob()
let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname
func FindFuncGlob(cmdarg, cmdcomplete)
let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
return glob(pat, v:false, v:true)
endfunc
set findexpr=FindExprGlob()
set findfunc=FindFuncGlob
" Use the 'git ls-files' output
func FindGitFiles()
func FindGitFiles(cmdarg, cmdcomplete)
let fnames = systemlist('git ls-files')
return fnames->filter('v:val =~? v:fname')
return fnames->filter('v:val =~? a:cmdarg')
endfunc
set findexpr=FindGitFiles()
set findfunc=FindGitFiles
<
*'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'*

View File

@ -705,7 +705,7 @@ Short explanation of each option: *option-list*
'fileignorecase' 'fic' ignore case when using file names
'filetype' 'ft' type of file, used for autocommands
'fillchars' 'fcs' characters to use for displaying special items
'findexpr' 'fexpr' expression to evaluate for |:find|
'findfunc' 'ffu' function to be called for the |:find| command
'fixendofline' 'fixeol' make sure last line in file has <EOL>
'foldclose' 'fcl' close a fold when the cursor leaves it
'foldcolumn' 'fdc' width of the column used to indicate folds

View File

@ -48,11 +48,6 @@ v:cmdbang
can only be used in autocommands. For user commands |<bang>|
can be used.
*v:cmdcomplete* *cmdcomplete-variable*
v:cmdcomplete
When evaluating 'findexpr': if 'findexpr' is used for cmdline
completion the value is |v:true|, otherwise it is |v:false|.
*v:collate* *collate-variable*
v:collate
The current locale setting for collation order of the runtime
@ -259,8 +254,7 @@ v:fcs_reason
*v:fname* *fname-variable*
v:fname
When evaluating 'includeexpr': the file name that was
detected. When evaluating 'findexpr': the argument passed to
the |:find| command. Empty otherwise.
detected. Empty otherwise.
*v:fname_diff* *fname_diff-variable*
v:fname_diff

View File

@ -2294,31 +2294,31 @@ vim.wo.fcs = vim.wo.fillchars
vim.go.fillchars = vim.o.fillchars
vim.go.fcs = vim.go.fillchars
--- Expression that is evaluated to obtain the filename(s) for the `:find`
--- Function that is called to obtain the filename(s) for the `:find`
--- command. When this option is empty, the internal `file-searching`
--- mechanism is used.
---
--- While evaluating the expression, the `v:fname` variable is set to the
--- argument of the `:find` command.
--- The value can be the name of a function, a `lambda` or a `Funcref`.
--- See `option-value-function` for more information.
---
--- The expression is evaluated only once per `:find` command invocation.
--- The expression can process all the directories specified in 'path'.
--- The function is called with two arguments. The first argument is a
--- `String` and is the `:find` command argument. The second argument is
--- a `Boolean` and is set to `v:true` when the function is called to get
--- a List of command-line completion matches for the `:find` command.
--- The function should return a List of strings.
---
--- The expression may be evaluated for command-line completion as well,
--- in which case the `v:cmdcomplete` variable will be set to `v:true`,
--- otherwise it will be set to `v:false`.
--- The function is called only once per `:find` command invocation.
--- The function can process all the directories specified in 'path'.
---
--- If a match is found, the expression should return a `List` containing
--- one or more file names. If a match is not found, the expression
--- If a match is found, the function should return a `List` containing
--- one or more file names. If a match is not found, the function
--- should return an empty List.
---
--- If any errors are encountered during the expression evaluation, an
--- If any errors are encountered during the function invocation, an
--- empty List is used as the return value.
---
--- Using a function call without arguments is faster `expr-option-function`
---
--- It is not allowed to change text or jump to another window while
--- evaluating 'findexpr' `textlock`.
--- executing the 'findfunc' `textlock`.
---
--- This option cannot be set from a `modeline` or in the `sandbox`, for
--- security reasons.
@ -2327,28 +2327,28 @@ vim.go.fcs = vim.go.fillchars
---
--- ```vim
--- " Use glob()
--- func FindExprGlob()
--- let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname
--- func FindFuncGlob(cmdarg, cmdcomplete)
--- let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
--- return glob(pat, v:false, v:true)
--- endfunc
--- set findexpr=FindExprGlob()
--- set findfunc=FindFuncGlob
---
--- " Use the 'git ls-files' output
--- func FindGitFiles()
--- func FindGitFiles(cmdarg, cmdcomplete)
--- let fnames = systemlist('git ls-files')
--- return fnames->filter('v:val =~? v:fname')
--- return fnames->filter('v:val =~? a:cmdarg')
--- endfunc
--- set findexpr=FindGitFiles()
--- set findfunc=FindGitFiles
--- ```
---
---
--- @type string
vim.o.findexpr = ""
vim.o.fexpr = vim.o.findexpr
vim.bo.findexpr = vim.o.findexpr
vim.bo.fexpr = vim.bo.findexpr
vim.go.findexpr = vim.o.findexpr
vim.go.fexpr = vim.go.findexpr
vim.o.findfunc = ""
vim.o.ffu = vim.o.findfunc
vim.bo.findfunc = vim.o.findfunc
vim.bo.ffu = vim.bo.findfunc
vim.go.findfunc = vim.o.findfunc
vim.go.ffu = vim.go.findfunc
--- When writing a file and this option is on, <EOL> at the end of file
--- will be restored if missing. Turn this option off if you want to

View File

@ -44,11 +44,6 @@ vim.v.cmdarg = ...
--- @type integer
vim.v.cmdbang = ...
--- When evaluating 'findexpr': if 'findexpr' is used for cmdline
--- completion the value is `v:true`, otherwise it is `v:false`.
--- @type boolean
vim.v.cmdcomplete = ...
--- The current locale setting for collation order of the runtime
--- environment. This allows Vim scripts to be aware of the
--- current locale encoding. Technical: it's the value of
@ -272,8 +267,7 @@ vim.v.fcs_choice = ...
vim.v.fcs_reason = ...
--- When evaluating 'includeexpr': the file name that was
--- detected. When evaluating 'findexpr': the argument passed to
--- the `:find` command. Empty otherwise.
--- detected. Empty otherwise.
--- @type string
vim.v.fname = ...

View File

@ -2049,7 +2049,6 @@ void free_buf_options(buf_T *buf, bool free_p_ff)
clear_string_option(&buf->b_p_indk);
clear_string_option(&buf->b_p_fp);
clear_string_option(&buf->b_p_fex);
clear_string_option(&buf->b_p_fexpr);
clear_string_option(&buf->b_p_kp);
clear_string_option(&buf->b_p_mps);
clear_string_option(&buf->b_p_fo);
@ -2098,6 +2097,8 @@ void free_buf_options(buf_T *buf, bool free_p_ff)
clear_string_option(&buf->b_p_tc);
clear_string_option(&buf->b_p_tfu);
callback_free(&buf->b_tfu_cb);
clear_string_option(&buf->b_p_ffu);
callback_free(&buf->b_ffu_cb);
clear_string_option(&buf->b_p_dict);
clear_string_option(&buf->b_p_tsr);
clear_string_option(&buf->b_p_qe);

View File

@ -543,8 +543,10 @@ struct file_buffer {
Callback b_cfu_cb; ///< 'completefunc' callback
char *b_p_ofu; ///< 'omnifunc'
Callback b_ofu_cb; ///< 'omnifunc' callback
char *b_p_tfu; ///< 'tagfunc'
char *b_p_tfu; ///< 'tagfunc' option value
Callback b_tfu_cb; ///< 'tagfunc' callback
char *b_p_ffu; ///< 'findfunc' option value
Callback b_ffu_cb; ///< 'findfunc' callback
int b_p_eof; ///< 'endoffile'
int b_p_eol; ///< 'endofline'
int b_p_fixeol; ///< 'fixendofline'
@ -608,7 +610,6 @@ struct file_buffer {
char *b_p_mp; ///< 'makeprg' local value
char *b_p_efm; ///< 'errorformat' local value
char *b_p_ep; ///< 'equalprg' local value
char *b_p_fexpr; ///< 'findexpr' local value
char *b_p_path; ///< 'path' local value
int b_p_ar; ///< 'autoread' local value
char *b_p_tags; ///< 'tags' local value

View File

@ -109,7 +109,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
&& xp->xp_context != EXPAND_FILES
&& xp->xp_context != EXPAND_FILES_IN_PATH
&& xp->xp_context != EXPAND_FILETYPE
&& xp->xp_context != EXPAND_FINDEXPR
&& xp->xp_context != EXPAND_FINDFUNC
&& xp->xp_context != EXPAND_HELP
&& xp->xp_context != EXPAND_KEYMAP
&& xp->xp_context != EXPAND_LUA
@ -1229,7 +1229,7 @@ char *addstar(char *fname, size_t len, int context)
// For help tags the translation is done in find_help_tags().
// For a tag pattern starting with "/" no translation is needed.
if (context == EXPAND_FINDEXPR
if (context == EXPAND_FINDFUNC
|| context == EXPAND_HELP
|| context == EXPAND_COLORS
|| context == EXPAND_COMPILER
@ -1829,7 +1829,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
case CMD_sfind:
case CMD_tabfind:
if (xp->xp_context == EXPAND_FILES) {
xp->xp_context = *get_findexpr() != NUL ? EXPAND_FINDEXPR : EXPAND_FILES_IN_PATH;
xp->xp_context = *get_findfunc() != NUL ? EXPAND_FINDFUNC : EXPAND_FILES_IN_PATH;
}
break;
case CMD_cd:
@ -2500,8 +2500,8 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int *
}
int ret = FAIL;
if (xp->xp_context == EXPAND_FINDEXPR) {
ret = expand_findexpr(pat, matches, numMatches);
if (xp->xp_context == EXPAND_FINDFUNC) {
ret = expand_findfunc(pat, matches, numMatches);
} else {
if (xp->xp_context == EXPAND_FILES) {
flags |= EW_FILE;
@ -2722,7 +2722,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM
if (xp->xp_context == EXPAND_FILES
|| xp->xp_context == EXPAND_DIRECTORIES
|| xp->xp_context == EXPAND_FILES_IN_PATH
|| xp->xp_context == EXPAND_FINDEXPR
|| xp->xp_context == EXPAND_FINDFUNC
|| xp->xp_context == EXPAND_DIRS_IN_CDPATH) {
return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options);
}

View File

@ -107,7 +107,7 @@ enum {
EXPAND_KEYMAP,
EXPAND_DIRS_IN_CDPATH,
EXPAND_SHELLCMDLINE,
EXPAND_FINDEXPR,
EXPAND_FINDFUNC,
EXPAND_CHECKHEALTH,
EXPAND_LUA,
};

View File

@ -186,7 +186,7 @@ INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch")
EXTERN const char e_winfixbuf_cannot_go_to_buffer[]
INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled"));
EXTERN const char e_invalid_return_type_from_findexpr[] INIT( = N_("E1514: 'findexpr' did not return a List type"));
EXTERN const char e_invalid_return_type_from_findfunc[] INIT( = N_("E1514: 'findfunc' did not return a List type"));
EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));

View File

@ -270,7 +270,6 @@ static struct vimvar {
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO),
VV(VV_CMDCOMPLETE, "cmdcomplete", VAR_BOOL, VV_RO),
// Neovim
VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
@ -462,7 +461,6 @@ void eval_init(void)
set_vim_var_nr(VV_HLSEARCH, 1);
set_vim_var_nr(VV_COUNT1, 1);
set_vim_var_special(VV_EXITING, kSpecialVarNull);
set_vim_var_bool(VV_CMDCOMPLETE, kBoolVarFalse);
set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER);
set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING);
@ -4793,6 +4791,7 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_in_callback)(&buf->b_ofu_cb, copyID, NULL, NULL);
ABORTING(set_ref_in_callback)(&buf->b_tsrfu_cb, copyID, NULL, NULL);
ABORTING(set_ref_in_callback)(&buf->b_tfu_cb, copyID, NULL, NULL);
ABORTING(set_ref_in_callback)(&buf->b_ffu_cb, copyID, NULL, NULL);
}
// 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks
@ -4804,6 +4803,9 @@ bool garbage_collect(bool testing)
// 'tagfunc' callback
ABORTING(set_ref_in_tagfunc)(copyID);
// 'findfunc' callback
ABORTING(set_ref_in_findfunc)(copyID);
FOR_ALL_TAB_WINDOWS(tp, wp) {
// window-local variables
ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL);

View File

@ -167,7 +167,6 @@ typedef enum {
VV_COLLATE,
VV_EXITING,
VV_MAXCOL,
VV_CMDCOMPLETE,
// Nvim
VV_STDERR,
VV_MSGPACK_TYPES,

View File

@ -5165,55 +5165,68 @@ static void ex_wrongmodifier(exarg_T *eap)
eap->errmsg = _(e_invcmd);
}
/// Evaluate the 'findexpr' expression and return the result. When evaluating
/// the expression, v:fname is set to the ":find" command argument.
static list_T *eval_findexpr(const char *pat, bool cmdcomplete)
/// callback function for 'findfunc'
static Callback ffu_cb;
static Callback *get_findfunc_callback(void)
{
return *curbuf->b_p_ffu != NUL ? &curbuf->b_ffu_cb : &ffu_cb;
}
/// Call 'findfunc' to obtain the list of file names.
static list_T *call_findfunc(char *pat, BoolVarValue cmdcomplete)
{
const sctx_T saved_sctx = current_sctx;
char *findexpr = get_findexpr();
set_vim_var_string(VV_FNAME, pat, -1);
set_vim_var_bool(VV_CMDCOMPLETE, cmdcomplete ? kBoolVarTrue : kBoolVarFalse);
current_sctx = curbuf->b_p_script_ctx[BV_FEXPR].script_ctx;
char *arg = skipwhite(findexpr);
typval_T args[3];
args[0].v_type = VAR_STRING;
args[0].vval.v_string = pat;
args[1].v_type = VAR_BOOL;
args[1].vval.v_bool = cmdcomplete;
args[2].v_type = VAR_UNKNOWN;
// 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.
typval_T tv;
list_T *retlist = NULL;
if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) {
retlist = NULL;
} else {
if (tv.v_type == VAR_LIST) {
retlist = tv_list_copy(NULL, tv.vval.v_list, true, get_copyID());
} else {
emsg(_(e_invalid_return_type_from_findexpr));
}
tv_clear(&tv);
sctx_T *ctx = get_option_sctx(kOptFindfunc);
if (ctx != NULL) {
current_sctx = *ctx;
}
textlock--;
clear_evalarg(&EVALARG_EVALUATE, NULL);
set_vim_var_string(VV_FNAME, NULL, 0);
set_vim_var_bool(VV_CMDCOMPLETE, kBoolVarFalse);
Callback *cb = get_findfunc_callback();
typval_T rettv;
int retval = callback_call(cb, 2, args, &rettv);
current_sctx = saved_sctx;
textlock--;
list_T *retlist = NULL;
if (retval == OK) {
if (rettv.v_type == VAR_LIST) {
retlist = tv_list_copy(NULL, rettv.vval.v_list, false, get_copyID());
} else {
emsg(_(e_invalid_return_type_from_findfunc));
}
tv_clear(&rettv);
}
return retlist;
}
/// Find file names matching "pat" using 'findexpr' and return it in "files".
/// Find file names matching "pat" using 'findfunc' and return it in "files".
/// Used for expanding the :find, :sfind and :tabfind command argument.
/// Returns OK on success and FAIL otherwise.
int expand_findexpr(const char *pat, char ***files, int *numMatches)
int expand_findfunc(char *pat, char ***files, int *numMatches)
{
*numMatches = 0;
*files = NULL;
list_T *l = eval_findexpr(pat, true);
list_T *l = call_findfunc(pat, kBoolVarTrue);
if (l == NULL) {
return FAIL;
}
@ -5240,16 +5253,16 @@ int expand_findexpr(const char *pat, char ***files, int *numMatches)
return OK;
}
/// Use 'findexpr' to find file 'findarg'. The 'count' argument is used to find
/// Use 'findfunc' to find file 'findarg'. The 'count' argument is used to find
/// the n'th matching file.
static char *findexpr_find_file(char *findarg, size_t findarg_len, int count)
static char *findfunc_find_file(char *findarg, size_t findarg_len, int count)
{
char *ret_fname = NULL;
const char cc = findarg[findarg_len];
findarg[findarg_len] = NUL;
list_T *fname_list = eval_findexpr(findarg, false);
list_T *fname_list = call_findfunc(findarg, kBoolVarFalse);
int fname_count = tv_list_len(fname_list);
if (fname_count == 0) {
@ -5274,6 +5287,51 @@ static char *findexpr_find_file(char *findarg, size_t findarg_len, int count)
return ret_fname;
}
/// Process the 'findfunc' option value.
/// Returns NULL on success and an error message on failure.
const char *did_set_findfunc(optset_T *args)
{
buf_T *buf = (buf_T *)args->os_buf;
int retval;
if (*buf->b_p_ffu != NUL) {
// buffer-local option set
retval = option_set_callback_func(buf->b_p_ffu, &buf->b_ffu_cb);
} else {
// global option set
retval = option_set_callback_func(p_ffu, &ffu_cb);
}
if (retval == FAIL) {
return e_invarg;
}
// If the option value starts with <SID> or s:, then replace that with
// the script identifier.
char **varp = (char **)args->os_varp;
char *name = get_scriptlocal_funcname(*varp);
if (name != NULL) {
free_string_option(*varp);
*varp = name;
}
return NULL;
}
void free_findfunc_option(void)
{
callback_free(&ffu_cb);
}
/// Mark the global 'findfunc' callback with "copyID" so that it is not
/// garbage collected.
bool set_ref_in_findfunc(int copyID)
{
bool abort = false;
abort = set_ref_in_callback(&ffu_cb, copyID, NULL, NULL);
return abort;
}
/// :sview [+command] file split window with new file, read-only
/// :split [[+command] file] split window with current or new file
/// :vsplit [[+command] file] split window vertically with current or new file
@ -5305,8 +5363,8 @@ void ex_splitview(exarg_T *eap)
}
if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
if (*get_findexpr() != NUL) {
fname = findexpr_find_file(eap->arg, strlen(eap->arg),
if (*get_findfunc() != NUL) {
fname = findfunc_find_file(eap->arg, strlen(eap->arg),
eap->addr_count > 0 ? eap->line2 : 1);
} else {
char *file_to_find = NULL;
@ -5512,8 +5570,8 @@ static void ex_find(exarg_T *eap)
}
char *fname = NULL;
if (*get_findexpr() != NUL) {
fname = findexpr_find_file(eap->arg, strlen(eap->arg),
if (*get_findfunc() != NUL) {
fname = findfunc_find_file(eap->arg, strlen(eap->arg),
eap->addr_count > 0 ? eap->line2 : 1);
} else {
char *file_to_find = NULL;

View File

@ -260,6 +260,7 @@ local function dump_option(i, o)
end
w([[
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/insexpand.h"
#include "nvim/mapping.h"

View File

@ -578,6 +578,7 @@ void free_all_options(void)
}
free_operatorfunc_option();
free_tagfunc_option();
free_findfunc_option();
XFREE_CLEAR(fenc_default);
XFREE_CLEAR(p_term);
XFREE_CLEAR(p_ttytype);
@ -4472,8 +4473,8 @@ void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
switch ((int)p->indir) {
case PV_FP:
return &(buf->b_p_fp);
case PV_FEXPR:
return &(buf->b_p_fexpr);
case PV_FFU:
return &(buf->b_p_ffu);
case PV_EFM:
return &(buf->b_p_efm);
case PV_GP:
@ -4595,8 +4596,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var;
case PV_FP:
return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var;
case PV_FEXPR:
return *buf->b_p_fexpr != NUL ? &(buf->b_p_fexpr) : p->var;
case PV_FFU:
return *buf->b_p_ffu != NUL ? &(buf->b_p_ffu) : p->var;
case PV_EFM:
return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var;
case PV_GP:
@ -4868,13 +4869,13 @@ char *get_equalprg(void)
return curbuf->b_p_ep;
}
/// Get the value of 'findexpr', either the buffer-local one or the global one.
char *get_findexpr(void)
/// Get the value of 'findfunc', either the buffer-local one or the global one.
char *get_findfunc(void)
{
if (*curbuf->b_p_fexpr == NUL) {
return p_fexpr;
if (*curbuf->b_p_ffu == NUL) {
return p_ffu;
}
return curbuf->b_p_fexpr;
return curbuf->b_p_ffu;
}
/// Copy options from one window to another.
@ -5275,8 +5276,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_mp = empty_string_option;
buf->b_p_efm = empty_string_option;
buf->b_p_ep = empty_string_option;
buf->b_p_fexpr = xstrdup(p_fexpr);
COPY_OPT_SCTX(buf, BV_FEXPR);
buf->b_p_ffu = empty_string_option;
buf->b_p_kp = empty_string_option;
buf->b_p_path = empty_string_option;
buf->b_p_tags = empty_string_option;

View File

@ -451,7 +451,7 @@ EXTERN char *p_ffs; ///< 'fileformats'
EXTERN int p_fic; ///< 'fileignorecase'
EXTERN char *p_ft; ///< 'filetype'
EXTERN char *p_fcs; ///< 'fillchar'
EXTERN char *p_fexpr; ///< 'findexpr'
EXTERN char *p_ffu; ///< 'findfunc'
EXTERN int p_fixeol; ///< 'fixendofline'
EXTERN char *p_fcl; ///< 'foldclose'
EXTERN OptInt p_fdls; ///< 'foldlevelstart'

View File

@ -2906,35 +2906,35 @@ return {
varname = 'p_fcs',
},
{
abbreviation = 'fexpr',
cb = 'did_set_optexpr',
abbreviation = 'ffu',
cb = 'did_set_findfunc',
defaults = { if_true = '' },
desc = [=[
Expression that is evaluated to obtain the filename(s) for the |:find|
Function that is called to obtain the filename(s) for the |:find|
command. When this option is empty, the internal |file-searching|
mechanism is used.
While evaluating the expression, the |v:fname| variable is set to the
argument of the |:find| command.
The value can be the name of a function, a |lambda| or a |Funcref|.
See |option-value-function| for more information.
The expression is evaluated only once per |:find| command invocation.
The expression can process all the directories specified in 'path'.
The function is called with two arguments. The first argument is a
|String| and is the |:find| command argument. The second argument is
a |Boolean| and is set to |v:true| when the function is called to get
a List of command-line completion matches for the |:find| command.
The function should return a List of strings.
The expression may be evaluated for command-line completion as well,
in which case the |v:cmdcomplete| variable will be set to |v:true|,
otherwise it will be set to |v:false|.
The function is called only once per |:find| command invocation.
The function can process all the directories specified in 'path'.
If a match is found, the expression should return a |List| containing
one or more file names. If a match is not found, the expression
If a match is found, the function should return a |List| containing
one or more file names. If a match is not found, the function
should return an empty List.
If any errors are encountered during the expression evaluation, an
If any errors are encountered during the function invocation, an
empty List is used as the return value.
Using a function call without arguments is faster |expr-option-function|
It is not allowed to change text or jump to another window while
evaluating 'findexpr' |textlock|.
executing the 'findfunc' |textlock|.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
@ -2942,27 +2942,28 @@ return {
Examples:
>vim
" Use glob()
func FindExprGlob()
let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname
func FindFuncGlob(cmdarg, cmdcomplete)
let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
return glob(pat, v:false, v:true)
endfunc
set findexpr=FindExprGlob()
set findfunc=FindFuncGlob
" Use the 'git ls-files' output
func FindGitFiles()
func FindGitFiles(cmdarg, cmdcomplete)
let fnames = systemlist('git ls-files')
return fnames->filter('v:val =~? v:fname')
return fnames->filter('v:val =~? a:cmdarg')
endfunc
set findexpr=FindGitFiles()
set findfunc=FindGitFiles
<
]=],
full_name = 'findexpr',
full_name = 'findfunc',
func = true,
scope = { 'global', 'buffer' },
secure = true,
short_desc = N_('expression used for :find'),
short_desc = N_('function called for :find'),
tags = { 'E1514' },
type = 'string',
varname = 'p_fexpr',
varname = 'p_ffu',
},
{
abbreviation = 'fixeol',

View File

@ -233,9 +233,9 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_mp);
check_string_option(&buf->b_p_efm);
check_string_option(&buf->b_p_ep);
check_string_option(&buf->b_p_fexpr);
check_string_option(&buf->b_p_path);
check_string_option(&buf->b_p_tags);
check_string_option(&buf->b_p_ffu);
check_string_option(&buf->b_p_tfu);
check_string_option(&buf->b_p_tc);
check_string_option(&buf->b_p_dict);
@ -1886,9 +1886,8 @@ int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches)
matches);
}
/// One of the '*expr' options is changed:, 'diffexpr', 'findexpr',
/// 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', 'indentexpr',
/// 'patchexpr' and 'charconvert'.
/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext',
/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'.
const char *did_set_optexpr(optset_T *args)
{
char **varp = (char **)args->os_varp;

View File

@ -50,13 +50,6 @@ M.vars = {
can be used.
]=],
},
cmdcomplete = {
type = 'boolean',
desc = [=[
When evaluating 'findexpr': if 'findexpr' is used for cmdline
completion the value is |v:true|, otherwise it is |v:false|.
]=],
},
collate = {
type = 'string',
desc = [=[
@ -291,8 +284,7 @@ M.vars = {
type = 'string',
desc = [=[
When evaluating 'includeexpr': the file name that was
detected. When evaluating 'findexpr': the argument passed to
the |:find| command. Empty otherwise.
detected. Empty otherwise.
]=],
},
fname_diff = {

View File

@ -1,6 +1,7 @@
" Test findfile() and finddir()
source check.vim
source vim9.vim
let s:files = [ 'Xfinddir1/foo',
\ 'Xfinddir1/bar',
@ -288,223 +289,491 @@ func Test_find_non_existing_path()
let &path = save_path
endfunc
" Test for 'findexpr'
func Test_findexpr()
" Test for 'findfunc'
func Test_findfunc()
CheckUnix
call assert_equal('', &findexpr)
call writefile(['aFile'], 'Xfindexpr1.c', 'D')
call writefile(['bFile'], 'Xfindexpr2.c', 'D')
call writefile(['cFile'], 'Xfindexpr3.c', 'D')
call assert_equal('', &findfunc)
call writefile(['aFile'], 'Xfindfunc1.c', 'D')
call writefile(['bFile'], 'Xfindfunc2.c', 'D')
call writefile(['cFile'], 'Xfindfunc3.c', 'D')
" basic tests
func FindExpr1()
let fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c']
return fnames->copy()->filter('v:val =~? v:fname')
func FindFuncBasic(pat, cmdcomplete)
let fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c']
return fnames->copy()->filter('v:val =~? a:pat')
endfunc
set findexpr=FindExpr1()
find Xfindexpr3
call assert_match('Xfindexpr3.c', @%)
set findfunc=FindFuncBasic
find Xfindfunc3
call assert_match('Xfindfunc3.c', @%)
bw!
2find Xfind
call assert_match('Xfindexpr2.c', @%)
call assert_match('Xfindfunc2.c', @%)
bw!
call assert_fails('4find Xfind', 'E347: No more file "Xfind" found in path')
call assert_fails('find foobar', 'E345: Can''t find file "foobar" in path')
sfind Xfindexpr2.c
call assert_match('Xfindexpr2.c', @%)
sfind Xfindfunc2.c
call assert_match('Xfindfunc2.c', @%)
call assert_equal(2, winnr('$'))
%bw!
call assert_fails('sfind foobar', 'E345: Can''t find file "foobar" in path')
tabfind Xfindexpr3.c
call assert_match('Xfindexpr3.c', @%)
tabfind Xfindfunc3.c
call assert_match('Xfindfunc3.c', @%)
call assert_equal(2, tabpagenr())
%bw!
call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path')
" Test garbage collection
call test_garbagecollect_now()
find Xfindfunc2
call assert_match('Xfindfunc2.c', @%)
bw!
delfunc FindFuncBasic
call test_garbagecollect_now()
call assert_fails('find Xfindfunc2', 'E117: Unknown function: FindFuncBasic')
" Buffer-local option
set findexpr=['abc']
func GlobalFindFunc(pat, cmdcomplete)
return ['global']
endfunc
func LocalFindFunc(pat, cmdcomplete)
return ['local']
endfunc
set findfunc=GlobalFindFunc
new
setlocal findexpr=['def']
setlocal findfunc=LocalFindFunc
find xxxx
call assert_equal('def', @%)
call assert_equal('local', @%)
wincmd w
find xxxx
call assert_equal('abc', @%)
call assert_equal('global', @%)
aboveleft new
call assert_equal("['abc']", &findexpr)
call assert_equal("GlobalFindFunc", &findfunc)
wincmd k
aboveleft new
call assert_equal("['abc']", &findexpr)
call assert_equal("GlobalFindFunc", &findfunc)
%bw!
delfunc GlobalFindFunc
delfunc LocalFindFunc
" Empty list
set findexpr=[]
call assert_fails('find xxxx', 'E345: Can''t find file "xxxx" in path')
" Assign an expression
set findfunc=[]
call assert_fails('find xxxx', 'E117: Unknown function: []')
" Error cases
" Syntax error in the expression
set findexpr=FindExpr1{}
call assert_fails('find Xfindexpr1.c', 'E15: Invalid expression')
" Function that doesn't any argument
func FindFuncNoArg()
endfunc
set findfunc=FindFuncNoArg
call assert_fails('find Xfindfunc1.c', 'E118: Too many arguments for function: FindFuncNoArg')
delfunc FindFuncNoArg
" Find expression throws an error
func FindExpr2()
" Syntax error in the function
func FindFuncSyntaxError(pat, cmdcomplete)
return l
endfunc
set findfunc=FindFuncSyntaxError
call assert_fails('find Xfindfunc1.c', 'E121: Undefined variable: l')
delfunc FindFuncSyntaxError
" Find function throws an error
func FindFuncWithThrow(pat, cmdcomplete)
throw 'find error'
endfunc
set findexpr=FindExpr2()
call assert_fails('find Xfindexpr1.c', 'find error')
set findfunc=FindFuncWithThrow
call assert_fails('find Xfindfunc1.c', 'find error')
delfunc FindFuncWithThrow
" Try using a null List as the expression
set findexpr=v:_null_list
call assert_fails('find Xfindexpr1.c', 'E345: Can''t find file "Xfindexpr1.c" in path')
" Try using a null function
"call assert_fails('let &findfunc = test_null_function()', 'E129: Function name required')
" Try to create a new window from the find expression
func FindExpr3()
" Try to create a new window from the find function
func FindFuncNewWindow(pat, cmdexpand)
new
return ["foo"]
endfunc
set findexpr=FindExpr3()
call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window')
set findfunc=FindFuncNewWindow
call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window')
delfunc FindFuncNewWindow
" Try to modify the current buffer from the find expression
func FindExpr4()
" Try to modify the current buffer from the find function
func FindFuncModifyBuf(pat, cmdexpand)
call setline(1, ['abc'])
return ["foo"]
endfunc
set findexpr=FindExpr4()
call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window')
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 CheckTransLegacySuccess(lines)
" Test for using a script-local function name
func s:FindFunc2(pat, cmdexpand)
let g:FindFunc2Args = [a:pat, a:cmdexpand]
return ['findfunc2']
endfunc
set findfunc=s:FindFunc2
let g:FindFunc2Args = []
find abc18
call assert_equal(['abc18', v:false], g:FindFunc2Args)
let &findfunc = 's:FindFunc2'
let g:FindFunc2Args = []
find abc19
call assert_equal(['abc19', v:false], g:FindFunc2Args)
delfunc s:FindFunc2
" Using Vim9 lambda expression in legacy context should fail
set findfunc=(pat,\ cmdexpand)\ =>\ FindFunc1(pat,\ v:false)
let g:FindFunc1Args = []
call assert_fails('find abc20', 'E117:')
call assert_equal([], g:FindFunc1Args)
" set 'findfunc' to a partial with dict.
func SetFindFunc()
let operator = {'execute': function('FindFuncExecute')}
let &findfunc = operator.execute
endfunc
func FindFuncExecute(pat, cmdexpand) dict
return ['findfuncexecute']
endfunc
call SetFindFunc()
call test_garbagecollect_now()
set findfunc=
delfunc SetFindFunc
delfunc FindFuncExecute
func FindFunc2(pat, cmdexpand)
let g:FindFunc2Args = [a:pat, a:cmdexpand]
return ['findfunc2']
endfunc
" Vim9 tests
let lines =<< trim END
vim9script
def g:Vim9findFunc(pat: string, cmdexpand: bool): list<string>
g:FindFunc1Args = [pat, cmdexpand]
return ['vim9findfunc']
enddef
# Test for using a def function with findfunc
set findfunc=function('g:Vim9findFunc')
g:FindFunc1Args = []
find abc21
assert_equal(['abc21', false], g:FindFunc1Args)
# Test for using a global function name
&findfunc = g:FindFunc2
g:FindFunc2Args = []
find abc22
assert_equal(['abc22', false], g:FindFunc2Args)
bw!
# Test for using a script-local function name
def LocalFindFunc(pat: string, cmdexpand: bool): list<string>
g:LocalFindFuncArgs = [pat, cmdexpand]
return ['localfindfunc']
enddef
&findfunc = LocalFindFunc
g:LocalFindFuncArgs = []
find abc23
assert_equal(['abc23', false], g:LocalFindFuncArgs)
bw!
END
call CheckScriptSuccess(lines)
" setting 'findfunc' to a script local function outside of a script context
" should fail
let cleanup =<< trim END
call writefile([execute('messages')], 'Xtest.out')
qall
END
call writefile(cleanup, 'Xverify.vim', 'D')
call RunVim([], [], "-c \"set findfunc=s:abc\" -S Xverify.vim")
call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0])
call delete('Xtest.out')
" cleanup
set findfunc&
delfunc FindFunc1
delfunc FindFunc2
unlet g:FindFunc1Args g:FindFunc2Args
%bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -1713,10 +1713,10 @@ func Test_completefunc_callback()
call feedkeys("A\<C-X>\<C-U>\<Esc>", 'x')
" Using Vim9 lambda expression in legacy context should fail
" set completefunc=(a,\ b)\ =>\ CompleteFunc1(21,\ a,\ b)
set completefunc=(a,\ b)\ =>\ CompleteFunc1(21,\ a,\ b)
new | only
let g:CompleteFunc1Args = []
" call assert_fails('call feedkeys("A\<C-X>\<C-U>\<Esc>", "x")', 'E117:')
call assert_fails('call feedkeys("A\<C-X>\<C-U>\<Esc>", "x")', 'E117:')
call assert_equal([], g:CompleteFunc1Args)
" set 'completefunc' to a partial with dict. This used to cause a crash.
@ -1970,10 +1970,10 @@ func Test_omnifunc_callback()
call feedkeys("A\<C-X>\<C-O>\<Esc>", 'x')
" Using Vim9 lambda expression in legacy context should fail
" set omnifunc=(a,\ b)\ =>\ OmniFunc1(21,\ a,\ b)
set omnifunc=(a,\ b)\ =>\ OmniFunc1(21,\ a,\ b)
new | only
let g:OmniFunc1Args = []
" call assert_fails('call feedkeys("A\<C-X>\<C-O>\<Esc>", "x")', 'E117:')
call assert_fails('call feedkeys("A\<C-X>\<C-O>\<Esc>", "x")', 'E117:')
call assert_equal([], g:OmniFunc1Args)
" set 'omnifunc' to a partial with dict. This used to cause a crash.
@ -2250,10 +2250,10 @@ func Test_thesaurusfunc_callback()
call feedkeys("A\<C-X>\<C-T>\<Esc>", 'x')
" Using Vim9 lambda expression in legacy context should fail
" set thesaurusfunc=(a,\ b)\ =>\ TsrFunc1(21,\ a,\ b)
set thesaurusfunc=(a,\ b)\ =>\ TsrFunc1(21,\ a,\ b)
new | only
let g:TsrFunc1Args = []
" call assert_fails('call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")', 'E117:')
call assert_fails('call feedkeys("A\<C-X>\<C-T>\<Esc>", "x")', 'E117:')
call assert_equal([], g:TsrFunc1Args)
bw!

View File

@ -217,7 +217,7 @@ func Test_modeline_fails_always()
call s:modeline_fails('equalprg', 'equalprg=Something()', 'E520:')
call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:')
call s:modeline_fails('exrc', 'exrc=Something()', 'E520:')
call s:modeline_fails('findexpr', 'findexpr=Something()', 'E520:')
call s:modeline_fails('findfunc', 'findfunc=Something', 'E520:')
call s:modeline_fails('formatprg', 'formatprg=Something()', 'E520:')
call s:modeline_fails('fsync', 'fsync=Something()', 'E520:')
call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:')

View File

@ -692,9 +692,9 @@ func Test_opfunc_callback()
delfunc s:OpFunc3
" Using Vim9 lambda expression in legacy context should fail
" set opfunc=(a)\ =>\ OpFunc1(24,\ a)
set opfunc=(a)\ =>\ OpFunc1(24,\ a)
let g:OpFunc1Args = []
" call assert_fails('normal! g@l', 'E117:')
call assert_fails('normal! g@l', 'E117:')
call assert_equal([], g:OpFunc1Args)
" set 'operatorfunc' to a partial with dict. This used to cause a crash.

View File

@ -1559,7 +1559,7 @@ endfunc
" Test for changing options in a sandbox
func Test_opt_sandbox()
for opt in ['backupdir', 'cdpath', 'exrc', 'findexpr']
for opt in ['backupdir', 'cdpath', 'exrc', 'findfunc']
call assert_fails('sandbox set ' .. opt .. '?', 'E48:')
call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:')
endfor

View File

@ -291,10 +291,10 @@ func Test_tagfunc_callback()
call assert_fails("echo taglist('a')", "E987:")
" Using Vim9 lambda expression in legacy context should fail
" set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c)
set tagfunc=(a,\ b,\ c)\ =>\ g:TagFunc1(21,\ a,\ b,\ c)
new
let g:TagFunc1Args = []
" call assert_fails("tag a17", "E117:")
call assert_fails("tag a17", "E117:")
call assert_equal([], g:TagFunc1Args)
bw!