diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-10-28 15:14:15 +0800 |
---|---|---|
committer | zeertzjq <zeertzjq@outlook.com> | 2024-10-29 08:20:00 +0800 |
commit | 378d9135e7ac0f91a4944be816dc9f693d5078af (patch) | |
tree | 73d8ee9d95a9e1ae134e1eca0f8e741716290044 | |
parent | 42fa3d080ec170b7927e36ec563efdd526c712d7 (diff) | |
download | rneovim-378d9135e7ac0f91a4944be816dc9f693d5078af.tar.gz rneovim-378d9135e7ac0f91a4944be816dc9f693d5078af.tar.bz2 rneovim-378d9135e7ac0f91a4944be816dc9f693d5078af.zip |
vim-patch:9.1.0810: cannot easily adjust the |:find| command
Problem: cannot easily adjust the |:find| command
Solution: Add support for the 'findexpr' option (Yegappan Lakshmanan)
closes: vim/vim#15901
closes: vim/vim#15905
https://github.com/vim/vim/commit/aeb1c97db5b9de4f4903e7f288f2aa5ad6c49440
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
-rw-r--r-- | runtime/doc/options.txt | 44 | ||||
-rw-r--r-- | runtime/doc/quickref.txt | 1 | ||||
-rw-r--r-- | runtime/doc/vvars.txt | 3 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/options.lua | 51 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/vvars.lua | 3 | ||||
-rw-r--r-- | src/nvim/buffer.c | 1 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/errors.h | 5 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 137 | ||||
-rw-r--r-- | src/nvim/file_search.c | 8 | ||||
-rw-r--r-- | src/nvim/option.c | 15 | ||||
-rw-r--r-- | src/nvim/option_vars.h | 1 | ||||
-rw-r--r-- | src/nvim/options.lua | 53 | ||||
-rw-r--r-- | src/nvim/optionstr.c | 6 | ||||
-rw-r--r-- | src/nvim/vvars.lua | 3 | ||||
-rw-r--r-- | test/old/testdir/test_findfile.vim | 168 | ||||
-rw-r--r-- | test/old/testdir/test_modeline.vim | 1 | ||||
-rw-r--r-- | test/old/testdir/test_options.vim | 2 |
19 files changed, 472 insertions, 33 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 90f7f56ca2..17b2d22173 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2598,6 +2598,50 @@ 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'* +'findexpr' 'fexpr' string (default "") + global or local to buffer |global-local| + Expression that is evaluated 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'. + + 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 + should return an empty List. + + If any errors are encountered during the expression evaluation, 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|. + + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. + + Examples: + >vim + " Use glob() + func FindExprGlob() + return glob(v:fname, v:false, v:true) + endfunc + set findexpr=FindExprGlob() + + " Use the 'git ls-files' output + func FindGitFiles() + let fnames = systemlist('git ls-files') + return fnames->filter('v:val =~? v:fname') + endfunc + set findexpr=FindGitFiles() +< + *'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'* 'fixendofline' 'fixeol' boolean (default on) local to buffer diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index d77750b485..f64865a031 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -705,6 +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| '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 diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt index 15d836a83d..3f9ce8cc96 100644 --- a/runtime/doc/vvars.txt +++ b/runtime/doc/vvars.txt @@ -254,7 +254,8 @@ v:fcs_reason *v:fname* *fname-variable* v:fname When evaluating 'includeexpr': the file name that was - detected. Empty otherwise. + detected. When evaluating 'findexpr': the argument passed to + the |:find| command. Empty otherwise. *v:fname_diff* *fname_diff-variable* v:fname_diff diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 00f7554832..8d5601ff6a 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -2294,6 +2294,57 @@ 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` +--- 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'. +--- +--- 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 +--- should return an empty List. +--- +--- If any errors are encountered during the expression evaluation, 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`. +--- +--- This option cannot be set from a `modeline` or in the `sandbox`, for +--- security reasons. +--- +--- Examples: +--- +--- ```vim +--- " Use glob() +--- func FindExprGlob() +--- return glob(v:fname, v:false, v:true) +--- endfunc +--- set findexpr=FindExprGlob() +--- +--- " Use the 'git ls-files' output +--- func FindGitFiles() +--- let fnames = systemlist('git ls-files') +--- return fnames->filter('v:val =~? v:fname') +--- endfunc +--- set findexpr=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 + --- 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 --- preserve the situation from the original file. diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index e00402ab3f..cba200101b 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -267,7 +267,8 @@ vim.v.fcs_choice = ... vim.v.fcs_reason = ... --- When evaluating 'includeexpr': the file name that was ---- detected. Empty otherwise. +--- detected. When evaluating 'findexpr': the argument passed to +--- the `:find` command. Empty otherwise. --- @type string vim.v.fname = ... diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 2142b5b298..42cc745fe6 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2049,6 +2049,7 @@ 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); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 134d69de96..88e8d59faa 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -608,6 +608,7 @@ 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 diff --git a/src/nvim/errors.h b/src/nvim/errors.h index 39095db952..bea56541a2 100644 --- a/src/nvim/errors.h +++ b/src/nvim/errors.h @@ -156,6 +156,11 @@ EXTERN const char e_luv_api_disabled[] INIT(= N_("E5560: %s must not be called i EXTERN const char e_floatonly[] INIT(= N_("E5601: Cannot close window, only floating window would remain")); EXTERN const char e_floatexchange[] INIT(= N_("E5602: Cannot exchange or rotate float")); +EXTERN const char e_cant_find_directory_str_in_cdpath[] INIT(= N_("E344: Can't find directory \"%s\" in cdpath")); +EXTERN const char e_cant_find_file_str_in_path[] INIT(= N_("E345: Can't find file \"%s\" in path")); +EXTERN const char e_no_more_directory_str_found_in_cdpath[] INIT(= N_("E346: No more directory \"%s\" found in cdpath")); +EXTERN const char e_no_more_file_str_found_in_path[] INIT(= N_("E347: No more file \"%s\" found in path")); + EXTERN const char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events")); EXTERN const char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too long")); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 93cff80bd4..6a455f70a6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2631,7 +2631,7 @@ static int may_call_simple_func(const char *arg, typval_T *rettv) /// Handle zero level expression with optimization for a simple function call. /// Same arguments and return value as eval0(). -static int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) +int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) { int r = may_call_simple_func(arg, rettv); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 293aaac036..5c61399003 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5165,6 +5165,90 @@ 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 *ptr, size_t len) +{ + const sctx_T saved_sctx = current_sctx; + bool use_sandbox = false; + + char *findexpr; + if (*curbuf->b_p_fexpr == NUL) { + use_sandbox = was_set_insecurely(curwin, kOptFindexpr, OPT_GLOBAL); + findexpr = p_fexpr; + } else { + use_sandbox = was_set_insecurely(curwin, kOptFindexpr, OPT_LOCAL); + findexpr = curbuf->b_p_fexpr; + } + + set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len); + current_sctx = curbuf->b_p_script_ctx[BV_FEXPR].script_ctx; + + char *arg = skipwhite(findexpr); + + if (use_sandbox) { + sandbox++; + } + 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()); + } + tv_clear(&tv); + } + if (use_sandbox) { + sandbox--; + } + textlock--; + clear_evalarg(&EVALARG_EVALUATE, NULL); + + set_vim_var_string(VV_FNAME, NULL, 0); + current_sctx = saved_sctx; + + return retlist; +} + +/// Use 'findexpr' 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) +{ + char *ret_fname = NULL; + + const char cc = findarg[findarg_len]; + findarg[findarg_len] = NUL; + + list_T *fname_list = eval_findexpr(findarg, findarg_len); + int fname_count = tv_list_len(fname_list); + + if (fname_count == 0) { + semsg(_(e_cant_find_file_str_in_path), findarg); + } else { + if (count > fname_count) { + semsg(_(e_no_more_file_str_found_in_path), findarg); + } else { + listitem_T *li = tv_list_find(fname_list, count - 1); + if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) { + ret_fname = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string); + } + } + } + + if (fname_list != NULL) { + tv_list_free(fname_list); + } + + findarg[findarg_len] = cc; + + return ret_fname; +} + /// :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 @@ -5196,13 +5280,17 @@ void ex_splitview(exarg_T *eap) } if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) { - char *file_to_find = NULL; - char *search_ctx = NULL; - fname = find_file_in_path(eap->arg, strlen(eap->arg), - FNAME_MESS, true, curbuf->b_ffname, - &file_to_find, &search_ctx); - xfree(file_to_find); - vim_findfile_cleanup(search_ctx); + if (*get_findexpr() != NUL) { + fname = findexpr_find_file(eap->arg, strlen(eap->arg), + eap->addr_count > 0 ? eap->line2 : 1); + } else { + char *file_to_find = NULL; + char *search_ctx = NULL; + fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true, + curbuf->b_ffname, &file_to_find, &search_ctx); + xfree(file_to_find); + vim_findfile_cleanup(search_ctx); + } if (fname == NULL) { goto theend; } @@ -5398,23 +5486,28 @@ static void ex_find(exarg_T *eap) return; } - char *file_to_find = NULL; - char *search_ctx = NULL; - char *fname = find_file_in_path(eap->arg, strlen(eap->arg), - FNAME_MESS, true, curbuf->b_ffname, - &file_to_find, &search_ctx); - if (eap->addr_count > 0) { - // Repeat finding the file "count" times. This matters when it appears - // several times in the path. - linenr_T count = eap->line2; - while (fname != NULL && --count > 0) { - xfree(fname); - fname = find_file_in_path(NULL, 0, FNAME_MESS, false, curbuf->b_ffname, - &file_to_find, &search_ctx); + char *fname = NULL; + if (*get_findexpr() != NUL) { + fname = findexpr_find_file(eap->arg, strlen(eap->arg), + eap->addr_count > 0 ? eap->line2 : 1); + } else { + char *file_to_find = NULL; + char *search_ctx = NULL; + fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true, + curbuf->b_ffname, &file_to_find, &search_ctx); + if (eap->addr_count > 0) { + // Repeat finding the file "count" times. This matters when it appears + // several times in the path. + linenr_T count = eap->line2; + while (fname != NULL && --count > 0) { + xfree(fname); + fname = find_file_in_path(NULL, 0, FNAME_MESS, false, + curbuf->b_ffname, &file_to_find, &search_ctx); + } } + xfree(file_to_find); + vim_findfile_cleanup(search_ctx); } - xfree(file_to_find); - vim_findfile_cleanup(search_ctx); if (fname == NULL) { return; diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index cdfd281718..aeaf448a05 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1489,15 +1489,15 @@ char *find_file_in_path_option(char *ptr, size_t len, int options, int first, ch if (file_name == NULL && (options & FNAME_MESS)) { if (first == true) { if (find_what == FINDFILE_DIR) { - semsg(_("E344: Can't find directory \"%s\" in cdpath"), *file_to_find); + semsg(_(e_cant_find_directory_str_in_cdpath), *file_to_find); } else { - semsg(_("E345: Can't find file \"%s\" in path"), *file_to_find); + semsg(_(e_cant_find_file_str_in_path), *file_to_find); } } else { if (find_what == FINDFILE_DIR) { - semsg(_("E346: No more directory \"%s\" found in cdpath"), *file_to_find); + semsg(_(e_no_more_directory_str_found_in_cdpath), *file_to_find); } else { - semsg(_("E347: No more file \"%s\" found in path"), *file_to_find); + semsg(_(e_no_more_file_str_found_in_path), *file_to_find); } } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 7c32c99d0e..65f03ca77f 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4530,6 +4530,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_EFM: return &(buf->b_p_efm); case PV_GP: @@ -4651,6 +4653,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_EFM: return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var; case PV_GP: @@ -4922,6 +4926,15 @@ 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) +{ + if (*curbuf->b_p_fexpr == NUL) { + return p_fexpr; + } + return curbuf->b_p_fexpr; +} + /// Copy options from one window to another. /// Used when splitting a window. void win_copy_options(win_T *wp_from, win_T *wp_to) @@ -5320,6 +5333,8 @@ 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_kp = empty_string_option; buf->b_p_path = empty_string_option; buf->b_p_tags = empty_string_option; diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index 5b630117ab..a88b51dae7 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -451,6 +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 int p_fixeol; ///< 'fixendofline' EXTERN char *p_fcl; ///< 'foldclose' EXTERN OptInt p_fdls; ///< 'foldlevelstart' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index a891d18364..65401fb3a9 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2906,6 +2906,59 @@ return { varname = 'p_fcs', }, { + abbreviation = 'fexpr', + cb = 'did_set_optexpr', + defaults = { if_true = '' }, + desc = [=[ + Expression that is evaluated 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'. + + 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 + should return an empty List. + + If any errors are encountered during the expression evaluation, 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|. + + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. + + Examples: + >vim + " Use glob() + func FindExprGlob() + return glob(v:fname, v:false, v:true) + endfunc + set findexpr=FindExprGlob() + + " Use the 'git ls-files' output + func FindGitFiles() + let fnames = systemlist('git ls-files') + return fnames->filter('v:val =~? v:fname') + endfunc + set findexpr=FindGitFiles() + < + ]=], + full_name = 'findexpr', + scope = { 'global', 'buffer' }, + secure = true, + short_desc = N_('expression used for :find'), + type = 'string', + varname = 'p_fexpr', + }, + { abbreviation = 'fixeol', cb = 'did_set_eof_eol_fixeol_bomb', defaults = { if_true = true }, diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index e07fcf2f0e..c66849800c 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -233,6 +233,7 @@ 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_tfu); @@ -1885,8 +1886,9 @@ int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches) matches); } -/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext', -/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'. +/// One of the '*expr' options is changed:, 'diffexpr', 'findexpr', +/// 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', 'indentexpr', +/// 'patchexpr' and 'charconvert'. const char *did_set_optexpr(optset_T *args) { char **varp = (char **)args->os_varp; diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua index ad139bbbfe..5c9f0f9e27 100644 --- a/src/nvim/vvars.lua +++ b/src/nvim/vvars.lua @@ -284,7 +284,8 @@ M.vars = { type = 'string', desc = [=[ When evaluating 'includeexpr': the file name that was - detected. Empty otherwise. + detected. When evaluating 'findexpr': the argument passed to + the |:find| command. Empty otherwise. ]=], }, fname_diff = { diff --git a/test/old/testdir/test_findfile.vim b/test/old/testdir/test_findfile.vim index 06d781ed69..baf33898fe 100644 --- a/test/old/testdir/test_findfile.vim +++ b/test/old/testdir/test_findfile.vim @@ -1,5 +1,7 @@ " Test findfile() and finddir() +source check.vim + let s:files = [ 'Xfinddir1/foo', \ 'Xfinddir1/bar', \ 'Xfinddir1/Xdir2/foo', @@ -286,4 +288,170 @@ func Test_find_non_existing_path() let &path = save_path endfunc +" Test for 'findexpr' +func Test_findexpr() + CheckUnix + call assert_equal('', &findexpr) + call writefile(['aFile'], 'Xfindexpr1.c', 'D') + call writefile(['bFile'], 'Xfindexpr2.c', 'D') + call writefile(['cFile'], 'Xfindexpr3.c', 'D') + + " basic tests + func FindExpr1() + let fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c'] + "return fnames->copy()->filter('v:val =~? v:fname')->join("\n") + return fnames->copy()->filter('v:val =~? v:fname') + endfunc + + set findexpr=FindExpr1() + find Xfindexpr3 + call assert_match('Xfindexpr3.c', @%) + bw! + 2find Xfind + call assert_match('Xfindexpr2.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', @%) + 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', @%) + call assert_equal(2, tabpagenr()) + %bw! + call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path') + + " Buffer-local option + set findexpr=['abc'] + new + setlocal findexpr=['def'] + find xxxx + call assert_equal('def', @%) + wincmd w + find xxxx + call assert_equal('abc', @%) + aboveleft new + call assert_equal("['abc']", &findexpr) + wincmd k + aboveleft new + call assert_equal("['abc']", &findexpr) + %bw! + + " Empty list + set findexpr=[] + call assert_fails('find xxxx', 'E345: Can''t find file "xxxx" in path') + + " Error cases + + " Syntax error in the expression + set findexpr=FindExpr1{} + call assert_fails('find Xfindexpr1.c', 'E15: Invalid expression') + + " Find expression throws an error + func FindExpr2() + throw 'find error' + endfunc + set findexpr=FindExpr2() + call assert_fails('find Xfindexpr1.c', 'find error') + + " Try using a null string as the expression + set findexpr=v:_null_string + call assert_fails('find Xfindexpr1.c', 'E345: Can''t find file "Xfindexpr1.c" in path') + + " Try to create a new window from the find expression + func FindExpr3() + new + return ["foo"] + endfunc + set findexpr=FindExpr3() + call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window') + + " Try to modify the current buffer from the find expression + func FindExpr4() + 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 findexpr& + delfunc! FindExpr1 + delfunc! FindExpr2 + delfunc! FindExpr3 + delfunc! FindExpr4 +endfunc + +" Test for using a script-local function for 'findexpr' +func Test_findexpr_scriptlocal_func() + func! s:FindExprScript() + let g:FindExprArg = v:fname + return ['xxx'] + endfunc + + set findexpr=s:FindExprScript() + call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) + call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + new | only + let g:FindExprArg = '' + find abc + call assert_equal('abc', g:FindExprArg) + bw! + + set findexpr=<SID>FindExprScript() + call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr) + call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + new | only + let g:FindExprArg = '' + find abc + call assert_equal('abc', g:FindExprArg) + bw! + + let &findexpr = 's:FindExprScript()' + call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + new | only + let g:FindExprArg = '' + find abc + call assert_equal('abc', g:FindExprArg) + bw! + + let &findexpr = '<SID>FindExprScript()' + call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr) + new | only + let g:FindExprArg = '' + find abc + call assert_equal('abc', g:FindExprArg) + 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) + new | only + let g:FindExprArg = '' + find abc + call assert_equal('abc', g:FindExprArg) + 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 = '' + find abc + call assert_equal('abc', g:FindExprArg) + bw! + + set findexpr= + delfunc s:FindExprScript +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_modeline.vim b/test/old/testdir/test_modeline.vim index 487a89e038..7b7163d372 100644 --- a/test/old/testdir/test_modeline.vim +++ b/test/old/testdir/test_modeline.vim @@ -217,6 +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('formatprg', 'formatprg=Something()', 'E520:') call s:modeline_fails('fsync', 'fsync=Something()', 'E520:') call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:') diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim index ba93778404..6902560518 100644 --- a/test/old/testdir/test_options.vim +++ b/test/old/testdir/test_options.vim @@ -1559,7 +1559,7 @@ endfunc " Test for changing options in a sandbox func Test_opt_sandbox() - for opt in ['backupdir', 'cdpath', 'exrc'] + for opt in ['backupdir', 'cdpath', 'exrc', 'findexpr'] call assert_fails('sandbox set ' .. opt .. '?', 'E48:') call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:') endfor |