diff options
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r-- | src/nvim/ex_docmd.c | 137 |
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; |