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: #15901
closes: #15905
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 32cd7c5..9447a2d 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -6923,6 +6923,103 @@
eap->errmsg = ex_errmsg(e_invalid_command_str, eap->cmd);
}
+#ifdef FEAT_EVAL
+/*
+ * 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(char_u *ptr, int len)
+{
+ sctx_T saved_sctx = current_sctx;
+ int use_sandbox = FALSE;
+ char_u *findexpr;
+ char_u *arg;
+ typval_T tv;
+ list_T *retlist = NULL;
+
+ if (*curbuf->b_p_fexpr == NUL)
+ {
+ use_sandbox = was_set_insecurely((char_u *)"findexpr", OPT_GLOBAL);
+ findexpr = p_fexpr;
+ }
+ else
+ {
+ use_sandbox = was_set_insecurely((char_u *)"findexpr", OPT_LOCAL);
+ findexpr = curbuf->b_p_fexpr;
+ }
+
+ set_vim_var_string(VV_FNAME, ptr, len);
+ current_sctx = curbuf->b_p_script_ctx[BV_FEXPR];
+
+ arg = skipwhite(findexpr);
+
+ if (use_sandbox)
+ ++sandbox;
+ ++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());
+ clear_tv(&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_u *
+findexpr_find_file(char_u *findarg, int findarg_len, int count)
+{
+ list_T *fname_list;
+ char_u *ret_fname = NULL;
+ char_u cc;
+ int fname_count;
+
+ cc = findarg[findarg_len];
+ findarg[findarg_len] = NUL;
+
+ fname_list = eval_findexpr(findarg, findarg_len);
+ fname_count = 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 = list_find(fname_list, count - 1);
+ if (li != NULL && li->li_tv.v_type == VAR_STRING)
+ ret_fname = vim_strsave(li->li_tv.vval.v_string);
+ }
+ }
+
+ if (fname_list != NULL)
+ list_free(fname_list);
+
+ findarg[findarg_len] = cc;
+
+ return ret_fname;
+}
+#endif
+
/*
* :sview [+command] file split window with new file, read-only
* :split [[+command] file] split window with current or new file
@@ -6972,11 +7069,22 @@
{
char_u *file_to_find = NULL;
char *search_ctx = NULL;
- fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg),
- FNAME_MESS, TRUE, curbuf->b_ffname,
- &file_to_find, &search_ctx);
- vim_free(file_to_find);
- vim_findfile_cleanup(search_ctx);
+
+ if (*get_findexpr() != NUL)
+ {
+#ifdef FEAT_EVAL
+ fname = findexpr_find_file(eap->arg, (int)STRLEN(eap->arg),
+ eap->addr_count > 0 ? eap->line2 : 1);
+#endif
+ }
+ else
+ {
+ fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg),
+ FNAME_MESS, TRUE, curbuf->b_ffname,
+ &file_to_find, &search_ctx);
+ vim_free(file_to_find);
+ vim_findfile_cleanup(search_ctx);
+ }
if (fname == NULL)
goto theend;
eap->arg = fname;
@@ -7241,27 +7349,37 @@
if (!check_can_set_curbuf_forceit(eap->forceit))
return;
- char_u *fname;
+ char_u *fname = NULL;
int count;
char_u *file_to_find = NULL;
char *search_ctx = NULL;
- fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg), FNAME_MESS,
- TRUE, curbuf->b_ffname, &file_to_find, &search_ctx);
- if (eap->addr_count > 0)
+ if (*get_findexpr() != NUL)
{
- // Repeat finding the file "count" times. This matters when it appears
- // several times in the path.
- count = eap->line2;
- while (fname != NULL && --count > 0)
- {
- vim_free(fname);
- fname = find_file_in_path(NULL, 0, FNAME_MESS,
- FALSE, curbuf->b_ffname, &file_to_find, &search_ctx);
- }
+#ifdef FEAT_EVAL
+ fname = findexpr_find_file(eap->arg, (int)STRLEN(eap->arg),
+ eap->addr_count > 0 ? eap->line2 : 1);
+#endif
}
- VIM_CLEAR(file_to_find);
- vim_findfile_cleanup(search_ctx);
+ else
+ {
+ fname = find_file_in_path(eap->arg, (int)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.
+ count = eap->line2;
+ while (fname != NULL && --count > 0)
+ {
+ vim_free(fname);
+ fname = find_file_in_path(NULL, 0, FNAME_MESS,
+ FALSE, curbuf->b_ffname, &file_to_find, &search_ctx);
+ }
+ }
+ VIM_CLEAR(file_to_find);
+ vim_findfile_cleanup(search_ctx);
+ }
if (fname == NULL)
return;