aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_docmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r--src/nvim/ex_docmd.c137
1 files changed, 115 insertions, 22 deletions
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;