fix(:restart): inherit stderr fd on Unix (#38755)

This in turn gives TTY access to channel_from_stdio() in the new server,
if the old server has access to a TTY.

(cherry picked from commit 9927d9259d)
This commit is contained in:
zeertzjq
2026-04-04 21:57:27 +08:00
committed by github-actions[bot]
parent 6ef5f59be6
commit 415626d46d
2 changed files with 39 additions and 6 deletions

View File

@@ -5023,9 +5023,13 @@ static void ex_restart(exarg_T *eap)
}
CallbackReader on_err = CALLBACK_READER_INIT;
// This temporary bootstrap channel is closed intentionally once we obtain
// the new server address. Don't forward child stderr to the current UI.
#ifdef MSWIN
// On Windows, don't forward stderr as it won't work after the current server exits.
on_err.fwd_err = false;
#else
// On Unix, stderr fd is inherited, so it works even after the current server exits.
on_err.fwd_err = true;
#endif
bool detach = true;
varnumber_T exit_status;
@@ -5110,8 +5114,18 @@ fail_2:
api_clear_error(&err);
}
arena_mem_free(result_mem);
result_mem = NULL;
// Kill the new nvim server.
#ifndef MSWIN
// Before killing the new server, close its stderr to avoid polluting the current UI.
MAXSIZE_TEMP_ARRAY(chanclose_expr_args, 1);
ADD_C(chanclose_expr_args, CSTR_AS_OBJ("chanclose(v:stderr)"));
rpc_send_call(channel->id, "nvim_eval", chanclose_expr_args, &result_mem, &err);
api_clear_error(&err);
arena_mem_free(result_mem);
#endif
// Kill the new Nvim server.
proc_stop(&channel->stream.proc);
if (proc_wait(&channel->stream.proc, -1, NULL) < 0) {
emsg("killing new nvim server failed");

View File

@@ -224,7 +224,7 @@ describe('TUI :restart', function()
'--cmd',
'colorscheme vim',
'--cmd',
'set laststatus=2 background=dark noruler',
'set laststatus=2 background=dark noruler noshowcmd',
-- XXX: New server starts before the UI connects to it.
-- So checking screen state for this pid is not possible.
-- '--cmd',
@@ -315,8 +315,15 @@ describe('TUI :restart', function()
assert_termguicolors_and_no_gui_running()
-- Check ":restart +echo" cannot restart server.
-- Check the full screen state to ensure this doesn't pollute the current UI.
tt.feed_data(':restart +echo\013')
screen:expect({ any = vim.pesc('+cmd did not quit the server') })
screen:expect([[
^ |
{1:~}{18: }|*3
{3:[No Name] }|
{9:restart failed: +cmd did not quit the server} |
{5:-- TERMINAL --} |
]])
tt.feed_data('ithis will be removed\027')
screen:expect({ any = vim.pesc('this will be remove^d') })
@@ -514,7 +521,7 @@ describe('TUI :restart', function()
'-u',
config_file,
'--cmd',
'set notermguicolors noswapfile laststatus=0 nowrap noruler',
'set notermguicolors noswapfile laststatus=0 nowrap noruler noshowcmd',
}, { env = env_notermguicolors })
screen:expect([[
^ |
@@ -2906,6 +2913,18 @@ describe('TUI', function()
{ 'tty', 'tty' },
child_exec_lua('return { vim.uv.guess_handle(0), vim.uv.guess_handle(1) }')
)
-- Also works after :restart #38745
feed_data(':restart lua ={ vim.uv.guess_handle(0), vim.uv.guess_handle(1) }\r')
screen:expect([[
^ |
{100:~ }|*3
{3:[No Name] }|
{ "tty", "tty" } |
{5:-- TERMINAL --} |
]])
-- The server is now detached and needs to be quit explicitly.
feed_data(':qall!\r')
screen:expect({ any = vim.pesc('[Process exited 0]') })
end)
end)