feat(:restart): reattach all UIs (#38683)

This is quite easy since [command] is now only executed once on UIEnter.
This commit is contained in:
zeertzjq
2026-04-02 20:57:02 +08:00
parent e5792f6353
commit 8a79d3a3bb
6 changed files with 27 additions and 38 deletions

View File

@@ -74,9 +74,8 @@ Restart Nvim
1. Stops Nvim using `:qall` (or |+cmd|, if given). 1. Stops Nvim using `:qall` (or |+cmd|, if given).
2. Starts a new Nvim server using the same |v:argv| (except 2. Starts a new Nvim server using the same |v:argv| (except
`-- [file…]` files). `-- [file…]` files).
3. Attaches the current UI to the new Nvim server and runs 3. Attaches all UIs to the new Nvim server and runs `[command]`
`[command]` on it. Other UIs (if any) will not reattach on it.
on restart (this may change in the future).
Example: discard changes and stop with `:qall!`, then restart: > Example: discard changes and stop with `:qall!`, then restart: >
:restart +qall! :restart +qall!
@@ -86,7 +85,7 @@ Restart Nvim
:restart lua vim.pack.update() :restart lua vim.pack.update()
< <
Note: Only works if the UI and server are on the same system. Note: Only works if the UI and server are on the same system.
Note: If the UI hasn't implemented the "restart" UI event, Note: If no attached UI implements the "restart" UI event,
this command will lead to a dangling server process. this command will lead to a dangling server process.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------

View File

@@ -404,7 +404,7 @@ UI
• Highlights the cmdline as you type. • Highlights the cmdline as you type.
• Provides the |pager| as a buffer + window. • Provides the |pager| as a buffer + window.
• Currently experimental. To enable it: `require('vim._core.ui2').enable()` • Currently experimental. To enable it: `require('vim._core.ui2').enable()`
• |:restart| restarts Nvim and reattaches the current UI. • |:restart| restarts Nvim and reattaches all UIs.
• |:connect| dynamically connects the current UI to the server at the given • |:connect| dynamically connects the current UI to the server at the given
address. address.
• |:checkhealth| shows a summary in the header for every healthcheck. • |:checkhealth| shows a summary in the header for every healthcheck.

View File

@@ -253,24 +253,6 @@ void nvim_ui_detach(uint64_t channel_id, Error *err)
remote_ui_disconnect(channel_id, err, false); remote_ui_disconnect(channel_id, err, false);
} }
/// Sends a "restart" UI event to the UI on the given channel.
///
/// @return false if there is no UI on the channel, otherwise true
bool remote_ui_restart(uint64_t channel_id, const char *listen_addr, Error *err)
{
RemoteUI *ui = get_ui_or_err(channel_id, err);
if (!ui) {
return false;
}
MAXSIZE_TEMP_ARRAY(args, 1);
ADD_C(args, CSTR_AS_OBJ(listen_addr));
push_call(ui, "restart", args);
ui_flush_buf(ui, false);
return true;
}
// Send a connect UI event to the UI on the given channel // Send a connect UI event to the UI on the given channel
void remote_ui_connect(uint64_t channel_id, char *server_addr, Error *err) void remote_ui_connect(uint64_t channel_id, char *server_addr, Error *err)
{ {

View File

@@ -30,7 +30,7 @@ void flush(void)
void connect(String server_addr) void connect(String server_addr)
FUNC_API_SINCE(14) FUNC_API_REMOTE_ONLY FUNC_API_REMOTE_IMPL FUNC_API_CLIENT_IMPL; FUNC_API_SINCE(14) FUNC_API_REMOTE_ONLY FUNC_API_REMOTE_IMPL FUNC_API_CLIENT_IMPL;
void restart(String listen_addr) void restart(String listen_addr)
FUNC_API_SINCE(14) FUNC_API_REMOTE_ONLY FUNC_API_REMOTE_IMPL FUNC_API_CLIENT_IMPL; FUNC_API_SINCE(14) FUNC_API_REMOTE_ONLY FUNC_API_CLIENT_IMPL;
void suspend(void) void suspend(void)
FUNC_API_SINCE(3); FUNC_API_SINCE(3);
void set_title(String title) void set_title(String title)

View File

@@ -5081,15 +5081,9 @@ static void ex_restart(exarg_T *eap)
arena_mem_free(result_mem); arena_mem_free(result_mem);
result_mem = NULL; result_mem = NULL;
// Send restart event with new listen address to current UI. // Send restart event with new listen address to all UIs.
if (!no_ui && !remote_ui_restart(current_ui, listen_addr, &err)) { ui_call_restart(cstr_as_string(listen_addr));
if (ERROR_SET(&err)) { ui_flush();
ELOG("%s", err.msg); // UI disappeared already?
api_clear_error(&err);
}
xfree(listen_addr);
goto fail_2;
}
xfree(listen_addr); xfree(listen_addr);
char *quit_cmd = (eap->do_ecmd_cmd) ? eap->do_ecmd_cmd : "qall"; char *quit_cmd = (eap->do_ecmd_cmd) ? eap->do_ecmd_cmd : "qall";

View File

@@ -4263,22 +4263,36 @@ describe('TUI client', function()
it(':restart works when connecting to remote instance (with its own TUI)', function() it(':restart works when connecting to remote instance (with its own TUI)', function()
local _, screen_server, screen_client = start_tui_and_remote_client() local _, screen_server, screen_client = start_tui_and_remote_client()
-- The remote client should attach to the new server. -- Both clients should attach to the new server.
feed_data(':restart +qall!\n') feed_data(':restart +qall!\n')
screen_client:expect([[ local screen_restarted = [[
^ | ^ |
{100:~ }|*3 {100:~ }|*3
{3:[No Name] }| {3:[No Name] }|
| |
{5:-- TERMINAL --} | {5:-- TERMINAL --} |
]]) ]]
screen_server:expect({ any = vim.pesc('[Process exited 0]') }) screen_client:expect(screen_restarted)
screen_server:expect(screen_restarted)
feed_data(':echo "GUI Running: " .. has("gui_running")\013') feed_data(':echo "GUI Running: " .. has("gui_running")\013')
screen_client:expect({ any = 'GUI Running: 0' }) screen_client:expect({ any = 'GUI Running: 0' })
feed_data(':q!\r') -- The :vsplit command should only be executed once.
feed_data(':restart vsplit\r')
screen_restarted = [[
^ │ |
{100:~ }│{100:~ }|*3
{3:[No Name] }{2:[No Name] }|
|
{5:-- TERMINAL --} |
]]
screen_client:expect(screen_restarted)
screen_server:expect(screen_restarted)
feed_data(':qall!\r')
screen_client:expect({ any = vim.pesc('[Process exited 0]') }) screen_client:expect({ any = vim.pesc('[Process exited 0]') })
screen_server:expect({ any = vim.pesc('[Process exited 0]') })
end) end)
local function start_headless_server_and_client(use_testlog) local function start_headless_server_and_client(use_testlog)