aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVikram Pal <vikrampal659@gmail.com>2019-10-09 18:34:37 +0530
committerJustin M. Keyes <justinkz@gmail.com>2019-12-01 19:07:57 -0800
commitbd43e011b5b0feba644ec5feae6c174def31a9e4 (patch)
tree98bbbfed1b18d9f58bb7a29122bc37651139661c
parent276c2da28616d7a4f504c328dbb8857d38ab7a4a (diff)
downloadrneovim-bd43e011b5b0feba644ec5feae6c174def31a9e4.tar.gz
rneovim-bd43e011b5b0feba644ec5feae6c174def31a9e4.tar.bz2
rneovim-bd43e011b5b0feba644ec5feae6c174def31a9e4.zip
API: nvim_source_output
- Similar to nvim_source but will capture the output - Add meaningful VimL tracebacks for nvim_source - Handle got_int - Add error reporting
-rw-r--r--src/nvim/api/vim.c55
-rw-r--r--src/nvim/ex_cmds2.c36
-rw-r--r--test/functional/api/vim_spec.lua32
3 files changed, 111 insertions, 12 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 10ece6bc22..bf722b4f4e 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -73,14 +73,63 @@ void api_vim_free_all_mem(void)
map_free(String, handle_T)(namespace_ids);
}
-void nvim_source(String command, Error *err)
- FUNC_API_SINCE(7)
+/// Executes a multiline block of ex-commands from a string.
+///
+/// On execution error: fails with VimL error, does not update v:errmsg.
+///
+/// @param src String containing the ex-commands
+/// @param[out] err Error details (Vim error), if any
+void nvim_source(String src, Error *err) FUNC_API_SINCE(7)
{
try_start();
- do_source_str(command.data);
+ do_source_str(src.data, "nvim_source(..)");
try_end(err);
}
+/// Executes a multiline block of ex-commands from a string and returns its
+/// (non-error) output. Shell |:!| output is not captured.
+///
+/// On execution error: fails with VimL error, does not update v:errmsg.
+///
+/// @param src String containing the ex-commands
+/// @param[out] err Error details (Vim error), if any
+String nvim_source_output(String src, Error *err) FUNC_API_SINCE(7)
+{
+ const int save_msg_silent = msg_silent;
+ garray_T *const save_capture_ga = capture_ga;
+ garray_T capture_local;
+ ga_init(&capture_local, 1, 80);
+
+ try_start();
+ msg_silent++;
+ capture_ga = &capture_local;
+ do_source_str(src.data, "nvim_source_output(..)");
+ capture_ga = save_capture_ga;
+ msg_silent = save_msg_silent;
+ try_end(err);
+
+ if (ERROR_SET(err)) {
+ goto theend;
+ }
+
+ if (capture_local.ga_len > 1) {
+ String s = (String){
+ .data = capture_local.ga_data,
+ .size = (size_t)capture_local.ga_len,
+ };
+ // redir usually (except :echon) prepends a newline.
+ if (s.data[0] == '\n') {
+ memmove(s.data, s.data + 1, s.size - 1);
+ s.data[s.size - 1] = '\0';
+ s.size = s.size - 1;
+ }
+ return s; // Caller will free the memory.
+ }
+theend:
+ ga_clear(&capture_local);
+ return (String)STRING_INIT;
+}
+
/// Executes an ex-command.
///
/// On execution error: fails with VimL error, does not update v:errmsg.
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index eec689a28d..bda7b4e8b5 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -3025,7 +3025,7 @@ typedef struct {
///
/// @return pointer to allocated line, or NULL for end-of-file or
/// some error.
-static char_u *get_str_line(int c, void *cookie, int ident)
+static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat)
{
GetStrLineCookie *p = cookie;
size_t i = p->offset;
@@ -3037,19 +3037,31 @@ static char_u *get_str_line(int c, void *cookie, int ident)
}
char buf[2046];
char *dst;
- dst = xstpncpy(buf, (char *)p->buf+p->offset, i - p->offset);
+ dst = xstpncpy(buf, (char *)p->buf + p->offset, i - p->offset);
if ((uint32_t)(dst - buf) != i - p->offset) {
smsg(_(":source error parsing command %s"), p->buf);
return NULL;
}
- buf[i-p->offset]='\0';
+ buf[i - p->offset] = '\0';
p->offset = i + 1;
return (char_u *)xstrdup(buf);
}
-int do_source_str(const char *cmd)
+int do_source_str(const char *cmd, const char *traceback_name)
{
- int retval;
+ char_u *save_sourcing_name = sourcing_name;
+ linenr_T save_sourcing_lnum = sourcing_lnum;
+ char_u sourcing_name_buf[256];
+ if (save_sourcing_name == NULL) {
+ sourcing_name = (char_u *)traceback_name;
+ } else {
+ snprintf((char *)sourcing_name_buf, sizeof sourcing_name_buf,
+ "%s called at %s:%"PRIdLINENR, traceback_name, save_sourcing_name,
+ save_sourcing_lnum);
+ sourcing_name = sourcing_name_buf;
+ }
+ sourcing_lnum = 0;
+
GetStrLineCookie cookie = {
.buf = (char_u *)cmd,
.offset = 0,
@@ -3057,10 +3069,18 @@ int do_source_str(const char *cmd)
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_STR;
current_sctx.sc_seq = 0;
- current_sctx.sc_lnum = 0;
- retval = do_cmdline(NULL, get_str_line, (void *)&cookie,
- DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
+ current_sctx.sc_lnum = save_sourcing_lnum;
+ int retval = FAIL;
+ do_cmdline(NULL, get_str_line, (void *)&cookie,
+ DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT);
+ retval = OK;
+ if (got_int) {
+ EMSG(_(e_interr));
+ }
+
current_sctx = save_current_sctx;
+ sourcing_lnum = save_sourcing_lnum;
+ sourcing_name = save_sourcing_name;
return retval;
}
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index a01ef2d6dd..9a7744142a 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -84,7 +84,7 @@ describe('API', function()
it(':verbose set {option}?', function()
nvim('source', 'set nowrap')
eq('nowrap\n\tLast set from :source (no file)',
- nvim('command_output', 'verbose set wrap?'))
+ nvim('source_output', 'verbose set wrap?'))
end)
it('multiline input', function()
@@ -165,6 +165,36 @@ describe('API', function()
eq('overwritten', request('nvim_get_var', 'x5'))
os.remove(fname)
end)
+
+ it('traceback', function()
+ local fname = helpers.tmpname()
+ write_file(fname, 'echo "hello"\n')
+ local sourcing_fname = helpers.tmpname()
+ write_file(sourcing_fname, 'call nvim_source("source '..fname..'")\n')
+ meths.source('set verbose=2')
+ print()
+ local traceback_output = 'line 0: sourcing "'..sourcing_fname..'"\n'..
+ 'line 0: sourcing "'..fname..'"\n'..
+ 'hello\n'..
+ 'finished sourcing '..fname..'\n'..
+ 'continuing in nvim_source(..) called at '..sourcing_fname..':1\n'..
+ 'finished sourcing '..sourcing_fname..'\n'..
+ 'continuing in nvim_source(..) called at nvim_source_output(..):0'
+ eq(traceback_output, meths.source_output('call nvim_source("source '..sourcing_fname..'")'))
+ os.remove(fname)
+ os.remove(sourcing_fname)
+ end)
+ end)
+
+ describe('nvim_source_output', function()
+ it('multiline input', function()
+ eq('this is spinal tap',
+ nvim('source_output', 'lua <<EOF\n\n\nprint("this is spinal tap")\n\n\nEOF'))
+ end)
+
+ it('empty output', function()
+ eq('', nvim('source_output', 'echo'))
+ end)
end)
describe('nvim_command', function()