diff options
-rw-r--r-- | runtime/doc/eval.txt | 34 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 7 | ||||
-rw-r--r-- | src/nvim/eval.c | 70 | ||||
-rw-r--r-- | src/nvim/eval.h | 3 | ||||
-rw-r--r-- | src/nvim/eval_defs.h | 20 | ||||
-rw-r--r-- | src/nvim/normal.c | 10 | ||||
-rw-r--r-- | src/nvim/regexp.c | 52 | ||||
-rw-r--r-- | src/nvim/testdir/test_expr.vim | 19 | ||||
-rw-r--r-- | src/nvim/version.c | 2 |
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, |