vim-patch:7.4.2072

Problem:    substitute() does not support a Funcref argument.
Solution:   Support a Funcref like it supports  a string starting with "\=".

72ab729c3d
This commit is contained in:
Michael Ennen 2016-12-15 13:53:14 -07:00
parent bb7d0deb2f
commit fc46efd3f2
4 changed files with 153 additions and 60 deletions

View File

@ -16997,19 +16997,26 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char_u *str = get_tv_string_chk(&argvars[0]);
char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf);
char_u *sub = get_tv_string_buf_chk(&argvars[2], subbuf);
char_u *sub = NULL;
typval_T *expr = NULL;
char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf);
if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) {
expr = &argvars[2];
} else {
sub = get_tv_string_buf_chk(&argvars[2], subbuf);
}
rettv->v_type = VAR_STRING;
if (str == NULL || pat == NULL || sub == NULL || flg == NULL)
if (str == NULL || pat == NULL || (sub == NULL && expr == NULL)
|| flg == NULL) {
rettv->vval.v_string = NULL;
else
rettv->vval.v_string = do_string_sub(str, pat, sub, flg);
} else {
rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg);
}
}
/*
* "synID(lnum, col, trans)" function
*/
/// "synID(lnum, col, trans)" function
static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
int id = 0;
@ -23133,7 +23140,7 @@ repeat:
sub = vim_strnsave(s, (int)(p - s));
str = vim_strnsave(*fnamep, *fnamelen);
*usedlen = (size_t)(p + 1 - src);
s = do_string_sub(str, pat, sub, flags);
s = do_string_sub(str, pat, sub, NULL, flags);
*fnamep = s;
*fnamelen = STRLEN(s);
xfree(*bufp);
@ -23169,12 +23176,12 @@ repeat:
return valid;
}
/*
* Perform a substitution on "str" with pattern "pat" and substitute "sub".
* "flags" can be "g" to do a global substitute.
* Returns an allocated string, NULL for error.
*/
char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
/// Perform a substitution on "str" with pattern "pat" and substitute "sub".
/// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL.
/// "flags" can be "g" to do a global substitute.
/// Returns an allocated string, NULL for error.
char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub,
typval_T *expr, char_u *flags)
{
int sublen;
regmatch_T regmatch;
@ -23212,23 +23219,21 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
zero_width = regmatch.startp[0];
}
/*
* Get some space for a temporary buffer to do the substitution
* into. It will contain:
* - The text up to where the match is.
* - The substituted text.
* - The text after the match.
*/
sublen = vim_regsub(&regmatch, sub, tail, FALSE, TRUE, FALSE);
// Get some space for a temporary buffer to do the substitution
// into. It will contain:
// - The text up to where the match is.
// - The substituted text.
// - The text after the match.
sublen = vim_regsub(&regmatch, sub, expr, tail, false, true, false);
ga_grow(&ga, (int)((end - tail) + sublen -
(regmatch.endp[0] - regmatch.startp[0])));
/* copy the text up to where the match is */
int i = (int)(regmatch.startp[0] - tail);
memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i);
/* add the substituted text */
(void)vim_regsub(&regmatch, sub, (char_u *)ga.ga_data
+ ga.ga_len + i, TRUE, TRUE, FALSE);
// add the substituted text
(void)vim_regsub(&regmatch, sub, expr, (char_u *)ga.ga_data
+ ga.ga_len + i, true, true, false);
ga.ga_len += i + sublen - 1;
tail = regmatch.endp[0];
if (*tail == NUL)
@ -23245,11 +23250,12 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
char_u *ret = vim_strsave(ga.ga_data == NULL ? str : (char_u *)ga.ga_data);
ga_clear(&ga);
if (p_cpo == empty_option)
if (p_cpo == empty_option) {
p_cpo = save_cpo;
else
/* Darn, evaluating {sub} expression changed the value. */
} else {
// Darn, evaluating {sub} expression or {expr} changed the value.
free_string_option(save_cpo);
}
return ret;
}

View File

@ -6442,32 +6442,71 @@ static linenr_T submatch_firstlnum;
static linenr_T submatch_maxline;
static int submatch_line_lbr;
/*
* vim_regsub() - perform substitutions after a vim_regexec() or
* vim_regexec_multi() match.
*
* If "copy" is TRUE really copy into "dest".
* If "copy" is FALSE nothing is copied, this is just to find out the length
* of the result.
*
* If "backslash" is TRUE, a backslash will be removed later, need to double
* them to keep them, and insert a backslash before a CR to avoid it being
* replaced with a line break later.
*
* Note: The matched text must not change between the call of
* vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back
* references invalid!
*
* Returns the size of the replacement, including terminating NUL.
*/
int vim_regsub(regmatch_T *rmp, char_u *source, char_u *dest, int copy, int magic, int backslash)
/// 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++) {
xfree(sl->sl_items[i].li_tv.vval.v_string);
}
}
/// vim_regsub() - perform substitutions after a vim_regexec() or
/// vim_regexec_multi() match.
///
/// If "copy" is TRUE really copy into "dest".
/// If "copy" is FALSE nothing is copied, this is just to find out the length
/// of the result.
///
/// If "backslash" is TRUE, a backslash will be removed later, need to double
/// them to keep them, and insert a backslash before a CR to avoid it being
/// replaced with a line break later.
///
/// Note: The matched text must not change between the call of
/// vim_regexec()/vim_regexec_multi() and vim_regsub()! It would make the back
/// references invalid!
///
/// Returns the size of the replacement, including terminating NUL.
int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest,
int copy, int magic, int backslash)
{
reg_match = rmp;
reg_mmatch = NULL;
reg_maxline = 0;
reg_buf = curbuf;
reg_line_lbr = TRUE;
return vim_regsub_both(source, dest, copy, magic, backslash);
reg_line_lbr = true;
return vim_regsub_both(source, expr, dest, copy, magic, backslash);
}
int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash)
@ -6477,11 +6516,12 @@ int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *de
reg_buf = curbuf; /* always works on the current buffer! */
reg_firstlnum = lnum;
reg_maxline = curbuf->b_ml.ml_line_count - lnum;
reg_line_lbr = FALSE;
return vim_regsub_both(source, dest, copy, magic, backslash);
reg_line_lbr = false;
return vim_regsub_both(source, NULL, dest, copy, magic, backslash);
}
static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, int backslash)
static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
int copy, int magic, int backslash)
{
char_u *src;
char_u *dst;
@ -6495,8 +6535,8 @@ static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, in
int len = 0; /* init for GCC */
static char_u *eval_result = NULL;
/* Be paranoid... */
if (source == NULL || dest == NULL) {
// Be paranoid...
if ((source == NULL && expr == NULL) || dest == NULL) {
EMSG(_(e_null));
return 0;
}
@ -6508,9 +6548,9 @@ static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, in
/*
* When the substitute part starts with "\=" evaluate it as an expression.
*/
if (source[0] == '\\' && source[1] == '='
&& !can_f_submatch /* can't do this recursively */
) {
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
@ -6525,6 +6565,7 @@ static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, in
} else {
win_T *save_reg_win;
int save_ireg_ic;
bool prev_can_f_submatch = can_f_submatch;
xfree(eval_result);
@ -6539,9 +6580,37 @@ static int vim_regsub_both(char_u *source, char_u *dest, int copy, int magic, in
submatch_line_lbr = reg_line_lbr;
save_reg_win = reg_win;
save_ireg_ic = ireg_ic;
can_f_submatch = TRUE;
can_f_submatch = true;
if (expr != NULL) {
typval_T argv[1];
int dummy;
char_u buf[NUMBUFLEN];
typval_T rettv;
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);
}
eval_result = get_tv_string_buf_chk(&rettv, buf);
if (eval_result != NULL) {
eval_result = vim_strsave(eval_result);
}
} else {
eval_result = eval_to_string(source + 2, NULL, true);
}
eval_result = eval_to_string(source + 2, NULL, TRUE);
if (eval_result != NULL) {
int had_backslash = FALSE;

View File

@ -106,3 +106,21 @@ func Test_setmatches()
call setmatches(set)
call assert_equal(exp, getmatches())
endfunc
func Test_substitute_expr()
let g:val = 'XXX'
call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
\ '\=nr2char("0x" . submatch(1))', 'g'))
call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
\ {-> nr2char("0x" . submatch(1))}, 'g'))
call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
\ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
func Recurse()
return substitute('yyy', 'y*', {-> g:val}, '')
endfunc
call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, ''))
endfunc

View File

@ -368,7 +368,7 @@ static int included_patches[] = {
2075,
2074,
// 2073 NA
// 2072,
2072,
2071,
// 2070 NA
// 2069,