changeset 9454:9da0cb39cbee v7.4.2008

commit https://github.com/vim/vim/commit/79815f1ec77406f2f21a618c053e5793b597db7a Author: Bram Moolenaar <Bram@vim.org> 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.
author Christian Brabandt <cb@256bit.org>
date Sat, 09 Jul 2016 17:15:06 +0200
parents 4481d0db8ec0
children 46bf551d7dbc
files runtime/doc/eval.txt src/Makefile src/eval.c src/ex_docmd.c src/globals.h src/message.c src/proto/eval.pro src/testdir/test_alot.vim src/testdir/test_evalcmd.vim src/testdir/test_execute_func.vim
diffstat 10 files changed, 239 insertions(+), 134 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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 \
--- 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
  */
--- 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
--- 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
--- 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
 				       ;
 }
--- 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);
--- 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
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
-
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