aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt34
-rw-r--r--src/nvim/api/vim.c7
-rw-r--r--src/nvim/eval.c70
-rw-r--r--src/nvim/eval.h3
-rw-r--r--src/nvim/eval_defs.h20
-rw-r--r--src/nvim/normal.c10
-rw-r--r--src/nvim/regexp.c52
-rw-r--r--src/nvim/testdir/test_expr.vim19
-rw-r--r--src/nvim/version.c2
9 files changed, 151 insertions, 66 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index b1502f13ca..62ee26cf54 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1519,7 +1519,7 @@ v:false Special value used to put "false" in JSON and msgpack. See
|json_encode()|. This value is converted to "v:false" when used
as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
- or |expr7| when used with numeric operators).
+ or |expr7| when used with numeric operators). Read-only.
*v:fcs_reason* *fcs_reason-variable*
v:fcs_reason The reason why the |FileChangedShell| event was triggered.
@@ -1669,7 +1669,7 @@ v:null Special value used to put "null" in JSON and NIL in msgpack.
See |json_encode()|. This value is converted to "v:null" when
used as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
- or |expr7| when used with numeric operators).
+ or |expr7| when used with numeric operators). Read-only.
*v:oldfiles* *oldfiles-variable*
v:oldfiles List of file names that is loaded from the |shada| file on
@@ -1855,7 +1855,7 @@ v:true Special value used to put "true" in JSON and msgpack. See
|json_encode()|. This value is converted to "v:true" when used
as a String (e.g. in |expr5| with string concatenation
operator) and to one when used as a Number (e.g. in |expr5| or
- |expr7| when used with numeric operators).
+ |expr7| when used with numeric operators). Read-only.
*v:val* *val-variable*
v:val Value of the current item of a |List| or |Dictionary|. Only
@@ -7108,6 +7108,14 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()*
:echo substitute(s, '%\(\x\x\)',
\ '\=nr2char("0x" . submatch(1))', 'g')
+< When {sub} is a Funcref that function is called, with one
+ optional argument. Example: >
+ :echo substitute(s, '%\(\x\x\)', SubNr, 'g')
+< The optional argument is a list which contains the whole
+ matched string and up to nine submatches,like what
+ |submatch()| returns. Example: >
+ :echo substitute(s, '\(\x\x\)', {m -> '0x' . m[1]}, 'g')
+
synID({lnum}, {col}, {trans}) *synID()*
The result is a Number, which is the syntax ID at the position
{lnum} and {col} in the current window.
@@ -7464,16 +7472,18 @@ trunc({expr}) *trunc()*
< 4.0
type({expr}) *type()*
- The result is a Number, depending on the type of {expr}:
- Number: 0
- String: 1
- Funcref: 2
- List: 3
- Dictionary: 4
- Float: 5
+ The result is a Number representing the type of {expr}.
+ Instead of using the number directly, it is better to use the
+ v:t_ variable that has the value:
+ Number: 0 (|v:t_number|)
+ String: 1 (|v:t_string|)
+ Funcref: 2 (|v:t_func|)
+ List: 3 (|v:t_list|)
+ Dictionary: 4 (|v:t_dict|)
+ Float: 5 (|v:t_float|)
Boolean: 6 (|v:true| and |v:false|)
Null: 7 (|v:null|)
- To avoid the magic numbers it should be used this way: >
+ For backward compatibility, this method can be used: >
:if type(myvar) == type(0)
:if type(myvar) == type("")
:if type(myvar) == type(function("tr"))
@@ -7484,6 +7494,8 @@ type({expr}) *type()*
< In place of checking for |v:null| type it is better to check
for |v:null| directly as it is the only value of this type: >
:if myvar is v:null
+< To check if the v:t_ variables exist use this: >
+ :if exists('v:t_number')
undofile({name}) *undofile()*
Return the name of the undo file that would be used for a file
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 1732ee0bae..fd0f98c017 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -221,11 +221,10 @@ Object nvim_call_function(String fname, Array args, Error *err)
// Call the function
typval_T rettv;
int dummy;
- int r = call_func((char_u *) fname.data, (int) fname.size,
- &rettv, (int) args.size, vim_args,
+ int r = call_func((char_u *)fname.data, (int)fname.size,
+ &rettv, (int)args.size, vim_args, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
- true,
- NULL, NULL);
+ true, NULL, NULL);
if (r == FAIL) {
api_set_error(err, Exception, _("Error calling function."));
}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 709ea0e2e1..e64d24baa0 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1230,8 +1230,8 @@ int call_vim_function(
++sandbox;
}
- rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */
- ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
+ rettv->v_type = VAR_UNKNOWN; // clear_tv() uses this
+ ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&doesrange, true, NULL, NULL);
if (safe) {
@@ -7388,6 +7388,11 @@ fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) {
}
/// Call a function with its resolved parameters
+///
+/// "argv_func", when not NULL, can be used to fill in arguments only when the
+/// invoked function uses them. It is called like this:
+/// new_argcount = argv_func(current_argcount, argv, called_func_argcount)
+///
/// Return FAIL when the function can't be called, OK otherwise.
/// Also returns OK when an error was encountered while executing the function.
int
@@ -7398,6 +7403,7 @@ call_func(
int argcount_in, // number of "argvars"
typval_T *argvars_in, // vars for arguments, must have "argcount"
// PLUS ONE elements!
+ ArgvFunc argv_func, // function to fill in argvars
linenr_T firstline, // first line of range
linenr_T lastline, // last line of range
int *doesrange, // return: function handled range
@@ -7484,15 +7490,19 @@ call_func(
}
if (fp != NULL) {
- if (fp->uf_flags & FC_RANGE)
- *doesrange = TRUE;
- if (argcount < fp->uf_args.ga_len)
+ if (argv_func != NULL) {
+ argcount = argv_func(argcount, argvars, fp->uf_args.ga_len);
+ }
+ if (fp->uf_flags & FC_RANGE) {
+ *doesrange = true;
+ }
+ if (argcount < fp->uf_args.ga_len) {
error = ERROR_TOOFEW;
- else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len)
+ } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
error = ERROR_TOOMANY;
- else if ((fp->uf_flags & FC_DICT) && selfdict == NULL)
+ } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
error = ERROR_DICT;
- else {
+ } else {
// Call the user function.
call_user_func(fp, argcount, argvars, rettv, firstline, lastline,
(fp->uf_flags & FC_DICT) ? selfdict : NULL);
@@ -8344,7 +8354,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial,
}
if (item == NULL) {
- r = call_func(name, (int)STRLEN(name), rettv, argc, argv,
+ r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&dummy, true, partial, selfdict);
}
@@ -9626,16 +9636,16 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
s = expr->vval.v_string;
if (expr->v_type == VAR_FUNC) {
s = expr->vval.v_string;
- if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, 0L, 0L, &dummy,
- true, NULL, NULL) == FAIL) {
+ if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
+ 0L, 0L, &dummy, true, NULL, NULL) == FAIL) {
goto theend;
}
} else if (expr->v_type == VAR_PARTIAL) {
partial_T *partial = expr->vval.v_partial;
s = partial->pt_name;
- if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, 0L, 0L, &dummy,
- true, partial, NULL) == FAIL) {
+ if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
+ 0L, 0L, &dummy, true, partial, NULL) == FAIL) {
goto theend;
}
} else {
@@ -16160,7 +16170,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
res = call_func(func_name,
(int)STRLEN(func_name),
- &rettv, 2, argv, 0L, 0L, &dummy, true,
+ &rettv, 2, argv, NULL, 0L, 0L, &dummy, true,
partial, sortinfo->item_compare_selfdict);
clear_tv(&argv[0]);
clear_tv(&argv[1]);
@@ -17666,7 +17676,7 @@ static bool callback_call(Callback *callback, int argcount_in,
int dummy;
return call_func(name, (int)STRLEN(name), rettv, argcount_in, argvars_in,
- curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
+ NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
true, partial, NULL);
}
@@ -18315,6 +18325,33 @@ static bool write_list(FILE *fd, list_T *list, bool binary)
return ret;
}
+/// Initializes a static list with 10 items.
+void init_static_list(staticList10_T *sl) {
+ list_T *l = &sl->sl_list;
+
+ memset(sl, 0, sizeof(staticList10_T));
+ l->lv_first = &sl->sl_items[0];
+ l->lv_last = &sl->sl_items[9];
+ l->lv_refcount = DO_NOT_FREE_CNT;
+ l->lv_lock = VAR_FIXED;
+ sl->sl_list.lv_len = 10;
+
+ for (int i = 0; i < 10; i++) {
+ listitem_T *li = &sl->sl_items[i];
+
+ if (i == 0) {
+ li->li_prev = NULL;
+ } else {
+ li->li_prev = li - 1;
+ }
+ if (i == 9) {
+ li->li_next = NULL;
+ } else {
+ li->li_next = li + 1;
+ }
+ }
+}
+
/// Saves a typval_T as a string.
///
/// For lists, replaces NLs with NUL and separates items with NLs.
@@ -19763,7 +19800,7 @@ char_u *get_tv_string_chk(const typval_T *varp)
return get_tv_string_buf_chk(varp, mybuf);
}
-static char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf)
+char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf)
FUNC_ATTR_NONNULL_ALL
{
switch (varp->v_type) {
@@ -23621,6 +23658,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)
&rettv,
2,
argvars,
+ NULL,
curwin->w_cursor.lnum,
curwin->w_cursor.lnum,
&dummy,
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 5bd508d161..3aba7e462f 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -157,6 +157,9 @@ extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1];
#undef LAST_MSGPACK_TYPE
+typedef int (*ArgvFunc)(int current_argcount, typval_T *argv,
+ int called_func_argcount);
+
/// Maximum number of function arguments
#define MAX_FUNC_ARGS 20
diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h
index 616c89671b..602fb175c7 100644
--- a/src/nvim/eval_defs.h
+++ b/src/nvim/eval_defs.h
@@ -104,15 +104,19 @@ struct listvar_S {
list_T *lv_used_prev; /* previous list in used lists list */
};
-/*
- * Structure to hold an item of a Dictionary.
- * Also used for a variable.
- * The key is copied into "di_key" to avoid an extra alloc/free for it.
- */
+// Static list with 10 items. Use init_static_list() to initialize.
+typedef struct {
+ list_T sl_list; // must be first
+ listitem_T sl_items[10];
+} staticList10_T;
+
+// Structure to hold an item of a Dictionary.
+// Also used for a variable.
+// The key is copied into "di_key" to avoid an extra alloc/free for it.
struct dictitem_S {
- typval_T di_tv; /* type and value of the variable */
- char_u di_flags; /* flags (only used for variable) */
- char_u di_key[1]; /* key (actually longer!) */
+ typval_T di_tv; // type and value of the variable
+ char_u di_flags; // flags (only used for variable)
+ char_u di_key[1]; // key (actually longer!)
};
typedef struct dictitem_S dictitem_T;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 5d7a8faeba..c2218d58be 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2460,11 +2460,11 @@ do_mouse (
};
typval_T rettv;
int doesrange;
- (void) call_func((char_u *) tab_page_click_defs[mouse_col].func,
- (int) strlen(tab_page_click_defs[mouse_col].func),
- &rettv, ARRAY_SIZE(argv), argv,
- curwin->w_cursor.lnum, curwin->w_cursor.lnum,
- &doesrange, true, NULL, NULL);
+ (void)call_func((char_u *)tab_page_click_defs[mouse_col].func,
+ (int)strlen(tab_page_click_defs[mouse_col].func),
+ &rettv, ARRAY_SIZE(argv), argv, NULL,
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &doesrange, true, NULL, NULL);
clear_tv(&rettv);
break;
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 09fafcb37e..5c6cee46a7 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -6444,7 +6444,7 @@ static int submatch_line_lbr;
/// Put the submatches in "argv[0]" which is a list passed into call_func() by
/// vim_regsub_both().
-static int fill_submatch_list(int argc UNUSED, typval_T *argv, int argcount) {
+static int fill_submatch_list(int argc, typval_T *argv, int argcount) {
listitem_T *li;
int i;
char_u *s;
@@ -6545,16 +6545,13 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
src = source;
dst = dest;
- /*
- * When the substitute part starts with "\=" evaluate it as an expression.
- */
+ // When the substitute part starts with "\=" evaluate it as an expression.
if (expr != NULL || (source[0] == '\\' && source[1] == '='
- && !can_f_submatch // can't do this recursively
- )) {
- /* To make sure that the length doesn't change between checking the
- * length and copying the string, and to speed up things, the
- * resulting string is saved from the call with "copy" == FALSE to the
- * call with "copy" == TRUE. */
+ && !can_f_submatch)) { // can't do this recursively
+ // To make sure that the length doesn't change between checking the
+ // length and copying the string, and to speed up things, the
+ // resulting string is saved from the call with "copy" == FALSE to the
+ // call with "copy" == TRUE.
if (copy) {
if (eval_result != NULL) {
STRCPY(dest, eval_result);
@@ -6583,30 +6580,43 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
can_f_submatch = true;
if (expr != NULL) {
- typval_T argv[1];
+ typval_T argv[2];
int dummy;
char_u buf[NUMBUFLEN];
typval_T rettv;
+ staticList10_T matchList;
rettv.v_type = VAR_STRING;
rettv.vval.v_string = NULL;
if (prev_can_f_submatch) {
// can't do this recursively
- } else if (expr->v_type == VAR_FUNC) {
- s = expr->vval.v_string;
- call_func(s, (int)STRLEN(s), &rettv, 0, argv,
- 0L, 0L, &dummy, true, NULL, NULL);
- } else if (expr->v_type == VAR_PARTIAL) {
- partial_T *partial = expr->vval.v_partial;
-
- s = partial->pt_name;
- call_func(s, (int)STRLEN(s), &rettv, 0, argv,
- 0L, 0L, &dummy, true, partial, NULL);
+ } else {
+ argv[0].v_type = VAR_LIST;
+ argv[0].vval.v_list = &matchList.sl_list;
+ matchList.sl_list.lv_len = 0;
+ if (expr->v_type == VAR_FUNC) {
+ s = expr->vval.v_string;
+ call_func(s, (int)STRLEN(s), &rettv, 1, argv,
+ fill_submatch_list, 0L, 0L, &dummy,
+ true, NULL, NULL);
+ } else if (expr->v_type == VAR_PARTIAL) {
+ partial_T *partial = expr->vval.v_partial;
+
+ s = partial->pt_name;
+ call_func(s, (int)STRLEN(s), &rettv, 1, argv,
+ fill_submatch_list, 0L, 0L, &dummy,
+ true, partial, NULL);
+ }
+ if (matchList.sl_list.lv_len > 0) {
+ // fill_submatch_list() was called.
+ clear_submatch_list(&matchList);
+ }
}
eval_result = get_tv_string_buf_chk(&rettv, buf);
if (eval_result != NULL) {
eval_result = vim_strsave(eval_result);
}
+ clear_tv(&rettv);
} else {
eval_result = eval_to_string(source + 2, NULL, true);
}
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 0e409009e0..0c4a91298c 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -124,3 +124,22 @@ func Test_substitute_expr()
endfunc
call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, ''))
endfunc
+
+func Test_substitute_expr_arg()
+ call assert_equal('123456789-123456789=', substitute('123456789',
+ \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_equal('123456-123456=789', substitute('123456789',
+ \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_equal('123456789-123456789x=', substitute('123456789',
+ \ '\(.\)\(.\)\(.*\)',
+ \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+ call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
+ call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
+endfunc
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 815c401279..3e9e543490 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -350,7 +350,7 @@ static int included_patches[] = {
// 2093 NA
// 2092 NA
// 2091 NA
- // 2090,
+ 2090,
// 2089 NA
2088,
2087,