commit 4bc7bac8842582cc1239373994327d5537155ec0
parent 71f3a9c5900fa1882a77f2300baac462a5bf480f
Author: Jeremy Fleischman <jeremyfleischman@gmail.com>
Date: Wed, 30 Apr 2025 16:57:29 -0700
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.
Diffstat:
2 files changed, 82 insertions(+), 10 deletions(-)
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
@@ -605,21 +605,32 @@ function lsp.enable(name, enable)
end
if not next(lsp._enabled_configs) then
+ -- If there are no remaining LSPs enabled, remove the enable autocmd.
if lsp_enable_autocmd_id then
api.nvim_del_autocmd(lsp_enable_autocmd_id)
lsp_enable_autocmd_id = nil
end
- return
+ else
+ -- Only ever create autocmd once to reuse computation of config merging.
+ lsp_enable_autocmd_id = lsp_enable_autocmd_id
+ or api.nvim_create_autocmd('FileType', {
+ group = api.nvim_create_augroup('nvim.lsp.enable', {}),
+ callback = function(args)
+ lsp_enable_callback(args.buf)
+ end,
+ })
end
- -- Only ever create autocmd once to reuse computation of config merging.
- lsp_enable_autocmd_id = lsp_enable_autocmd_id
- or api.nvim_create_autocmd('FileType', {
- group = api.nvim_create_augroup('nvim.lsp.enable', {}),
- callback = function(args)
- lsp_enable_callback(args.buf)
- 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
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
@@ -6324,7 +6324,7 @@ describe('LSP', function()
)
end)
- it('attaches to buffers', function()
+ it('attaches to buffers when they are opened', function()
exec_lua(create_server_definition)
local tmp1 = t.tmpname(true)
@@ -6373,6 +6373,67 @@ describe('LSP', function()
)
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()
exec_lua(create_server_definition)