neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 78f4994627b7d9c3b85f30028fe55ff38024a39d
parent 39e402caa511f311b4f43a6c6f18bacba8c47c49
Author: Sathya Pramodh <94102031+sathya-pramodh@users.noreply.github.com>
Date:   Sun,  3 Aug 2025 11:20:45 +0530

feat: ":restart [cmd]" can run commands #35045

Problem:
Not easy for a user to tell ":restart" to "run this command(s) after restarting".

Solution:
All ":restart" args following the optional +cmd arg are treated as a big cmdline that is passed as a "-c" CLI arg when restarting nvim.
Diffstat:
Mruntime/doc/gui.txt | 5++++-
Msrc/nvim/api/ui.c | 20++++++++++++++++++--
Msrc/nvim/ex_cmds.lua | 2+-
Msrc/nvim/ex_docmd.c | 22++++++++++++++++++++++
Mtest/functional/terminal/tui_spec.lua | 44++++++++++++++++++++++++++++++++++++++++++--
5 files changed, 87 insertions(+), 6 deletions(-)

diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt @@ -70,7 +70,7 @@ Stop or detach the current UI Restart Nvim *:restart* -:restart [+cmd] +:restart [+cmd] [command] 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. @@ -78,6 +78,9 @@ Restart Nvim Use with `:confirm` to prompt if changes have been made. Example: `:restart +qall!` stops the server using `:qall!`. + Example: `:restart +qall! lua vim.pack.update()` stops the + server using `:qall!` and starts the new server with the + argument `-c lua vim.pack.update()`. See also |-c|. Note: |+cmd| defaults to `qall!` if not specified. Note: If the current UI hasn't implemented the "restart" UI diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c @@ -286,15 +286,31 @@ bool remote_ui_restart(uint64_t channel_id, Error *err) assert(argc > 0); Array argv = arena_array(&arena, (size_t)argc + 1); bool had_minmin = false; + bool skipping_minc = false; // Skip -c <cmd> from argv. + bool first_minc = true; // Avoid skipping the first -c <cmd> from argv. TV_LIST_ITER_CONST(l, li, { const char *arg = tv_get_string(TV_LIST_ITEM_TV(li)); if (argv.size > 0 && !had_minmin && strequal(arg, "--")) { had_minmin = true; + skipping_minc = false; } - // Exclude --embed/--headless from `argv`, as the client may start the server in a + bool startswith_min = strlen(arg) > 0 && arg[0] == '-'; + bool startswith_minmin = strlen(arg) > 1 && arg[0] == '-' && arg[1] == '-'; + if (skipping_minc && (startswith_min || startswith_minmin)) { + skipping_minc = false; + } + if (!had_minmin && !skipping_minc && strequal(arg, "-c")) { + if (!first_minc) { + skipping_minc = true; + continue; + } + first_minc = false; + } + // Exclude --embed/--headless/-c <cmd> from `argv`, as the client may start the server in a // different way than how the server was originally started. + // Eg: 'nvim -c foo -c bar --embed --headless -- example.txt' would be parsed as { 'nvim', '-c', 'foo', '--', 'example.txt' }. if (argv.size == 0 || had_minmin - || (!strequal(arg, "--embed") && !strequal(arg, "--headless"))) { + || (!strequal(arg, "--embed") && !strequal(arg, "--headless") && !skipping_minc)) { ADD_C(argv, CSTR_AS_OBJ(arg)); } }); diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua @@ -2248,7 +2248,7 @@ M.cmds = { }, { command = 'restart', - flags = bit.bor(CMDARG, TRLBAR), + flags = bit.bor(CMDARG, EXTRA, NOTRLCOM), addr_type = 'ADDR_NONE', func = 'ex_restart', }, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c @@ -4852,8 +4852,30 @@ static void ex_quitall(exarg_T *eap) /// ":restart": restart the Nvim server (using ":qall!"). /// ":restart +cmd": restart the Nvim server using ":cmd". +/// ":restart +cmd <command>": restart the Nvim server using ":cmd" and add -c <command> to the new server. static void ex_restart(exarg_T *eap) { + // Patch v:argv to include "-c <arg>" when it restarts. + if (eap->arg != NULL) { + const list_T *l = get_vim_var_list(VV_ARGV); + int argc = tv_list_len(l); + list_T *argv_cpy = tv_list_alloc(argc + 2); + bool added_startup_arg = false; + TV_LIST_ITER_CONST(l, li, { + const char *arg = tv_get_string(TV_LIST_ITEM_TV(li)); + size_t arg_size = strlen(arg); + assert(arg_size <= (size_t)SSIZE_MAX); + tv_list_append_string(argv_cpy, arg, (ssize_t)arg_size); + if (!added_startup_arg) { + tv_list_append_string(argv_cpy, "-c", 2); + size_t cmd_size = strlen(eap->arg); + assert(cmd_size <= (size_t)SSIZE_MAX); + tv_list_append_string(argv_cpy, eap->arg, (ssize_t)cmd_size); + added_startup_arg = true; + } + }); + set_vim_var_list(VV_ARGV, argv_cpy); + } 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)) { diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua @@ -255,8 +255,37 @@ describe('TUI :restart', function() tt.feed_data(':1restart\013') screen:expect({ any = vim.pesc('{101:E481: No range allowed}') }) - tt.feed_data(':restart foo\013') - screen:expect({ any = vim.pesc('{101:E488: Trailing characters: foo}') }) + local s1 = [[ + | + | + {2: }| + {MATCH:%d+ +}| + Hello | + {102:Press ENTER or type command to continue}^ | + {5:-- TERMINAL --} | + ]] + + -- Check trailing characters are considered in -c + tt.feed_data(':restart echo "Hello"\013') + screen_expect(s1) + tt.feed_data('\013') + restart_pid_check() + gui_running_check() + + -- Check trailing characters after +cmd are considered in -c + tt.feed_data(':restart +qall echo "Hello" | echo "World"\013') + screen_expect([[ + | + {2: }| + {MATCH:%d+ +}| + Hello | + World | + {102:Press ENTER or type command to continue}^ | + {5:-- TERMINAL --} | + ]]) + tt.feed_data('\013') + restart_pid_check() + gui_running_check() -- Check ":restart" on an unmodified buffer. tt.feed_data(':restart\013') @@ -290,6 +319,17 @@ describe('TUI :restart', function() -- Cancel the operation (abandons restart). tt.feed_data('C\013') + -- Check ":confirm restart <cmd>" on a modified buffer. + tt.feed_data(':confirm restart echo "Hello"\013') + screen:expect({ any = vim.pesc('Save changes to "Untitled"?') }) + tt.feed_data('N\013') + + -- Check if the -c <cmd> runs after restart. + screen_expect(s1) + tt.feed_data('\013') + restart_pid_check() + gui_running_check() + -- Check ":restart" on the modified buffer. tt.feed_data(':restart\013') screen_expect(s0)