Merge #38967 ZR performs :restart

This commit is contained in:
Justin M. Keyes
2026-04-21 11:50:49 -04:00
committed by GitHub
8 changed files with 92 additions and 12 deletions

View File

@@ -1208,6 +1208,10 @@ ZZ Write current file, if modified, and close the current
*ZQ*
ZQ Quit without checking for changes (same as ":q!").
*ZR*
[count]ZR Performs |:restart|. If a [count] is given, restarts
without checking for changes (":restart +qall!").
MULTIPLE WINDOWS AND BUFFERS *window-exit*
*:qa* *:qall*

View File

@@ -69,7 +69,7 @@ Restart Nvim
*:restart*
:restart [+cmd] [command]
Restarts Nvim.
Restarts Nvim. See also |ZR|.
1. Stops Nvim using `:qall` (or |+cmd|, if given).
2. Starts a new Nvim server using the same |v:argv| (except

View File

@@ -125,6 +125,7 @@ EDITOR
• |gf| and |<cfile>| support `file://…` URIs.
• |:log| opens log files.
• |ZR| restarts Nvim (|:restart|).
EVENTS

View File

@@ -388,6 +388,7 @@ Input/Mappings:
Normal commands:
- |gO| shows a filetype-defined "outline" of the current buffer.
- |Q| replays the last recorded macro instead of switching to Ex mode (|gQ|).
- |ZR| performs |:restart|
Options:

View File

@@ -295,17 +295,17 @@ do
)
end
--- Execute a command and print errors without a stacktrace.
--- @param opts vim.api.keyset.cmd Arguments to |nvim_cmd()|
local function cmd(opts)
local ok, err = pcall(vim.api.nvim_cmd, opts, {})
if not ok then
vim.api.nvim_echo({ { err:sub(#'Vim:' + 1) } }, true, { err = true })
end
end
--- vim-unimpaired style mappings. See: https://github.com/tpope/vim-unimpaired
do
--- Execute a command and print errors without a stacktrace.
--- @param opts table Arguments to |nvim_cmd()|
local function cmd(opts)
local ok, err = pcall(vim.api.nvim_cmd, opts, {})
if not ok then
vim.api.nvim_echo({ { err:sub(#'Vim:' + 1) } }, true, { err = true })
end
end
-- Quickfix mappings
vim.keymap.set('n', '[q', function()
cmd({ cmd = 'cprevious', count = vim.v.count1 })

View File

@@ -3299,6 +3299,15 @@ static void nv_Zet(cmdarg_T *cap)
do_cmdline_cmd("q!");
break;
// "ZR": restart. With count, restart without checking for changes.
case 'R':
if (cap->count0 >= 1) {
do_cmdline_cmd("restart +qall!");
} else {
do_cmdline_cmd("restart");
}
break;
default:
clearopbeep(cap->oap);
}

View File

@@ -122,7 +122,9 @@ function SocketStream:read_stop()
end
function SocketStream:close()
uv.close(self._socket)
if not self._socket:is_closing() then
uv.close(self._socket)
end
end
--- Stream over child process stdio.

View File

@@ -10,7 +10,6 @@ local Screen = require('test.functional.ui.screen')
local tt = require('test.functional.testterm')
local eq = t.eq
local pcall_err = t.pcall_err
local feed_data = tt.feed_data
local clear = n.clear
local command = n.command
@@ -208,10 +207,74 @@ describe('TUI :restart', function()
before_each(n.clear)
after_each(n.check_close)
---@param exp boolean Restart expected
---@param sess table Session
---@param addr string
local function assert_restarted(exp, sess, addr)
local s = sess
retry(nil, 5000, function()
if exp then
s:close()
s = n.connect(addr)
end
-- Cheesy but reliable: :restart drops "-- [files…]", so empty v:argf means restart happened.
-- TODO(justinmk): add `v:startreason`, `v:starttime`
local _, argf = s:request('nvim_eval', 'v:argf')
ok(vim.tbl_count(argf) == (exp and 0 or 1), exp and 'empty v:argf' or 'nonempty v:argf', argf)
end)
end
it('validation', function()
eq('Vim(restart):E481: No range allowed: :1restart', t.pcall_err(n.command, ':1restart'))
end)
it('ZR', function()
-- Just exercise ZR, don't need to test all :restart functionality here.
t.skip(is_os('win'), 'FIXME: --listen not preserved by :restart on Windows #38539')
local server_pipe = new_pipename()
local server_session
finally(function()
if server_session and not server_session.closed then
server_session:close()
end
end)
local screen = tt.setup_child_nvim({
'-u',
'NONE',
'-i',
'NONE',
'--listen',
server_pipe,
'--cmd',
'set notermguicolors',
'--',
'file1.txt', -- XXX: see comment in assert_restarted()
}, {
env = vim.tbl_extend('force', env_notermguicolors, {
-- Ignore logs, because assert_restarted may log "connection refused" while it retries.
NVIM_LOG_FILE = testlog,
}),
})
finally(function()
os.remove(testlog)
end)
screen:expect({ any = 'file1%.txt' })
server_session = n.connect(server_pipe)
assert_restarted(false, server_session, server_pipe)
-- ZR on modified buffer fails with E37.
tt.feed_data('ifoo\027')
tt.feed_data('ZR')
screen:expect({ any = 'E37' })
-- [count]ZR discards unsaved changes.
tt.feed_data('1ZR')
assert_restarted(true, server_session, server_pipe)
end)
it('works', function()
local server_pipe = new_pipename()
local screen = tt.setup_child_nvim({