diff options
author | Shougo Matsushita <Shougo.Matsu@gmail.com> | 2016-05-03 13:35:04 +0900 |
---|---|---|
committer | Shougo Matsushita <Shougo.Matsu@gmail.com> | 2016-05-27 10:33:44 +0900 |
commit | 41c0dfd5454702d639d97c44a8fd38cbdd2ff0ba (patch) | |
tree | 808a8322cf61a56ba3a84bf0fc49b882cdb3c484 | |
parent | bfbc974d13c46ace84bec261263ab044b8c81265 (diff) | |
download | rneovim-41c0dfd5454702d639d97c44a8fd38cbdd2ff0ba.tar.gz rneovim-41c0dfd5454702d639d97c44a8fd38cbdd2ff0ba.tar.bz2 rneovim-41c0dfd5454702d639d97c44a8fd38cbdd2ff0ba.zip |
Port capture() function
https://groups.google.com/forum/#!msg/vim_dev/H3Z3ChSUh_4/beZs6KzYdBsJ
-rw-r--r-- | runtime/doc/eval.txt | 21 | ||||
-rw-r--r-- | runtime/doc/various.txt | 5 | ||||
-rw-r--r-- | src/nvim/eval.c | 33 | ||||
-rw-r--r-- | src/nvim/globals.h | 9 | ||||
-rw-r--r-- | src/nvim/message.c | 13 | ||||
-rw-r--r-- | test/functional/eval/capture_spec.lua | 86 |
6 files changed, 161 insertions, 6 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 8b23d2ff5f..b5fe9967b2 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1808,6 +1808,7 @@ byteidx({expr}, {nr}) Number byte index of {nr}'th char in {expr} byteidxcomp({expr}, {nr}) Number byte index of {nr}'th char in {expr} call({func}, {arglist} [, {dict}]) any call {func} with arguments {arglist} +capture({command}) String capture output of {command} ceil({expr}) Float round {expr} up changenr() Number current change number char2nr({expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr} @@ -2481,6 +2482,26 @@ call({func}, {arglist} [, {dict}]) *call()* *E699* {dict} is for functions with the "dict" attribute. It will be used to set the local variable "self". |Dictionary-function| +capture({command}) *capture()* + Capture output of {command}. + If {command} is a |String|, it returns {command} output. + If {command} is a |List|, it returns concatenated outputs of + each item. + Examples: > + let a = capture('echon "foo"') + echo a +< foo > + let a = capture(['echon "foo"', 'echon "bar"']) + echo a +< foobar + This function is not available in the |sandbox|. + Note: The commands are run as if they were prepended with + |:silent| modifier. |:redir| and |capture()| work together. + Unlike nested |:redir|s, outer capture will not catch + commands' output of the inner one, but the inner capture will + not cancel the outer. + Note: highlighting is ignored. + ceil({expr}) *ceil()* Return the smallest integral value greater than or equal to {expr} as a |Float| (round up). diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index af4224993f..02c5fad22f 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -416,8 +416,9 @@ m *+xpm_w32* Win32 GUI only: pixmap support |w32-xpm-support| To stop the messages and commands from being echoed to the screen, put the commands in a function and call it with ":silent call Function()". - An alternative is to use the 'verbosefile' option, - this can be used in combination with ":redir". + The alternatives are to use the 'verbosefile' option + or |capture()| function, these can be used in + combination with ":redir". :redi[r] >> {file} Redirect messages to file {file}. Append if {file} already exists. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0d060e5b70..e950dfb5c4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6699,6 +6699,7 @@ static struct fst { { "byteidx", 2, 2, f_byteidx }, { "byteidxcomp", 2, 2, f_byteidxcomp }, { "call", 2, 3, f_call }, + { "capture", 1, 1, f_capture }, { "ceil", 1, 1, f_ceil }, { "changenr", 0, 0, f_changenr }, { "char2nr", 1, 2, f_char2nr }, @@ -8039,6 +8040,38 @@ static void f_call(typval_T *argvars, typval_T *rettv) (void)func_call(func, &argvars[1], selfdict, rettv); } +// "capture(command)" function +static void f_capture(typval_T *argvars, typval_T *rettv) +{ + int save_msg_silent = msg_silent; + garray_T *save_capture_ga = capture_ga; + + if (check_secure()) { + return; + } + + garray_T capture_local; + capture_ga = &capture_local; + ga_init(capture_ga, (int)sizeof(char), 80); + + msg_silent++; + if (argvars[0].v_type != VAR_LIST) { + do_cmdline_cmd((char *)get_tv_string(&argvars[0])); + } else if (argvars[0].vval.v_list != NULL) { + for (listitem_T *li = argvars[0].vval.v_list->lv_first; + li != NULL; li = li->li_next) { + do_cmdline_cmd((char *)get_tv_string(&li->li_tv)); + } + } + msg_silent = save_msg_silent; + + ga_append(capture_ga, NUL); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = capture_ga->ga_data; + + capture_ga = save_capture_ga; +} + /* * "ceil({float})" function */ diff --git a/src/nvim/globals.h b/src/nvim/globals.h index dafb75ca87..d399db1381 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -985,10 +985,11 @@ EXTERN int keep_help_flag INIT(= FALSE); /* doing :ta from help file */ */ EXTERN char_u *empty_option INIT(= (char_u *)""); -EXTERN int redir_off INIT(= FALSE); /* no redirection for a moment */ -EXTERN FILE *redir_fd INIT(= NULL); /* message redirection file */ -EXTERN int redir_reg INIT(= 0); /* message redirection register */ -EXTERN int redir_vname INIT(= 0); /* message redirection variable */ +EXTERN int redir_off INIT(= false); // no redirection for a moment +EXTERN FILE *redir_fd INIT(= NULL); // message redirection file +EXTERN int redir_reg INIT(= 0); // message redirection register +EXTERN int redir_vname INIT(= 0); // message redirection variable +EXTERN garray_T *capture_ga INIT(= NULL); // capture() buffer EXTERN char_u langmap_mapchar[256]; /* mapping for language keys */ diff --git a/src/nvim/message.c b/src/nvim/message.c index 521db85cf0..02201107db 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2389,6 +2389,19 @@ static void redir_write(char_u *str, int maxlen) char_u *s = str; static int cur_col = 0; + if (maxlen == 0) { + return; + } + + // Append output to capture(). + if (capture_ga) { + size_t len = 0; + while (str[len] && (maxlen < 0 ? 1 : (len < (size_t)maxlen))) { + len++; + } + ga_concat_len(capture_ga, (const char *)str, len); + } + /* Don't do anything for displaying prompts and the like. */ if (redir_off) return; diff --git a/test/functional/eval/capture_spec.lua b/test/functional/eval/capture_spec.lua new file mode 100644 index 0000000000..1ee5a49df2 --- /dev/null +++ b/test/functional/eval/capture_spec.lua @@ -0,0 +1,86 @@ +local helpers = require('test.functional.helpers') +local eq = helpers.eq +local eval = helpers.eval +local clear = helpers.clear +local source = helpers.source +local redir_exec = helpers.redir_exec +local exc_exec = helpers.exc_exec +local funcs = helpers.funcs +local Screen = require('test.functional.ui.screen') +local feed = helpers.feed + +describe('capture()', function() + before_each(clear) + + it('returns the same result with :redir', function() + eq(redir_exec('messages'), funcs.capture('messages')) + end) + + it('returns the output of the commands if the argument is List', function() + eq("foobar", funcs.capture({'echon "foo"', 'echon "bar"'})) + eq("\nfoo\nbar", funcs.capture({'echo "foo"', 'echo "bar"'})) + end) + + it('supports the nested redirection', function() + source([[ + function! g:Foo() + let a = '' + redir => a + silent echon "foo" + redir END + return a + endfunction + function! g:Bar() + let a = '' + redir => a + call g:Foo() + redir END + return a + endfunction + ]]) + eq('foo', funcs.capture('call g:Bar()')) + + eq('42', funcs.capture([[echon capture("echon capture('echon 42')")]])) + end) + + it('returns the transformed string', function() + eq('^A', funcs.capture('echon "\\<C-a>"')) + end) + + it('returns the empty string if the argument list is empty', function() + eq('', funcs.capture({})) + eq(0, exc_exec('let g:ret = capture(v:_null_list)')) + eq('', eval('g:ret')) + end) + + it('returns the errors', function() + local ret + ret = exc_exec('call capture(0.0)') + eq('Vim(call):E806: using Float as a String', ret) + ret = exc_exec('call capture(v:_null_dict)') + eq('Vim(call):E731: using Dictionary as a String', ret) + ret = exc_exec('call capture(function("tr"))') + eq('Vim(call):E729: using Funcref as a String', ret) + ret = exc_exec('call capture(["echo 42", 0.0, "echo 44"])') + eq('Vim(call):E806: using Float as a String', ret) + ret = exc_exec('call capture(["echo 42", v:_null_dict, "echo 44"])') + eq('Vim(call):E731: using Dictionary as a String', ret) + ret = exc_exec('call capture(["echo 42", function("tr"), "echo 44"])') + eq('Vim(call):E729: using Funcref as a String', ret) + end) + + it('silences command run inside', function() + local screen = Screen.new(20, 5) + screen:attach() + screen:set_default_attr_ignore({{bold=true, foreground=255}}) + feed(':let g:mes = capture("echon 42")<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + | + ]]) + eq('42', eval('g:mes')) + end) +end) |