To: vim_dev@googlegroups.com Subject: Patch 7.4.2008 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.2008 Problem: evalcmd() has a confusing name. Solution: Rename to execute(). Make silent optional. Support a list of commands. Files: src/eval.c, src/ex_docmd.c, src/message.c, src/globals.h, src/proto/eval.pro, src/Makefile, src/testdir/test_evalcmd.vim, src/testdir/test_execute_func.vim, src/testdir/test_alot.vim, runtime/doc/eval.txt *** ../vim-7.4.2007/src/eval.c 2016-07-08 23:06:17.183567923 +0200 --- src/eval.c 2016-07-09 16:50:27.620916933 +0200 *************** *** 555,563 **** static void f_empty(typval_T *argvars, typval_T *rettv); static void f_escape(typval_T *argvars, typval_T *rettv); static void f_eval(typval_T *argvars, typval_T *rettv); - static void f_evalcmd(typval_T *argvars, typval_T *rettv); static void f_eventhandler(typval_T *argvars, typval_T *rettv); static void f_executable(typval_T *argvars, typval_T *rettv); static void f_exepath(typval_T *argvars, typval_T *rettv); static void f_exists(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT --- 555,563 ---- static void f_empty(typval_T *argvars, typval_T *rettv); static void f_escape(typval_T *argvars, typval_T *rettv); static void f_eval(typval_T *argvars, typval_T *rettv); static void f_eventhandler(typval_T *argvars, typval_T *rettv); static void f_executable(typval_T *argvars, typval_T *rettv); + static void f_execute(typval_T *argvars, typval_T *rettv); static void f_exepath(typval_T *argvars, typval_T *rettv); static void f_exists(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT *************** *** 8564,8572 **** {"empty", 1, 1, f_empty}, {"escape", 2, 2, f_escape}, {"eval", 1, 1, f_eval}, - {"evalcmd", 1, 1, f_evalcmd}, {"eventhandler", 0, 0, f_eventhandler}, {"executable", 1, 1, f_executable}, {"exepath", 1, 1, f_exepath}, {"exists", 1, 1, f_exists}, #ifdef FEAT_FLOAT --- 8564,8572 ---- {"empty", 1, 1, f_empty}, {"escape", 2, 2, f_escape}, {"eval", 1, 1, f_eval}, {"eventhandler", 0, 0, f_eventhandler}, {"executable", 1, 1, f_executable}, + {"execute", 1, 2, f_execute}, {"exepath", 1, 1, f_exepath}, {"exists", 1, 1, f_exists}, #ifdef FEAT_FLOAT *************** *** 11345,11357 **** EMSG(_(e_trailing)); } ! static garray_T redir_evalcmd_ga; /* ! * Append "value[value_len]" to the evalcmd() output. */ void ! evalcmd_redir_str(char_u *value, int value_len) { int len; --- 11345,11379 ---- EMSG(_(e_trailing)); } ! /* ! * "eventhandler()" function ! */ ! static void ! f_eventhandler(typval_T *argvars UNUSED, typval_T *rettv) ! { ! rettv->vval.v_number = vgetc_busy; ! } ! ! /* ! * "executable()" function ! */ ! static void ! f_executable(typval_T *argvars, typval_T *rettv) ! { ! char_u *name = get_tv_string(&argvars[0]); ! ! /* Check in $PATH and also check directly if there is a directory name. */ ! rettv->vval.v_number = mch_can_exe(name, NULL, TRUE) ! || (gettail(name) != name && mch_can_exe(name, NULL, FALSE)); ! } ! ! static garray_T redir_execute_ga; /* ! * Append "value[value_len]" to the execute() output. */ void ! execute_redir_str(char_u *value, int value_len) { int len; *************** *** 11359,11429 **** len = (int)STRLEN(value); /* Append the entire string */ else len = value_len; /* Append only "value_len" characters */ ! if (ga_grow(&redir_evalcmd_ga, len) == OK) { ! mch_memmove((char *)redir_evalcmd_ga.ga_data ! + redir_evalcmd_ga.ga_len, value, len); ! redir_evalcmd_ga.ga_len += len; } } /* ! * "evalcmd()" function */ ! static void ! f_evalcmd(typval_T *argvars, typval_T *rettv) { char_u *s; int save_msg_silent = msg_silent; ! int save_redir_evalcmd = redir_evalcmd; garray_T save_ga; rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; ! s = get_tv_string_chk(&argvars[0]); ! if (s != NULL) { ! if (redir_evalcmd) ! save_ga = redir_evalcmd_ga; ! ga_init2(&redir_evalcmd_ga, (int)sizeof(char), 500); ! redir_evalcmd = TRUE; ++msg_silent; ! do_cmdline_cmd(s); ! rettv->vval.v_string = redir_evalcmd_ga.ga_data; ! msg_silent = save_msg_silent; ! ! redir_evalcmd = save_redir_evalcmd; ! if (redir_evalcmd) ! redir_evalcmd_ga = save_ga; ! ! /* "silent reg" or "silent echo x" leaves msg_col somewhere in the ! * line. Put it back in the first column. */ ! msg_col = 0; } - } ! /* ! * "eventhandler()" function ! */ ! static void ! f_eventhandler(typval_T *argvars UNUSED, typval_T *rettv) ! { ! rettv->vval.v_number = vgetc_busy; ! } ! /* ! * "executable()" function ! */ ! static void ! f_executable(typval_T *argvars, typval_T *rettv) ! { ! char_u *name = get_tv_string(&argvars[0]); ! /* Check in $PATH and also check directly if there is a directory name. */ ! rettv->vval.v_number = mch_can_exe(name, NULL, TRUE) ! || (gettail(name) != name && mch_can_exe(name, NULL, FALSE)); } /* --- 11381,11496 ---- len = (int)STRLEN(value); /* Append the entire string */ else len = value_len; /* Append only "value_len" characters */ ! if (ga_grow(&redir_execute_ga, len) == OK) { ! mch_memmove((char *)redir_execute_ga.ga_data ! + redir_execute_ga.ga_len, value, len); ! redir_execute_ga.ga_len += len; } } /* ! * Get next line from a list. ! * Called by do_cmdline() to get the next line. ! * Returns allocated string, or NULL for end of function. */ ! ! static char_u * ! get_list_line( ! int c UNUSED, ! void *cookie, ! int indent UNUSED) { + listitem_T **p = (listitem_T **)cookie; + listitem_T *item = *p; + char_u buf[NUMBUFLEN]; char_u *s; + + if (item == NULL) + return NULL; + s = get_tv_string_buf_chk(&item->li_tv, buf); + *p = item->li_next; + return s == NULL ? NULL : vim_strsave(s); + } + + /* + * "execute()" function + */ + static void + f_execute(typval_T *argvars, typval_T *rettv) + { + char_u *cmd = NULL; + list_T *list = NULL; int save_msg_silent = msg_silent; ! int save_emsg_silent = emsg_silent; ! int save_emsg_noredir = emsg_noredir; ! int save_redir_execute = redir_execute; garray_T save_ga; rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; ! if (argvars[0].v_type == VAR_LIST) { ! list = argvars[0].vval.v_list; ! if (list == NULL || list->lv_first == NULL) ! /* empty list, no commands, empty output */ ! return; ! ++list->lv_refcount; ! } ! else ! { ! cmd = get_tv_string_chk(&argvars[0]); ! if (cmd == NULL) ! return; ! } ! ! if (redir_execute) ! save_ga = redir_execute_ga; ! ga_init2(&redir_execute_ga, (int)sizeof(char), 500); ! redir_execute = TRUE; ! ! if (argvars[1].v_type != VAR_UNKNOWN) ! { ! char_u buf[NUMBUFLEN]; ! char_u *s = get_tv_string_buf_chk(&argvars[1], buf); + if (s == NULL) + return; + if (STRNCMP(s, "silent", 6) == 0) + ++msg_silent; + if (STRCMP(s, "silent!") == 0) + { + emsg_silent = TRUE; + emsg_noredir = TRUE; + } + } + else ++msg_silent; ! ! if (cmd != NULL) ! do_cmdline_cmd(cmd); ! else ! { ! listitem_T *item = list->lv_first; ! ! do_cmdline(NULL, get_list_line, (void *)&item, ! DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); ! --list->lv_refcount; } ! rettv->vval.v_string = redir_execute_ga.ga_data; ! msg_silent = save_msg_silent; ! emsg_silent = save_emsg_silent; ! emsg_noredir = save_emsg_noredir; ! redir_execute = save_redir_execute; ! if (redir_execute) ! redir_execute_ga = save_ga; ! /* "silent reg" or "silent echo x" leaves msg_col somewhere in the ! * line. Put it back in the first column. */ ! msg_col = 0; } /* *** ../vim-7.4.2007/src/ex_docmd.c 2016-07-08 15:32:49.945197584 +0200 --- src/ex_docmd.c 2016-07-09 16:26:47.626043687 +0200 *************** *** 9456,9464 **** char_u *arg = eap->arg; #ifdef FEAT_EVAL ! if (redir_evalcmd) { ! EMSG(_("E930: Cannot use :redir inside evalcmd()")); return; } #endif --- 9456,9464 ---- char_u *arg = eap->arg; #ifdef FEAT_EVAL ! if (redir_execute) { ! EMSG(_("E930: Cannot use :redir inside execute()")); return; } #endif *** ../vim-7.4.2007/src/message.c 2016-07-07 23:04:13.437510647 +0200 --- src/message.c 2016-07-09 16:49:34.449707787 +0200 *************** *** 566,587 **** */ if (emsg_silent != 0) { ! msg_start(); ! p = get_emsg_source(); ! if (p != NULL) ! { ! STRCAT(p, "\n"); ! redir_write(p, -1); ! vim_free(p); ! } ! p = get_emsg_lnum(); ! if (p != NULL) ! { ! STRCAT(p, "\n"); ! redir_write(p, -1); ! vim_free(p); } - redir_write(s, -1); return TRUE; } --- 566,590 ---- */ if (emsg_silent != 0) { ! if (emsg_noredir == 0) ! { ! msg_start(); ! p = get_emsg_source(); ! if (p != NULL) ! { ! STRCAT(p, "\n"); ! redir_write(p, -1); ! vim_free(p); ! } ! p = get_emsg_lnum(); ! if (p != NULL) ! { ! STRCAT(p, "\n"); ! redir_write(p, -1); ! vim_free(p); ! } ! redir_write(s, -1); } return TRUE; } *************** *** 3063,3070 **** while (cur_col < msg_col) { #ifdef FEAT_EVAL ! if (redir_evalcmd) ! evalcmd_redir_str((char_u *)" ", -1); else if (redir_reg) write_reg_contents(redir_reg, (char_u *)" ", -1, TRUE); else if (redir_vname) --- 3066,3073 ---- while (cur_col < msg_col) { #ifdef FEAT_EVAL ! if (redir_execute) ! execute_redir_str((char_u *)" ", -1); else if (redir_reg) write_reg_contents(redir_reg, (char_u *)" ", -1, TRUE); else if (redir_vname) *************** *** 3080,3087 **** } #ifdef FEAT_EVAL ! if (redir_evalcmd) ! evalcmd_redir_str(s, maxlen); else if (redir_reg) write_reg_contents(redir_reg, s, maxlen, TRUE); else if (redir_vname) --- 3083,3090 ---- } #ifdef FEAT_EVAL ! if (redir_execute) ! execute_redir_str(s, maxlen); else if (redir_reg) write_reg_contents(redir_reg, s, maxlen, TRUE); else if (redir_vname) *************** *** 3092,3098 **** while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) { #ifdef FEAT_EVAL ! if (!redir_reg && !redir_vname && !redir_evalcmd) #endif if (redir_fd != NULL) putc(*s, redir_fd); --- 3095,3101 ---- while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) { #ifdef FEAT_EVAL ! if (!redir_reg && !redir_vname && !redir_execute) #endif if (redir_fd != NULL) putc(*s, redir_fd); *************** *** 3117,3123 **** { return redir_fd != NULL || *p_vfile != NUL #ifdef FEAT_EVAL ! || redir_reg || redir_vname || redir_evalcmd #endif ; } --- 3120,3126 ---- { return redir_fd != NULL || *p_vfile != NUL #ifdef FEAT_EVAL ! || redir_reg || redir_vname || redir_execute #endif ; } *** ../vim-7.4.2007/src/globals.h 2016-07-07 23:04:13.441510589 +0200 --- src/globals.h 2016-07-09 16:49:12.430035327 +0200 *************** *** 971,976 **** --- 971,977 ---- EXTERN int msg_silent INIT(= 0); /* don't print messages */ EXTERN int emsg_silent INIT(= 0); /* don't print error messages */ + EXTERN int emsg_noredir INIT(= 0); /* don't redirect error messages */ EXTERN int cmd_silent INIT(= FALSE); /* don't echo the command line */ #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) \ *************** *** 1106,1112 **** #ifdef FEAT_EVAL EXTERN int redir_reg INIT(= 0); /* message redirection register */ EXTERN int redir_vname INIT(= 0); /* message redirection variable */ ! EXTERN int redir_evalcmd INIT(= 0); /* evalcmd() redirection */ #endif #ifdef FEAT_LANGMAP --- 1107,1113 ---- #ifdef FEAT_EVAL EXTERN int redir_reg INIT(= 0); /* message redirection register */ EXTERN int redir_vname INIT(= 0); /* message redirection variable */ ! EXTERN int redir_execute INIT(= 0); /* execute() redirection */ #endif #ifdef FEAT_LANGMAP *** ../vim-7.4.2007/src/proto/eval.pro 2016-07-07 23:04:13.445510530 +0200 --- src/proto/eval.pro 2016-07-09 16:29:53.835271030 +0200 *************** *** 88,94 **** 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); buf_T *buflist_find_by_name(char_u *name, int curtab_only); int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); ! void evalcmd_redir_str(char_u *value, int value_len); void dict_extend(dict_T *d1, dict_T *d2, char_u *action); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); --- 88,94 ---- 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); buf_T *buflist_find_by_name(char_u *name, int curtab_only); int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); ! void execute_redir_str(char_u *value, int value_len); void dict_extend(dict_T *d1, dict_T *d2, char_u *action); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); *** ../vim-7.4.2007/src/Makefile 2016-07-07 17:32:48.290238465 +0200 --- src/Makefile 2016-07-09 16:22:52.041553378 +0200 *************** *** 2023,2030 **** test_cmdline \ test_cursor_func \ test_delete \ - test_evalcmd \ test_ex_undo \ test_expand \ test_expand_dllpath \ test_expr \ --- 2023,2030 ---- test_cmdline \ test_cursor_func \ test_delete \ test_ex_undo \ + test_execute_func \ test_expand \ test_expand_dllpath \ test_expr \ *** ../vim-7.4.2007/src/testdir/test_evalcmd.vim 2016-07-07 23:04:13.445510530 +0200 --- src/testdir/test_evalcmd.vim 1970-01-01 01:00:00.000000000 +0100 *************** *** 1,33 **** - " test evalcmd() - - func NestedEval() - let nested = evalcmd('echo "nested\nlines"') - echo 'got: "' . nested . '"' - endfunc - - func NestedRedir() - redir => var - echo 'broken' - redir END - endfunc - - func Test_evalcmd() - call assert_equal("\nnocompatible", evalcmd('set compatible?')) - call assert_equal("\nsomething\nnice", evalcmd('echo "something\nnice"')) - call assert_equal("noendofline", evalcmd('echon "noendofline"')) - call assert_equal("", evalcmd(123)) - - call assert_equal("\ngot: \"\nnested\nlines\"", evalcmd('call NestedEval()')) - redir => redired - echo 'this' - let evaled = evalcmd('echo "that"') - echo 'theend' - redir END - call assert_equal("\nthis\ntheend", redired) - call assert_equal("\nthat", evaled) - - call assert_fails('call evalcmd("doesnotexist")', 'E492:') - call assert_fails('call evalcmd(3.4)', 'E806:') - call assert_fails('call evalcmd("call NestedRedir()")', 'E930:') - endfunc - --- 0 ---- *** ../vim-7.4.2007/src/testdir/test_execute_func.vim 2016-07-09 17:03:10.313581869 +0200 --- src/testdir/test_execute_func.vim 2016-07-09 16:44:29.638243199 +0200 *************** *** 0 **** --- 1,51 ---- + " test execute() + + func NestedEval() + let nested = execute('echo "nested\nlines"') + echo 'got: "' . nested . '"' + endfunc + + func NestedRedir() + redir => var + echo 'broken' + redir END + endfunc + + func Test_execute_string() + call assert_equal("\nnocompatible", execute('set compatible?')) + call assert_equal("\nsomething\nnice", execute('echo "something\nnice"')) + call assert_equal("noendofline", execute('echon "noendofline"')) + call assert_equal("", execute(123)) + + call assert_equal("\ngot: \"\nnested\nlines\"", execute('call NestedEval()')) + redir => redired + echo 'this' + let evaled = execute('echo "that"') + echo 'theend' + redir END + call assert_equal("\nthis\ntheend", redired) + call assert_equal("\nthat", evaled) + + call assert_fails('call execute("doesnotexist")', 'E492:') + call assert_fails('call execute(3.4)', 'E806:') + call assert_fails('call execute("call NestedRedir()")', 'E930:') + + call assert_equal("\nsomething", execute('echo "something"', '')) + call assert_equal("\nsomething", execute('echo "something"', 'silent')) + call assert_equal("\nsomething", execute('echo "something"', 'silent!')) + call assert_equal("", execute('burp', 'silent!')) + call assert_fails('call execute("echo \"x\"", 3.4)', 'E806:') + + call assert_equal("", execute(test_null_string())) + endfunc + + func Test_execute_list() + call assert_equal("\nsomething\nnice", execute(['echo "something"', 'echo "nice"'])) + let l = ['for n in range(0, 3)', + \ 'echo n', + \ 'endfor'] + call assert_equal("\n0\n1\n2\n3", execute(l)) + + call assert_equal("", execute([])) + call assert_equal("", execute(test_null_list())) + endfunc *** ../vim-7.4.2007/src/testdir/test_alot.vim 2016-07-07 17:32:48.286238524 +0200 --- src/testdir/test_alot.vim 2016-07-09 16:22:20.722020133 +0200 *************** *** 5,11 **** source test_autocmd.vim source test_cursor_func.vim source test_delete.vim ! source test_evalcmd.vim source test_ex_undo.vim source test_expand.vim source test_expr.vim --- 5,11 ---- source test_autocmd.vim source test_cursor_func.vim source test_delete.vim ! source test_execute_func.vim source test_ex_undo.vim source test_expand.vim source test_expr.vim *** ../vim-7.4.2007/runtime/doc/eval.txt 2016-07-07 17:32:48.286238524 +0200 --- runtime/doc/eval.txt 2016-07-09 16:59:49.684562045 +0200 *************** *** 1942,1950 **** empty({expr}) Number |TRUE| if {expr} is empty escape({string}, {chars}) String escape {chars} in {string} with '\' eval({string}) any evaluate {string} into its value - evalcmd({command}) String execute {command} and get the output eventhandler() Number |TRUE| if inside an event handler executable({expr}) Number 1 if executable {expr} exists exepath({expr}) String full path of the command {expr} exists({expr}) Number |TRUE| if {expr} exists extend({expr1}, {expr2} [, {expr3}]) --- 1961,1969 ---- empty({expr}) Number |TRUE| if {expr} is empty escape({string}, {chars}) String escape {chars} in {string} with '\' eval({string}) any evaluate {string} into its value eventhandler() Number |TRUE| if inside an event handler executable({expr}) Number 1 if executable {expr} exists + execute({command}) String execute {command} and get the output exepath({expr}) String full path of the command {expr} exists({expr}) Number |TRUE| if {expr} exists extend({expr1}, {expr2} [, {expr3}]) *************** *** 3219,3232 **** them. Also works for |Funcref|s that refer to existing functions. ! evalcmd({command}) *evalcmd()* ! Execute Ex {command} and return the output as a string. This ! is equivalent to: > redir => var {command} redir END ! < To get a list of lines use: > ! split(evalcmd(cmd), "\n") eventhandler() *eventhandler()* Returns 1 when inside an event handler. That is that Vim got --- 3232,3261 ---- them. Also works for |Funcref|s that refer to existing functions. ! execute({command} [, {silent}]) *execute()* ! Execute an Ex command or commands and return the output as a ! string. ! {command} can be a string or a List. In case of a List the ! lines are executed one by one. ! This is equivalent to: > redir => var {command} redir END ! < ! The optional {silent} argument can have these values: ! "" no `:silent` used ! "silent" `:silent` used ! "silent!" `:silent!` used ! The default is 'silent'. Note that with "silent!", unlike ! `:redir`, error messages are dropped. ! *E930* ! It is not possible to use `:redir` anywhere in {command}. ! ! To get a list of lines use |split()| on the result: > ! split(evalcmd('args'), "\n") ! ! < When used recursively the output of the recursive call is not ! included in the output of the higher level call. eventhandler() *eventhandler()* Returns 1 when inside an event handler. That is that Vim got -- "Marriage is when a man and woman become as one; the trouble starts when they try to decide which one" /// 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 ///