mirror of
https://github.com/neovim/neovim.git
synced 2026-03-31 12:52:13 +00:00
fix(lsp): :lsp restart restarts on client exit #37125
Problem: `:lsp restart` detects when a client has exited by using the `LspDetach` autocommand. This works correctly in common cases, but breaks when restarting a client which is not attached to any buffer. It also breaks if a client is detached in between `:lsp restart` and the actual stopping of the client. Solution: Move restart logic into `vim/lsp/client.lua`, so it can hook in to `_on_exit()`. The public `on_exit` callback cannot be used for this, as `:lsp restart` needs to ensure the restart only happens once, even if the command is run multiple times on the same client.
This commit is contained in:
@@ -123,7 +123,7 @@ COMMANDS *:lsp* *lsp-commands*
|
||||
Activates LSP for current and future buffers. See |vim.lsp.enable()|.
|
||||
|
||||
:lsp disable [config_name] *:lsp-disable*
|
||||
Disables (and stops) LSP for current and future buffers. See
|
||||
Disables LSP (and stops if running) for current and future buffers. See
|
||||
|vim.lsp.enable()|.
|
||||
|
||||
:lsp restart [client_name] *:lsp-restart*
|
||||
|
||||
@@ -122,7 +122,7 @@ end
|
||||
--- @param client_names string[]
|
||||
--- @return vim.lsp.Client[]
|
||||
local function get_clients_from_names(client_names)
|
||||
-- Default to stopping all active clients attached to the current buffer.
|
||||
-- Default to all active clients attached to the current buffer.
|
||||
if #client_names == 0 then
|
||||
local clients = lsp.get_clients { bufnr = api.nvim_get_current_buf() }
|
||||
if #clients == 0 then
|
||||
@@ -149,29 +149,7 @@ local function ex_lsp_restart(client_names)
|
||||
local clients = get_clients_from_names(client_names)
|
||||
|
||||
for _, client in ipairs(clients) do
|
||||
--- @type integer[]
|
||||
local attached_buffers = vim.tbl_keys(client.attached_buffers)
|
||||
|
||||
-- Reattach new client once the old one exits
|
||||
api.nvim_create_autocmd('LspDetach', {
|
||||
group = api.nvim_create_augroup('nvim.lsp.ex_restart_' .. client.id, {}),
|
||||
callback = function(info)
|
||||
if info.data.client_id ~= client.id then
|
||||
return
|
||||
end
|
||||
|
||||
local new_client_id = lsp.start(client.config, { attach = false })
|
||||
if new_client_id then
|
||||
for _, buffer in ipairs(attached_buffers) do
|
||||
lsp.buf_attach_client(buffer, new_client_id)
|
||||
end
|
||||
end
|
||||
|
||||
return true -- Delete autocmd
|
||||
end,
|
||||
})
|
||||
|
||||
client:stop(client.exit_timeout)
|
||||
client:_restart(client.exit_timeout)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -908,6 +908,31 @@ function Client:stop(force)
|
||||
end)
|
||||
end
|
||||
|
||||
--- Stops a client, then starts a new client with the same config and attached
|
||||
--- buffers.
|
||||
---
|
||||
--- @param force? integer|boolean See [Client:stop()] for details.
|
||||
--- (default: `self.exit_timeout`)
|
||||
function Client:_restart(force)
|
||||
validate('force', force, { 'number', 'boolean' }, true)
|
||||
|
||||
self._handle_restart = function()
|
||||
--- @type integer[]
|
||||
local attached_buffers = vim.tbl_keys(self.attached_buffers)
|
||||
|
||||
vim.schedule(function()
|
||||
local new_client_id = lsp.start(self.config, { attach = false })
|
||||
if new_client_id then
|
||||
for _, buffer in ipairs(attached_buffers) do
|
||||
lsp.buf_attach_client(buffer, new_client_id)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
self:stop(force)
|
||||
end
|
||||
|
||||
--- Get options for a method that is registered dynamically.
|
||||
--- @param method vim.lsp.protocol.Method | vim.lsp.protocol.Method.Registration
|
||||
function Client:_supports_registration(method)
|
||||
@@ -1334,6 +1359,11 @@ function Client:_on_exit(code, signal)
|
||||
end
|
||||
end)
|
||||
|
||||
if self._handle_restart ~= nil then
|
||||
self._handle_restart()
|
||||
self._handle_restart = nil
|
||||
end
|
||||
|
||||
-- Schedule the deletion of the client object so that it exists in the execution of LspDetach
|
||||
-- autocommands
|
||||
vim.schedule(function()
|
||||
|
||||
@@ -62,18 +62,22 @@ describe(':lsp', function()
|
||||
end)
|
||||
|
||||
it('restart' .. test_message_suffix, function()
|
||||
local ids_differ = exec_lua(function()
|
||||
--- @type boolean, integer?
|
||||
local ids_differ, attached_buffer_count = exec_lua(function()
|
||||
vim.lsp.enable('dummy')
|
||||
local old_id = vim.lsp.get_clients()[1].id
|
||||
|
||||
vim.cmd('lsp restart' .. lsp_command_suffix)
|
||||
vim.wait(1000, function()
|
||||
return old_id ~= vim.lsp.get_clients()[1].id
|
||||
return vim.wait(1000, function()
|
||||
local new_client = vim.lsp.get_clients()[1]
|
||||
if new_client == nil then
|
||||
return false
|
||||
end
|
||||
return old_id ~= new_client.id, #new_client.attached_buffers
|
||||
end)
|
||||
local new_id = vim.lsp.get_clients()[1].id
|
||||
return old_id ~= new_id
|
||||
end)
|
||||
eq(true, ids_differ)
|
||||
eq(1, attached_buffer_count)
|
||||
end)
|
||||
|
||||
it('stop' .. test_message_suffix, function()
|
||||
|
||||
Reference in New Issue
Block a user