]> granicus.if.org Git - vim/commitdiff
patch 7.4.2090 v7.4.2090
authorBram Moolenaar <Bram@vim.org>
Fri, 22 Jul 2016 19:50:18 +0000 (21:50 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 22 Jul 2016 19:50:18 +0000 (21:50 +0200)
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.

13 files changed:
runtime/doc/eval.txt
src/channel.c
src/eval.c
src/evalfunc.c
src/ex_cmds2.c
src/list.c
src/proto/list.pro
src/proto/userfunc.pro
src/regexp.c
src/structs.h
src/testdir/test_expr.vim
src/userfunc.c
src/version.c

index d33f516594bf984f444ee6fe797c4804dece27c7..fa3c756a7d1bd9a519d510519aa52bda7bcd5b53 100644 (file)
@@ -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
index ae5bdf220bb7afded9132f1890d750c9d25cf8bb..722dcb8de969e8213bb183c79a0fb61303dfe104 100644 (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;
index 2ba90f94d6ca7e9aa6b771a550ad80450ac49fcb..53c188baeffc813142bedd8cdd21359afa8c2688 100644 (file)
@@ -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
index 54b5a53bb9b740e91cdfc040cf25063c54652d01..61dce74fd3d4d261b528e0c61e8c4a04ccf5eb91 100644 (file)
@@ -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]);
index 2b2d63549f3cf06d3088faf4997d3d4ba50f7a5f..adc53529b4471d2e56d69ac629cc2b3aa9e02392 100644 (file)
@@ -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);
 }
index 13f920039cf5938efc11385012bb92f130020032..9e0e26fafd5a25b6fcb6b99f484b058701909758 100644 (file)
@@ -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) */
index 9849379597fbd65ca544a4a5d37e63759eb08ba0..56f0ddc4fbe99cc2765814428b4885893357c31c 100644 (file)
@@ -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 : */
index b354197a32675191eccbbff78aedc935e13687c7..feacd4c0042e7c486b53eddc76d2d0a6dc91cced 100644 (file)
@@ -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);
index 1a7775894a4f30ac43cb4d4e8f89146478a7691d..6b23cfbec9554e0ec3ecb096d128a01d2e02e8db 100644 (file)
@@ -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);
index 191fa1f923ebdaab34b059ce896f95218d802964..7375c7c2b8e1a1a5b538665995220dc9d0c49644 100644 (file)
@@ -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.
index 235dd8d65a1c667f3b1303647fe7c68d29ed38f7..8db4d8e5f35fb9cd1ab56b596f6dc439e50244b9 100644 (file)
@@ -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
index ffbbc2d21f754e9e81b8092c8b30fdd334cdcb64..af1863f32818b08c505f1b592221bbd0975c6299 100644 (file)
@@ -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)
index dccf2ce3dc771d4ffbe96f2d93dbcad0baa693d6..e42229eea03e5f28d01808fbd7bbf7b3f698f3b9 100644 (file)
@@ -758,6 +758,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2090,
 /**/
     2089,
 /**/