1
0
forked from aniani/vim

patch 7.4.2090

Problem:    Using submatch() in a lambda passed to substitute() is verbose.
Solution:   Use a static list and pass it as an optional argument to the
            function.  Fix memory leak.
This commit is contained in:
Bram Moolenaar 2016-07-22 21:50:18 +02:00
parent 36edf0685c
commit df48fb456f
13 changed files with 184 additions and 45 deletions

View File

@ -1,4 +1,4 @@
*eval.txt* For Vim version 7.4. Last change: 2016 Jul 16 *eval.txt* For Vim version 7.4. Last change: 2016 Jul 22
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -1535,7 +1535,7 @@ v:false A Number with value zero. Used to put "false" in JSON. See
echo v:false echo v:false
< v:false ~ < v:false ~
That is so that eval() can parse the string back to the same That is so that eval() can parse the string back to the same
value. value. Read-only.
*v:fcs_reason* *fcs_reason-variable* *v:fcs_reason* *fcs_reason-variable*
v:fcs_reason The reason why the |FileChangedShell| event was triggered. v:fcs_reason The reason why the |FileChangedShell| event was triggered.
@ -1682,7 +1682,7 @@ v:none An empty String. Used to put an empty item in JSON. See
echo v:none echo v:none
< v:none ~ < v:none ~
That is so that eval() can parse the string back to the same That is so that eval() can parse the string back to the same
value. value. Read-only.
*v:null* *null-variable* *v:null* *null-variable*
v:null An empty String. Used to put "null" in JSON. See v:null An empty String. Used to put "null" in JSON. See
@ -1692,7 +1692,7 @@ v:null An empty String. Used to put "null" in JSON. See
echo v:null echo v:null
< v:null ~ < v:null ~
That is so that eval() can parse the string back to the same That is so that eval() can parse the string back to the same
value. value. Read-only.
*v:oldfiles* *oldfiles-variable* *v:oldfiles* *oldfiles-variable*
v:oldfiles List of file names that is loaded from the |viminfo| file on v:oldfiles List of file names that is loaded from the |viminfo| file on
@ -1890,7 +1890,7 @@ v:true A Number with value one. Used to put "true" in JSON. See
echo v:true echo v:true
< v:true ~ < v:true ~
That is so that eval() can parse the string back to the same That is so that eval() can parse the string back to the same
value. value. Read-only.
*v:val* *val-variable* *v:val* *val-variable*
v:val Value of the current item of a |List| or |Dictionary|. Only v:val Value of the current item of a |List| or |Dictionary|. Only
valid while evaluating the expression used with |map()| and valid while evaluating the expression used with |map()| and
@ -7154,6 +7154,14 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()*
:echo substitute(s, '%\(\x\x\)', :echo substitute(s, '%\(\x\x\)',
\ '\=nr2char("0x" . submatch(1))', 'g') \ '\=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()* synID({lnum}, {col}, {trans}) *synID()*
The result is a Number, which is the syntax ID at the position The result is a Number, which is the syntax ID at the position
{lnum} and {col} in the current window. {lnum} and {col} in the current window.
@ -7546,18 +7554,20 @@ trunc({expr}) *trunc()*
{only available when compiled with the |+float| feature} {only available when compiled with the |+float| feature}
*type()* *type()*
type({expr}) The result is a Number, depending on the type of {expr}: type({expr}) The result is a Number representing the type of {expr}.
Number: 0 Instead of using the number directly, it is better to use the
String: 1 v:t_ variable that has the value:
Funcref: 2 Number: 0 |v:t_number|
List: 3 String: 1 |v:t_string|
Dictionary: 4 Funcref: 2 |v:t_func|
Float: 5 List: 3 |v:t_list|
Boolean: 6 (v:false and v:true) Dictionary: 4 |v:t_dict|
None 7 (v:null and v:none) Float: 5 |v:t_float|
Job 8 Boolean: 6 |v:t_bool| (v:false and v:true)
Channel 9 None 7 |v:t_none| (v:null and v:none)
To avoid the magic numbers it should be used this way: > Job 8 |v:t_job|
Channel 9 |v:t_channel|
For backward compatibility, this method can be used: >
:if type(myvar) == type(0) :if type(myvar) == type(0)
:if type(myvar) == type("") :if type(myvar) == type("")
:if type(myvar) == type(function("tr")) :if type(myvar) == type(function("tr"))
@ -7566,6 +7576,8 @@ type({expr}) The result is a Number, depending on the type of {expr}:
:if type(myvar) == type(0.0) :if type(myvar) == type(0.0)
:if type(myvar) == type(v:false) :if type(myvar) == type(v:false)
:if type(myvar) == type(v:none) :if type(myvar) == type(v:none)
< To check if the v:t_ variables exist use this: >
:if exists('v:t_number')
undofile({name}) *undofile()* undofile({name}) *undofile()*
Return the name of the undo file that would be used for a file Return the name of the undo file that would be used for a file

View File

@ -1533,8 +1533,8 @@ invoke_callback(channel_T *channel, char_u *callback, partial_T *partial,
argv[0].v_type = VAR_CHANNEL; argv[0].v_type = VAR_CHANNEL;
argv[0].vval.v_channel = channel; argv[0].vval.v_channel = channel;
call_func(callback, (int)STRLEN(callback), call_func(callback, (int)STRLEN(callback), &rettv, 2, argv, NULL,
&rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL); 0L, 0L, &dummy, TRUE, partial, NULL);
clear_tv(&rettv); clear_tv(&rettv);
channel_need_redraw = TRUE; channel_need_redraw = TRUE;
} }
@ -2695,7 +2695,7 @@ channel_close(channel_T *channel, int invoke_close_cb)
argv[0].v_type = VAR_CHANNEL; argv[0].v_type = VAR_CHANNEL;
argv[0].vval.v_channel = channel; argv[0].vval.v_channel = channel;
call_func(channel->ch_close_cb, (int)STRLEN(channel->ch_close_cb), call_func(channel->ch_close_cb, (int)STRLEN(channel->ch_close_cb),
&rettv, 1, argv, 0L, 0L, &dummy, TRUE, &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
channel->ch_close_partial, NULL); channel->ch_close_partial, NULL);
clear_tv(&rettv); clear_tv(&rettv);
channel_need_redraw = TRUE; channel_need_redraw = TRUE;
@ -4758,7 +4758,7 @@ job_status(job_T *job)
argv[1].v_type = VAR_NUMBER; argv[1].v_type = VAR_NUMBER;
argv[1].vval.v_number = job->jv_exitval; argv[1].vval.v_number = job->jv_exitval;
call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb), call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
&rettv, 2, argv, 0L, 0L, &dummy, TRUE, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
job->jv_exit_partial, NULL); job->jv_exit_partial, NULL);
clear_tv(&rettv); clear_tv(&rettv);
--job->jv_refcount; --job->jv_refcount;

View File

@ -988,7 +988,7 @@ call_vim_function(
} }
rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */ rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */
ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, NULL,
curwin->w_cursor.lnum, curwin->w_cursor.lnum, curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&doesrange, TRUE, NULL, NULL); &doesrange, TRUE, NULL, NULL);
if (safe) if (safe)
@ -9930,8 +9930,8 @@ filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
if (expr->v_type == VAR_FUNC) if (expr->v_type == VAR_FUNC)
{ {
s = expr->vval.v_string; s = expr->vval.v_string;
if (call_func(s, (int)STRLEN(s), if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
&rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL) 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL)
goto theend; goto theend;
} }
else if (expr->v_type == VAR_PARTIAL) else if (expr->v_type == VAR_PARTIAL)
@ -9939,9 +9939,8 @@ filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
partial_T *partial = expr->vval.v_partial; partial_T *partial = expr->vval.v_partial;
s = partial->pt_name; s = partial->pt_name;
if (call_func(s, (int)STRLEN(s), if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
&rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL) 0L, 0L, &dummy, TRUE, partial, NULL) == FAIL)
== FAIL)
goto theend; goto theend;
} }
else else

View File

@ -10101,7 +10101,7 @@ item_compare2(const void *s1, const void *s2)
rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
res = call_func(func_name, (int)STRLEN(func_name), 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); partial, sortinfo->item_compare_selfdict);
clear_tv(&argv[0]); clear_tv(&argv[0]);
clear_tv(&argv[1]); clear_tv(&argv[1]);

View File

@ -1163,7 +1163,7 @@ timer_callback(timer_T *timer)
argv[1].v_type = VAR_UNKNOWN; argv[1].v_type = VAR_UNKNOWN;
call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback), call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
&rettv, 1, argv, 0L, 0L, &dummy, TRUE, &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
timer->tr_partial, NULL); timer->tr_partial, NULL);
clear_tv(&rettv); clear_tv(&rettv);
} }

View File

@ -924,4 +924,35 @@ write_list(FILE *fd, list_T *list, int binary)
return ret; return ret;
} }
/*
* Initialize a static list with 10 items.
*/
void
init_static_list(staticList10_T *sl)
{
list_T *l = &sl->sl_list;
int i;
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 (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;
}
}
#endif /* defined(FEAT_EVAL) */ #endif /* defined(FEAT_EVAL) */

View File

@ -32,4 +32,5 @@ char_u *list2string(typval_T *tv, int copyID, int restore_copyID);
int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID); int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID);
int get_list_tv(char_u **arg, typval_T *rettv, int evaluate); int get_list_tv(char_u **arg, typval_T *rettv, int evaluate);
int write_list(FILE *fd, list_T *list, int binary); int write_list(FILE *fd, list_T *list, int binary);
void init_static_list(staticList10_T *sl);
/* vim: set ft=c : */ /* vim: set ft=c : */

View File

@ -5,7 +5,7 @@ char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_au
int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict); int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict);
void free_all_functions(void); void free_all_functions(void);
int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv);
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in); int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, int (*argv_func)(int, typval_T *, int), linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in);
void ex_function(exarg_T *eap); void ex_function(exarg_T *eap);
int eval_fname_script(char_u *p); int eval_fname_script(char_u *p);
int translated_function_exists(char_u *name); int translated_function_exists(char_u *name);

View File

@ -7290,6 +7290,50 @@ static int submatch_line_lbr;
#endif #endif
#if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) || defined(PROTO) #if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) || defined(PROTO)
/*
* 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)
{
listitem_T *li;
int i;
char_u *s;
if (argcount == 0)
/* called function doesn't take an argument */
return 0;
/* Relies on sl_list to be the first item in staticList10_T. */
init_static_list((staticList10_T *)(argv->vval.v_list));
/* There are always 10 list items in staticList10_T. */
li = argv->vval.v_list->lv_first;
for (i = 0; i < 10; ++i)
{
s = submatch_match->startp[i];
if (s == NULL || submatch_match->endp[i] == NULL)
s = NULL;
else
s = vim_strnsave(s, (int)(submatch_match->endp[i] - s));
li->li_tv.v_type = VAR_STRING;
li->li_tv.vval.v_string = s;
li = li->li_next;
}
return 1;
}
static void
clear_submatch_list(staticList10_T *sl)
{
int i;
for (i = 0; i < 10; ++i)
vim_free(sl->sl_items[i].li_tv.vval.v_string);
}
/* /*
* vim_regsub() - perform substitutions after a vim_regexec() or * vim_regsub() - perform substitutions after a vim_regexec() or
* vim_regexec_multi() match. * vim_regexec_multi() match.
@ -7427,10 +7471,11 @@ vim_regsub_both(
if (expr != NULL) if (expr != NULL)
{ {
typval_T argv[1]; typval_T argv[2];
int dummy; int dummy;
char_u buf[NUMBUFLEN]; char_u buf[NUMBUFLEN];
typval_T rettv; typval_T rettv;
staticList10_T matchList;
rettv.v_type = VAR_STRING; rettv.v_type = VAR_STRING;
rettv.vval.v_string = NULL; rettv.vval.v_string = NULL;
@ -7438,10 +7483,16 @@ vim_regsub_both(
{ {
/* can't do this recursively */ /* can't do this recursively */
} }
else if (expr->v_type == VAR_FUNC) 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; s = expr->vval.v_string;
call_func(s, (int)STRLEN(s), &rettv, 0, argv, call_func(s, (int)STRLEN(s), &rettv,
1, argv, fill_submatch_list,
0L, 0L, &dummy, TRUE, NULL, NULL); 0L, 0L, &dummy, TRUE, NULL, NULL);
} }
else if (expr->v_type == VAR_PARTIAL) else if (expr->v_type == VAR_PARTIAL)
@ -7449,12 +7500,18 @@ vim_regsub_both(
partial_T *partial = expr->vval.v_partial; partial_T *partial = expr->vval.v_partial;
s = partial->pt_name; s = partial->pt_name;
call_func(s, (int)STRLEN(s), &rettv, 0, argv, call_func(s, (int)STRLEN(s), &rettv,
1, argv, fill_submatch_list,
0L, 0L, &dummy, TRUE, partial, NULL); 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); eval_result = get_tv_string_buf_chk(&rettv, buf);
if (eval_result != NULL) if (eval_result != NULL)
eval_result = vim_strsave(eval_result); eval_result = vim_strsave(eval_result);
clear_tv(&rettv);
} }
else else
eval_result = eval_to_string(source + 2, NULL, TRUE); eval_result = eval_to_string(source + 2, NULL, TRUE);

View File

@ -1244,6 +1244,14 @@ struct listvar_S
list_T *lv_used_prev; /* previous list in used lists list */ list_T *lv_used_prev; /* previous list in used lists list */
}; };
/*
* 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. * Structure to hold an item of a Dictionary.
* Also used for a variable. * Also used for a variable.

View File

@ -153,3 +153,22 @@ func Test_substitute_expr()
endfunc endfunc
call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, '')) call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, ''))
endfunc 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

View File

@ -480,7 +480,7 @@ get_func_tv(
&argvars[i]; &argvars[i];
} }
ret = call_func(name, len, rettv, argcount, argvars, ret = call_func(name, len, rettv, argcount, argvars, NULL,
firstline, lastline, doesrange, evaluate, partial, selfdict); firstline, lastline, doesrange, evaluate, partial, selfdict);
funcargs.ga_len -= i; funcargs.ga_len -= i;
@ -1139,7 +1139,7 @@ func_call(
} }
if (item == NULL) 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, curwin->w_cursor.lnum, curwin->w_cursor.lnum,
&dummy, TRUE, partial, selfdict); &dummy, TRUE, partial, selfdict);
@ -1152,6 +1152,11 @@ func_call(
/* /*
* Call a function with its resolved parameters * 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. * Return FAIL when the function can't be called, OK otherwise.
* Also returns OK when an error was encountered while executing the function. * Also returns OK when an error was encountered while executing the function.
*/ */
@ -1163,6 +1168,8 @@ call_func(
int argcount_in, /* number of "argvars" */ int argcount_in, /* number of "argvars" */
typval_T *argvars_in, /* vars for arguments, must have "argcount" typval_T *argvars_in, /* vars for arguments, must have "argcount"
PLUS ONE elements! */ PLUS ONE elements! */
int (* argv_func)(int, typval_T *, int),
/* function to fill in argvars */
linenr_T firstline, /* first line of range */ linenr_T firstline, /* first line of range */
linenr_T lastline, /* last line of range */ linenr_T lastline, /* last line of range */
int *doesrange, /* return: function handled range */ int *doesrange, /* return: function handled range */
@ -1254,6 +1261,9 @@ call_func(
if (fp != NULL) if (fp != NULL)
{ {
if (argv_func != NULL)
argcount = argv_func(argcount, argvars, fp->uf_args.ga_len);
if (fp->uf_flags & FC_RANGE) if (fp->uf_flags & FC_RANGE)
*doesrange = TRUE; *doesrange = TRUE;
if (argcount < fp->uf_args.ga_len) if (argcount < fp->uf_args.ga_len)

View File

@ -758,6 +758,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
2090,
/**/ /**/
2089, 2089,
/**/ /**/