From 378d9135e7ac0f91a4944be816dc9f693d5078af Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 28 Oct 2024 15:14:15 +0800 Subject: 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 --- src/nvim/buffer.c | 1 + src/nvim/buffer_defs.h | 1 + src/nvim/errors.h | 5 ++ src/nvim/eval.c | 2 +- src/nvim/ex_docmd.c | 137 +++++++++++++++++++++++++++++++++++++++++-------- src/nvim/file_search.c | 8 +-- src/nvim/option.c | 15 ++++++ src/nvim/option_vars.h | 1 + src/nvim/options.lua | 53 +++++++++++++++++++ src/nvim/optionstr.c | 6 ++- src/nvim/vvars.lua | 3 +- 11 files changed, 202 insertions(+), 30 deletions(-) (limited to 'src') 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 @@ -2905,6 +2905,59 @@ return { type = 'string', 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', 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 = { -- cgit From 6b8c3d146ec0308fd19a9142b23b5e342e2cdcf8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 28 Oct 2024 16:09:39 +0800 Subject: vim-patch:9.1.0811: :find expansion does not consider 'findexpr' Problem: :find expansion does not consider 'findexpr' Solution: Support expanding :find command argument using 'findexpr' (Yegappan Lakshmanan) closes: vim/vim#15929 https://github.com/vim/vim/commit/2f6efaccfd5c4e7df1f54ed0f41f329abbe05f60 Co-authored-by: Yegappan Lakshmanan --- src/nvim/cmdexpand.c | 30 ++++++++++++---------- src/nvim/errors.h | 1 + src/nvim/ex_docmd.c | 70 ++++++++++++++++++++++++++++++++++++++-------------- src/nvim/options.lua | 1 + 4 files changed, 71 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 549ed826bc..5b7017481e 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -2497,21 +2497,25 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int * } } - if (xp->xp_context == EXPAND_FILES) { - flags |= EW_FILE; - } else if (xp->xp_context == EXPAND_FILES_IN_PATH) { - flags |= (EW_FILE | EW_PATH); - } else if (xp->xp_context == EXPAND_DIRS_IN_CDPATH) { - flags = (flags | EW_DIR | EW_CDPATH) & ~EW_FILE; + int ret = FAIL; + if (xp->xp_context == EXPAND_FILES_IN_PATH && *get_findexpr() != NUL) { + ret = expand_findexpr(pat, matches, numMatches); } else { - flags = (flags | EW_DIR) & ~EW_FILE; - } - if (options & WILD_ICASE) { - flags |= EW_ICASE; + if (xp->xp_context == EXPAND_FILES) { + flags |= EW_FILE; + } else if (xp->xp_context == EXPAND_FILES_IN_PATH) { + flags |= (EW_FILE | EW_PATH); + } else if (xp->xp_context == EXPAND_DIRS_IN_CDPATH) { + flags = (flags | EW_DIR | EW_CDPATH) & ~EW_FILE; + } else { + flags = (flags | EW_DIR) & ~EW_FILE; + } + if (options & WILD_ICASE) { + flags |= EW_ICASE; + } + // Expand wildcards, supporting %:h and the like. + ret = expand_wildcards_eval(&pat, numMatches, matches, flags); } - - // Expand wildcards, supporting %:h and the like. - int ret = expand_wildcards_eval(&pat, numMatches, matches, flags); if (free_pat) { xfree(pat); } diff --git a/src/nvim/errors.h b/src/nvim/errors.h index bea56541a2..6bbec84ff9 100644 --- a/src/nvim/errors.h +++ b/src/nvim/errors.h @@ -186,6 +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_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s")); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5c61399003..f89c992cf9 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5167,28 +5167,17 @@ static void ex_wrongmodifier(exarg_T *eap) /// 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) +static list_T *eval_findexpr(const char *ptr) { 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; - } + char *findexpr = get_findexpr(); - set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len); + set_vim_var_string(VV_FNAME, ptr, -1); 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 @@ -5200,12 +5189,11 @@ static list_T *eval_findexpr(const char *ptr, size_t len) } 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); } - if (use_sandbox) { - sandbox--; - } textlock--; clear_evalarg(&EVALARG_EVALUATE, NULL); @@ -5215,6 +5203,52 @@ static list_T *eval_findexpr(const char *ptr, size_t len) return retlist; } +/// Find file names matching "pat" using 'findexpr' 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) +{ + *numMatches = 0; + *files = NULL; + + // File name expansion uses wildchars. But the 'findexpr' expression + // expects a regular expression argument. So convert wildchars in the + // argument to regular expression patterns. + char *regpat = file_pat_to_reg_pat(pat, NULL, NULL, false); + if (regpat == NULL) { + return FAIL; + } + + list_T *l = eval_findexpr(regpat); + + xfree(regpat); + + if (l == NULL) { + return FAIL; + } + + int len = tv_list_len(l); + if (len == 0) { // empty List + return FAIL; + } + + *files = xmalloc(sizeof(char *) * (size_t)len); + + // Copy all the List items + int idx = 0; + TV_LIST_ITER_CONST(l, li, { + if (TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) { + (*files)[idx] = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string); + idx++; + } + }); + + *numMatches = idx; + tv_list_free(l); + + return OK; +} + /// 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) @@ -5224,7 +5258,7 @@ static char *findexpr_find_file(char *findarg, size_t findarg_len, int count) const char cc = findarg[findarg_len]; findarg[findarg_len] = NUL; - list_T *fname_list = eval_findexpr(findarg, findarg_len); + list_T *fname_list = eval_findexpr(findarg); int fname_count = tv_list_len(fname_list); if (fname_count == 0) { diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 65401fb3a9..2e037d80d3 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2955,6 +2955,7 @@ return { scope = { 'global', 'buffer' }, secure = true, short_desc = N_('expression used for :find'), + tags = { 'E1514' }, type = 'string', varname = 'p_fexpr', }, -- cgit From 60b3ccd850ca038af6fc0a19cad12578f63d67ec Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 29 Oct 2024 07:02:04 +0800 Subject: vim-patch:9.1.0821: 'findexpr' completion doesn't set v:fname to cmdline argument Problem: 'findexpr' completion doesn't set v:fname to cmdline argument. Solution: Set v:fname to the cmdline argument as-is (zeertzjq). closes: vim/vim#15934 https://github.com/vim/vim/commit/20e045f78148c0ef0143c33ffe686fee72d29376 --- src/nvim/cmdexpand.c | 9 ++++++--- src/nvim/cmdexpand_defs.h | 1 + src/nvim/errors.h | 2 +- src/nvim/eval.c | 5 ++++- src/nvim/eval.h | 1 + src/nvim/ex_docmd.c | 21 ++++++--------------- src/nvim/options.lua | 7 ++++++- src/nvim/vvars.lua | 7 +++++++ 8 files changed, 32 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 5b7017481e..aeaed536fc 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -109,6 +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_HELP && xp->xp_context != EXPAND_KEYMAP && xp->xp_context != EXPAND_LUA @@ -1228,7 +1229,8 @@ 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_HELP + if (context == EXPAND_FINDEXPR + || context == EXPAND_HELP || context == EXPAND_COLORS || context == EXPAND_COMPILER || context == EXPAND_OWNSYNTAX @@ -1827,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 = EXPAND_FILES_IN_PATH; + xp->xp_context = *get_findexpr() != NUL ? EXPAND_FINDEXPR : EXPAND_FILES_IN_PATH; } break; case CMD_cd: @@ -2498,7 +2500,7 @@ static int expand_files_and_dirs(expand_T *xp, char *pat, char ***matches, int * } int ret = FAIL; - if (xp->xp_context == EXPAND_FILES_IN_PATH && *get_findexpr() != NUL) { + if (xp->xp_context == EXPAND_FINDEXPR) { ret = expand_findexpr(pat, matches, numMatches); } else { if (xp->xp_context == EXPAND_FILES) { @@ -2720,6 +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_DIRS_IN_CDPATH) { return expand_files_and_dirs(xp, pat, matches, numMatches, flags, options); } diff --git a/src/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h index 86725eafd6..ce51f30bec 100644 --- a/src/nvim/cmdexpand_defs.h +++ b/src/nvim/cmdexpand_defs.h @@ -107,6 +107,7 @@ enum { EXPAND_KEYMAP, EXPAND_DIRS_IN_CDPATH, EXPAND_SHELLCMDLINE, + EXPAND_FINDEXPR, EXPAND_CHECKHEALTH, EXPAND_LUA, }; diff --git a/src/nvim/errors.h b/src/nvim/errors.h index 6bbec84ff9..6682a42d61 100644 --- a/src/nvim/errors.h +++ b/src/nvim/errors.h @@ -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_findexpr[] INIT( = N_("E1514: 'findexpr' did not return a List type")); EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s")); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6a455f70a6..bf85ed1646 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -270,6 +270,7 @@ 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), @@ -460,6 +461,9 @@ void eval_init(void) set_vim_var_nr(VV_SEARCHFORWARD, 1); 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); set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC); @@ -475,7 +479,6 @@ void eval_init(void) set_vim_var_nr(VV_NUMBERMAX, VARNUMBER_MAX); set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN); set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8); - set_vim_var_special(VV_EXITING, kSpecialVarNull); set_vim_var_nr(VV_MAXCOL, MAXCOL); set_vim_var_nr(VV_ECHOSPACE, sc_col - 1); diff --git a/src/nvim/eval.h b/src/nvim/eval.h index bb9b00abc7..b5605bb644 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -167,6 +167,7 @@ typedef enum { VV_COLLATE, VV_EXITING, VV_MAXCOL, + VV_CMDCOMPLETE, // Nvim VV_STDERR, VV_MSGPACK_TYPES, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index f89c992cf9..74ba19b30a 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5167,13 +5167,14 @@ static void ex_wrongmodifier(exarg_T *eap) /// 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) +static list_T *eval_findexpr(const char *pat, bool cmdcomplete) { const sctx_T saved_sctx = current_sctx; char *findexpr = get_findexpr(); - set_vim_var_string(VV_FNAME, ptr, -1); + 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); @@ -5198,6 +5199,7 @@ static list_T *eval_findexpr(const char *ptr) clear_evalarg(&EVALARG_EVALUATE, NULL); set_vim_var_string(VV_FNAME, NULL, 0); + set_vim_var_bool(VV_CMDCOMPLETE, kBoolVarFalse); current_sctx = saved_sctx; return retlist; @@ -5211,18 +5213,7 @@ int expand_findexpr(const char *pat, char ***files, int *numMatches) *numMatches = 0; *files = NULL; - // File name expansion uses wildchars. But the 'findexpr' expression - // expects a regular expression argument. So convert wildchars in the - // argument to regular expression patterns. - char *regpat = file_pat_to_reg_pat(pat, NULL, NULL, false); - if (regpat == NULL) { - return FAIL; - } - - list_T *l = eval_findexpr(regpat); - - xfree(regpat); - + list_T *l = eval_findexpr(pat, true); if (l == NULL) { return FAIL; } @@ -5258,7 +5249,7 @@ static char *findexpr_find_file(char *findarg, size_t findarg_len, int count) const char cc = findarg[findarg_len]; findarg[findarg_len] = NUL; - list_T *fname_list = eval_findexpr(findarg); + list_T *fname_list = eval_findexpr(findarg, false); int fname_count = tv_list_len(fname_list); if (fname_count == 0) { diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 2e037d80d3..d59958eaf2 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2920,6 +2920,10 @@ return { 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 should return an empty List. @@ -2939,7 +2943,8 @@ return { >vim " Use glob() func FindExprGlob() - return glob(v:fname, v:false, v:true) + let pat = v:cmdcomplete ? $'{v:fname}*' : v:fname + return glob(pat, v:false, v:true) endfunc set findexpr=FindExprGlob() diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua index 5c9f0f9e27..6c6edd4ee2 100644 --- a/src/nvim/vvars.lua +++ b/src/nvim/vvars.lua @@ -50,6 +50,13 @@ 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 = [=[ -- cgit