diff options
-rw-r--r-- | runtime/autoload/remote/host.vim | 2 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 32 | ||||
-rw-r--r-- | runtime/doc/vim_diff.txt | 9 | ||||
-rw-r--r-- | runtime/ftplugin/man.vim | 2 | ||||
-rw-r--r-- | scripts/gendispatch.lua | 102 | ||||
-rw-r--r-- | src/nvim/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/nvim/api/private/dispatch.c | 45 | ||||
-rw-r--r-- | src/nvim/api/private/dispatch.h | 1 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 21 | ||||
-rw-r--r-- | src/nvim/edit.c | 3 | ||||
-rw-r--r-- | src/nvim/event/rstream.c | 4 | ||||
-rw-r--r-- | src/nvim/fold.c | 13 | ||||
-rw-r--r-- | src/nvim/normal.c | 3 | ||||
-rw-r--r-- | src/nvim/os/shell.c | 10 | ||||
-rw-r--r-- | test/functional/eval/system_spec.lua (renamed from test/functional/shell/viml_system_spec.lua) | 14 | ||||
-rw-r--r-- | test/functional/ex_cmds/bang_filter_spec.lua (renamed from test/functional/shell/bang_filter_spec.lua) | 0 | ||||
-rw-r--r-- | test/functional/ex_cmds/dict_notifications_spec.lua (renamed from test/functional/dict_notifications_spec.lua) | 0 | ||||
-rw-r--r-- | test/functional/terminal/api_spec.lua | 12 |
18 files changed, 162 insertions, 121 deletions
diff --git a/runtime/autoload/remote/host.vim b/runtime/autoload/remote/host.vim index 1f30b91ab8..51f7e5886f 100644 --- a/runtime/autoload/remote/host.vim +++ b/runtime/autoload/remote/host.vim @@ -190,7 +190,7 @@ function! s:RegistrationCommands(host) abort call remote#host#RegisterClone(host_id, a:host) let pattern = s:plugin_patterns[a:host] let paths = globpath(&rtp, 'rplugin/'.a:host.'/'.pattern, 0, 1) - let paths = map(paths, 'tr(v:val,"\\","/")') " Normalize slashes #4795 + let paths = map(paths, 'tr(resolve(v:val),"\\","/")') " Normalize slashes #4795 let paths = uniq(sort(paths)) if empty(paths) return [] diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index dedbe49605..3c9b4dafcd 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -6838,23 +6838,30 @@ system({cmd} [, {input}]) *system()* *E677* Get the output of the shell command {cmd} as a |string|. {cmd} will be run the same as in |jobstart()|. See |systemlist()| to get the output as a |List|. - - When {input} is given and is a string this string is written - to a file and passed as stdin to the command. The string is - written as-is, you need to take care of using the correct line - separators yourself. - If {input} is given and is a |List| it is written to the file - in a way |writefile()| does with {binary} set to "b" (i.e. - with a newline between each list item with newlines inside - list items converted to NULs). - Pipes are not used. + Not to be used for interactive commands. + + If {input} is a string it is written to a pipe and passed as + stdin to the command. The string is written as-is, line + separators are not changed. + If {input} is a |List| it is written to the pipe as + |writefile()| does with {binary} set to "b" (i.e. with + a newline between each list item, and newlines inside list + items converted to NULs). + *E5677* + Note: system() cannot write to or read from backgrounded ("&") + shell commands, e.g.: > + :echo system("cat - &", "foo")) +< which is equivalent to: > + $ echo foo | bash -c 'cat - &' +< The pipes are disconnected (unless overridden by shell + redirection syntax) before input can reach it. Use + |jobstart()| instead. Note: Use |shellescape()| or |::S| with |expand()| or |fnamemodify()| to escape special characters in a command argument. Newlines in {cmd} may cause the command to fail. The characters in 'shellquote' and 'shellxquote' may also cause trouble. - This is not to be used for interactive commands. The result is a String. Example: > :let files = system("ls " . shellescape(expand('%:h'))) @@ -6869,9 +6876,6 @@ system({cmd} [, {input}]) *system()* *E677* The command executed is constructed using several options when {cmd} is a string: 'shell' 'shellcmdflag' {cmd} - The command will be executed in "cooked" mode, so that a - CTRL-C will interrupt the command (on Unix at least). - The resulting error code can be found in |v:shell_error|. This function will fail in |restricted-mode|. diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index f036b4427e..bb1f993ab6 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -108,6 +108,7 @@ Options: Commands: |:CheckHealth| + |:drop| is available on all platforms |:Man| is available by default, with many improvements such as completion Functions: @@ -140,10 +141,10 @@ are always available and may be used simultaneously in separate plugins. The `neovim` pip package must be installed to use Python plugins in Nvim (see |provider-python|). -|:!| and |system()| do not support "interactive" commands; use |:terminal| for -that instead. Terminal Vim supports interactive |:!| and |system()|, but gui -Vim does not. See ":help gui-pty" in Vim: - http://vimdoc.sourceforge.net/htmldoc/gui_x11.html#gui-pty +|:!| does not support "interactive" commands. Use |:terminal| instead. +(GUI Vim has a similar limitation, see ":help gui-pty" in Vim.) + +|system()| does not support writing/reading "backgrounded" commands. |E5677| |mkdir()| behaviour changed: 1. Assuming /tmp/foo does not exist and /tmp can be written to diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 02d2b4e557..f6fefd0155 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -21,7 +21,7 @@ if has('vim_starting') " all caps it is impossible to tell what the original capitilization was. let ref = tolower(matchstr(getline(1), '^\S\+')) let b:man_sect = man#extract_sect_and_name_ref(ref)[0] - execute 'file man://'.ref + execute 'silent file man://'.ref endif setlocal buftype=nofile diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua index 00ed84212b..4f00783825 100644 --- a/scripts/gendispatch.lua +++ b/scripts/gendispatch.lua @@ -52,16 +52,18 @@ package.path = nvimsrcdir .. '/?.lua;' .. package.path -- names of all headers relative to the source root (for inclusion in the -- generated file) headers = {} --- output c file(dispatch function + metadata serialized with msgpack) -outputf = arg[#arg-1] --- output mpack file (metadata) +-- output h file with generated dispatch functions +dispatch_outputf = arg[#arg-2] +-- output h file with packed metadata +funcs_metadata_outputf = arg[#arg-1] +-- output metadata mpack file, for use by other build scripts mpack_outputf = arg[#arg] -- set of function names, used to detect duplicates function_names = {} -- read each input file, parse and append to the api metadata -for i = 2, #arg - 2 do +for i = 2, #arg - 3 do local full_path = arg[i] local parts = {} for part in string.gmatch(full_path, '[^/]+') do @@ -165,66 +167,27 @@ for _,f in ipairs(functions) do end --- start building the output -output = io.open(outputf, 'wb') - -output:write([[ -#include <inttypes.h> -#include <stdbool.h> -#include <stdint.h> -#include <assert.h> -#include <msgpack.h> - -#include "nvim/map.h" -#include "nvim/log.h" -#include "nvim/vim.h" -#include "nvim/msgpack_rpc/helpers.h" -#include "nvim/api/private/dispatch.h" -#include "nvim/api/private/helpers.h" -#include "nvim/api/private/defs.h" +funcs_metadata_output = io.open(funcs_metadata_outputf, 'wb') +funcs_metadata_output:write([[ +static const uint8_t funcs_metadata[] = { ]]) -for i = 1, #headers do - if headers[i]:sub(-12) ~= '.generated.h' then - output:write('\n#include "nvim/'..headers[i]..'"') - end -end - -output:write([[ - - -static const uint8_t msgpack_metadata[] = { - -]]) -- serialize the API metadata using msgpack and embed into the resulting -- binary for easy querying by clients packed_exported_functions = mpack.pack(exported_functions) for i = 1, #packed_exported_functions do - output:write(string.byte(packed_exported_functions, i)..', ') + funcs_metadata_output:write(string.byte(packed_exported_functions, i)..', ') if i % 10 == 0 then - output:write('\n ') + funcs_metadata_output:write('\n ') end end -output:write([[ +funcs_metadata_output:write([[ }; - -void msgpack_rpc_init_function_metadata(Dictionary *metadata) -{ - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - if (msgpack_unpack_next(&unpacked, - (const char *)msgpack_metadata, - sizeof(msgpack_metadata), - NULL) != MSGPACK_UNPACK_SUCCESS) { - abort(); - } - Object functions; - msgpack_rpc_to_object(&unpacked.data, &functions); - msgpack_unpacked_destroy(&unpacked); - PUT(*metadata, "functions", functions); -} - ]]) +funcs_metadata_output:close() + +-- start building the dispatch wrapper output +output = io.open(dispatch_outputf, 'wb') local function real_type(type) local rv = type @@ -336,22 +299,12 @@ end -- Generate a function that initializes method names with handler functions output:write([[ -static Map(String, MsgpackRpcRequestHandler) *methods = NULL; - -void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler) -{ - map_put(String, MsgpackRpcRequestHandler)(methods, method, handler); -} - void msgpack_rpc_init_method_table(void) { methods = map_new(String, MsgpackRpcRequestHandler)(); ]]) --- Keep track of the maximum method name length in order to avoid walking --- strings longer than that when searching for a method handler -local max_fname_len = 0 for i = 1, #functions do local fn = functions[i] output:write(' msgpack_rpc_add_method_handler('.. @@ -360,32 +313,9 @@ for i = 1, #functions do '(MsgpackRpcRequestHandler) {.fn = handle_'.. (fn.impl_name or fn.name).. ', .async = '..tostring(fn.async)..'});\n') - if #fn.name > max_fname_len then - max_fname_len = #fn.name - end end output:write('\n}\n\n') - -output:write([[ -MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, - size_t name_len) -{ - String m = { - .data=(char *)name, - .size=MIN(name_len, ]]..max_fname_len..[[) - }; - MsgpackRpcRequestHandler rv = - map_get(String, MsgpackRpcRequestHandler)(methods, m); - - if (!rv.fn) { - rv.fn = msgpack_rpc_handle_missing_method; - } - - return rv; -} -]]) - output:close() mpack_output = io.open(mpack_outputf, 'wb') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index cbea6a05c9..49edfda838 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -18,7 +18,8 @@ set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack) set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua) set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) -set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch.c) +set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h) +set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) set(GENERATED_FUNCS_HASH_INPUT ${GENERATED_DIR}/funcs.generated.h.gperf) @@ -197,8 +198,11 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES} ${UNICODE_FILES} ) -add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${API_METADATA} - COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${API_HEADERS} ${GENERATED_API_DISPATCH} ${API_METADATA} +add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} + ${API_METADATA} + COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} + ${API_HEADERS} ${GENERATED_API_DISPATCH} + ${GENERATED_FUNCS_METADATA} ${API_METADATA} DEPENDS ${API_HEADERS} ${MSGPACK_RPC_HEADERS} diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c new file mode 100644 index 0000000000..9b3bcc380a --- /dev/null +++ b/src/nvim/api/private/dispatch.c @@ -0,0 +1,45 @@ +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <assert.h> +#include <msgpack.h> + +#include "nvim/map.h" +#include "nvim/log.h" +#include "nvim/vim.h" +#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/api/private/dispatch.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/private/defs.h" + +#include "nvim/api/buffer.h" +#include "nvim/api/tabpage.h" +#include "nvim/api/ui.h" +#include "nvim/api/vim.h" +#include "nvim/api/window.h" + +static Map(String, MsgpackRpcRequestHandler) *methods = NULL; + +static void msgpack_rpc_add_method_handler(String method, + MsgpackRpcRequestHandler handler) +{ + map_put(String, MsgpackRpcRequestHandler)(methods, method, handler); +} + +MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, + size_t name_len) +{ + String m = { .data = (char *)name, .size = name_len }; + MsgpackRpcRequestHandler rv = + map_get(String, MsgpackRpcRequestHandler)(methods, m); + + if (!rv.fn) { + rv.fn = msgpack_rpc_handle_missing_method; + } + + return rv; +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +#include "api/private/dispatch_wrappers.generated.h" +#endif diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h index d91456c306..c12cf9e698 100644 --- a/src/nvim/api/private/dispatch.h +++ b/src/nvim/api/private/dispatch.h @@ -18,6 +18,7 @@ typedef struct { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/dispatch.h.generated.h" +# include "api/private/dispatch_wrappers.h.generated.h" #endif #endif // NVIM_API_PRIVATE_DISPATCH_H diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d80ee7dc67..c0ee735d1a 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -7,6 +7,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/handle.h" +#include "nvim/msgpack_rpc/helpers.h" #include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/buffer.h" @@ -27,6 +28,7 @@ typedef struct { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.c.generated.h" +# include "api/private/funcs_metadata.generated.h" #endif /// Start block that may cause vimscript exceptions @@ -761,7 +763,7 @@ Dictionary api_metadata(void) static Dictionary metadata = ARRAY_DICT_INIT; if (!metadata.size) { - msgpack_rpc_init_function_metadata(&metadata); + init_function_metadata(&metadata); init_error_type_metadata(&metadata); init_type_metadata(&metadata); } @@ -769,6 +771,22 @@ Dictionary api_metadata(void) return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary; } +static void init_function_metadata(Dictionary *metadata) +{ + msgpack_unpacked unpacked; + msgpack_unpacked_init(&unpacked); + if (msgpack_unpack_next(&unpacked, + (const char *)funcs_metadata, + sizeof(funcs_metadata), + NULL) != MSGPACK_UNPACK_SUCCESS) { + abort(); + } + Object functions; + msgpack_rpc_to_object(&unpacked.data, &functions); + msgpack_unpacked_destroy(&unpacked); + PUT(*metadata, "functions", functions); +} + static void init_error_type_metadata(Dictionary *metadata) { Dictionary types = ARRAY_DICT_INIT; @@ -784,6 +802,7 @@ static void init_error_type_metadata(Dictionary *metadata) PUT(*metadata, "error_types", DICTIONARY_OBJ(types)); } + static void init_type_metadata(Dictionary *metadata) { Dictionary types = ARRAY_DICT_INIT; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 51c9fb1556..a0ae1a3e6b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -462,8 +462,7 @@ static void insert_enter(InsertState *s) o_lnum = curwin->w_cursor.lnum; } - foldUpdateAll(curwin); - foldOpenCursor(); + foldUpdateAfterInsert(); if (s->cmdchar != 'r' && s->cmdchar != 'v') { apply_autocmds(EVENT_INSERTLEAVE, NULL, NULL, false, curbuf); } diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 5126dfd84e..92efc9fa2e 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -112,8 +112,8 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) // to `alloc_cb` will return the same unused pointer(`rbuffer_produced` // won't be called) && cnt != 0) { - DLOG("Closing Stream(%p) because of %s(%zd)", stream, - uv_strerror((int)cnt), cnt); + DLOG("Closing Stream (%p): %s (%s)", stream, + uv_err_name((int)cnt), os_strerror((int)cnt)); // Read error or EOF, either way stop the stream and invoke the callback // with eof == true uv_read_stop(uvstream); diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 70030b8525..c84d738cb4 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -788,6 +788,19 @@ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) } } +/// Updates folds when leaving insert-mode. +void foldUpdateAfterInsert(void) +{ + if (foldmethodIsManual(curwin) // foldmethod=manual: No need to update. + // These foldmethods are too slow, do not auto-update on insert-leave. + || foldmethodIsSyntax(curwin) || foldmethodIsExpr(curwin)) { + return; + } + + foldUpdateAll(curwin); + foldOpenCursor(); +} + /* foldUpdateAll() {{{2 */ /* * Update all lines in a window for folding. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 6dcbf50750..76e3829bee 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5925,8 +5925,7 @@ static void nv_replace(cmdarg_T *cap) set_last_insert(cap->nchar); } - foldUpdateAll(curwin); - foldOpenCursor(); + foldUpdateAfterInsert(); } /* diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index e9a3dcbff8..18ee008d66 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -545,6 +545,16 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer, static void shell_write_cb(Stream *stream, void *data, int status) { + if (status) { + // Can happen if system() tries to send input to a shell command that was + // backgrounded (:call system("cat - &", "foo")). #3529 #5241 + EMSG2(_("E5677: Error writing input to shell-command: %s"), + uv_err_name(status)); + } + if (stream->closed) { // Process may have exited before this write. + ELOG("stream was already closed"); + return; + } stream_close(stream, NULL, NULL); } diff --git a/test/functional/shell/viml_system_spec.lua b/test/functional/eval/system_spec.lua index b8de7cc86f..b8f1f87f30 100644 --- a/test/functional/shell/viml_system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -1,7 +1,3 @@ --- Specs for --- - `system()` --- - `systemlist()` - local helpers = require('test.functional.helpers')(after_each) local eq, clear, eval, feed, nvim = helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.nvim @@ -120,12 +116,22 @@ describe('system()', function() it('returns the program output', function() eq("echoed", eval('system("echo -n echoed")')) end) + it('to backgrounded command does not crash', function() + -- This is indeterminate, just exercise the codepath. + eval('system("echo -n echoed &")') + eq(2, eval("1+1")) -- Still alive? + end) end) describe('passing input', function() it('returns the program output', function() eq("input", eval('system("cat -", "input")')) end) + it('to backgrounded command does not crash', function() + -- This is indeterminate, just exercise the codepath. + eval('system("cat - &", "input")') + eq(2, eval("1+1")) -- Still alive? + end) end) describe('passing a lot of input', function() diff --git a/test/functional/shell/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua index a320e6d018..a320e6d018 100644 --- a/test/functional/shell/bang_filter_spec.lua +++ b/test/functional/ex_cmds/bang_filter_spec.lua diff --git a/test/functional/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index dc87312911..dc87312911 100644 --- a/test/functional/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua index 58d6c75940..045bdb0749 100644 --- a/test/functional/terminal/api_spec.lua +++ b/test/functional/terminal/api_spec.lua @@ -22,7 +22,7 @@ describe('api', function() -- Start the socket from the child nvim. child_session.feed_data(":echo serverstart('"..socket_name.."')\n") - -- Wait for socket creation by abusing expect(). + -- Wait for socket creation. screen:expect([[ {1: } | {4:~ }| @@ -37,6 +37,16 @@ describe('api', function() local socket_session2 = helpers.connect(socket_name) child_session.feed_data("i[tui] insert-mode") + -- Wait for stdin to be processed. + screen:expect([[ + [tui] insert-mode{1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]) ok(socket_session1:request("nvim_ui_attach", 42, 6, {rgb=true})) ok(socket_session2:request("nvim_ui_attach", 25, 30, {rgb=true})) |