From c095f83116eb8ef87983ca5fea61053755fbc4e5 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 5 Jan 2018 11:17:21 +0100 Subject: api: change nvim_command_output behavior Implement nvim_command_output with `execute({cmd},"silent")`. Behavior changes: - does not provoke any hit-enter prompt - no longer prepends a newline char - does not capture some noise (like the "[New File]" message, see the change to tabnewentered_spec.lua) Technically ("bug-for-bug") this a breaking change. But the previous behavior of nvim_command_output meant that it probably wasn't used for anything outside of tests. Also remove the undocumented `v:command_output` variable which was a hack introduced only for the purposes of nvim_command_output. closes #7726 --- src/nvim/api/vim.c | 40 +++++++++++++++++++++++++++++++--------- src/nvim/eval.c | 1 - src/nvim/eval.h | 1 - 3 files changed, 31 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 172f2ce18e..8929bc5b9b 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -43,14 +43,15 @@ #endif /// Executes an ex-command. -/// On VimL error: Returns the VimL error; v:errmsg is not updated. +/// +/// On parse error: forwards the Vim error; does not update v:errmsg. +/// On runtime error: forwards the Vim error; does not update v:errmsg. /// /// @param command Ex-command string -/// @param[out] err Error details (including actual VimL error), if any +/// @param[out] err Error details (Vim error), if any void nvim_command(String command, Error *err) FUNC_API_SINCE(1) { - // Run the command try_start(); do_cmdline_cmd(command.data); update_screen(VALID); @@ -207,18 +208,39 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, return cstr_as_string(ptr); } -String nvim_command_output(String str, Error *err) +/// Executes an ex-command and returns its (non-error) output. +/// Shell |:!| output is not captured. +/// +/// On parse error: forwards the Vim error; does not update v:errmsg. +/// On runtime error: forwards the Vim error; does not update v:errmsg. +/// +/// @param command Ex-command string +/// @param[out] err Error details (Vim error), if any +String nvim_command_output(String command, Error *err) FUNC_API_SINCE(1) { - do_cmdline_cmd("redir => v:command_output"); - nvim_command(str, err); - do_cmdline_cmd("redir END"); - + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(copy_string(command))); + ADD(args, STRING_OBJ(cstr_to_string("silent"))); + String fn = cstr_to_string("execute"); + Object rv = nvim_call_function(fn, args, err); + api_free_string(fn); + api_free_array(args); if (ERROR_SET(err)) { + assert(rv.type == kObjectTypeNil); return (String)STRING_INIT; } - return cstr_to_string((char *)get_vim_var_str(VV_COMMAND_OUTPUT)); + assert(rv.type == kObjectTypeString); + // execute() always(?) prepends a newline; remove it. + if (rv.data.string.size > 1) { + assert(rv.data.string.data[0] == '\n'); + String *s = &rv.data.string; + s->size--; + memmove(s->data, s->data + 1, s->size); + s->data[s->size] = '\0'; + } + return rv.data.string; } /// Evaluates a VimL expression (:help expression). diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f34b6db8d8..875f323a28 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -402,7 +402,6 @@ static struct vimvar { VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMMAND_OUTPUT, "command_output", VAR_STRING, 0), VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 0c0a6881f6..b798eae187 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -86,7 +86,6 @@ typedef enum { VV_OLDFILES, VV_WINDOWID, VV_PROGPATH, - VV_COMMAND_OUTPUT, VV_COMPLETED_ITEM, VV_OPTION_NEW, VV_OPTION_OLD, -- cgit From 5055d4a755d3c100cddf51bded77abca04beb971 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 9 Jan 2018 10:36:25 +0100 Subject: api: nvim_command_output: direct impl --- src/nvim/README.md | 57 +++++++++++++++++++++++++++++++++++++++++------------- src/nvim/api/vim.c | 46 ++++++++++++++++++++++++++----------------- src/nvim/eval.c | 3 +-- 3 files changed, 73 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/nvim/README.md b/src/nvim/README.md index da87a0208e..1ece92e44d 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -1,10 +1,22 @@ -Nvim core source -================ +Nvim core +========= Module-specific details are documented at the top of each module (`terminal.c`, -`screen.c`, ...). +`screen.c`, …). -See `:help development` for more guidelines. +See `:help dev` for guidelines. + +Filename conventions +-------------------- + +The source files use extensions to hint about their purpose. + +- `*.c`, `*.generated.c` - full C files, with all includes, etc. +- `*.c.h` - parametrized C files, contain all necessary includes, but require + defining macros before actually using. Example: `typval_encode.c.h` +- `*.h` - full headers, with all includes. Does *not* apply to `*.generated.h`. +- `*.h.generated.h` - exported functions’ declarations. +- `*.c.generated.h` - static functions’ declarations. Logs ---- @@ -20,17 +32,36 @@ UI events are logged at level 0 (`DEBUG_LOG_LEVEL`). rm -rf build/ make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0" -Filename conventions --------------------- +Build with ASAN +--------------- -The source files use extensions to hint about their purpose. +Building Nvim with Clang sanitizers (Address Sanitizer: ASan, Undefined +Behavior Sanitizer: UBSan, Memory Sanitizer: MSan, Thread Sanitizer: TSan) is +a good way to catch undefined behavior, leaks and other errors as soon as they +happen. It's significantly faster than Valgrind. -- `*.c`, `*.generated.c` - full C files, with all includes, etc. -- `*.c.h` - parametrized C files, contain all necessary includes, but require - defining macros before actually using. Example: `typval_encode.c.h` -- `*.h` - full headers, with all includes. Does *not* apply to `*.generated.h`. -- `*.h.generated.h` - exported functions’ declarations. -- `*.c.generated.h` - static functions’ declarations. +Requires clang 3.4 or later: + + clang --version + +Build Nvim with sanitizer instrumentation: + + CC=clang make CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=ON" + +Create a directory to store logs: + + mkdir -p "$HOME/logs" + +Enable the sanitizer(s) via these environment variables: + + # Change to detect_leaks=1 to detect memory leaks (slower). + export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan" + export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-5.0/bin/llvm-symbolizer + + export MSAN_SYMBOLIZER_PATH=/usr/lib/llvm-5.0/bin/llvm-symbolizer + export TSAN_OPTIONS="external_symbolizer_path=/usr/lib/llvm-5.0/bin/llvm-symbolizer log_path=${HOME}/logs/tsan" + +Logs will be written to `${HOME}/logs/*san.PID`. TUI debugging ------------- diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 8929bc5b9b..0e7cc428d4 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -219,28 +219,38 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, String nvim_command_output(String command, Error *err) FUNC_API_SINCE(1) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(copy_string(command))); - ADD(args, STRING_OBJ(cstr_to_string("silent"))); - String fn = cstr_to_string("execute"); - Object rv = nvim_call_function(fn, args, err); - api_free_string(fn); - api_free_array(args); + 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_cmdline_cmd(command.data); + capture_ga = save_capture_ga; + msg_silent = save_msg_silent; + try_end(err); + if (ERROR_SET(err)) { - assert(rv.type == kObjectTypeNil); - return (String)STRING_INIT; + goto theend; } - assert(rv.type == kObjectTypeString); - // execute() always(?) prepends a newline; remove it. - if (rv.data.string.size > 1) { - assert(rv.data.string.data[0] == '\n'); - String *s = &rv.data.string; - s->size--; - memmove(s->data, s->data + 1, s->size); - s->data[s->size] = '\0'; + if (capture_local.ga_len > 1) { + // redir always(?) prepends a newline; remove it. + char *s = capture_local.ga_data; + assert(s[0] == '\n'); + memmove(s, s + 1, (size_t)capture_local.ga_len); + s[capture_local.ga_len - 1] = '\0'; + return (String) { // Caller will free the memory. + .data = s, + .size = (size_t)(capture_local.ga_len - 1), + }; } - return rv.data.string; + +theend: + ga_clear(&capture_local); + return (String)STRING_INIT; } /// Evaluates a VimL expression (:help expression). diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 875f323a28..2e8bf18f2d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8116,8 +8116,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_append(capture_ga, NUL); rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(capture_ga->ga_data); - ga_clear(capture_ga); + rettv->vval.v_string = capture_ga->ga_data; capture_ga = save_capture_ga; } -- cgit