To: vim_dev@googlegroups.com Subject: Patch 7.4.2090 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@Moolenaar.net -- 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 ///