# HG changeset patch # User Christian Brabandt # Date 1468077306 -7200 # Node ID 9da0cb39cbee6f33d9f8442edf02e64f11a03678 # Parent 4481d0db8ec0d91cb81c6713c3cc7463124206af commit https://github.com/vim/vim/commit/79815f1ec77406f2f21a618c053e5793b597db7a Author: Bram Moolenaar Date: Sat Jul 9 17:07:29 2016 +0200 patch 7.4.2008 Problem: evalcmd() has a confusing name. Solution: Rename to execute(). Make silent optional. Support a list of commands. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Jul 06 +*eval.txt* For Vim version 7.4. Last change: 2016 Jul 09 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1961,9 +1961,9 @@ diff_hlID({lnum}, {col}) Number diff hig 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 +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}]) @@ -3232,15 +3232,6 @@ eval({string}) Evaluate {string} and ret 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 interrupted while waiting for the user to type a character, @@ -3271,6 +3262,31 @@ executable({expr}) *executable()* 0 does not exist -1 not implemented on this system +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. + exepath({expr}) *exepath()* If {expr} is an executable and is either an absolute path, a relative path or found in $PATH, return the full path. @@ -7046,9 +7062,9 @@ synID({lnum}, {col}, {trans}) *synID( that's where the cursor can be in Insert mode, synID() returns zero. - When {trans} is non-zero, transparent items are reduced to the + When {trans} is |TRUE|, transparent items are reduced to the item that they reveal. This is useful when wanting to know - the effective color. When {trans} is zero, the transparent + the effective color. When {trans} is |FALSE|, the transparent item is returned. This is useful when wanting to know which syntax item is effective (e.g. inside parens). Warning: This function can be very slow. Best speed is diff --git a/src/Makefile b/src/Makefile --- a/src/Makefile +++ b/src/Makefile @@ -2023,8 +2023,8 @@ test_arglist \ test_cmdline \ test_cursor_func \ test_delete \ - test_evalcmd \ test_ex_undo \ + test_execute_func \ test_expand \ test_expand_dllpath \ test_expr \ diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -555,9 +555,9 @@ static void f_diff_hlID(typval_T *argvar 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_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,9 +8564,9 @@ static struct fst {"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}, + {"execute", 1, 2, f_execute}, {"exepath", 1, 1, f_exepath}, {"exists", 1, 1, f_exists}, #ifdef FEAT_FLOAT @@ -11345,65 +11345,6 @@ f_eval(typval_T *argvars, typval_T *rett 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; - - if (value_len == -1) - 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 */ @@ -11426,6 +11367,132 @@ f_executable(typval_T *argvars, typval_T || (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; + + if (value_len == -1) + 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; +} + /* * "exepath()" function */ diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -9456,9 +9456,9 @@ ex_redir(exarg_T *eap) char_u *arg = eap->arg; #ifdef FEAT_EVAL - if (redir_evalcmd) - { - EMSG(_("E930: Cannot use :redir inside evalcmd()")); + if (redir_execute) + { + EMSG(_("E930: Cannot use :redir inside execute()")); return; } #endif diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -971,6 +971,7 @@ EXTERN cmdmod_T cmdmod; /* Ex command 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,7 +1107,7 @@ EXTERN FILE *redir_fd INIT(= NULL); /* m #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 */ +EXTERN int redir_execute INIT(= 0); /* execute() redirection */ #endif #ifdef FEAT_LANGMAP diff --git a/src/message.c b/src/message.c --- a/src/message.c +++ b/src/message.c @@ -566,22 +566,25 @@ emsg(char_u *s) */ if (emsg_silent != 0) { - msg_start(); - p = get_emsg_source(); - if (p != NULL) + if (emsg_noredir == 0) { - STRCAT(p, "\n"); - redir_write(p, -1); - vim_free(p); + 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); } - p = get_emsg_lnum(); - if (p != NULL) - { - STRCAT(p, "\n"); - redir_write(p, -1); - vim_free(p); - } - redir_write(s, -1); return TRUE; } @@ -3063,8 +3066,8 @@ redir_write(char_u *str, int maxlen) while (cur_col < msg_col) { #ifdef FEAT_EVAL - if (redir_evalcmd) - evalcmd_redir_str((char_u *)" ", -1); + 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,8 +3083,8 @@ redir_write(char_u *str, int maxlen) } #ifdef FEAT_EVAL - if (redir_evalcmd) - evalcmd_redir_str(s, maxlen); + 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,7 +3095,7 @@ redir_write(char_u *str, int maxlen) while (*s != NUL && (maxlen < 0 || (int)(s - str) < maxlen)) { #ifdef FEAT_EVAL - if (!redir_reg && !redir_vname && !redir_evalcmd) + if (!redir_reg && !redir_vname && !redir_execute) #endif if (redir_fd != NULL) putc(*s, redir_fd); @@ -3117,7 +3120,7 @@ redirecting(void) { return redir_fd != NULL || *p_vfile != NUL #ifdef FEAT_EVAL - || redir_reg || redir_vname || redir_evalcmd + || redir_reg || redir_vname || redir_execute #endif ; } diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -88,7 +88,7 @@ char_u *get_expr_name(expand_T *xp, int 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 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); diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim --- a/src/testdir/test_alot.vim +++ b/src/testdir/test_alot.vim @@ -5,7 +5,7 @@ source test_assign.vim source test_autocmd.vim source test_cursor_func.vim source test_delete.vim -source test_evalcmd.vim +source test_execute_func.vim source test_ex_undo.vim source test_expand.vim source test_expr.vim diff --git a/src/testdir/test_evalcmd.vim b/src/testdir/test_evalcmd.vim deleted file mode 100644 --- a/src/testdir/test_evalcmd.vim +++ /dev/null @@ -1,33 +0,0 @@ -" 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 - diff --git a/src/testdir/test_execute_func.vim b/src/testdir/test_execute_func.vim new file mode 100644 --- /dev/null +++ b/src/testdir/test_execute_func.vim @@ -0,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