diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 191c8e3ea8..16abf10484 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -120,23 +120,9 @@ void remote_ui_free_all_mem(void) #endif /// Wait until UI has connected. -/// -/// @param only_stdio UI is expected to connect on stdio. -void remote_ui_wait_for_attach(bool only_stdio) +void remote_ui_wait_for_attach(void) { - if (only_stdio) { - Channel *channel = find_channel(CHAN_STDIO); - if (!channel) { - // `only_stdio` implies --embed mode, thus stdio channel can be assumed. - abort(); - } - - LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, - map_has(uint64_t, &connected_uis, CHAN_STDIO)); - } else { - LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, -1, - ui_active()); - } + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, -1, ui_active()); } /// Activates UI events on the channel. diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index f03ce00690..a3bf5c68cd 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4963,6 +4963,7 @@ static void ex_quitall(exarg_T *eap) static void ex_restart(exarg_T *eap) { Error err = ERROR_INIT; + const bool no_ui = !ui_active(); const char *exepath = get_vim_var_str(VV_PROGPATH); const list_T *l = get_vim_var_list(VV_ARGV); int argc = tv_list_len(l); @@ -4996,14 +4997,18 @@ static void ex_restart(exarg_T *eap) } } } - // Replace `--embed` OR `--headless` with `--embed --headless` once. + // Replace `--embed` OR `--headless` with `--embed` or `--embed --headless` once. // Drop stdin ("-") argument. if (i == 0 || (!strequal(arg, "--embed") && !strequal(arg, "--headless") && !strequal(arg, "-"))) { argv[i++] = xstrdup(arg); if (i == 1) { argv[i++] = xstrdup("--embed"); - argv[i++] = xstrdup("--headless"); + // Without --headless, embed waits for UI to attach. + // Only add --headless when there is no UI. + if (no_ui) { + argv[i++] = xstrdup("--headless"); + } } } }); @@ -5075,7 +5080,7 @@ static void ex_restart(exarg_T *eap) result_mem = NULL; // Send restart event with new listen address to current UI. - if (!remote_ui_restart(current_ui, listen_addr, &err)) { + if (!no_ui && !remote_ui_restart(current_ui, listen_addr, &err)) { if (ERROR_SET(&err)) { ELOG("%s", err.msg); // UI disappeared already? api_clear_error(&err); diff --git a/src/nvim/main.c b/src/nvim/main.c index b0c98293ae..5e64681cb5 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -436,10 +436,9 @@ int main(int argc, char **argv) // Wait for UIs to set up Nvim or show early messages // and prompts (--cmd, swapfile dialog, …). bool use_remote_ui = (embedded_mode && !headless_mode); - bool listen_and_embed = params.listen_addr != NULL; if (use_remote_ui) { TIME_MSG("waiting for UI"); - remote_ui_wait_for_attach(!listen_and_embed); + remote_ui_wait_for_attach(); TIME_MSG("done waiting for UI"); firstwin->w_prev_height = firstwin->w_height; // may have changed } diff --git a/test/functional/core/server_spec.lua b/test/functional/core/server_spec.lua index 3d65885376..246b49e02f 100644 --- a/test/functional/core/server_spec.lua +++ b/test/functional/core/server_spec.lua @@ -351,3 +351,31 @@ describe('startup --listen', function() matches([[[/\\]test%-name[^/\\]*]], api.nvim_get_vvar('servername')) end) end) + +it(':restart works in headless server (no UI)', function() + t.skip(is_os('win'), 'FIXME: --listen not preserved by :restart on Windows') + + local nvim0 = clear() + local server_pipe = n.new_pipename() + + finally(function() + n.expect_exit(n.command, 'qall!') + nvim0:close() + n.set_session(nil) + end) + + fn.jobstart({ n.nvim_prog, '--clean', '--headless', '--listen', server_pipe }) + t.retry(nil, nil, function() + neq(nil, vim.uv.fs_stat(server_pipe)) + end) + n.set_session(n.connect(server_pipe)) + + n.expect_exit(n.command, 'restart') + n.set_session(n.connect(server_pipe)) + eq(1, api.nvim_get_vvar('vim_did_enter')) + + -- TODO: [command] is currently not executed without UI + -- n.expect_exit(n.command, 'restart lua _G.new_server = 1') + -- n.set_session(n.connect(server_pipe)) + -- eq(1, n.exec_lua('return _G.new_server')) +end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 7156b3cbfb..6189b1c88d 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -466,7 +466,7 @@ describe('TUI :restart', function() screen:expect({ any = vim.pesc('[Process exited 0]') }) end) - it('autocommands are triggered by [command] properly #38549', function() + it('[command] triggers autocommands properly #38549', function() local screen = tt.setup_child_nvim({ '--clean', '--cmd', @@ -497,6 +497,80 @@ describe('TUI :restart', function() feed_data(':qall!\r') screen:expect({ any = vim.pesc('[Process exited 0]') }) end) + + it('new server loads user config after old server exits #38569', function() + local config_file = 'Xrestart_session_config.lua' + local session_file = 'Xrestart_session.vim' + write_file( + config_file, + ([[ + vim.api.nvim_create_autocmd("VimLeavePre", { + callback = function() + vim.cmd('mksession! %s') + end, + }) + + if vim.v.vim_did_enter and vim.uv.fs_stat('%s') then + vim.cmd('source %s') + end + ]]):format(session_file, session_file, session_file) + ) + finally(function() + os.remove(config_file) + os.remove(session_file) + end) + + local screen = tt.setup_child_nvim({ + '--clean', + '-u', + config_file, + '--cmd', + 'set notermguicolors noswapfile laststatus=0 nowrap noruler', + }, { env = env_notermguicolors }) + screen:expect([[ + ^ | + ~ |*4 + | + {5:-- TERMINAL --} | + ]]) + + feed_data(':rightbelow 28vsplit test/functional/fixtures/bigfile.txt\r') + screen:expect([[ + │^0000;;Cc;0;BN;;;;;N| + ~ │0001;;Cc;0;BN;;;;;N| + ~ │0002;;Cc;0;BN;;;;;N| + ~ │0003;;Cc;0;BN;;;;;N| + ~ │0004;;Cc;0;BN;;;;;N| + | + {5:-- TERMINAL --} | + ]]) + + feed_data(':restart echo "restarted"\r') + screen:expect([[ + ^ │0000;;Cc;0;BN;;;;;N| + ~ │0001;;Cc;0;BN;;;;;N| + ~ │0002;;Cc;0;BN;;;;;N| + ~ │0003;;Cc;0;BN;;;;;N| + ~ │0004;;Cc;0;BN;;;;;N| + restarted | + {5:-- TERMINAL --} | + ]]) + + feed_data(':set sessionoptions-=winsize | restart\r') + screen:expect([[ + ^ │0000;;Cc;0;BN;;| + ~ │0001;;Cc;0;BN;;| + ~ │0002;;Cc;0;BN;;| + ~ │0003;;Cc;0;BN;;| + ~ │0004;;Cc;0;BN;;| + | + {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) describe('TUI :connect', function()