diff options
| author | Sanzhar Kuandyk <92693103+SanzharKuandyk@users.noreply.github.com> | 2026-04-21 21:49:16 +0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-21 12:49:16 -0400 |
| commit | 5891f2f3dc41eda44c0072d726cf95e54aba85ad (patch) | |
| tree | ea03f3d86655c9b24a1226ce305e62cbcadb26d5 /runtime/lua | |
| parent | d99e01ca8064c7785702519f3192b8191386f717 (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.lua | 53 |
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 |
