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:
parent
36edf0685c
commit
df48fb456f
@ -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
|
||||
|
@ -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;
|
||||
|
11
src/eval.c
11
src/eval.c
@ -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
|
||||
|
@ -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]);
|
||||
|
@ -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);
|
||||
}
|
||||
|
31
src/list.c
31
src/list.c
@ -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) */
|
||||
|
@ -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 : */
|
||||
|
@ -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);
|
||||
|
77
src/regexp.c
77
src/regexp.c
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -758,6 +758,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
2090,
|
||||
/**/
|
||||
2089,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user