Mercurial > vim
changeset 36447:fb5b2d1f2439 draft v9.1.0831
patch 9.1.0831: 'findexpr' can't be used as lambad or Funcref
Commit: https://github.com/vim/vim/commit/a13f3a4f5de9c150f70298850e34747838904995
Author: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Sat Nov 2 18:40:10 2024 +0100
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>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Sat, 02 Nov 2024 18:45:23 +0100 |
parents | efc16615e72a |
children | 3363588b46a2 |
files | runtime/doc/eval.txt runtime/doc/options.txt runtime/doc/quickref.txt runtime/doc/tags runtime/doc/version9.txt src/buffer.c src/cmdexpand.c src/errors.h src/evalbuffer.c src/evalvars.c src/ex_docmd.c src/gc.c src/option.c src/option.h src/optiondefs.h src/optionstr.c src/proto/ex_docmd.pro src/proto/option.pro src/structs.h src/testdir/test_findfile.vim src/testdir/test_modeline.vim src/testdir/test_options.vim src/testdir/test_vim9_import.vim src/version.c src/vim.h |
diffstat | 25 files changed, 639 insertions(+), 266 deletions(-) [+] |
line wrap: on
line diff
--- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 9.1. Last change: 2024 Oct 28 +*eval.txt* For Vim version 9.1. Last change: 2024 Nov 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2027,10 +2027,6 @@ v:cmdbang Set like v:cmdarg for a file r can only be used in autocommands. For user commands |<bang>| can be used. - *v:cmdcomplete* *cmdcomplete-variable* -v:cmdcomplete When evaluating 'findexpr': if 'findexpr' is used for cmdline - completion the value is |v:true|, otherwise it is |v:false|. - *v:collate* *collate-variable* v:collate The current locale setting for collation order of the runtime environment. This allows Vim scripts to be aware of the @@ -2228,8 +2224,7 @@ v:fcs_choice What should happen after a *v:fname* *fname-variable* v:fname When evaluating 'includeexpr': the file name that was - detected. When evaluating 'findexpr': the argument passed to - the |:find| command. Empty otherwise. + detected. Empty otherwise. *v:fname_in* *fname_in-variable* v:fname_in The name of the input file. Valid while evaluating:
--- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.1. Last change: 2024 Oct 28 +*options.txt* For Vim version 9.1. Last change: 2024 Nov 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -439,10 +439,11 @@ Note: In the future more global options ":setlocal" on a global option might work differently then. *option-value-function* -Some options ('completefunc', 'imactivatefunc', 'imstatusfunc', 'omnifunc', -'operatorfunc', 'quickfixtextfunc', 'tagfunc' and 'thesaurusfunc') are set to -a function name or a function reference or a lambda function. When using a -lambda it will be converted to the name, e.g. "<lambda>123". Examples: +Some options ('completefunc', 'findfunc', 'imactivatefunc', 'imstatusfunc', +'omnifunc', 'operatorfunc', 'quickfixtextfunc', 'tagfunc' and 'thesaurusfunc') +are set to a function name or a function reference or a lambda function. When +using a lambda it will be converted to the name, e.g. "<lambda>123". +Examples: > set opfunc=MyOpFunc set opfunc=function('MyOpFunc') @@ -3552,36 +3553,36 @@ A jump table for the options with a shor eob EndOfBuffer |hl-EndOfBuffer| lastline NonText |hl-NonText| - *'findexpr'* *'fexpr'* *E1514* -'findexpr' 'fexpr' string (default "") + *'findfunc'* *'ffu'* *E1514* +'findfunc' 'ffu' string (default empty) global or local to buffer |global-local| {not available when compiled without the |+eval| feature} - Expression that is evaluated to obtain the filename(s) for the |:find| + Function that is called to obtain the filename(s) for the |:find| command. When this option is empty, the internal |file-searching| mechanism is used. - While evaluating the expression, the |v:fname| variable is set to the - argument of the |:find| command. - - The expression is evaluated only once per |:find| command invocation. - The expression can process all the directories specified in 'path'. - - 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|. - - 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 + The value can be the name of a function, a |lambda| or a |Funcref|. + See |option-value-function| for more information. + + 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 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 function should return a |List| containing + one or more file names. If a match is not found, the function should return an empty List. - If any errors are encountered during the expression evaluation, an + If any errors are encountered during the function invocation, an empty List is used as the return value. - Using a function call without arguments is faster |expr-option-function| - It is not allowed to change text or jump to another window while - evaluating 'findexpr' |textlock|. + executing the 'findfunc' |textlock|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. @@ -3589,18 +3590,18 @@ A jump table for the options with a shor Examples: > " Use glob() - func FindExprGlob() - let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname + func FindFuncGlob(cmdarg, cmdcomplete) + let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg return glob(pat, v:false, v:true) endfunc - set findexpr=FindExprGlob() + set findfunc=FindFuncGlob " Use the 'git ls-files' output - func FindGitFiles() + func FindGitFiles(cmdarg, cmdcomplete) let fnames = systemlist('git ls-files') - return fnames->filter('v:val =~? v:fname') + return fnames->filter('v:val =~? a:cmdarg') endfunc - set findexpr=FindGitFiles() + set findfunc=FindGitFiles < *'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'* 'fixendofline' 'fixeol' boolean (default on)
--- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -1,4 +1,4 @@ -*quickref.txt* For Vim version 9.1. Last change: 2024 Oct 22 +*quickref.txt* For Vim version 9.1. Last change: 2024 Nov 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -707,7 +707,7 @@ Short explanation of each option: *opti 'fileignorecase' 'fic' ignore case when using file names 'filetype' 'ft' type of file, used for autocommands 'fillchars' 'fcs' characters to use for displaying special items -'findexpr' 'fexpr' expression to evaluate for |:find| +'findfunc' 'ffu' function to be called for the |:find| command 'fixendofline' 'fixeol' make sure last line in file has <EOL> 'fkmap' 'fk' obsolete option for Farsi 'foldclose' 'fcl' close a fold when the cursor leaves it
--- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -267,9 +267,9 @@ 'fenc' options.txt /*'fenc'* 'fencs' options.txt /*'fencs'* 'fex' options.txt /*'fex'* -'fexpr' options.txt /*'fexpr'* 'ff' options.txt /*'ff'* 'ffs' options.txt /*'ffs'* +'ffu' options.txt /*'ffu'* 'fic' options.txt /*'fic'* 'fileencoding' options.txt /*'fileencoding'* 'fileencodings' options.txt /*'fileencodings'* @@ -278,7 +278,7 @@ 'fileignorecase' options.txt /*'fileignorecase'* 'filetype' options.txt /*'filetype'* 'fillchars' options.txt /*'fillchars'* -'findexpr' options.txt /*'findexpr'* +'findfunc' options.txt /*'findfunc'* 'fixendofline' options.txt /*'fixendofline'* 'fixeol' options.txt /*'fixeol'* 'fk' options.txt /*'fk'* @@ -6510,7 +6510,6 @@ close_cb channel.txt /*close_cb* closure eval.txt /*closure* cmdarg-variable eval.txt /*cmdarg-variable* cmdbang-variable eval.txt /*cmdbang-variable* -cmdcomplete-variable eval.txt /*cmdcomplete-variable* cmdline-arguments vi_diff.txt /*cmdline-arguments* cmdline-changed version5.txt /*cmdline-changed* cmdline-completion cmdline.txt /*cmdline-completion* @@ -10942,7 +10941,6 @@ v:charconvert_from eval.txt /*v:charconv v:charconvert_to eval.txt /*v:charconvert_to* v:cmdarg eval.txt /*v:cmdarg* v:cmdbang eval.txt /*v:cmdbang* -v:cmdcomplete eval.txt /*v:cmdcomplete* v:collate eval.txt /*v:collate* v:colornames eval.txt /*v:colornames* v:completed_item eval.txt /*v:completed_item*
--- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -41652,7 +41652,7 @@ Options: ~ 'completeitemalign' Order of |complete-items| in Insert mode completion popup -'findexpr' Vim expression to obtain the results for a |:find| +'findfunc' Vim function to obtain the results for a |:find| command 'winfixbuf' Keep buffer focused in a window 'tabclose' Which tab page to focus after closing a tab page
--- a/src/buffer.c +++ b/src/buffer.c @@ -2412,7 +2412,6 @@ free_buf_options( clear_string_option(&buf->b_p_fp); #if defined(FEAT_EVAL) clear_string_option(&buf->b_p_fex); - clear_string_option(&buf->b_p_fexpr); #endif #ifdef FEAT_CRYPT # ifdef FEAT_SODIUM @@ -2485,6 +2484,8 @@ free_buf_options( #ifdef FEAT_EVAL clear_string_option(&buf->b_p_tfu); free_callback(&buf->b_tfu_cb); + clear_string_option(&buf->b_p_ffu); + free_callback(&buf->b_ffu_cb); #endif clear_string_option(&buf->b_p_dict); clear_string_option(&buf->b_p_tsr);
--- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -50,7 +50,7 @@ cmdline_fuzzy_completion_supported(expan && xp->xp_context != EXPAND_FILES && xp->xp_context != EXPAND_FILES_IN_PATH && xp->xp_context != EXPAND_FILETYPE - && xp->xp_context != EXPAND_FINDEXPR + && xp->xp_context != EXPAND_FINDFUNC && xp->xp_context != EXPAND_HELP && xp->xp_context != EXPAND_KEYMAP && xp->xp_context != EXPAND_OLD_SETTING @@ -1419,7 +1419,7 @@ addstar( // For help tags the translation is done in find_help_tags(). // For a tag pattern starting with "/" no translation is needed. - if (context == EXPAND_FINDEXPR + if (context == EXPAND_FINDFUNC || context == EXPAND_HELP || context == EXPAND_COLORS || context == EXPAND_COMPILER @@ -2140,7 +2140,7 @@ set_context_by_cmdname( case CMD_sfind: case CMD_tabfind: if (xp->xp_context == EXPAND_FILES) - xp->xp_context = *get_findexpr() != NUL ? EXPAND_FINDEXPR + xp->xp_context = *get_findfunc() != NUL ? EXPAND_FINDFUNC : EXPAND_FILES_IN_PATH; break; case CMD_cd: @@ -2853,10 +2853,10 @@ expand_files_and_dirs( } } - if (xp->xp_context == EXPAND_FINDEXPR) + if (xp->xp_context == EXPAND_FINDFUNC) { #ifdef FEAT_EVAL - ret = expand_findexpr(pat, matches, numMatches); + ret = expand_findfunc(pat, matches, numMatches); #endif } else @@ -3122,7 +3122,7 @@ ExpandFromContext( if (xp->xp_context == EXPAND_FILES || xp->xp_context == EXPAND_DIRECTORIES || xp->xp_context == EXPAND_FILES_IN_PATH - || xp->xp_context == EXPAND_FINDEXPR + || xp->xp_context == EXPAND_FINDFUNC || xp->xp_context == EXPAND_DIRS_IN_CDPATH) return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options);
--- a/src/errors.h +++ b/src/errors.h @@ -3652,5 +3652,5 @@ EXTERN char e_wrong_character_width_for_ INIT(= N_("E1512: Wrong character width for field \"%s\"")); EXTERN char e_winfixbuf_cannot_go_to_buffer[] INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled")); -EXTERN char e_invalid_return_type_from_findexpr[] - INIT(= N_("E1514: 'findexpr' did not return a List type")); +EXTERN char e_invalid_return_type_from_findfunc[] + INIT(= N_("E1514: 'findfunc' did not return a List type"));
--- a/src/evalbuffer.c +++ b/src/evalbuffer.c @@ -45,6 +45,8 @@ set_ref_in_buffers(int copyID) #endif if (!abort) abort = abort || set_ref_in_callback(&bp->b_tfu_cb, copyID); + if (!abort) + abort = abort || set_ref_in_callback(&bp->b_ffu_cb, copyID); if (abort) break; }
--- a/src/evalvars.c +++ b/src/evalvars.c @@ -160,8 +160,7 @@ static struct vimvar {VV_NAME("python3_version", VAR_NUMBER), NULL, VV_RO}, {VV_NAME("t_typealias", VAR_NUMBER), NULL, VV_RO}, {VV_NAME("t_enum", VAR_NUMBER), NULL, VV_RO}, - {VV_NAME("t_enumvalue", VAR_NUMBER), NULL, VV_RO}, - {VV_NAME("cmdcomplete", VAR_BOOL), NULL, VV_RO}, + {VV_NAME("t_enumvalue", VAR_NUMBER), NULL, VV_RO} }; // shorthand @@ -235,7 +234,6 @@ evalvars_init(void) set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); set_vim_var_nr(VV_EXITING, VVAL_NULL); - set_vim_var_nr(VV_CMDCOMPLETE, VVAL_FALSE); set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc_lock(VAR_FIXED)); set_vim_var_list(VV_ERRORS, list_alloc()); set_vim_var_dict(VV_EVENT, dict_alloc_lock(VAR_FIXED));
--- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -6924,58 +6924,71 @@ ex_wrongmodifier(exarg_T *eap) } #if defined(FEAT_EVAL) || defined(PROTO) -/* - * Evaluate the 'findexpr' expression and return the result. When evaluating - * the expression, v:fname is set to the ":find" command argument. - */ + +// callback function for 'findfunc' +static callback_T ffu_cb; + + static callback_T * +get_findfunc_callback(void) +{ + return *curbuf->b_p_ffu != NUL ? &curbuf->b_ffu_cb : &ffu_cb; +} + static list_T * -eval_findexpr(char_u *pat, int cmdcomplete) -{ +call_findfunc(char_u *pat, int cmdcomplete) +{ + typval_T args[3]; + callback_T *cb; + typval_T rettv; + int retval; sctx_T saved_sctx = current_sctx; - char_u *findexpr; - char_u *arg; - typval_T tv; - list_T *retlist = NULL; - - findexpr = get_findexpr(); - - set_vim_var_string(VV_FNAME, pat, -1); - set_vim_var_nr(VV_CMDCOMPLETE, cmdcomplete ? VVAL_TRUE : VVAL_FALSE); - current_sctx = curbuf->b_p_script_ctx[BV_FEXPR]; - - arg = skipwhite(findexpr); - + sctx_T *ctx; + + // Call 'findfunc' to obtain the list of file names. + args[0].v_type = VAR_STRING; + args[0].vval.v_string = pat; + args[1].v_type = VAR_BOOL; + args[1].vval.v_number = cmdcomplete; + args[2].v_type = VAR_UNKNOWN; + + // Lock the text to prevent weird things from happening. Also disallow + // switching to another window, it should not be needed and may end up in + // Insert mode in another buffer. ++textlock; - // Evaluate the expression. If the expression is "FuncName()" call the - // function directly. - if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) - retlist = NULL; - else - { - if (tv.v_type == VAR_LIST) - retlist = list_copy(tv.vval.v_list, TRUE, TRUE, get_copyID()); + ctx = get_option_sctx("findfunc"); + if (ctx != NULL) + current_sctx = *ctx; + + cb = get_findfunc_callback(); + retval = call_callback(cb, -1, &rettv, 2, args); + + 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_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; + emsg(_(e_invalid_return_type_from_findfunc)); + + clear_tv(&rettv); + } return retlist; } /* - * Find file names matching "pat" using 'findexpr' and return it in "files". + * Find file names matching "pat" using 'findfunc' and return it in "files". * Used for expanding the :find, :sfind and :tabfind command argument. * Returns OK on success and FAIL otherwise. */ int -expand_findexpr(char_u *pat, char_u ***files, int *numMatches) +expand_findfunc(char_u *pat, char_u ***files, int *numMatches) { list_T *l; int len; @@ -6983,7 +6996,7 @@ expand_findexpr(char_u *pat, char_u ***f *numMatches = 0; *files = NULL; - l = eval_findexpr(pat, TRUE); + l = call_findfunc(pat, VVAL_TRUE); if (l == NULL) return FAIL; @@ -7015,11 +7028,11 @@ expand_findexpr(char_u *pat, char_u ***f } /* - * Use 'findexpr' to find file 'findarg'. The 'count' argument is used to find + * Use 'findfunc' to find file 'findarg'. The 'count' argument is used to find * the n'th matching file. */ static char_u * -findexpr_find_file(char_u *findarg, int findarg_len, int count) +findfunc_find_file(char_u *findarg, int findarg_len, int count) { list_T *fname_list; char_u *ret_fname = NULL; @@ -7029,7 +7042,7 @@ findexpr_find_file(char_u *findarg, int cc = findarg[findarg_len]; findarg[findarg_len] = NUL; - fname_list = eval_findexpr(findarg, FALSE); + fname_list = call_findfunc(findarg, VVAL_FALSE); fname_count = list_len(fname_list); if (fname_count == 0) @@ -7053,6 +7066,62 @@ findexpr_find_file(char_u *findarg, int return ret_fname; } + +/* + * Process the 'findfunc' option value. + * Returns NULL on success and an error message on failure. + */ + char * +did_set_findfunc(optset_T *args UNUSED) +{ + int retval; + + if (*curbuf->b_p_ffu != NUL) + { + // buffer-local option set + retval = option_set_callback_func(curbuf->b_p_ffu, &curbuf->b_ffu_cb); + } + else + { + // global option set + retval = option_set_callback_func(p_ffu, &ffu_cb); + } + + if (retval == FAIL) + return e_invalid_argument; + + // If the option value starts with <SID> or s:, then replace that with + // the script identifier. + char_u **varp = (char_u **)args->os_varp; + char_u *name = get_scriptlocal_funcname(*varp); + if (name != NULL) + { + free_string_option(*varp); + *varp = name; + } + + return NULL; +} + +# if defined(EXITFREE) || defined(PROTO) + void +free_findfunc_option(void) +{ + free_callback(&ffu_cb); +} +# endif + +/* + * Mark the global 'findfunc' callback with "copyID" so that it is not + * garbage collected. + */ + int +set_ref_in_findfunc(int copyID UNUSED) +{ + int abort = FALSE; + abort = set_ref_in_callback(&ffu_cb, copyID); + return abort; +} #endif /* @@ -7105,10 +7174,10 @@ ex_splitview(exarg_T *eap) char_u *file_to_find = NULL; char *search_ctx = NULL; - if (*get_findexpr() != NUL) + if (*get_findfunc() != NUL) { #ifdef FEAT_EVAL - fname = findexpr_find_file(eap->arg, (int)STRLEN(eap->arg), + fname = findfunc_find_file(eap->arg, (int)STRLEN(eap->arg), eap->addr_count > 0 ? eap->line2 : 1); #endif } @@ -7389,10 +7458,10 @@ ex_find(exarg_T *eap) char_u *file_to_find = NULL; char *search_ctx = NULL; - if (*get_findexpr() != NUL) + if (*get_findfunc() != NUL) { #ifdef FEAT_EVAL - fname = findexpr_find_file(eap->arg, (int)STRLEN(eap->arg), + fname = findfunc_find_file(eap->arg, (int)STRLEN(eap->arg), eap->addr_count > 0 ? eap->line2 : 1); #endif }
--- a/src/gc.c +++ b/src/gc.c @@ -183,6 +183,9 @@ garbage_collect(int testing) // 'imactivatefunc' and 'imstatusfunc' callbacks abort = abort || set_ref_in_im_funcs(copyID); + // 'findfunc' callback + abort = abort || set_ref_in_findfunc(copyID); + #ifdef FEAT_LUA abort = abort || set_ref_in_lua(copyID); #endif
--- a/src/option.c +++ b/src/option.c @@ -1014,6 +1014,9 @@ free_all_options(void) } free_operatorfunc_option(); free_tagfunc_option(); +# if defined(FEAT_EVAL) + free_findfunc_option(); +# endif } #endif @@ -6372,8 +6375,8 @@ unset_global_local_option(char_u *name, clear_string_option(&buf->b_p_fp); break; # ifdef FEAT_EVAL - case PV_FEXPR: - clear_string_option(&buf->b_p_fexpr); + case PV_FFU: + clear_string_option(&buf->b_p_ffu); break; # endif # ifdef FEAT_QUICKFIX @@ -6455,7 +6458,7 @@ get_varp_scope(struct vimoption *p, int { case PV_FP: return (char_u *)&(curbuf->b_p_fp); #ifdef FEAT_EVAL - case PV_FEXPR: return (char_u *)&(curbuf->b_p_fexpr); + case PV_FFU: return (char_u *)&(curbuf->b_p_ffu); #endif #ifdef FEAT_QUICKFIX case PV_EFM: return (char_u *)&(curbuf->b_p_efm); @@ -6568,8 +6571,8 @@ get_varp(struct vimoption *p) case PV_FP: return *curbuf->b_p_fp != NUL ? (char_u *)&(curbuf->b_p_fp) : p->var; #ifdef FEAT_EVAL - case PV_FEXPR: return *curbuf->b_p_fexpr != NUL - ? (char_u *)&curbuf->b_p_fexpr : p->var; + case PV_FFU: return *curbuf->b_p_ffu != NUL + ? (char_u *)&(curbuf->b_p_ffu) : p->var; #endif #ifdef FEAT_QUICKFIX case PV_EFM: return *curbuf->b_p_efm != NUL @@ -6818,15 +6821,15 @@ get_equalprg(void) } /* - * Get the value of 'findexpr', either the buffer-local one or the global one. + * Get the value of 'findfunc', either the buffer-local one or the global one. */ char_u * -get_findexpr(void) +get_findfunc(void) { #ifdef FEAT_EVAL - if (*curbuf->b_p_fexpr == NUL) - return p_fexpr; - return curbuf->b_p_fexpr; + if (*curbuf->b_p_ffu == NUL) + return p_ffu; + return curbuf->b_p_ffu; #else return (char_u *)""; #endif @@ -7361,8 +7364,7 @@ buf_copy_options(buf_T *buf, int flags) #endif buf->b_p_ep = empty_option; #if defined(FEAT_EVAL) - buf->b_p_fexpr = vim_strsave(p_fexpr); - COPY_OPT_SCTX(buf, BV_FEXPR); + buf->b_p_ffu = empty_option; #endif buf->b_p_kp = empty_option; buf->b_p_path = empty_option; @@ -8749,6 +8751,7 @@ option_set_callback_func(char_u *optval #ifdef FEAT_EVAL typval_T *tv; callback_T cb; + int funcname = FALSE; if (optval == NULL || *optval == NUL) { @@ -8762,8 +8765,11 @@ option_set_callback_func(char_u *optval // Lambda expression or a funcref tv = eval_expr(optval, NULL); else + { // treat everything else as a function name string tv = alloc_string_tv(vim_strsave(optval)); + funcname = TRUE; + } if (tv == NULL) return FAIL; @@ -8780,6 +8786,16 @@ option_set_callback_func(char_u *optval vim_free(cb.cb_name); free_tv(tv); + if (in_vim9script() && funcname && (vim_strchr(optval, '.') != NULL)) + { + // When a Vim9 imported function name is used, it is expanded by the + // call to get_callback() above to <SNR>_funcname. Revert the name to + // back to "import.funcname". + if (optcb->cb_free_name) + vim_free(optcb->cb_name); + optcb->cb_name = vim_strsave(optval); + optcb->cb_free_name = TRUE; + } // when using Vim9 style "import.funcname" it needs to be expanded to // "import#funcname". expand_autload_callback(optcb);
--- a/src/option.h +++ b/src/option.h @@ -597,7 +597,7 @@ EXTERN int p_fic; // 'fileignorecase' EXTERN char_u *p_ft; // 'filetype' EXTERN char_u *p_fcs; // 'fillchar' #ifdef FEAT_EVAL -EXTERN char_u *p_fexpr; // 'findexpr' +EXTERN char_u *p_ffu; // 'findfunc' #endif EXTERN int p_fixeol; // 'fixendofline' #ifdef FEAT_FOLDING @@ -1171,11 +1171,11 @@ enum , BV_EP , BV_ET , BV_FENC - , BV_FEXPR , BV_FP #ifdef FEAT_EVAL , BV_BEXPR , BV_FEX + , BV_FFU #endif , BV_FF , BV_FLP
--- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -54,6 +54,7 @@ #define PV_CPT OPT_BUF(BV_CPT) #define PV_DICT OPT_BOTH(OPT_BUF(BV_DICT)) #define PV_TSR OPT_BOTH(OPT_BUF(BV_TSR)) +#define PV_FFU OPT_BOTH(OPT_BUF(BV_FFU)) #define PV_CSL OPT_BUF(BV_CSL) #ifdef FEAT_COMPL_FUNC # define PV_CFU OPT_BUF(BV_CFU) @@ -74,7 +75,6 @@ #define PV_FP OPT_BOTH(OPT_BUF(BV_FP)) #ifdef FEAT_EVAL # define PV_FEX OPT_BUF(BV_FEX) -# define PV_FEXPR OPT_BOTH(OPT_BUF(BV_FEXPR)) #endif #define PV_FF OPT_BUF(BV_FF) #define PV_FLP OPT_BUF(BV_FLP) @@ -959,9 +959,10 @@ static struct vimoption options[] = {(char_u *)"vert:|,fold:-,eob:~,lastline:@", (char_u *)0L} SCTX_INIT}, - {"findexpr", "fexpr", P_STRING|P_ALLOCED|P_VI_DEF|P_VIM|P_SECURE, -#if defined(FEAT_EVAL) - (char_u *)&p_fexpr, PV_FEXPR, did_set_optexpr, NULL, + {"findfunc", "ffu", P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE|P_FUNC, +#ifdef FEAT_EVAL + (char_u *)&p_ffu, PV_FFU, + did_set_findfunc, NULL, {(char_u *)"", (char_u *)0L} #else (char_u *)NULL, PV_NONE, NULL, NULL,
--- a/src/optionstr.c +++ b/src/optionstr.c @@ -313,6 +313,7 @@ check_buf_options(buf_T *buf) check_string_option(&buf->b_p_tsrfu); #endif #ifdef FEAT_EVAL + check_string_option(&buf->b_p_ffu); check_string_option(&buf->b_p_tfu); #endif #ifdef FEAT_KEYMAP @@ -324,9 +325,6 @@ check_buf_options(buf_T *buf) check_string_option(&buf->b_p_efm); #endif check_string_option(&buf->b_p_ep); -#ifdef FEAT_EVAL - check_string_option(&buf->b_p_fexpr); -#endif check_string_option(&buf->b_p_path); check_string_option(&buf->b_p_tags); check_string_option(&buf->b_p_tc); @@ -3135,9 +3133,8 @@ expand_set_nrformats(optexpand_T *args, #if defined(FEAT_EVAL) || defined(PROTO) /* * One of the '*expr' options is changed: 'balloonexpr', 'diffexpr', - * 'findexpr', 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', - * 'indentexpr', 'patchexpr', 'printexpr' and 'charconvert'. - * + * 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', 'indentexpr', + * 'patchexpr', 'printexpr' and 'charconvert'. */ char * did_set_optexpr(optset_T *args)
--- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -46,7 +46,10 @@ void tabpage_close_other(tabpage_T *tp, void ex_stop(exarg_T *eap); void handle_drop(int filec, char_u **filev, int split, void (*callback)(void *), void *cookie); void handle_any_postponed_drop(void); -int expand_findexpr(char_u *pat, char_u ***files, int *numMatches); +int expand_findfunc(char_u *pat, char_u ***files, int *numMatches); +char *did_set_findfunc(optset_T *args); +void free_findfunc_option(void); +int set_ref_in_findfunc(int copyID); void ex_splitview(exarg_T *eap); void tabpage_new(void); void do_exedit(exarg_T *eap, win_T *old_curwin);
--- a/src/proto/option.pro +++ b/src/proto/option.pro @@ -120,7 +120,7 @@ char_u *get_option_var(int opt_idx); char_u *get_option_fullname(int opt_idx); opt_did_set_cb_T get_option_did_set_cb(int opt_idx); char_u *get_equalprg(void); -char_u *get_findexpr(void); +char_u *get_findfunc(void); void win_copy_options(win_T *wp_from, win_T *wp_to); void after_copy_winopt(win_T *wp); void copy_winopt(winopt_T *from, winopt_T *to);
--- a/src/structs.h +++ b/src/structs.h @@ -3248,6 +3248,8 @@ struct file_buffer #ifdef FEAT_EVAL char_u *b_p_tfu; // 'tagfunc' option value callback_T b_tfu_cb; // 'tagfunc' callback + char_u *b_p_ffu; // 'findfunc' option value + callback_T b_ffu_cb; // 'findfunc' callback #endif int b_p_eof; // 'endoffile' int b_p_eol; // 'endofline' @@ -3334,9 +3336,6 @@ struct file_buffer char_u *b_p_efm; // 'errorformat' local value #endif char_u *b_p_ep; // 'equalprg' local value -#ifdef FEAT_EVAL - char_u *b_p_fexpr; // 'findexpr' local value -#endif char_u *b_p_path; // 'path' local value int b_p_ar; // 'autoread' local value char_u *b_p_tags; // 'tags' local value
--- a/src/testdir/test_findfile.vim +++ b/src/testdir/test_findfile.vim @@ -1,6 +1,7 @@ " Test findfile() and finddir() source check.vim +import './vim9.vim' as v9 let s:files = [ 'Xfinddir1/foo', \ 'Xfinddir1/bar', @@ -283,223 +284,491 @@ func Test_find_non_existing_path() let &path = save_path endfunc -" Test for 'findexpr' -func Test_findexpr() +" Test for 'findfunc' +func Test_findfunc() CheckUnix - call assert_equal('', &findexpr) - call writefile(['aFile'], 'Xfindexpr1.c', 'D') - call writefile(['bFile'], 'Xfindexpr2.c', 'D') - call writefile(['cFile'], 'Xfindexpr3.c', 'D') + call assert_equal('', &findfunc) + call writefile(['aFile'], 'Xfindfunc1.c', 'D') + call writefile(['bFile'], 'Xfindfunc2.c', 'D') + call writefile(['cFile'], 'Xfindfunc3.c', 'D') " basic tests - func FindExpr1() - let fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c'] - return fnames->copy()->filter('v:val =~? v:fname') + func FindFuncBasic(pat, cmdcomplete) + let fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c'] + return fnames->copy()->filter('v:val =~? a:pat') endfunc - set findexpr=FindExpr1() - find Xfindexpr3 - call assert_match('Xfindexpr3.c', @%) + set findfunc=FindFuncBasic + find Xfindfunc3 + call assert_match('Xfindfunc3.c', @%) bw! 2find Xfind - call assert_match('Xfindexpr2.c', @%) + call assert_match('Xfindfunc2.c', @%) bw! call assert_fails('4find Xfind', 'E347: No more file "Xfind" found in path') call assert_fails('find foobar', 'E345: Can''t find file "foobar" in path') - sfind Xfindexpr2.c - call assert_match('Xfindexpr2.c', @%) + sfind Xfindfunc2.c + call assert_match('Xfindfunc2.c', @%) call assert_equal(2, winnr('$')) %bw! call assert_fails('sfind foobar', 'E345: Can''t find file "foobar" in path') - tabfind Xfindexpr3.c - call assert_match('Xfindexpr3.c', @%) + tabfind Xfindfunc3.c + call assert_match('Xfindfunc3.c', @%) call assert_equal(2, tabpagenr()) %bw! call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path') + " Test garbage collection + call test_garbagecollect_now() + find Xfindfunc2 + call assert_match('Xfindfunc2.c', @%) + bw! + delfunc FindFuncBasic + call test_garbagecollect_now() + call assert_fails('find Xfindfunc2', 'E117: Unknown function: FindFuncBasic') + " Buffer-local option - set findexpr=['abc'] + func GlobalFindFunc(pat, cmdcomplete) + return ['global'] + endfunc + func LocalFindFunc(pat, cmdcomplete) + return ['local'] + endfunc + set findfunc=GlobalFindFunc new - setlocal findexpr=['def'] + setlocal findfunc=LocalFindFunc find xxxx - call assert_equal('def', @%) + call assert_equal('local', @%) wincmd w find xxxx - call assert_equal('abc', @%) + call assert_equal('global', @%) aboveleft new - call assert_equal("['abc']", &findexpr) + call assert_equal("GlobalFindFunc", &findfunc) wincmd k aboveleft new - call assert_equal("['abc']", &findexpr) + call assert_equal("GlobalFindFunc", &findfunc) %bw! + delfunc GlobalFindFunc + delfunc LocalFindFunc - " Empty list - set findexpr=[] - call assert_fails('find xxxx', 'E345: Can''t find file "xxxx" in path') + " Assign an expression + set findfunc=[] + call assert_fails('find xxxx', 'E117: Unknown function: []') " Error cases - " Syntax error in the expression - set findexpr=FindExpr1{} - call assert_fails('find Xfindexpr1.c', 'E15: Invalid expression') + " Function that doesn't any argument + func FindFuncNoArg() + endfunc + set findfunc=FindFuncNoArg + call assert_fails('find Xfindfunc1.c', 'E118: Too many arguments for function: FindFuncNoArg') + delfunc FindFuncNoArg - " Find expression throws an error - func FindExpr2() + " Syntax error in the function + func FindFuncSyntaxError(pat, cmdcomplete) + return l + endfunc + set findfunc=FindFuncSyntaxError + call assert_fails('find Xfindfunc1.c', 'E121: Undefined variable: l') + delfunc FindFuncSyntaxError + + " Find function throws an error + func FindFuncWithThrow(pat, cmdcomplete) throw 'find error' endfunc - set findexpr=FindExpr2() - call assert_fails('find Xfindexpr1.c', 'find error') + set findfunc=FindFuncWithThrow + call assert_fails('find Xfindfunc1.c', 'find error') + delfunc FindFuncWithThrow - " Try using a null List as the expression - set findexpr=test_null_list() - call assert_fails('find Xfindexpr1.c', 'E345: Can''t find file "Xfindexpr1.c" in path') + " Try using a null function + call assert_fails('let &findfunc = test_null_function()', 'E129: Function name required') - " Try to create a new window from the find expression - func FindExpr3() + " Try to create a new window from the find function + func FindFuncNewWindow(pat, cmdexpand) new return ["foo"] endfunc - set findexpr=FindExpr3() - call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window') + set findfunc=FindFuncNewWindow + call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window') + delfunc FindFuncNewWindow - " Try to modify the current buffer from the find expression - func FindExpr4() + " Try to modify the current buffer from the find function + func FindFuncModifyBuf(pat, cmdexpand) call setline(1, ['abc']) return ["foo"] endfunc - set findexpr=FindExpr4() - call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window') + set findfunc=FindFuncModifyBuf + call assert_fails('find Xfindfunc1.c', 'E565: Not allowed to change text or change window') + delfunc FindFuncModifyBuf - " Expression returning a string - set findexpr='abc' - call assert_fails('find Xfindexpr1.c', "E1514: 'findexpr' did not return a List type") + " Return the wrong type from the function + func FindFuncWrongRet(pat, cmdexpand) + return 'foo' + endfunc + set findfunc=FindFuncWrongRet + call assert_fails('find Xfindfunc1.c', "E1514: 'findfunc' did not return a List type") + delfunc FindFuncWrongRet - set findexpr& - delfunc! FindExpr1 - delfunc! FindExpr2 - delfunc! FindExpr3 - delfunc! FindExpr4 + set findfunc& endfunc -" Test for using a script-local function for 'findexpr' -func Test_findexpr_scriptlocal_func() - func! s:FindExprScript() - let g:FindExprArg = v:fname +" Test for using a script-local function for 'findfunc' +func Test_findfunc_scriptlocal_func() + func! s:FindFuncScript(pat, cmdexpand) + let g:FindFuncArg = a:pat return ['xxx'] endfunc - set findexpr=s:FindExprScript() - call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + set findfunc=s:FindFuncScript + call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc) + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - set findexpr=<SID>FindExprScript() - call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + set findfunc=<SID>FindFuncScript + call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc) + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - let &findexpr = 's:FindExprScript()' - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + let &findfunc = 's:FindFuncScript' + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - let &findexpr = '<SID>FindExprScript()' - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + let &findfunc = '<SID>FindFuncScript' + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - set findexpr= - setglobal findexpr=s:FindExprScript() - setlocal findexpr= - call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) - call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) - call assert_equal('', &l:findexpr) + set findfunc= + setglobal findfunc=s:FindFuncScript + setlocal findfunc= + call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc) + call assert_equal(expand('<SID>') .. 'FindFuncScript', &g:findfunc) + call assert_equal('', &l:findfunc) new | only - let g:FindExprArg = '' + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! new | only - set findexpr= - setglobal findexpr= - setlocal findexpr=s:FindExprScript() - call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) - call assert_equal(expand('<SID>') .. 'FindExprScript()', &l:findexpr) - call assert_equal('', &g:findexpr) - let g:FindExprArg = '' + set findfunc= + setglobal findfunc= + setlocal findfunc=s:FindFuncScript + call assert_equal(expand('<SID>') .. 'FindFuncScript', &findfunc) + call assert_equal(expand('<SID>') .. 'FindFuncScript', &l:findfunc) + call assert_equal('', &g:findfunc) + let g:FindFuncArg = '' find abc - call assert_equal('abc', g:FindExprArg) + call assert_equal('abc', g:FindFuncArg) bw! - set findexpr= - delfunc s:FindExprScript + set findfunc= + delfunc s:FindFuncScript endfunc -" Test for expanding the argument to the :find command using 'findexpr' -func Test_findexpr_expand_arg() - let s:fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c'] +" Test for expanding the argument to the :find command using 'findfunc' +func Test_findfunc_expand_arg() + let s:fnames = ['Xfindfunc1.c', 'Xfindfunc2.c', 'Xfindfunc3.c'] - " 'findexpr' that accepts a regular expression - func FindExprRegexp() - return s:fnames->copy()->filter('v:val =~? v:fname') + " 'findfunc' that accepts a regular expression + func FindFuncRegexp(pat, cmdcomplete) + return s:fnames->copy()->filter('v:val =~? a:pat') endfunc - " 'findexpr' that accepts a glob - func FindExprGlob() - let pat = glob2regpat(v:cmdcomplete ? $'*{v:fname}*' : v:fname) + " 'findfunc' that accepts a glob + func FindFuncGlob(pat_arg, cmdcomplete) + let pat = glob2regpat(a:cmdcomplete ? $'*{a:pat_arg}*' : a:pat_arg) return s:fnames->copy()->filter('v:val =~? pat') endfunc for regexp in [v:true, v:false] - let &findexpr = regexp ? 'FindExprRegexp()' : 'FindExprGlob()' + let &findfunc = regexp ? 'FindFuncRegexp' : 'FindFuncGlob' call feedkeys(":find \<Tab>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr1.c', @:) + call assert_equal('"find Xfindfunc1.c', @:) call feedkeys(":find Xfind\<Tab>\<Tab>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr2.c', @:) + call assert_equal('"find Xfindfunc2.c', @:) call assert_equal(s:fnames, getcompletion('find ', 'cmdline')) call assert_equal(s:fnames, getcompletion('find Xfind', 'cmdline')) let pat = regexp ? 'X.*1\.c' : 'X*1.c' call feedkeys($":find {pat}\<Tab>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr1.c', @:) - call assert_equal(['Xfindexpr1.c'], getcompletion($'find {pat}', 'cmdline')) + call assert_equal('"find Xfindfunc1.c', @:) + call assert_equal(['Xfindfunc1.c'], getcompletion($'find {pat}', 'cmdline')) call feedkeys(":find 3\<Tab>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr3.c', @:) - call assert_equal(['Xfindexpr3.c'], getcompletion($'find 3', 'cmdline')) + call assert_equal('"find Xfindfunc3.c', @:) + call assert_equal(['Xfindfunc3.c'], getcompletion($'find 3', 'cmdline')) call feedkeys(":find Xfind\<C-A>\<C-B>\"\<CR>", "xt") - call assert_equal('"find Xfindexpr1.c Xfindexpr2.c Xfindexpr3.c', @:) + call assert_equal('"find Xfindfunc1.c Xfindfunc2.c Xfindfunc3.c', @:) call feedkeys(":find abc\<Tab>\<C-B>\"\<CR>", "xt") call assert_equal('"find abc', @:) call assert_equal([], getcompletion('find abc', 'cmdline')) endfor - set findexpr& - delfunc! FindExprRegexp - delfunc! FindExprGlob + set findfunc& + delfunc! FindFuncRegexp + delfunc! FindFuncGlob unlet s:fnames endfunc +" Test for different ways of setting the 'findfunc' option +func Test_findfunc_callback() + new + func FindFunc1(pat, cmdexpand) + let g:FindFunc1Args = [a:pat, a:cmdexpand] + return ['findfunc1'] + endfunc + + let lines =<< trim END + #" Test for using a function name + LET &findfunc = 'g:FindFunc1' + LET g:FindFunc1Args = [] + find abc1 + call assert_equal(['abc1', v:false], g:FindFunc1Args) + + #" Test for using a function() + set findfunc=function('g:FindFunc1') + LET g:FindFunc1Args = [] + find abc2 + call assert_equal(['abc2', v:false], g:FindFunc1Args) + + #" Using a funcref variable to set 'findfunc' + VAR Fn = function('g:FindFunc1') + LET &findfunc = Fn + LET g:FindFunc1Args = [] + find abc3 + call assert_equal(['abc3', v:false], g:FindFunc1Args) + + #" Using a string(funcref_variable) to set 'findfunc' + LET Fn = function('g:FindFunc1') + LET &findfunc = string(Fn) + LET g:FindFunc1Args = [] + find abc4 + call assert_equal(['abc4', v:false], g:FindFunc1Args) + + #" Test for using a funcref() + set findfunc=funcref('g:FindFunc1') + LET g:FindFunc1Args = [] + find abc5 + call assert_equal(['abc5', v:false], g:FindFunc1Args) + + #" Using a funcref variable to set 'findfunc' + LET Fn = funcref('g:FindFunc1') + LET &findfunc = Fn + LET g:FindFunc1Args = [] + find abc6 + call assert_equal(['abc6', v:false], g:FindFunc1Args) + + #" Using a string(funcref_variable) to set 'findfunc' + LET Fn = funcref('g:FindFunc1') + LET &findfunc = string(Fn) + LET g:FindFunc1Args = [] + find abc7 + call assert_equal(['abc7', v:false], g:FindFunc1Args) + + #" Test for using a lambda function using set + VAR optval = "LSTART pat, cmdexpand LMIDDLE FindFunc1(pat, cmdexpand) LEND" + LET optval = substitute(optval, ' ', '\\ ', 'g') + exe "set findfunc=" .. optval + LET g:FindFunc1Args = [] + find abc8 + call assert_equal(['abc8', v:false], g:FindFunc1Args) + + #" Test for using a lambda function using LET + LET &findfunc = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND + LET g:FindFunc1Args = [] + find abc9 + call assert_equal(['abc9', v:false], g:FindFunc1Args) + + #" Set 'findfunc' to a string(lambda expression) + LET &findfunc = 'LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND' + LET g:FindFunc1Args = [] + find abc10 + call assert_equal(['abc10', v:false], g:FindFunc1Args) + + #" Set 'findfunc' to a variable with a lambda expression + VAR Lambda = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND + LET &findfunc = Lambda + LET g:FindFunc1Args = [] + find abc11 + call assert_equal(['abc11', v:false], g:FindFunc1Args) + + #" Set 'findfunc' to a string(variable with a lambda expression) + LET Lambda = LSTART pat, _ LMIDDLE FindFunc1(pat, v:false) LEND + LET &findfunc = string(Lambda) + LET g:FindFunc1Args = [] + find abc12 + call assert_equal(['abc12', v:false], g:FindFunc1Args) + + #" Try to use 'findfunc' after the function is deleted + func g:TmpFindFunc(pat, cmdexpand) + let g:TmpFindFunc1Args = [a:pat, a:cmdexpand] + endfunc + LET &findfunc = function('g:TmpFindFunc') + delfunc g:TmpFindFunc + call test_garbagecollect_now() + LET g:TmpFindFunc1Args = [] + call assert_fails('find abc13', 'E117:') + call assert_equal([], g:TmpFindFunc1Args) + + #" Try to use a function with three arguments for 'findfunc' + func g:TmpFindFunc2(x, y, z) + let g:TmpFindFunc2Args = [a:x, a:y, a:z] + endfunc + set findfunc=TmpFindFunc2 + LET g:TmpFindFunc2Args = [] + call assert_fails('find abc14', 'E119:') + call assert_equal([], g:TmpFindFunc2Args) + delfunc TmpFindFunc2 + + #" Try to use a function with zero arguments for 'findfunc' + func g:TmpFindFunc3() + let g:TmpFindFunc3Called = v:true + endfunc + set findfunc=TmpFindFunc3 + LET g:TmpFindFunc3Called = v:false + call assert_fails('find abc15', 'E118:') + call assert_equal(v:false, g:TmpFindFunc3Called) + delfunc TmpFindFunc3 + + #" Try to use a lambda function with three arguments for 'findfunc' + LET &findfunc = LSTART a, b, c LMIDDLE FindFunc1(a, v:false) LEND + LET g:FindFunc1Args = [] + call assert_fails('find abc16', 'E119:') + call assert_equal([], g:FindFunc1Args) + + #" Test for clearing the 'findfunc' option + set findfunc='' + set findfunc& + call assert_fails("set findfunc=function('abc')", "E700:") + call assert_fails("set findfunc=funcref('abc')", "E700:") + + #" set 'findfunc' to a non-existing function + LET &findfunc = function('g:FindFunc1') + call assert_fails("set findfunc=function('NonExistingFunc')", 'E700:') + call assert_fails("LET &findfunc = function('NonExistingFunc')", 'E700:') + LET g:FindFunc1Args = [] + find abc17 + call assert_equal(['abc17', v:false], g:FindFunc1Args) + END + call v9.CheckTransLegacySuccess(lines) + + " Test for using a script-local function name + func s:FindFunc2(pat, cmdexpand) + let g:FindFunc2Args = [a:pat, a:cmdexpand] + return ['findfunc2'] + endfunc + set findfunc=s:FindFunc2 + let g:FindFunc2Args = [] + find abc18 + call assert_equal(['abc18', v:false], g:FindFunc2Args) + + let &findfunc = 's:FindFunc2' + let g:FindFunc2Args = [] + find abc19 + call assert_equal(['abc19', v:false], g:FindFunc2Args) + delfunc s:FindFunc2 + + " Using Vim9 lambda expression in legacy context should fail + set findfunc=(pat,\ cmdexpand)\ =>\ FindFunc1(pat,\ v:false) + let g:FindFunc1Args = [] + call assert_fails('find abc20', 'E117:') + call assert_equal([], g:FindFunc1Args) + + " set 'findfunc' to a partial with dict. + func SetFindFunc() + let operator = {'execute': function('FindFuncExecute')} + let &findfunc = operator.execute + endfunc + func FindFuncExecute(pat, cmdexpand) dict + return ['findfuncexecute'] + endfunc + call SetFindFunc() + call test_garbagecollect_now() + set findfunc= + delfunc SetFindFunc + delfunc FindFuncExecute + + func FindFunc2(pat, cmdexpand) + let g:FindFunc2Args = [a:pat, a:cmdexpand] + return ['findfunc2'] + endfunc + + " Vim9 tests + let lines =<< trim END + vim9script + + def g:Vim9findFunc(pat: string, cmdexpand: bool): list<string> + g:FindFunc1Args = [pat, cmdexpand] + return ['vim9findfunc'] + enddef + + # Test for using a def function with findfunc + set findfunc=function('g:Vim9findFunc') + g:FindFunc1Args = [] + find abc21 + assert_equal(['abc21', false], g:FindFunc1Args) + + # Test for using a global function name + &findfunc = g:FindFunc2 + g:FindFunc2Args = [] + find abc22 + assert_equal(['abc22', false], g:FindFunc2Args) + bw! + + # Test for using a script-local function name + def LocalFindFunc(pat: string, cmdexpand: bool): list<string> + g:LocalFindFuncArgs = [pat, cmdexpand] + return ['localfindfunc'] + enddef + &findfunc = LocalFindFunc + g:LocalFindFuncArgs = [] + find abc23 + assert_equal(['abc23', false], g:LocalFindFuncArgs) + bw! + END + call v9.CheckScriptSuccess(lines) + + " setting 'findfunc' to a script local function outside of a script context + " should fail + let cleanup =<< trim END + call writefile([execute('messages')], 'Xtest.out') + qall + END + call writefile(cleanup, 'Xverify.vim', 'D') + call RunVim([], [], "-c \"set findfunc=s:abc\" -S Xverify.vim") + call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0]) + call delete('Xtest.out') + + " cleanup + set findfunc& + delfunc FindFunc1 + delfunc FindFunc2 + unlet g:FindFunc1Args g:FindFunc2Args + %bw! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_modeline.vim +++ b/src/testdir/test_modeline.vim @@ -208,7 +208,7 @@ func Test_modeline_fails_always() call s:modeline_fails('equalprg', 'equalprg=Something()', 'E520:') call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:') call s:modeline_fails('exrc', 'exrc=Something()', 'E520:') - call s:modeline_fails('findexpr', 'findexpr=Something()', 'E520:') + call s:modeline_fails('findfunc', 'findfunc=Something', 'E520:') call s:modeline_fails('formatprg', 'formatprg=Something()', 'E520:') call s:modeline_fails('fsync', 'fsync=Something()', 'E520:') call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:')
--- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -1570,7 +1570,7 @@ endfunc " Test for changing options in a sandbox func Test_opt_sandbox() - for opt in ['backupdir', 'cdpath', 'exrc', 'findexpr'] + for opt in ['backupdir', 'cdpath', 'exrc', 'findfunc'] call assert_fails('sandbox set ' .. opt .. '?', 'E48:') call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:') endfor
--- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -1560,27 +1560,28 @@ def Run_Test_import_in_printexpr() set printexpr= enddef -" Test for using an imported function as 'findexpr' -func Test_import_in_findexpr() - call Run_Test_import_in_findexpr() +" Test for using an imported function as 'findfunc' +func Test_import_in_findfunc() + call Run_Test_import_in_findfunc() endfunc -def Run_Test_import_in_findexpr() +def Run_Test_import_in_findfunc() var lines =<< trim END - vim9script - - export def FindExpr(): list<string> - var fnames = ['Xfile1.c', 'Xfile2.c', 'Xfile3.c'] - return fnames->copy()->filter('v:val =~? v:fname') - enddef + vim9script + + export def FindFunc(pat: string, cmdexpand: bool): list<string> + var fnames = ['Xfile1.c', 'Xfile2.c', 'Xfile3.c'] + return fnames->filter((_, v) => v =~? pat) + enddef END - writefile(lines, 'Xfindexpr', 'D') - + writefile(lines, 'Xfindfunc', 'D') + + # Test using the "set" command lines =<< trim END - vim9script - import './Xfindexpr' as find - - set findexpr=find.FindExpr() + vim9script + import './Xfindfunc' as find1 + + set findfunc=find1.FindFunc END v9.CheckScriptSuccess(lines) @@ -1592,8 +1593,27 @@ def Run_Test_import_in_findexpr() botright vert new find Xfile1 assert_equal('Xfile1.c', @%) - - set findexpr= + bw! + + # Test using the option variable + lines =<< trim END + vim9script + import './Xfindfunc' as find2 + + &findfunc = find2.FindFunc + END + v9.CheckScriptSuccess(lines) + + enew! + find Xfile2 + assert_equal('Xfile2.c', @%) + bwipe! + + botright vert new + find Xfile1 + assert_equal('Xfile1.c', @%) + + set findfunc= bwipe! enddef
--- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 831, +/**/ 830, /**/ 829,
--- a/src/vim.h +++ b/src/vim.h @@ -845,7 +845,7 @@ extern int (*dyn_libintl_wputenv)(const #define EXPAND_KEYMAP 58 #define EXPAND_DIRS_IN_CDPATH 59 #define EXPAND_SHELLCMDLINE 60 -#define EXPAND_FINDEXPR 61 +#define EXPAND_FINDFUNC 61 // Values for exmode_active (0 is no exmode) @@ -2189,8 +2189,7 @@ typedef int sock_T; #define VV_TYPE_TYPEALIAS 107 #define VV_TYPE_ENUM 108 #define VV_TYPE_ENUMVALUE 109 -#define VV_CMDCOMPLETE 110 -#define VV_LEN 111 // number of v: vars +#define VV_LEN 110 // number of v: vars // used for v_number in VAR_BOOL and VAR_SPECIAL #define VVAL_FALSE 0L // VAR_BOOL