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

@@ -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 = {