feat(lsp): start/stop LSPs as necessary during vim.lsp.enable() #33702

Problem:
enable() could be more flexible, so that it works even if called "late".

Solution:
- enable(true) calls `doautoall nvim.lsp.enable FileType`.
- enable(false) calls `client:stop()` on matching clients.

This will be useful for e.g. :LspStop/:LspStart also.

(cherry picked from commit 4bc7bac884)
This commit is contained in:
Jeremy Fleischman
2025-04-30 16:57:29 -07:00
committed by github-actions[bot]
parent 560c6ca947
commit c4b9bdbdf4
2 changed files with 82 additions and 10 deletions

View File

@@ -585,13 +585,12 @@ function lsp.enable(name, enable)
end end
if not next(lsp._enabled_configs) then if not next(lsp._enabled_configs) then
-- If there are no remaining LSPs enabled, remove the enable autocmd.
if lsp_enable_autocmd_id then if lsp_enable_autocmd_id then
api.nvim_del_autocmd(lsp_enable_autocmd_id) api.nvim_del_autocmd(lsp_enable_autocmd_id)
lsp_enable_autocmd_id = nil lsp_enable_autocmd_id = nil
end end
return else
end
-- Only ever create autocmd once to reuse computation of config merging. -- Only ever create autocmd once to reuse computation of config merging.
lsp_enable_autocmd_id = lsp_enable_autocmd_id lsp_enable_autocmd_id = lsp_enable_autocmd_id
or api.nvim_create_autocmd('FileType', { or api.nvim_create_autocmd('FileType', {
@@ -602,6 +601,18 @@ function lsp.enable(name, enable)
}) })
end end
-- Ensure any pre-existing buffers start/stop their LSP clients.
if enable ~= false then
vim.api.nvim_command('doautoall nvim.lsp.enable FileType')
else
for _, nm in ipairs(names) do
for _, client in ipairs(lsp.get_clients({ name = nm })) do
client:stop()
end
end
end
end
--- @class vim.lsp.start.Opts --- @class vim.lsp.start.Opts
--- @inlinedoc --- @inlinedoc
--- ---

View File

@@ -6324,7 +6324,7 @@ describe('LSP', function()
) )
end) end)
it('attaches to buffers', function() it('attaches to buffers when they are opened', function()
exec_lua(create_server_definition) exec_lua(create_server_definition)
local tmp1 = t.tmpname(true) local tmp1 = t.tmpname(true)
@@ -6373,6 +6373,67 @@ describe('LSP', function()
) )
end) end)
it('attaches/detaches preexisting buffers', function()
exec_lua(create_server_definition)
local tmp1 = t.tmpname(true)
local tmp2 = t.tmpname(true)
exec_lua(function()
vim.cmd.edit(tmp1)
vim.bo.filetype = 'foo'
_G.foo_buf = vim.api.nvim_get_current_buf()
vim.cmd.edit(tmp2)
vim.bo.filetype = 'bar'
_G.bar_buf = vim.api.nvim_get_current_buf()
local server = _G._create_server({
handlers = {
initialize = function(_, _, callback)
callback(nil, { capabilities = {} })
end,
},
})
vim.lsp.config('foo', {
cmd = server.cmd,
filetypes = { 'foo' },
root_markers = { '.foorc' },
})
vim.lsp.config('bar', {
cmd = server.cmd,
filetypes = { 'bar' },
root_markers = { '.foorc' },
})
vim.lsp.enable('foo')
vim.lsp.enable('bar')
end)
eq(
{ 1, 'foo', 1, 'bar' },
exec_lua(function()
local foos = vim.lsp.get_clients({ bufnr = assert(_G.foo_buf) })
local bars = vim.lsp.get_clients({ bufnr = assert(_G.bar_buf) })
return { #foos, foos[1].name, #bars, bars[1].name }
end)
)
-- Now disable the 'foo' lsp and confirm that it's detached from the buffer it was previous
-- attached to.
exec_lua([[vim.lsp.enable('foo', false)]])
eq(
{ 0, 'foo', 1, 'bar' },
exec_lua(function()
local foos = vim.lsp.get_clients({ bufnr = assert(_G.foo_buf) })
local bars = vim.lsp.get_clients({ bufnr = assert(_G.bar_buf) })
return { #foos, 'foo', #bars, bars[1].name }
end)
)
end)
it('does not attach to buffers more than once if no root_dir', function() it('does not attach to buffers more than once if no root_dir', function()
exec_lua(create_server_definition) exec_lua(create_server_definition)