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).
2. Starts a new Nvim server using the same |v:argv| (except
`-- [file…]` files).
3. Attaches the current UI to the new Nvim server and runs
`[command]` on it. Other UIs (if any) will not reattach
on restart (this may change in the future).
3. Attaches all UIs to the new Nvim server and runs `[command]`
on it.
Example: discard changes and stop with `:qall!`, then restart: >
:restart +qall!
@@ -86,7 +85,7 @@ Restart Nvim
:restart lua vim.pack.update()
<
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.
------------------------------------------------------------------------------

View File

@@ -404,7 +404,7 @@ UI
• Highlights the cmdline as you type.
• Provides the |pager| as a buffer + window.
• 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
address.
• |: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);
}
/// 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
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)
FUNC_API_SINCE(14) FUNC_API_REMOTE_ONLY FUNC_API_REMOTE_IMPL FUNC_API_CLIENT_IMPL;
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)
FUNC_API_SINCE(3);
void set_title(String title)

View File

@@ -5081,15 +5081,9 @@ static void ex_restart(exarg_T *eap)
arena_mem_free(result_mem);
result_mem = NULL;
// Send restart event with new listen address to current UI.
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);
}
xfree(listen_addr);
goto fail_2;
}
// Send restart event with new listen address to all UIs.
ui_call_restart(cstr_as_string(listen_addr));
ui_flush();
xfree(listen_addr);
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()
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')
screen_client:expect([[
local screen_restarted = [[
^ |
{100:~ }|*3
{3:[No Name] }|
|
{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')
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_server:expect({ any = vim.pesc('[Process exited 0]') })
end)
local function start_headless_server_and_client(use_testlog)