mirror of
https://github.com/neovim/neovim.git
synced 2024-12-24 13:15:09 -07:00
api: nvim_command_output: direct impl
This commit is contained in:
parent
c095f83116
commit
5055d4a755
@ -1,10 +1,22 @@
|
|||||||
Nvim core source
|
Nvim core
|
||||||
================
|
=========
|
||||||
|
|
||||||
Module-specific details are documented at the top of each module (`terminal.c`,
|
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
|
Logs
|
||||||
----
|
----
|
||||||
@ -20,17 +32,36 @@ UI events are logged at level 0 (`DEBUG_LOG_LEVEL`).
|
|||||||
rm -rf build/
|
rm -rf build/
|
||||||
make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0"
|
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.
|
Requires clang 3.4 or later:
|
||||||
- `*.c.h` - parametrized C files, contain all necessary includes, but require
|
|
||||||
defining macros before actually using. Example: `typval_encode.c.h`
|
clang --version
|
||||||
- `*.h` - full headers, with all includes. Does *not* apply to `*.generated.h`.
|
|
||||||
- `*.h.generated.h` - exported functions’ declarations.
|
Build Nvim with sanitizer instrumentation:
|
||||||
- `*.c.generated.h` - static functions’ declarations.
|
|
||||||
|
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
|
TUI debugging
|
||||||
-------------
|
-------------
|
||||||
|
@ -219,28 +219,38 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
|
|||||||
String nvim_command_output(String command, Error *err)
|
String nvim_command_output(String command, Error *err)
|
||||||
FUNC_API_SINCE(1)
|
FUNC_API_SINCE(1)
|
||||||
{
|
{
|
||||||
Array args = ARRAY_DICT_INIT;
|
const int save_msg_silent = msg_silent;
|
||||||
ADD(args, STRING_OBJ(copy_string(command)));
|
garray_T *const save_capture_ga = capture_ga;
|
||||||
ADD(args, STRING_OBJ(cstr_to_string("silent")));
|
garray_T capture_local;
|
||||||
String fn = cstr_to_string("execute");
|
ga_init(&capture_local, 1, 80);
|
||||||
Object rv = nvim_call_function(fn, args, err);
|
|
||||||
api_free_string(fn);
|
try_start();
|
||||||
api_free_array(args);
|
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)) {
|
if (ERROR_SET(err)) {
|
||||||
assert(rv.type == kObjectTypeNil);
|
goto theend;
|
||||||
return (String)STRING_INIT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(rv.type == kObjectTypeString);
|
if (capture_local.ga_len > 1) {
|
||||||
// execute() always(?) prepends a newline; remove it.
|
// redir always(?) prepends a newline; remove it.
|
||||||
if (rv.data.string.size > 1) {
|
char *s = capture_local.ga_data;
|
||||||
assert(rv.data.string.data[0] == '\n');
|
assert(s[0] == '\n');
|
||||||
String *s = &rv.data.string;
|
memmove(s, s + 1, (size_t)capture_local.ga_len);
|
||||||
s->size--;
|
s[capture_local.ga_len - 1] = '\0';
|
||||||
memmove(s->data, s->data + 1, s->size);
|
return (String) { // Caller will free the memory.
|
||||||
s->data[s->size] = '\0';
|
.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).
|
/// Evaluates a VimL expression (:help expression).
|
||||||
|
@ -8116,8 +8116,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
ga_append(capture_ga, NUL);
|
ga_append(capture_ga, NUL);
|
||||||
rettv->v_type = VAR_STRING;
|
rettv->v_type = VAR_STRING;
|
||||||
rettv->vval.v_string = vim_strsave(capture_ga->ga_data);
|
rettv->vval.v_string = capture_ga->ga_data;
|
||||||
ga_clear(capture_ga);
|
|
||||||
|
|
||||||
capture_ga = save_capture_ga;
|
capture_ga = save_capture_ga;
|
||||||
}
|
}
|
||||||
|
@ -69,11 +69,36 @@ describe('api', function()
|
|||||||
eq({mode='n', blocking=false}, nvim("get_mode"))
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('returns command output', function()
|
it('captures command output', function()
|
||||||
eq('this is\nspinal tap',
|
eq('this is\nspinal tap',
|
||||||
nvim('command_output', [[echo "this is\nspinal tap"]]))
|
nvim('command_output', [[echo "this is\nspinal tap"]]))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('captures empty command output', function()
|
||||||
|
eq('', nvim('command_output', 'echo'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('captures single-char command output', function()
|
||||||
|
eq('x', nvim('command_output', 'echo "x"'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('captures multiple commands', function()
|
||||||
|
eq('foo\n 1 %a "[No Name]" line 1',
|
||||||
|
nvim('command_output', 'echo "foo" | ls'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('captures nested execute()', function()
|
||||||
|
eq('\nnested1\nnested2\n 1 %a "[No Name]" line 1',
|
||||||
|
nvim('command_output',
|
||||||
|
[[echo execute('echo "nested1\nnested2"') | ls]]))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('captures nested nvim_command_output()', function()
|
||||||
|
eq('nested1\nnested2\n 1 %a "[No Name]" line 1',
|
||||||
|
nvim('command_output',
|
||||||
|
[[echo nvim_command_output('echo "nested1\nnested2"') | ls]]))
|
||||||
|
end)
|
||||||
|
|
||||||
it('does not return shell |:!| output', function()
|
it('does not return shell |:!| output', function()
|
||||||
eq(':!echo "foo"\r\n', nvim('command_output', [[!echo "foo"]]))
|
eq(':!echo "foo"\r\n', nvim('command_output', [[!echo "foo"]]))
|
||||||
end)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user