# HG changeset patch # User Christian Brabandt # Date 1468948506 -7200 # Node ID bf204ab1ce7df91f3d3f1fe306f5f9d62195fdfc # Parent 58eb7e19428680cec50baf97a79eba27d665e08d commit https://github.com/vim/vim/commit/72ab729c3dcdea0fba44d8e676602c847e841bcd Author: Bram Moolenaar Date: Tue Jul 19 19:10:51 2016 +0200 patch 7.4.2072 Problem: substitute() does not support a Funcref argument. Solution: Support a Funcref like it supports a string starting with "\=". diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -9769,7 +9769,7 @@ repeat: if (sub != NULL && str != NULL) { *usedlen = (int)(p + 1 - src); - s = do_string_sub(str, pat, sub, flags); + s = do_string_sub(str, pat, sub, NULL, flags); if (s != NULL) { *fnamep = s; @@ -9813,6 +9813,7 @@ repeat: /* * 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. */ @@ -9821,6 +9822,7 @@ do_string_sub( char_u *str, char_u *pat, char_u *sub, + typval_T *expr, char_u *flags) { int sublen; @@ -9873,7 +9875,7 @@ do_string_sub( * - The substituted text. * - The text after the match. */ - sublen = vim_regsub(®match, sub, tail, FALSE, TRUE, FALSE); + sublen = vim_regsub(®match, sub, expr, tail, FALSE, TRUE, FALSE); if (ga_grow(&ga, (int)((end - tail) + sublen - (regmatch.endp[0] - regmatch.startp[0]))) == FAIL) { @@ -9885,7 +9887,7 @@ do_string_sub( i = (int)(regmatch.startp[0] - tail); mch_memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); /* add the substituted text */ - (void)vim_regsub(®match, sub, (char_u *)ga.ga_data + (void)vim_regsub(®match, sub, expr, (char_u *)ga.ga_data + ga.ga_len + i, TRUE, TRUE, FALSE); ga.ga_len += i + sublen - 1; tail = regmatch.endp[0]; @@ -9906,7 +9908,7 @@ do_string_sub( if (p_cpo == empty_option) p_cpo = save_cpo; else - /* Darn, evaluating {sub} expression changed the value. */ + /* Darn, evaluating {sub} expression or {expr} changed the value. */ free_string_option(save_cpo); return ret; diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -11061,14 +11061,21 @@ f_substitute(typval_T *argvars, typval_T 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); + rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg); } /* diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -126,6 +126,6 @@ void assert_exception(typval_T *argvars) void assert_fails(typval_T *argvars); void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, typval_T *got_tv, assert_type_T atype); int modify_fname(char_u *src, int *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen); -char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags); +char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, char_u *flags); void filter_map(typval_T *argvars, typval_T *rettv, int map); /* vim: set ft=c : */ diff --git a/src/proto/regexp.pro b/src/proto/regexp.pro --- a/src/proto/regexp.pro +++ b/src/proto/regexp.pro @@ -7,7 +7,7 @@ void free_regexp_stuff(void); reg_extmatch_T *ref_extmatch(reg_extmatch_T *em); void unref_extmatch(reg_extmatch_T *em); char_u *regtilde(char_u *source, int magic); -int vim_regsub(regmatch_T *rmp, char_u *source, char_u *dest, int copy, int magic, int backslash); +int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int copy, int magic, int backslash); int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash); char_u *reg_submatch(int no); list_T *reg_submatch_list(int no); diff --git a/src/regexp.c b/src/regexp.c --- a/src/regexp.c +++ b/src/regexp.c @@ -7169,7 +7169,7 @@ static fptr_T do_Upper(int *, int); static fptr_T do_lower(int *, int); static fptr_T do_Lower(int *, int); -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); static fptr_T do_upper(int *d, int c) @@ -7312,6 +7312,7 @@ static int submatch_line_lbr; vim_regsub( regmatch_T *rmp, char_u *source, + typval_T *expr, char_u *dest, int copy, int magic, @@ -7322,7 +7323,7 @@ vim_regsub( reg_maxline = 0; reg_buf = curbuf; reg_line_lbr = TRUE; - return vim_regsub_both(source, dest, copy, magic, backslash); + return vim_regsub_both(source, expr, dest, copy, magic, backslash); } #endif @@ -7342,12 +7343,13 @@ vim_regsub_multi( 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); + return vim_regsub_both(source, NULL, dest, copy, magic, backslash); } static int vim_regsub_both( char_u *source, + typval_T *expr, char_u *dest, int copy, int magic, @@ -7364,11 +7366,11 @@ vim_regsub_both( linenr_T clnum = 0; /* init for GCC */ int len = 0; /* init for GCC */ #ifdef FEAT_EVAL - static char_u *eval_result = NULL; + static char_u *eval_result = NULL; #endif /* Be paranoid... */ - if (source == NULL || dest == NULL) + if ((source == NULL && expr == NULL) || dest == NULL) { EMSG(_(e_null)); return 0; @@ -7381,11 +7383,11 @@ vim_regsub_both( /* * When the substitute part starts with "\=" evaluate it as an expression. */ - if (source[0] == '\\' && source[1] == '=' + if (expr != NULL || (source[0] == '\\' && source[1] == '=' #ifdef FEAT_EVAL && !can_f_submatch /* can't do this recursively */ #endif - ) + )) { #ifdef FEAT_EVAL /* To make sure that the length doesn't change between checking the @@ -7406,6 +7408,7 @@ vim_regsub_both( { win_T *save_reg_win; int save_ireg_ic; + int prev_can_f_submatch = can_f_submatch; vim_free(eval_result); @@ -7422,7 +7425,40 @@ vim_regsub_both( save_ireg_ic = ireg_ic; can_f_submatch = TRUE; - eval_result = eval_to_string(source + 2, NULL, 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); + if (eval_result != NULL) { int had_backslash = FALSE; diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim --- a/src/testdir/test_expr.vim +++ b/src/testdir/test_expr.vim @@ -135,3 +135,21 @@ function Test_printf_64bit() call assert_equal("123456789012345", printf('%d', 123456789012345)) endif 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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -759,6 +759,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2072, +/**/ 2071, /**/ 2070,