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 /src | |
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>
Diffstat (limited to 'src')
-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 |
11 files changed, 202 insertions, 30 deletions
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 = { |