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
@ -1535,7 +1535,7 @@ v:false A Number with value zero. Used to put "false" in JSON. See
echo v:false
< v:false ~
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 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
< v:none ~
That is so that eval() can parse the string back to the same
value.
value. Read-only.
*v:null* *null-variable*
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
< v:null ~
That is so that eval() can parse the string back to the same
value.
value. Read-only.
*v:oldfiles* *oldfiles-variable*
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
< v:true ~
That is so that eval() can parse the string back to the same
value.
value. Read-only.
*v:val* *val-variable*
v:val Value of the current item of a |List| or |Dictionary|. Only
valid while evaluating the expression used with |map()| and
@ -7144,16 +7144,24 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()*
unmodified.
Example: >
:let &path = substitute(&path, ",\\=[^,]*$", "", "")
:let &path = substitute(&path, ",\\=[^,]*$", "", "")
< This removes the last component of the 'path' option. >
:echo substitute("testing", ".*", "\\U\\0", "")
:echo substitute("testing", ".*", "\\U\\0", "")
< results in "TESTING".
When {sub} starts with "\=", the remainder is interpreted as
an expression. See |sub-replace-expression|. Example: >
:echo substitute(s, '%\(\x\x\)',
: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.
@ -7546,18 +7554,20 @@ trunc({expr}) *trunc()*
{only available when compiled with the |+float| feature}
*type()*
type({expr}) The result is a Number, depending on the type of {expr}:
Number: 0
String: 1
Funcref: 2
List: 3
Dictionary: 4
Float: 5
Boolean: 6 (v:false and v:true)
None 7 (v:null and v:none)
Job 8
Channel 9
To avoid the magic numbers it should be used this way: >
type({expr}) 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:t_bool| (v:false and v:true)
None 7 |v:t_none| (v:null and v:none)
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("")
: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(v:false)
:if type(myvar) == type(v:none)
< 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

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].vval.v_channel = channel;
call_func(callback, (int)STRLEN(callback),
&rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL);
call_func(callback, (int)STRLEN(callback), &rettv, 2, argv, NULL,
0L, 0L, &dummy, TRUE, partial, NULL);
clear_tv(&rettv);
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].vval.v_channel = channel;
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);
clear_tv(&rettv);
channel_need_redraw = TRUE;
@ -4758,7 +4758,7 @@ job_status(job_T *job)
argv[1].v_type = VAR_NUMBER;
argv[1].vval.v_number = job->jv_exitval;
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);
clear_tv(&rettv);
--job->jv_refcount;

View File

@ -988,7 +988,7 @@ call_vim_function(
}
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,
&doesrange, TRUE, NULL, NULL);
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)
{
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)
@ -9939,9 +9939,8 @@ filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
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

View File

@ -10101,7 +10101,7 @@ item_compare2(const void *s1, const void *s2)
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]);

View File

@ -1163,7 +1163,7 @@ timer_callback(timer_T *timer)
argv[1].v_type = VAR_UNKNOWN;
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);
clear_tv(&rettv);
}

View File

@ -924,4 +924,35 @@ write_list(FILE *fd, list_T *list, int binary)
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) */

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 get_list_tv(char_u **arg, typval_T *rettv, int evaluate);
int write_list(FILE *fd, list_T *list, int binary);
void init_static_list(staticList10_T *sl);
/* 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);
void free_all_functions(void);
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);
int eval_fname_script(char_u *p);
int translated_function_exists(char_u *name);

View File

@ -7290,6 +7290,50 @@ static int submatch_line_lbr;
#endif
#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_regexec_multi() match.
@ -7427,10 +7471,11 @@ vim_regsub_both(
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;
@ -7438,23 +7483,35 @@ vim_regsub_both(
{
/* can't do this recursively */
}
else if (expr->v_type == VAR_FUNC)
else
{
s = expr->vval.v_string;
call_func(s, (int)STRLEN(s), &rettv, 0, argv,
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;
}
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,
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);

View File

@ -1244,6 +1244,14 @@ struct listvar_S
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.
* Also used for a variable.

View File

@ -153,3 +153,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

View File

@ -480,7 +480,7 @@ get_func_tv(
&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);
funcargs.ga_len -= i;
@ -1139,7 +1139,7 @@ func_call(
}
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);
@ -1152,6 +1152,11 @@ func_call(
/*
* 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.
*/
@ -1163,6 +1168,8 @@ call_func(
int argcount_in, /* number of "argvars" */
typval_T *argvars_in, /* vars for arguments, must have "argcount"
PLUS ONE elements! */
int (* argv_func)(int, typval_T *, int),
/* 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 */
@ -1254,6 +1261,9 @@ call_func(
if (fp != NULL)
{
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)

View File

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