diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index a641e78510..f378e6c18c 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -148,7 +148,7 @@ end --- @param config vim.lsp.ClientConfig --- @return boolean 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 end diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 1f505f53f3..a7abe38ecb 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -387,6 +387,7 @@ function Client.create(config) capabilities = config.capabilities, workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir), root_dir = config.root_dir, + _is_stopping = false, _before_init_cb = config.before_init, _on_init_cbs = vim._ensure_list(config.on_init), _on_exit_cbs = vim._ensure_list(config.on_exit), @@ -804,12 +805,13 @@ end --- --- @param force? boolean function Client:stop(force) - local rpc = self.rpc - - if rpc.is_closing() then + if self:is_stopped() then return end + self._is_stopping = true + local rpc = self.rpc + vim.lsp._watchfiles.cancel(self.id) 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 --- stopped; false otherwise function Client:is_stopped() - return self.rpc.is_closing() + return self.rpc.is_closing() or self._is_stopping end --- Execute a lsp command, either via client command function (if available) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 3a2804ccaf..334a3c5961 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -218,6 +218,25 @@ describe('LSP', function() ) 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) describe('basic_init test', function()