Merge pull request #5919 from jamessan/vim-7.4.2008

vim-patch:7.4.2008,7.4.2009
This commit is contained in:
James McCoy 2017-01-10 09:56:53 -05:00 committed by GitHub
commit 77de73c7c8
9 changed files with 214 additions and 53 deletions

View File

@ -3070,7 +3070,7 @@ executable({expr}) *executable()*
0 does not exist 0 does not exist
-1 not implemented on this system -1 not implemented on this system
execute({command}) *execute()* execute({command} [, {silent}]) *execute()*
Execute {command} and capture its output. Execute {command} and capture its output.
If {command} is a |String|, returns {command} output. If {command} is a |String|, returns {command} output.
If {command} is a |List|, returns concatenated outputs. If {command} is a |List|, returns concatenated outputs.
@ -3079,10 +3079,17 @@ execute({command}) *execute()*
< foo > < foo >
echo execute(['echon "foo"', 'echon "bar"']) echo execute(['echon "foo"', 'echon "bar"'])
< foobar < foobar
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.
This function is not available in the |sandbox|. This function is not available in the |sandbox|.
Note: {command} executes as if prepended with |:silent| Note: If nested, an outer execute() will not observe output of
(output is collected but not displayed). If nested, an outer the inner calls.
execute() will not observe output of the inner calls.
Note: Text attributes (highlights) are not captured. Note: Text attributes (highlights) are not captured.
exepath({expr}) *exepath()* exepath({expr}) *exepath()*
@ -7007,9 +7014,9 @@ synID({lnum}, {col}, {trans}) *synID()*
that's where the cursor can be in Insert mode, synID() returns that's where the cursor can be in Insert mode, synID() returns
zero. 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 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 item is returned. This is useful when wanting to know which
syntax item is effective (e.g. inside parens). syntax item is effective (e.g. inside parens).
Warning: This function can be very slow. Best speed is Warning: This function can be very slow. Best speed is

View File

@ -8828,34 +8828,73 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|| (gettail_dir(name) != name && os_can_exe(name, NULL, false)); || (gettail_dir(name) != name && os_can_exe(name, NULL, false));
} }
static char_u * get_list_line(int c, void *cookie, int indent)
{
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(command)" function // "execute(command)" function
static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
int save_msg_silent = msg_silent; int save_msg_silent = msg_silent;
int save_emsg_silent = emsg_silent;
bool save_emsg_noredir = emsg_noredir;
garray_T *save_capture_ga = capture_ga; garray_T *save_capture_ga = capture_ga;
if (check_secure()) { if (check_secure()) {
return; return;
} }
garray_T capture_local; if (argvars[1].v_type != VAR_UNKNOWN) {
capture_ga = &capture_local; char_u buf[NUMBUFLEN];
ga_init(capture_ga, (int)sizeof(char), 80); 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++;
}
garray_T capture_local;
ga_init(&capture_local, (int)sizeof(char), 80);
capture_ga = &capture_local;
msg_silent++;
if (argvars[0].v_type != VAR_LIST) { if (argvars[0].v_type != VAR_LIST) {
do_cmdline_cmd((char *)get_tv_string(&argvars[0])); do_cmdline_cmd((char *)get_tv_string(&argvars[0]));
} else if (argvars[0].vval.v_list != NULL) { } else if (argvars[0].vval.v_list != NULL) {
for (listitem_T *li = argvars[0].vval.v_list->lv_first; list_T *list = argvars[0].vval.v_list;
li != NULL; li = li->li_next) { list->lv_refcount++;
do_cmdline_cmd((char *)get_tv_string(&li->li_tv)); 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--;
} }
msg_silent = save_msg_silent; msg_silent = save_msg_silent;
emsg_silent = save_emsg_silent;
emsg_noredir = save_emsg_noredir;
ga_append(capture_ga, NUL); ga_append(capture_ga, NUL);
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
rettv->vval.v_string = capture_ga->ga_data; rettv->vval.v_string = vim_strsave(capture_ga->ga_data);
ga_clear(capture_ga);
capture_ga = save_capture_ga; capture_ga = save_capture_ga;
} }

View File

@ -79,7 +79,7 @@ return {
eval={args=1}, eval={args=1},
eventhandler={}, eventhandler={},
executable={args=1}, executable={args=1},
execute={args=1}, execute={args={1, 2}},
exepath={args=1}, exepath={args=1},
exists={args=1}, exists={args=1},
exp={args=1, func="float_op_wrapper", data="&exp"}, exp={args=1, func="float_op_wrapper", data="&exp"},

View File

@ -869,9 +869,10 @@ EXTERN int mapped_ctrl_c INIT(= 0); // Modes where CTRL-C is mapped.
EXTERN cmdmod_T cmdmod; /* Ex command modifiers */ EXTERN cmdmod_T cmdmod; /* Ex command modifiers */
EXTERN int msg_silent INIT(= 0); /* don't print messages */ EXTERN int msg_silent INIT(= 0); // don't print messages
EXTERN int emsg_silent INIT(= 0); /* don't print error messages */ EXTERN int emsg_silent INIT(= 0); // don't print error messages
EXTERN int cmd_silent INIT(= FALSE); /* don't echo the command line */ EXTERN bool emsg_noredir INIT(= false); // don't redirect error messages
EXTERN int cmd_silent INIT(= false); // don't echo the command line
/* Values for swap_exists_action: what to do when swap file already exists */ /* Values for swap_exists_action: what to do when swap file already exists */
#define SEA_NONE 0 /* don't use dialog */ #define SEA_NONE 0 /* don't use dialog */

View File

@ -508,20 +508,22 @@ int emsg(char_u *s)
* But do write it to the redirection file. * But do write it to the redirection file.
*/ */
if (emsg_silent != 0) { if (emsg_silent != 0) {
msg_start(); if (!emsg_noredir) {
p = get_emsg_source(); msg_start();
if (p != NULL) { p = get_emsg_source();
STRCAT(p, "\n"); if (p != NULL) {
redir_write(p, STRLEN(p)); STRCAT(p, "\n");
xfree(p); redir_write(p, STRLEN(p));
xfree(p);
}
p = get_emsg_lnum();
if (p != NULL) {
STRCAT(p, "\n");
redir_write(p, STRLEN(p));
xfree(p);
}
redir_write(s, STRLEN(s));
} }
p = get_emsg_lnum();
if (p != NULL) {
STRCAT(p, "\n");
redir_write(p, STRLEN(p));
xfree(p);
}
redir_write(s, STRLEN(s));
return true; return true;
} }
@ -2508,8 +2510,7 @@ static void redir_write(char_u *str, int maxlen)
int redirecting(void) int redirecting(void)
{ {
return redir_fd != NULL || *p_vfile != NUL return redir_fd != NULL || *p_vfile != NUL
|| redir_reg || redir_vname || redir_reg || redir_vname || capture_ga != NULL;
;
} }
/* /*

View File

@ -4,6 +4,7 @@
source test_assign.vim source test_assign.vim
source test_autocmd.vim source test_autocmd.vim
source test_cursor_func.vim source test_cursor_func.vim
source test_execute_func.vim
source test_ex_undo.vim source test_ex_undo.vim
source test_expr.vim source test_expr.vim
source test_expr_utf8.vim source test_expr_utf8.vim

View File

@ -0,0 +1,55 @@
" 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
" Nvim supports execute('... :redir ...'), so this test is intentionally
" disabled.
" 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:')
" Nvim supports execute('... :redir ...'), so this test is intentionally
" disabled.
" 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(""))
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(v:_null_list))
endfunc

View File

@ -431,8 +431,8 @@ static int included_patches[] = {
2012, 2012,
2011, 2011,
2010, 2010,
// 2009, 2009,
// 2008, 2008,
2007, 2007,
// 2006, // 2006,
2005, 2005,

View File

@ -7,7 +7,7 @@ local redir_exec = helpers.redir_exec
local exc_exec = helpers.exc_exec local exc_exec = helpers.exc_exec
local funcs = helpers.funcs local funcs = helpers.funcs
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local feed = helpers.feed local command = helpers.command
describe('execute()', function() describe('execute()', function()
before_each(clear) before_each(clear)
@ -62,11 +62,11 @@ describe('execute()', function()
ret = exc_exec('call execute(function("tr"))') ret = exc_exec('call execute(function("tr"))')
eq('Vim(call):E729: using Funcref as a String', ret) eq('Vim(call):E729: using Funcref as a String', ret)
ret = exc_exec('call execute(["echo 42", 0.0, "echo 44"])') ret = exc_exec('call execute(["echo 42", 0.0, "echo 44"])')
eq('Vim(call):E806: using Float as a String', ret) eq('Vim:E806: using Float as a String', ret)
ret = exc_exec('call execute(["echo 42", v:_null_dict, "echo 44"])') ret = exc_exec('call execute(["echo 42", v:_null_dict, "echo 44"])')
eq('Vim(call):E731: using Dictionary as a String', ret) eq('Vim:E731: using Dictionary as a String', ret)
ret = exc_exec('call execute(["echo 42", function("tr"), "echo 44"])') ret = exc_exec('call execute(["echo 42", function("tr"), "echo 44"])')
eq('Vim(call):E729: using Funcref as a String', ret) eq('Vim:E729: using Funcref as a String', ret)
end) end)
-- This matches Vim behavior. -- This matches Vim behavior.
@ -74,18 +74,75 @@ describe('execute()', function()
eq('\n:!echo "foo"\13\n', funcs.execute('!echo "foo"')) eq('\n:!echo "foo"\13\n', funcs.execute('!echo "foo"'))
end) end)
it('silences command run inside', function() describe('{silent} argument', function()
local screen = Screen.new(40, 5) it('captures & displays output for ""', function()
screen:attach() local screen = Screen.new(40, 5)
screen:set_default_attr_ids( {[0] = {bold=true, foreground=255}} ) screen:attach()
feed(':let g:mes = execute("echon 42")<CR>') command('let g:mes = execute("echon 42", "")')
screen:expect([[ screen:expect([[
^ | ^ |
{0:~ }| ~ |
{0:~ }| ~ |
{0:~ }| ~ |
:let g:mes = execute("echon 42") | 42 |
]]) ]])
eq('42', eval('g:mes')) eq('42', eval('g:mes'))
end)
it('captures but does not display output for "silent"', function()
local screen = Screen.new(40, 5)
screen:attach()
command('let g:mes = execute("echon 42")')
screen:expect([[
^ |
~ |
~ |
~ |
|
]])
eq('42', eval('g:mes'))
command('let g:mes = execute("echon 13", "silent")')
screen:expect([[
^ |
~ |
~ |
~ |
|
]])
eq('13', eval('g:mes'))
end)
it('suppresses errors for "silent!"', function()
eq(0, exc_exec('let g:mes = execute(0.0, "silent!")'))
eq('', eval('g:mes'))
eq(0, exc_exec('let g:mes = execute("echon add(1, 1)", "silent!")'))
eq('1', eval('g:mes'))
eq(0, exc_exec('let g:mes = execute(["echon 42", "echon add(1, 1)"], "silent!")'))
eq('421', eval('g:mes'))
end)
it('propagates errors for "" and "silent"', function()
local ret
ret = exc_exec('call execute(0.0, "")')
eq('Vim(call):E806: using Float as a String', ret)
ret = exc_exec('call execute(v:_null_dict, "silent")')
eq('Vim(call):E731: using Dictionary as a String', ret)
ret = exc_exec('call execute("echo add(1, 1)", "")')
eq('Vim(echo):E714: List required', ret)
ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "")')
eq('Vim(echo):E714: List required', ret)
ret = exc_exec('call execute("echo add(1, 1)", "silent")')
eq('Vim(echo):E714: List required', ret)
ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "silent")')
eq('Vim(echo):E714: List required', ret)
end)
end) end)
end) end)