summaryrefslogtreecommitdiffstatshomepage
path: root/runtime/lua
diff options
context:
space:
mode:
authorSanzhar Kuandyk <92693103+SanzharKuandyk@users.noreply.github.com>2026-04-21 21:49:16 +0500
committerGitHub <noreply@github.com>2026-04-21 12:49:16 -0400
commit5891f2f3dc41eda44c0072d726cf95e54aba85ad (patch)
treeea03f3d86655c9b24a1226ce305e62cbcadb26d5 /runtime/lua
parentd99e01ca8064c7785702519f3192b8191386f717 (diff)
fix(:restart): reuse --listen addr on Windows #38539
Problem: On Windows, :restart cannot immediately reuse the canonical --listen address because named pipe release is asynchronous. Solution: Start the new Nvim server on a temporary address; in the new Nvim, retry serverstart() with the original ("canonical") address until it succeeds.
Diffstat (limited to 'runtime/lua')
-rw-r--r--runtime/lua/vim/_core/server.lua53
1 files changed, 53 insertions, 0 deletions
diff --git a/runtime/lua/vim/_core/server.lua b/runtime/lua/vim/_core/server.lua
index 54d8d104f0..a79f871cd6 100644
--- a/runtime/lua/vim/_core/server.lua
+++ b/runtime/lua/vim/_core/server.lua
@@ -38,4 +38,57 @@ function M.serverlist(opts, addrs)
return addrs
end
+-- (Windows only) Canonical --listen address persisted across restarts.
+M.restart_canonical_addr = nil ---@type string?
+
+--- (Windows only)
+--- Called on the new server via nvim_exec_lua RPC from the old server (:restart).
+--- Windows named pipes can't be rebound immediately, so the new server starts on a
+--- temporary bootstrap address and polls until the canonical address is reclaimable.
+--- @param canonical_addr string The original --listen address to reclaim.
+--- @param bootstrap_addr string Temporary address the new server started on.
+--- @param expected_uis integer Number of UIs expected to reattach (0 = don't wait).
+function M.rebind_old_addr_after_restart(canonical_addr, bootstrap_addr, expected_uis)
+ M.restart_canonical_addr = canonical_addr
+ local poll_ms = 50
+ local max_wait_ms = 30000
+ local timer = assert(vim.uv.new_timer())
+
+ -- Poll until the canonical address can be reclaimed (or timeout).
+ local poll_elapsed = 0
+ timer:start(poll_ms, poll_ms, function()
+ vim.schedule(function()
+ poll_elapsed = poll_elapsed + poll_ms
+ if poll_elapsed >= max_wait_ms then
+ timer:stop()
+ timer:close()
+ return
+ end
+ if not vim.list_contains(vim.fn.serverlist(), canonical_addr) then
+ local ok = pcall(vim.fn.serverstart, canonical_addr)
+ if not ok then
+ return -- pipe still held by old server; retry next tick
+ end
+ end
+
+ -- Wait for UIs to reattach, then retire the bootstrap address.
+ local elapsed = 0
+ timer:stop()
+ timer:start(poll_ms, poll_ms, function()
+ vim.schedule(function()
+ elapsed = elapsed + poll_ms
+ local all_uis = expected_uis <= 0 or #vim.api.nvim_list_uis() >= expected_uis
+ if all_uis or elapsed >= max_wait_ms then
+ if canonical_addr ~= bootstrap_addr then
+ pcall(vim.fn.serverstop, bootstrap_addr)
+ end
+ timer:stop()
+ timer:close()
+ end
+ end)
+ end)
+ end)
+ end)
+end
+
return M