feat: ":restart +cmd" #34788

Problem:
":restart" always executes ":qall" to exit the server.

Solution:
Support ":restart +cmd" so the user can control the command
used to exit the server.

Co-authored-by: Justin M. Keyes <justinkz@gmail.com>
Co-authored-by: zeertzjq <zeertzjq@outlook.com>
This commit is contained in:
Sathya Pramodh
2025-07-24 08:15:31 +05:30
committed by GitHub
parent 0dcdd65dcc
commit 54b8c99e51
6 changed files with 77 additions and 35 deletions

View File

@@ -70,21 +70,20 @@ Stop or detach the current UI
Restart Nvim
*:restart*
:restart
:restart [+cmd]
Restarts the Nvim server with the same startup arguments
|v:argv| and reattaches the current UI to the new server.
All other UIs will detach.
This fails when changes have been made and Vim refuses to
|abandon| the current buffer.
Use with `:confirm` to prompt if changes have been made.
Example: `:restart +qall!` stops the server using `:qall!`.
Note: |+cmd| defaults to `qall!` if not specified.
Note: If the current UI hasn't implemented the "restart" UI
event, this command is equivalent to `:qall`.
Note: Only works if the UI and server are on the same system.
:restart!
Force restarts the Nvim server, abandoning unsaved buffers.
------------------------------------------------------------------------------
GUI commands

View File

@@ -2184,13 +2184,13 @@ M.cmds = {
command = 'quitall',
flags = bit.bor(BANG, TRLBAR),
addr_type = 'ADDR_NONE',
func = 'ex_quitall_or_restart',
func = 'ex_quitall',
},
{
command = 'qall',
flags = bit.bor(BANG, TRLBAR, CMDWIN, LOCK_OK),
addr_type = 'ADDR_NONE',
func = 'ex_quitall_or_restart',
func = 'ex_quitall',
},
{
command = 'read',
@@ -2248,9 +2248,9 @@ M.cmds = {
},
{
command = 'restart',
flags = bit.bor(BANG, TRLBAR),
flags = bit.bor(CMDARG, TRLBAR),
addr_type = 'ADDR_NONE',
func = 'ex_quitall_or_restart',
func = 'ex_restart',
},
{
command = 'retab',

View File

@@ -15,6 +15,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/ui.h"
#include "nvim/api/vimscript.h"
#include "nvim/arglist.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
@@ -4700,6 +4701,13 @@ void not_exiting(void)
exiting = false;
}
/// Call this function if we thought we were going to restart, but we won't
/// (because of an error).
void not_restarting(void)
{
restarting = false;
}
bool before_quit_autocmds(win_T *wp, bool quit_all, bool forceit)
{
apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, wp->w_buffer);
@@ -4829,22 +4837,39 @@ int before_quit_all(exarg_T *eap)
}
/// ":qall": try to quit all windows
/// ":restart": restart the Nvim server
static void ex_quitall_or_restart(exarg_T *eap)
static void ex_quitall(exarg_T *eap)
{
if (before_quit_all(eap) == FAIL) {
return;
}
exiting = true;
Error err = ERROR_INIT;
if ((eap->forceit || !check_changed_any(false, false))
&& (eap->cmdidx != CMD_restart || remote_ui_restart(current_ui, &err))) {
getout(0);
if (!eap->forceit && check_changed_any(false, false)) {
not_exiting();
return;
}
not_exiting();
getout(0);
}
/// ":restart": restart the Nvim server (using ":qall!").
/// ":restart +cmd": restart the Nvim server using ":cmd".
static void ex_restart(exarg_T *eap)
{
char *quit_cmd = (eap->do_ecmd_cmd) ? eap->do_ecmd_cmd : "qall!";
Error err = ERROR_INIT;
if ((cmdmod.cmod_flags & CMOD_CONFIRM) && check_changed_any(false, false)) {
return;
}
restarting = true;
nvim_command(cstr_as_string(quit_cmd), &err);
if (ERROR_SET(&err)) {
emsg(err.msg); // UI disappeared already?
emsg(err.msg); // Could not exit
api_clear_error(&err);
not_restarting();
return;
}
if (!exiting) {
emsg("restart failed: +cmd did not quit the server");
not_restarting();
}
}

View File

@@ -423,6 +423,8 @@ EXTERN int sc_col; // column for shown command
EXTERN int starting INIT( = NO_SCREEN);
// true when planning to exit. Might keep running if there is a changed buffer.
EXTERN bool exiting INIT( = false);
// true when planning to restart.
EXTERN bool restarting INIT( = false);
// internal value of v:dying
EXTERN int v_dying INIT( = 0);
// is stdin a terminal?

View File

@@ -809,6 +809,17 @@ void getout(int exitval)
ui_call_set_title(cstr_as_string(p_titleold));
}
if (restarting) {
Error err = ERROR_INIT;
if (!remote_ui_restart(current_ui, &err)) {
if (ERROR_SET(&err)) {
ELOG("%s", err.msg); // UI disappeared already?
api_clear_error(&err);
}
}
restarting = false;
}
if (garbage_collect_at_exit) {
garbage_collect(false);
}

View File

@@ -264,6 +264,16 @@ describe('TUI :restart', function()
restart_pid_check()
gui_running_check()
-- Check ":restart +qall" on an unmodified buffer.
tt.feed_data(':restart +qall\013')
screen_expect(s0)
restart_pid_check()
gui_running_check()
-- Check ":restart +echo" cannot restart server.
tt.feed_data(':restart +echo\013')
screen:expect({ any = vim.pesc('+cmd did not quit the server') })
tt.feed_data('ithis will be removed\027')
screen_expect([[
this will be remove^d |
@@ -273,20 +283,15 @@ describe('TUI :restart', function()
{5:-- TERMINAL --} |
]])
-- Check ":restart" on a modified buffer.
tt.feed_data(':restart\013')
screen_expect([[
this will be removed |
{3: }|
{101:E37: No write since last change} |
{101:E162: No write since last change for buffer "[No N}|
{101:ame]"} |
{102:Press ENTER or type command to continue}^ |
{5:-- TERMINAL --} |
]])
-- Check ":confirm restart" on a modified buffer.
tt.feed_data(':confirm restart\013')
screen:expect({ any = vim.pesc('Save changes to "Untitled"?') })
-- Check ":restart!".
tt.feed_data(':restart!\013')
-- Cancel the operation (abandons restart).
tt.feed_data('C\013')
-- Check ":restart" on the modified buffer.
tt.feed_data(':restart\013')
screen_expect(s0)
restart_pid_check()
gui_running_check()
@@ -3728,9 +3733,9 @@ describe('TUI client', function()
screen_client:expect(s1)
screen_server:expect(s1)
-- Run :restart! on the remote client.
-- Run :restart on the remote client.
-- The remote client should start a new server while the original one should exit.
feed_data(':restart!\n')
feed_data(':restart\n')
screen_client:expect([[
^ |
{100:~ }|*3
@@ -3805,9 +3810,9 @@ describe('TUI client', function()
feed_data(':echo "GUI Running: " .. has("gui_running")\013')
screen_client:expect({ any = 'GUI Running: 1' })
-- Run :restart! on the client.
-- Run :restart on the client.
-- The client should start a new server while the original server should exit.
feed_data(':restart!\n')
feed_data(':restart\n')
screen_client:expect([[
^ |
{100:~ }|*4