mirror of
https://github.com/neovim/neovim.git
synced 2025-11-20 09:06:31 +00:00
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
aeb1c97db5
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
@@ -2598,6 +2598,50 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
eob EndOfBuffer |hl-EndOfBuffer|
|
eob EndOfBuffer |hl-EndOfBuffer|
|
||||||
lastline NonText |hl-NonText|
|
lastline NonText |hl-NonText|
|
||||||
|
|
||||||
|
*'findexpr'* *'fexpr'*
|
||||||
|
'findexpr' 'fexpr' string (default "")
|
||||||
|
global or local to buffer |global-local|
|
||||||
|
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()
|
||||||
|
<
|
||||||
|
|
||||||
*'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'*
|
*'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'*
|
||||||
'fixendofline' 'fixeol' boolean (default on)
|
'fixendofline' 'fixeol' boolean (default on)
|
||||||
local to buffer
|
local to buffer
|
||||||
|
|||||||
@@ -705,6 +705,7 @@ Short explanation of each option: *option-list*
|
|||||||
'fileignorecase' 'fic' ignore case when using file names
|
'fileignorecase' 'fic' ignore case when using file names
|
||||||
'filetype' 'ft' type of file, used for autocommands
|
'filetype' 'ft' type of file, used for autocommands
|
||||||
'fillchars' 'fcs' characters to use for displaying special items
|
'fillchars' 'fcs' characters to use for displaying special items
|
||||||
|
'findexpr' 'fexpr' expression to evaluate for |:find|
|
||||||
'fixendofline' 'fixeol' make sure last line in file has <EOL>
|
'fixendofline' 'fixeol' make sure last line in file has <EOL>
|
||||||
'foldclose' 'fcl' close a fold when the cursor leaves it
|
'foldclose' 'fcl' close a fold when the cursor leaves it
|
||||||
'foldcolumn' 'fdc' width of the column used to indicate folds
|
'foldcolumn' 'fdc' width of the column used to indicate folds
|
||||||
|
|||||||
@@ -254,7 +254,8 @@ v:fcs_reason
|
|||||||
*v:fname* *fname-variable*
|
*v:fname* *fname-variable*
|
||||||
v:fname
|
v:fname
|
||||||
When evaluating 'includeexpr': the file name that was
|
When evaluating 'includeexpr': the file name that was
|
||||||
detected. Empty otherwise.
|
detected. When evaluating 'findexpr': the argument passed to
|
||||||
|
the |:find| command. Empty otherwise.
|
||||||
|
|
||||||
*v:fname_diff* *fname_diff-variable*
|
*v:fname_diff* *fname_diff-variable*
|
||||||
v:fname_diff
|
v:fname_diff
|
||||||
|
|||||||
51
runtime/lua/vim/_meta/options.lua
generated
51
runtime/lua/vim/_meta/options.lua
generated
@@ -2294,6 +2294,57 @@ vim.wo.fcs = vim.wo.fillchars
|
|||||||
vim.go.fillchars = vim.o.fillchars
|
vim.go.fillchars = vim.o.fillchars
|
||||||
vim.go.fcs = vim.go.fillchars
|
vim.go.fcs = vim.go.fillchars
|
||||||
|
|
||||||
|
--- 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()
|
||||||
|
--- ```
|
||||||
|
---
|
||||||
|
---
|
||||||
|
--- @type string
|
||||||
|
vim.o.findexpr = ""
|
||||||
|
vim.o.fexpr = vim.o.findexpr
|
||||||
|
vim.bo.findexpr = vim.o.findexpr
|
||||||
|
vim.bo.fexpr = vim.bo.findexpr
|
||||||
|
vim.go.findexpr = vim.o.findexpr
|
||||||
|
vim.go.fexpr = vim.go.findexpr
|
||||||
|
|
||||||
--- When writing a file and this option is on, <EOL> at the end of file
|
--- When writing a file and this option is on, <EOL> at the end of file
|
||||||
--- will be restored if missing. Turn this option off if you want to
|
--- will be restored if missing. Turn this option off if you want to
|
||||||
--- preserve the situation from the original file.
|
--- preserve the situation from the original file.
|
||||||
|
|||||||
3
runtime/lua/vim/_meta/vvars.lua
generated
3
runtime/lua/vim/_meta/vvars.lua
generated
@@ -267,7 +267,8 @@ vim.v.fcs_choice = ...
|
|||||||
vim.v.fcs_reason = ...
|
vim.v.fcs_reason = ...
|
||||||
|
|
||||||
--- When evaluating 'includeexpr': the file name that was
|
--- When evaluating 'includeexpr': the file name that was
|
||||||
--- detected. Empty otherwise.
|
--- detected. When evaluating 'findexpr': the argument passed to
|
||||||
|
--- the `:find` command. Empty otherwise.
|
||||||
--- @type string
|
--- @type string
|
||||||
vim.v.fname = ...
|
vim.v.fname = ...
|
||||||
|
|
||||||
|
|||||||
@@ -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_indk);
|
||||||
clear_string_option(&buf->b_p_fp);
|
clear_string_option(&buf->b_p_fp);
|
||||||
clear_string_option(&buf->b_p_fex);
|
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_kp);
|
||||||
clear_string_option(&buf->b_p_mps);
|
clear_string_option(&buf->b_p_mps);
|
||||||
clear_string_option(&buf->b_p_fo);
|
clear_string_option(&buf->b_p_fo);
|
||||||
|
|||||||
@@ -608,6 +608,7 @@ struct file_buffer {
|
|||||||
char *b_p_mp; ///< 'makeprg' local value
|
char *b_p_mp; ///< 'makeprg' local value
|
||||||
char *b_p_efm; ///< 'errorformat' local value
|
char *b_p_efm; ///< 'errorformat' local value
|
||||||
char *b_p_ep; ///< 'equalprg' local value
|
char *b_p_ep; ///< 'equalprg' local value
|
||||||
|
char *b_p_fexpr; ///< 'findexpr' local value
|
||||||
char *b_p_path; ///< 'path' local value
|
char *b_p_path; ///< 'path' local value
|
||||||
int b_p_ar; ///< 'autoread' local value
|
int b_p_ar; ///< 'autoread' local value
|
||||||
char *b_p_tags; ///< 'tags' local value
|
char *b_p_tags; ///< 'tags' local value
|
||||||
|
|||||||
@@ -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_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_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_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"));
|
EXTERN const char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too long"));
|
||||||
|
|||||||
@@ -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.
|
/// Handle zero level expression with optimization for a simple function call.
|
||||||
/// Same arguments and return value as eval0().
|
/// 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);
|
int r = may_call_simple_func(arg, rettv);
|
||||||
|
|
||||||
|
|||||||
@@ -5165,6 +5165,90 @@ static void ex_wrongmodifier(exarg_T *eap)
|
|||||||
eap->errmsg = _(e_invcmd);
|
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
|
/// :sview [+command] file split window with new file, read-only
|
||||||
/// :split [[+command] file] split window with current or new file
|
/// :split [[+command] file] split window with current or new file
|
||||||
/// :vsplit [[+command] file] split window vertically 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) {
|
if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
|
||||||
|
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 *file_to_find = NULL;
|
||||||
char *search_ctx = NULL;
|
char *search_ctx = NULL;
|
||||||
fname = find_file_in_path(eap->arg, strlen(eap->arg),
|
fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true,
|
||||||
FNAME_MESS, true, curbuf->b_ffname,
|
curbuf->b_ffname, &file_to_find, &search_ctx);
|
||||||
&file_to_find, &search_ctx);
|
|
||||||
xfree(file_to_find);
|
xfree(file_to_find);
|
||||||
vim_findfile_cleanup(search_ctx);
|
vim_findfile_cleanup(search_ctx);
|
||||||
|
}
|
||||||
if (fname == NULL) {
|
if (fname == NULL) {
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
@@ -5398,23 +5486,28 @@ static void ex_find(exarg_T *eap)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 *file_to_find = NULL;
|
||||||
char *search_ctx = NULL;
|
char *search_ctx = NULL;
|
||||||
char *fname = find_file_in_path(eap->arg, strlen(eap->arg),
|
fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true,
|
||||||
FNAME_MESS, true, curbuf->b_ffname,
|
curbuf->b_ffname, &file_to_find, &search_ctx);
|
||||||
&file_to_find, &search_ctx);
|
|
||||||
if (eap->addr_count > 0) {
|
if (eap->addr_count > 0) {
|
||||||
// Repeat finding the file "count" times. This matters when it appears
|
// Repeat finding the file "count" times. This matters when it appears
|
||||||
// several times in the path.
|
// several times in the path.
|
||||||
linenr_T count = eap->line2;
|
linenr_T count = eap->line2;
|
||||||
while (fname != NULL && --count > 0) {
|
while (fname != NULL && --count > 0) {
|
||||||
xfree(fname);
|
xfree(fname);
|
||||||
fname = find_file_in_path(NULL, 0, FNAME_MESS, false, curbuf->b_ffname,
|
fname = find_file_in_path(NULL, 0, FNAME_MESS, false,
|
||||||
&file_to_find, &search_ctx);
|
curbuf->b_ffname, &file_to_find, &search_ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xfree(file_to_find);
|
xfree(file_to_find);
|
||||||
vim_findfile_cleanup(search_ctx);
|
vim_findfile_cleanup(search_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
if (fname == NULL) {
|
if (fname == NULL) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -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 (file_name == NULL && (options & FNAME_MESS)) {
|
||||||
if (first == true) {
|
if (first == true) {
|
||||||
if (find_what == FINDFILE_DIR) {
|
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 {
|
} 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 {
|
} else {
|
||||||
if (find_what == FINDFILE_DIR) {
|
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 {
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4530,6 +4530,8 @@ void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
|
|||||||
switch ((int)p->indir) {
|
switch ((int)p->indir) {
|
||||||
case PV_FP:
|
case PV_FP:
|
||||||
return &(buf->b_p_fp);
|
return &(buf->b_p_fp);
|
||||||
|
case PV_FEXPR:
|
||||||
|
return &(buf->b_p_fexpr);
|
||||||
case PV_EFM:
|
case PV_EFM:
|
||||||
return &(buf->b_p_efm);
|
return &(buf->b_p_efm);
|
||||||
case PV_GP:
|
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;
|
return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var;
|
||||||
case PV_FP:
|
case PV_FP:
|
||||||
return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var;
|
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:
|
case PV_EFM:
|
||||||
return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var;
|
return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var;
|
||||||
case PV_GP:
|
case PV_GP:
|
||||||
@@ -4922,6 +4926,15 @@ char *get_equalprg(void)
|
|||||||
return curbuf->b_p_ep;
|
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.
|
/// Copy options from one window to another.
|
||||||
/// Used when splitting a window.
|
/// Used when splitting a window.
|
||||||
void win_copy_options(win_T *wp_from, win_T *wp_to)
|
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_mp = empty_string_option;
|
||||||
buf->b_p_efm = empty_string_option;
|
buf->b_p_efm = empty_string_option;
|
||||||
buf->b_p_ep = 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_kp = empty_string_option;
|
||||||
buf->b_p_path = empty_string_option;
|
buf->b_p_path = empty_string_option;
|
||||||
buf->b_p_tags = empty_string_option;
|
buf->b_p_tags = empty_string_option;
|
||||||
|
|||||||
@@ -451,6 +451,7 @@ EXTERN char *p_ffs; ///< 'fileformats'
|
|||||||
EXTERN int p_fic; ///< 'fileignorecase'
|
EXTERN int p_fic; ///< 'fileignorecase'
|
||||||
EXTERN char *p_ft; ///< 'filetype'
|
EXTERN char *p_ft; ///< 'filetype'
|
||||||
EXTERN char *p_fcs; ///< 'fillchar'
|
EXTERN char *p_fcs; ///< 'fillchar'
|
||||||
|
EXTERN char *p_fexpr; ///< 'findexpr'
|
||||||
EXTERN int p_fixeol; ///< 'fixendofline'
|
EXTERN int p_fixeol; ///< 'fixendofline'
|
||||||
EXTERN char *p_fcl; ///< 'foldclose'
|
EXTERN char *p_fcl; ///< 'foldclose'
|
||||||
EXTERN OptInt p_fdls; ///< 'foldlevelstart'
|
EXTERN OptInt p_fdls; ///< 'foldlevelstart'
|
||||||
|
|||||||
@@ -2905,6 +2905,59 @@ return {
|
|||||||
type = 'string',
|
type = 'string',
|
||||||
varname = 'p_fcs',
|
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',
|
abbreviation = 'fixeol',
|
||||||
cb = 'did_set_eof_eol_fixeol_bomb',
|
cb = 'did_set_eof_eol_fixeol_bomb',
|
||||||
|
|||||||
@@ -233,6 +233,7 @@ void check_buf_options(buf_T *buf)
|
|||||||
check_string_option(&buf->b_p_mp);
|
check_string_option(&buf->b_p_mp);
|
||||||
check_string_option(&buf->b_p_efm);
|
check_string_option(&buf->b_p_efm);
|
||||||
check_string_option(&buf->b_p_ep);
|
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_path);
|
||||||
check_string_option(&buf->b_p_tags);
|
check_string_option(&buf->b_p_tags);
|
||||||
check_string_option(&buf->b_p_tfu);
|
check_string_option(&buf->b_p_tfu);
|
||||||
@@ -1885,8 +1886,9 @@ int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches)
|
|||||||
matches);
|
matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext',
|
/// One of the '*expr' options is changed:, 'diffexpr', 'findexpr',
|
||||||
/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'.
|
/// 'foldexpr', 'foldtext', 'formatexpr', 'includeexpr', 'indentexpr',
|
||||||
|
/// 'patchexpr' and 'charconvert'.
|
||||||
const char *did_set_optexpr(optset_T *args)
|
const char *did_set_optexpr(optset_T *args)
|
||||||
{
|
{
|
||||||
char **varp = (char **)args->os_varp;
|
char **varp = (char **)args->os_varp;
|
||||||
|
|||||||
@@ -284,7 +284,8 @@ M.vars = {
|
|||||||
type = 'string',
|
type = 'string',
|
||||||
desc = [=[
|
desc = [=[
|
||||||
When evaluating 'includeexpr': the file name that was
|
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 = {
|
fname_diff = {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
" Test findfile() and finddir()
|
" Test findfile() and finddir()
|
||||||
|
|
||||||
|
source check.vim
|
||||||
|
|
||||||
let s:files = [ 'Xfinddir1/foo',
|
let s:files = [ 'Xfinddir1/foo',
|
||||||
\ 'Xfinddir1/bar',
|
\ 'Xfinddir1/bar',
|
||||||
\ 'Xfinddir1/Xdir2/foo',
|
\ 'Xfinddir1/Xdir2/foo',
|
||||||
@@ -286,4 +288,170 @@ func Test_find_non_existing_path()
|
|||||||
let &path = save_path
|
let &path = save_path
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Test for 'findexpr'
|
||||||
|
func Test_findexpr()
|
||||||
|
CheckUnix
|
||||||
|
call assert_equal('', &findexpr)
|
||||||
|
call writefile(['aFile'], 'Xfindexpr1.c', 'D')
|
||||||
|
call writefile(['bFile'], 'Xfindexpr2.c', 'D')
|
||||||
|
call writefile(['cFile'], 'Xfindexpr3.c', 'D')
|
||||||
|
|
||||||
|
" basic tests
|
||||||
|
func FindExpr1()
|
||||||
|
let fnames = ['Xfindexpr1.c', 'Xfindexpr2.c', 'Xfindexpr3.c']
|
||||||
|
"return fnames->copy()->filter('v:val =~? v:fname')->join("\n")
|
||||||
|
return fnames->copy()->filter('v:val =~? v:fname')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
set findexpr=FindExpr1()
|
||||||
|
find Xfindexpr3
|
||||||
|
call assert_match('Xfindexpr3.c', @%)
|
||||||
|
bw!
|
||||||
|
2find Xfind
|
||||||
|
call assert_match('Xfindexpr2.c', @%)
|
||||||
|
bw!
|
||||||
|
call assert_fails('4find Xfind', 'E347: No more file "Xfind" found in path')
|
||||||
|
call assert_fails('find foobar', 'E345: Can''t find file "foobar" in path')
|
||||||
|
|
||||||
|
sfind Xfindexpr2.c
|
||||||
|
call assert_match('Xfindexpr2.c', @%)
|
||||||
|
call assert_equal(2, winnr('$'))
|
||||||
|
%bw!
|
||||||
|
call assert_fails('sfind foobar', 'E345: Can''t find file "foobar" in path')
|
||||||
|
|
||||||
|
tabfind Xfindexpr3.c
|
||||||
|
call assert_match('Xfindexpr3.c', @%)
|
||||||
|
call assert_equal(2, tabpagenr())
|
||||||
|
%bw!
|
||||||
|
call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path')
|
||||||
|
|
||||||
|
" Buffer-local option
|
||||||
|
set findexpr=['abc']
|
||||||
|
new
|
||||||
|
setlocal findexpr=['def']
|
||||||
|
find xxxx
|
||||||
|
call assert_equal('def', @%)
|
||||||
|
wincmd w
|
||||||
|
find xxxx
|
||||||
|
call assert_equal('abc', @%)
|
||||||
|
aboveleft new
|
||||||
|
call assert_equal("['abc']", &findexpr)
|
||||||
|
wincmd k
|
||||||
|
aboveleft new
|
||||||
|
call assert_equal("['abc']", &findexpr)
|
||||||
|
%bw!
|
||||||
|
|
||||||
|
" Empty list
|
||||||
|
set findexpr=[]
|
||||||
|
call assert_fails('find xxxx', 'E345: Can''t find file "xxxx" in path')
|
||||||
|
|
||||||
|
" Error cases
|
||||||
|
|
||||||
|
" Syntax error in the expression
|
||||||
|
set findexpr=FindExpr1{}
|
||||||
|
call assert_fails('find Xfindexpr1.c', 'E15: Invalid expression')
|
||||||
|
|
||||||
|
" Find expression throws an error
|
||||||
|
func FindExpr2()
|
||||||
|
throw 'find error'
|
||||||
|
endfunc
|
||||||
|
set findexpr=FindExpr2()
|
||||||
|
call assert_fails('find Xfindexpr1.c', 'find error')
|
||||||
|
|
||||||
|
" Try using a null string as the expression
|
||||||
|
set findexpr=v:_null_string
|
||||||
|
call assert_fails('find Xfindexpr1.c', 'E345: Can''t find file "Xfindexpr1.c" in path')
|
||||||
|
|
||||||
|
" Try to create a new window from the find expression
|
||||||
|
func FindExpr3()
|
||||||
|
new
|
||||||
|
return ["foo"]
|
||||||
|
endfunc
|
||||||
|
set findexpr=FindExpr3()
|
||||||
|
call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window')
|
||||||
|
|
||||||
|
" Try to modify the current buffer from the find expression
|
||||||
|
func FindExpr4()
|
||||||
|
call setline(1, ['abc'])
|
||||||
|
return ["foo"]
|
||||||
|
endfunc
|
||||||
|
set findexpr=FindExpr4()
|
||||||
|
call assert_fails('find Xfindexpr1.c', 'E565: Not allowed to change text or change window')
|
||||||
|
|
||||||
|
set findexpr&
|
||||||
|
delfunc! FindExpr1
|
||||||
|
delfunc! FindExpr2
|
||||||
|
delfunc! FindExpr3
|
||||||
|
delfunc! FindExpr4
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Test for using a script-local function for 'findexpr'
|
||||||
|
func Test_findexpr_scriptlocal_func()
|
||||||
|
func! s:FindExprScript()
|
||||||
|
let g:FindExprArg = v:fname
|
||||||
|
return ['xxx']
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
set findexpr=s:FindExprScript()
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
|
||||||
|
new | only
|
||||||
|
let g:FindExprArg = ''
|
||||||
|
find abc
|
||||||
|
call assert_equal('abc', g:FindExprArg)
|
||||||
|
bw!
|
||||||
|
|
||||||
|
set findexpr=<SID>FindExprScript()
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
|
||||||
|
new | only
|
||||||
|
let g:FindExprArg = ''
|
||||||
|
find abc
|
||||||
|
call assert_equal('abc', g:FindExprArg)
|
||||||
|
bw!
|
||||||
|
|
||||||
|
let &findexpr = 's:FindExprScript()'
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
|
||||||
|
new | only
|
||||||
|
let g:FindExprArg = ''
|
||||||
|
find abc
|
||||||
|
call assert_equal('abc', g:FindExprArg)
|
||||||
|
bw!
|
||||||
|
|
||||||
|
let &findexpr = '<SID>FindExprScript()'
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
|
||||||
|
new | only
|
||||||
|
let g:FindExprArg = ''
|
||||||
|
find abc
|
||||||
|
call assert_equal('abc', g:FindExprArg)
|
||||||
|
bw!
|
||||||
|
|
||||||
|
set findexpr=
|
||||||
|
setglobal findexpr=s:FindExprScript()
|
||||||
|
setlocal findexpr=
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &g:findexpr)
|
||||||
|
call assert_equal('', &l:findexpr)
|
||||||
|
new | only
|
||||||
|
let g:FindExprArg = ''
|
||||||
|
find abc
|
||||||
|
call assert_equal('abc', g:FindExprArg)
|
||||||
|
bw!
|
||||||
|
|
||||||
|
new | only
|
||||||
|
set findexpr=
|
||||||
|
setglobal findexpr=
|
||||||
|
setlocal findexpr=s:FindExprScript()
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &findexpr)
|
||||||
|
call assert_equal(expand('<SID>') .. 'FindExprScript()', &l:findexpr)
|
||||||
|
call assert_equal('', &g:findexpr)
|
||||||
|
let g:FindExprArg = ''
|
||||||
|
find abc
|
||||||
|
call assert_equal('abc', g:FindExprArg)
|
||||||
|
bw!
|
||||||
|
|
||||||
|
set findexpr=
|
||||||
|
delfunc s:FindExprScript
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ func Test_modeline_fails_always()
|
|||||||
call s:modeline_fails('equalprg', 'equalprg=Something()', 'E520:')
|
call s:modeline_fails('equalprg', 'equalprg=Something()', 'E520:')
|
||||||
call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:')
|
call s:modeline_fails('errorfile', 'errorfile=Something()', 'E520:')
|
||||||
call s:modeline_fails('exrc', 'exrc=Something()', 'E520:')
|
call s:modeline_fails('exrc', 'exrc=Something()', 'E520:')
|
||||||
|
call s:modeline_fails('findexpr', 'findexpr=Something()', 'E520:')
|
||||||
call s:modeline_fails('formatprg', 'formatprg=Something()', 'E520:')
|
call s:modeline_fails('formatprg', 'formatprg=Something()', 'E520:')
|
||||||
call s:modeline_fails('fsync', 'fsync=Something()', 'E520:')
|
call s:modeline_fails('fsync', 'fsync=Something()', 'E520:')
|
||||||
call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:')
|
call s:modeline_fails('grepprg', 'grepprg=Something()', 'E520:')
|
||||||
|
|||||||
@@ -1559,7 +1559,7 @@ endfunc
|
|||||||
|
|
||||||
" Test for changing options in a sandbox
|
" Test for changing options in a sandbox
|
||||||
func Test_opt_sandbox()
|
func Test_opt_sandbox()
|
||||||
for opt in ['backupdir', 'cdpath', 'exrc']
|
for opt in ['backupdir', 'cdpath', 'exrc', 'findexpr']
|
||||||
call assert_fails('sandbox set ' .. opt .. '?', 'E48:')
|
call assert_fails('sandbox set ' .. opt .. '?', 'E48:')
|
||||||
call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:')
|
call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:')
|
||||||
endfor
|
endfor
|
||||||
|
|||||||
Reference in New Issue
Block a user