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.
Files:      src/structs.h, src/list.c, src/userfunc.c, src/channel.c,
            src/eval.c, src/evalfunc.c, src/ex_cmds2.c, src/regexp.c,
            src/proto/list.pro, src/proto/userfunc.pro,
            src/testdir/test_expr.vim, runtime/doc/eval.txt


*** ../vim-7.4.2089/src/structs.h       2016-07-20 21:44:33.370131700 +0200
--- src/structs.h       2016-07-22 21:06:32.461017423 +0200
***************
*** 1245,1250 ****
--- 1245,1258 ----
  };
  
  /*
+  * 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.
   * The key is copied into "di_key" to avoid an extra alloc/free for it.
*** ../vim-7.4.2089/src/list.c  2016-07-17 22:13:26.813095293 +0200
--- src/list.c  2016-07-22 20:05:23.972169701 +0200
***************
*** 924,927 ****
--- 924,958 ----
      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) */
*** ../vim-7.4.2089/src/userfunc.c      2016-07-20 22:11:01.465544002 +0200
--- src/userfunc.c      2016-07-22 20:51:49.272591413 +0200
***************
*** 480,486 ****
                                                                  &argvars[i];
        }
  
!       ret = call_func(name, len, rettv, argcount, argvars,
                 firstline, lastline, doesrange, evaluate, partial, selfdict);
  
        funcargs.ga_len -= i;
--- 480,486 ----
                                                                  &argvars[i];
        }
  
!       ret = call_func(name, len, rettv, argcount, argvars, NULL,
                 firstline, lastline, doesrange, evaluate, partial, selfdict);
  
        funcargs.ga_len -= i;
***************
*** 1139,1145 ****
      }
  
      if (item == NULL)
!       r = call_func(name, (int)STRLEN(name), rettv, argc, argv,
                                 curwin->w_cursor.lnum, curwin->w_cursor.lnum,
                                             &dummy, TRUE, partial, selfdict);
  
--- 1139,1145 ----
      }
  
      if (item == NULL)
!       r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL,
                                 curwin->w_cursor.lnum, curwin->w_cursor.lnum,
                                             &dummy, TRUE, partial, selfdict);
  
***************
*** 1152,1157 ****
--- 1152,1162 ----
  
  /*
   * 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,1168 ****
--- 1168,1175 ----
      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,1259 ****
--- 1261,1269 ----
  
            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)
*** ../vim-7.4.2089/src/channel.c       2016-07-15 21:29:31.413268255 +0200
--- src/channel.c       2016-07-22 20:50:06.649413041 +0200
***************
*** 1533,1540 ****
      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);
      clear_tv(&rettv);
      channel_need_redraw = TRUE;
  }
--- 1533,1540 ----
      argv[0].v_type = VAR_CHANNEL;
      argv[0].vval.v_channel = channel;
  
!     call_func(callback, (int)STRLEN(callback), &rettv, 2, argv, NULL,
!                                         0L, 0L, &dummy, TRUE, partial, NULL);
      clear_tv(&rettv);
      channel_need_redraw = TRUE;
  }
***************
*** 2695,2701 ****
              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,
                           channel->ch_close_partial, NULL);
              clear_tv(&rettv);
              channel_need_redraw = TRUE;
--- 2695,2701 ----
              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, NULL, 0L, 0L, &dummy, TRUE,
                           channel->ch_close_partial, NULL);
              clear_tv(&rettv);
              channel_need_redraw = TRUE;
***************
*** 4758,4764 ****
            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,
                           job->jv_exit_partial, NULL);
            clear_tv(&rettv);
            --job->jv_refcount;
--- 4758,4764 ----
            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, NULL, 0L, 0L, &dummy, TRUE,
                           job->jv_exit_partial, NULL);
            clear_tv(&rettv);
            --job->jv_refcount;
*** ../vim-7.4.2089/src/eval.c  2016-07-19 19:10:48.020177776 +0200
--- src/eval.c  2016-07-22 20:50:51.941054916 +0200
***************
*** 988,994 ****
      }
  
      rettv->v_type = VAR_UNKNOWN;              /* clear_tv() uses this */
!     ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
                    curwin->w_cursor.lnum, curwin->w_cursor.lnum,
                    &doesrange, TRUE, NULL, NULL);
      if (safe)
--- 988,994 ----
      }
  
      rettv->v_type = VAR_UNKNOWN;              /* clear_tv() uses this */
!     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,9937 ****
      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)
            goto theend;
      }
      else if (expr->v_type == VAR_PARTIAL)
--- 9930,9937 ----
      if (expr->v_type == VAR_FUNC)
      {
        s = expr->vval.v_string;
!       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,9947 ****
        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)
            goto theend;
      }
      else
--- 9939,9946 ----
        partial_T   *partial = expr->vval.v_partial;
  
        s = partial->pt_name;
!       if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
!                                 0L, 0L, &dummy, TRUE, partial, NULL) == FAIL)
            goto theend;
      }
      else
*** ../vim-7.4.2089/src/evalfunc.c      2016-07-19 19:10:48.016177817 +0200
--- src/evalfunc.c      2016-07-22 20:51:02.860968713 +0200
***************
*** 10101,10107 ****
  
      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,
                                 partial, sortinfo->item_compare_selfdict);
      clear_tv(&argv[0]);
      clear_tv(&argv[1]);
--- 10101,10107 ----
  
      rettv.v_type = VAR_UNKNOWN;               /* clear_tv() uses this */
      res = call_func(func_name, (int)STRLEN(func_name),
!                                &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
                                 partial, sortinfo->item_compare_selfdict);
      clear_tv(&argv[0]);
      clear_tv(&argv[1]);
*** ../vim-7.4.2089/src/ex_cmds2.c      2016-07-11 22:41:09.580781837 +0200
--- src/ex_cmds2.c      2016-07-22 20:51:10.972904709 +0200
***************
*** 1163,1169 ****
      argv[1].v_type = VAR_UNKNOWN;
  
      call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
!                       &rettv, 1, argv, 0L, 0L, &dummy, TRUE,
                        timer->tr_partial, NULL);
      clear_tv(&rettv);
  }
--- 1163,1169 ----
      argv[1].v_type = VAR_UNKNOWN;
  
      call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
!                       &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
                        timer->tr_partial, NULL);
      clear_tv(&rettv);
  }
*** ../vim-7.4.2089/src/regexp.c        2016-07-19 19:10:48.016177817 +0200
--- src/regexp.c        2016-07-22 21:18:08.054715137 +0200
***************
*** 7290,7295 ****
--- 7290,7339 ----
  #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,7436 ****
  
            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;
--- 7471,7481 ----
  
            if (expr != NULL)
            {
!               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,7460 ****
                {
                    /* 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);
--- 7483,7517 ----
                {
                    /* can't do this recursively */
                }
!               else
                {
!                   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;
! 
!                       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);
*** ../vim-7.4.2089/src/proto/list.pro  2016-07-17 22:13:26.817095253 +0200
--- src/proto/list.pro  2016-07-22 19:46:42.452921112 +0200
***************
*** 32,35 ****
--- 32,36 ----
  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 : */
*** ../vim-7.4.2089/src/proto/userfunc.pro      2016-07-17 18:28:59.027697464 
+0200
--- src/proto/userfunc.pro      2016-07-22 20:49:18.705793227 +0200
***************
*** 5,11 ****
  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);
  void ex_function(exarg_T *eap);
  int eval_fname_script(char_u *p);
  int translated_function_exists(char_u *name);
--- 5,11 ----
  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, 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);
*** ../vim-7.4.2089/src/testdir/test_expr.vim   2016-07-19 19:10:48.020177776 
+0200
--- src/testdir/test_expr.vim   2016-07-22 21:42:57.445352255 +0200
***************
*** 153,155 ****
--- 153,174 ----
    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
*** ../vim-7.4.2089/runtime/doc/eval.txt        2016-07-19 17:25:19.074023425 
+0200
--- runtime/doc/eval.txt        2016-07-22 21:46:52.043393822 +0200
***************
*** 7030,7045 ****
                unmodified.
  
                Example: >
!                       :let &path = substitute(&path, ",\\=[^,]*$", "", "")
  <             This removes the last component of the 'path' option. >
!                       :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\)',
                           \ '\=nr2char("0x" . submatch(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.
--- 7144,7167 ----
                unmodified.
  
                Example: >
!                  :let &path = substitute(&path, ",\\=[^,]*$", "", "")
  <             This removes the last component of the 'path' option. >
!                  :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\)',
                           \ '\=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.
*** ../vim-7.4.2089/src/version.c       2016-07-21 22:10:06.051248382 +0200
--- src/version.c       2016-07-22 20:29:13.180082769 +0200
***************
*** 760,761 ****
--- 760,763 ----
  {   /* Add new patch number below this line */
+ /**/
+     2090,
  /**/

-- 
FATHER: We are here today to witness the union of two young people in the
        joyful bond of the holy wedlock.  Unfortunately, one of them, my son
        Herbert, has just fallen to his death.
   [Murmurs from CROWD;  the BRIDE smiles with relief, coughs.]
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui