diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/eval.c | 62 | ||||
| -rw-r--r-- | src/nvim/regexp.c | 131 | ||||
| -rw-r--r-- | src/nvim/testdir/test_expr.vim | 18 | ||||
| -rw-r--r-- | src/nvim/version.c | 2 | 
4 files changed, 153 insertions, 60 deletions
| diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bf5ee7a530..709ea0e2e1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -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(®match, 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(®match, 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(®match, sub, (char_u *)ga.ga_data -          + ga.ga_len + i, TRUE, TRUE, FALSE); +      // add the substituted text +      (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];        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;  } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 6613d284d7..09fafcb37e 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -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; diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 39dcacb55f..0e409009e0 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -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 diff --git a/src/nvim/version.c b/src/nvim/version.c index e5b66eff27..70a70a6e83 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -368,7 +368,7 @@ static int included_patches[] = {    2075,    2074,    // 2073 NA -  // 2072, +  2072,    2071,    // 2070 NA    // 2069, | 
