diff options
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 3 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 18 | ||||
-rw-r--r-- | src/nvim/channel.c | 34 | ||||
-rw-r--r-- | src/nvim/fileio.c | 10 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 1 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/core/job_spec.lua | 15 | ||||
-rw-r--r-- | third-party/cmake/BuildLuv.cmake | 3 |
9 files changed, 62 insertions, 28 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b021ad6e7..47e873a845 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,8 +67,8 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY # If not in a git repo (e.g., a tarball) these tokens define the complete # version string, else they are combined with the result of `git describe`. set(NVIM_VERSION_MAJOR 0) -set(NVIM_VERSION_MINOR 2) -set(NVIM_VERSION_PATCH 3) +set(NVIM_VERSION_MINOR 3) +set(NVIM_VERSION_PATCH 0) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers # API level diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index c165d38394..4cd2657561 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -213,7 +213,8 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error) return; } - api_set_error(error, kErrorTypeValidation, "No such UI option: %s", name); + api_set_error(error, kErrorTypeValidation, "No such UI option: %s", + name.data); #undef UI_EXT_OPTION } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 962081cc23..07ec6e8c27 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -239,15 +239,17 @@ String nvim_command_output(String command, Error *err) } 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), + 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); + s.data[s.size - 1] = '\0'; + s.size = s.size - 1; + } + return s; // Caller will free the memory. } theend: diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 2e32af2e9a..776e2bfa86 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -237,15 +237,16 @@ void channel_create_event(Channel *chan, const char *ext_source) #endif } -void channel_incref(Channel *channel) +void channel_incref(Channel *chan) { - channel->refcount++; + chan->refcount++; } -void channel_decref(Channel *channel) +void channel_decref(Channel *chan) { - if (!(--channel->refcount)) { - multiqueue_put(main_loop.fast_events, free_channel_event, 1, channel); + if (!(--chan->refcount)) { + // delay free, so that libuv is done with the handles + multiqueue_put(main_loop.events, free_channel_event, 1, chan); } } @@ -267,18 +268,18 @@ void callback_reader_start(CallbackReader *reader) static void free_channel_event(void **argv) { - Channel *channel = argv[0]; - if (channel->is_rpc) { - rpc_free(channel); + Channel *chan = argv[0]; + if (chan->is_rpc) { + rpc_free(chan); } - callback_reader_free(&channel->on_stdout); - callback_reader_free(&channel->on_stderr); - callback_free(&channel->on_exit); + callback_reader_free(&chan->on_stdout); + callback_reader_free(&chan->on_stderr); + callback_free(&chan->on_exit); - pmap_del(uint64_t)(channels, channel->id); - multiqueue_free(channel->events); - xfree(channel); + pmap_del(uint64_t)(channels, chan->id); + multiqueue_free(chan->events); + xfree(chan); } static void channel_destroy_early(Channel *chan) @@ -286,12 +287,15 @@ static void channel_destroy_early(Channel *chan) if ((chan->id != --next_chan_id)) { abort(); } + pmap_del(uint64_t)(channels, chan->id); + chan->id = 0; if ((--chan->refcount != 0)) { abort(); } - free_channel_event((void **)&chan); + // uv will keep a reference to handles until next loop tick, so delay free + multiqueue_put(main_loop.events, free_channel_event, 1, chan); } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 25653deb3e..4adff63b95 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1737,9 +1737,17 @@ failed: xfree(buffer); if (read_stdin) { - /* Use stderr for stdin, makes shell commands work. */ close(0); +#ifndef WIN32 + // On Unix, use stderr for stdin, makes shell commands work. ignored = dup(2); +#else + // On Windows, use the console input handle for stdin. + HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, 0, (HANDLE)NULL); + ignored = _open_osfhandle(conin, _O_RDONLY); +#endif } if (tmpname != NULL) { diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index a7d4d49e0c..72a25b0b59 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -937,6 +937,7 @@ static void tui_set_mode(UI *ui, ModeShape mode) int shape; switch (c.shape) { + default: abort(); break; case SHAPE_BLOCK: shape = 1; break; case SHAPE_HOR: shape = 3; break; case SHAPE_VER: shape = 5; break; diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 7ac20a99af..718294d941 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -73,6 +73,8 @@ describe('api', function() it('captures command output', function() eq('this is\nspinal tap', nvim('command_output', [[echo "this is\nspinal tap"]])) + eq('no line ending!', + nvim('command_output', [[echon "no line ending!"]])) end) it('captures empty command output', function() diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 6d4cadbdc8..24bff423df 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -640,6 +640,21 @@ describe('jobs', function() ok(string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") ~= nil) end) + it('does not crash when repeatedly failing to start shell', function() + source([[ + set shell=nosuchshell + func! DoIt() + call jobstart('true') + call jobstart('true') + endfunc + ]]) + -- The crash only triggered if both jobs are cleaned up on the same event + -- loop tick. This is also prevented by try-block, so feed must be used. + feed_command("call DoIt()") + feed('<cr>') -- press RETURN + eq(2,eval('1+1')) + end) + it('jobstop() kills entire process tree #6530', function() command('set shell& shellcmdflag& shellquote& shellpipe& shellredir& shellxquote&') diff --git a/third-party/cmake/BuildLuv.cmake b/third-party/cmake/BuildLuv.cmake index 7b8ae09ec4..339264746c 100644 --- a/third-party/cmake/BuildLuv.cmake +++ b/third-party/cmake/BuildLuv.cmake @@ -86,7 +86,8 @@ else() "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} ${LUV_INCLUDE_FLAGS} -fPIC") endif() -if(CMAKE_SYSTEM_NAME MATCHES ".*BSD" OR CMAKE_SYSTEM_NAME MATCHES "DragonFly") +if(CMAKE_GENERATOR MATCHES "Unix Makefiles" AND + (CMAKE_SYSTEM_NAME MATCHES ".*BSD" OR CMAKE_SYSTEM_NAME MATCHES "DragonFly")) set(LUV_BUILD_COMMAND ${CMAKE_COMMAND} "-DCMAKE_MAKE_PROGRAM=gmake" --build .) else() set(LUV_BUILD_COMMAND ${CMAKE_COMMAND} --build .) |