mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00
fix(lsp): check if client is stopping before reuse #33796
Problem: Stopping a language server and then calling vim.lsp.start() with the same name/root will return the old language server that's in the middle of shutting down. vim.lsp.start() won't return a new server until the old process has terminated. Solution: Introducing a client._is_stopping field that tracks the shutdown phase, preventing the client from being reused.
This commit is contained in:
@@ -148,7 +148,7 @@ end
|
|||||||
--- @param config vim.lsp.ClientConfig
|
--- @param config vim.lsp.ClientConfig
|
||||||
--- @return boolean
|
--- @return boolean
|
||||||
local function reuse_client_default(client, config)
|
local function reuse_client_default(client, config)
|
||||||
if client.name ~= config.name then
|
if client.name ~= config.name or client:is_stopped() then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -387,6 +387,7 @@ function Client.create(config)
|
|||||||
capabilities = config.capabilities,
|
capabilities = config.capabilities,
|
||||||
workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),
|
workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir),
|
||||||
root_dir = config.root_dir,
|
root_dir = config.root_dir,
|
||||||
|
_is_stopping = false,
|
||||||
_before_init_cb = config.before_init,
|
_before_init_cb = config.before_init,
|
||||||
_on_init_cbs = vim._ensure_list(config.on_init),
|
_on_init_cbs = vim._ensure_list(config.on_init),
|
||||||
_on_exit_cbs = vim._ensure_list(config.on_exit),
|
_on_exit_cbs = vim._ensure_list(config.on_exit),
|
||||||
@@ -804,12 +805,13 @@ end
|
|||||||
---
|
---
|
||||||
--- @param force? boolean
|
--- @param force? boolean
|
||||||
function Client:stop(force)
|
function Client:stop(force)
|
||||||
local rpc = self.rpc
|
if self:is_stopped() then
|
||||||
|
|
||||||
if rpc.is_closing() then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self._is_stopping = true
|
||||||
|
local rpc = self.rpc
|
||||||
|
|
||||||
vim.lsp._watchfiles.cancel(self.id)
|
vim.lsp._watchfiles.cancel(self.id)
|
||||||
|
|
||||||
if force or not self.initialized or self._graceful_shutdown_failed then
|
if force or not self.initialized or self._graceful_shutdown_failed then
|
||||||
@@ -936,7 +938,7 @@ end
|
|||||||
--- @return boolean # true if client is stopped or in the process of being
|
--- @return boolean # true if client is stopped or in the process of being
|
||||||
--- stopped; false otherwise
|
--- stopped; false otherwise
|
||||||
function Client:is_stopped()
|
function Client:is_stopped()
|
||||||
return self.rpc.is_closing()
|
return self.rpc.is_closing() or self._is_stopping
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Execute a lsp command, either via client command function (if available)
|
--- Execute a lsp command, either via client command function (if available)
|
||||||
|
@@ -218,6 +218,25 @@ describe('LSP', function()
|
|||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('does not reuse an already-stopping client #33616', function()
|
||||||
|
-- we immediately try to start a second client with the same name/root
|
||||||
|
-- before the first one has finished shutting down; we must get a new id.
|
||||||
|
local clients = exec_lua([[
|
||||||
|
local client1 = vim.lsp.start({
|
||||||
|
name = 'dup-test',
|
||||||
|
cmd = { vim.v.progpath, '-l', fake_lsp_code, 'basic_init' },
|
||||||
|
}, { attach = false })
|
||||||
|
vim.lsp.get_client_by_id(client1):stop()
|
||||||
|
local client2 = vim.lsp.start({
|
||||||
|
name = 'dup-test',
|
||||||
|
cmd = { vim.v.progpath, '-l', fake_lsp_code, 'basic_init' },
|
||||||
|
}, { attach = false })
|
||||||
|
return { client1, client2 }
|
||||||
|
]])
|
||||||
|
local c1, c2 = clients[1], clients[2]
|
||||||
|
eq(false, c1 == c2, 'Expected a fresh client while the old one is stopping')
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('basic_init test', function()
|
describe('basic_init test', function()
|
||||||
|
Reference in New Issue
Block a user