feat(lsp): vim.lsp.get_configs() #37237

Problem:
No way to iterate configs. Users need to reach
for `vim.lsp.config._configs`, an internal interface.

Solution:
Provide vim.lsp.get_configs().
Also indirectly improves :lsp enable/disable completion
by discarding invalid configs from completion.
This commit is contained in:
Olivia Kinnear
2026-03-19 06:33:34 -05:00
committed by GitHub
parent 4c48f19e51
commit e406c4efd6
5 changed files with 147 additions and 43 deletions

View File

@@ -1153,6 +1153,23 @@ get_clients({filter}) *vim.lsp.get_clients()*
Return: ~
(`vim.lsp.Client[]`) List of |vim.lsp.Client| objects
get_configs({filter}) *vim.lsp.get_configs()*
Get LSP configs.
Note: Will eagerly evaluate config files in `'runtimepath'` if necessary.
Parameters: ~
• {filter} (`table?`) Key-value pairs used to filter the returned
configs.
• {enabled}? (`boolean`) If true, only return enabled
configs. If false, only return configs that aren't
enabled.
• {filetype}? (`string`) Only return configs which attach to
the given filetype.
Return: ~
(`vim.lsp.Config[]`) List of |vim.lsp.Config| objects
is_enabled({name}) *vim.lsp.is_enabled()*
Checks if the given LSP config is enabled (globally, not per-buffer).

View File

@@ -330,6 +330,7 @@ LSP
• Support for `workspace/codeLens/refresh`:
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#codeLens_refresh
• |gx| opens `textDocument/documentLink` items found at cursor.
• |vim.lsp.get_configs()| can get all LSP configs matching certain conditions.
LUA

View File

@@ -19,45 +19,22 @@ local function get_client_names()
:totable()
end
--- @return string[]
local function get_config_names()
local config_names = vim
.iter(api.nvim_get_runtime_file('lsp/*.lua', true))
--- @param path string
:map(function(path)
local file_name = path:match('[^/]*.lua$')
return file_name:sub(0, #file_name - 4)
end)
:totable()
--- @diagnostic disable-next-line
vim.list_extend(config_names, vim.tbl_keys(lsp.config._configs))
return vim
.iter(config_names)
:unique()
--- @param name string
:filter(function(name)
return name ~= '*'
end)
:totable()
end
--- @param filter fun(string):boolean
--- @param filter vim.lsp.get_configs.Filter
--- @return fun():string[]
local function filtered_config_names(filter)
return function()
return vim.iter(get_config_names()):filter(filter):totable()
return vim
.iter(lsp.get_configs(filter))
:map(function(config)
return config.name
end)
:totable()
end
end
local complete_args = {
enable = filtered_config_names(function(name)
return not lsp.is_enabled(name)
end),
disable = filtered_config_names(function(name)
return lsp.is_enabled(name)
end),
enable = filtered_config_names { enabled = false },
disable = filtered_config_names { enabled = true },
restart = get_client_names,
stop = get_client_names,
}
@@ -79,17 +56,10 @@ local function ex_lsp_enable(config_names)
-- Default to enabling all clients matching the filetype of the current buffer.
if #config_names == 0 then
local filetype = vim.bo.filetype
for _, name in ipairs(get_config_names()) do
local success, result = pcall(function()
return lsp.config[name]
end)
if success then
local filetypes = result.filetypes
if filetypes == nil or vim.list_contains(filetypes, filetype) then
table.insert(config_names, name)
end
else
echo_err(result --[[@as string]])
for _, config in ipairs(lsp.get_configs()) do
local filetypes = config.filetypes
if filetypes == nil or vim.list_contains(filetypes, filetype) then
table.insert(config_names, config.name)
end
end
if #config_names == 0 then

View File

@@ -387,6 +387,76 @@ lsp.config = setmetatable({ _configs = {} }, {
end,
})
--- @return string[]
local function get_config_names()
local config_names = vim
.iter(api.nvim_get_runtime_file('lsp/*.lua', true))
--- @param path string
:map(function(path)
local file_name = path:match('[^/]*.lua$')
return file_name:sub(0, #file_name - 4)
end)
:totable()
vim.list_extend(config_names, vim.tbl_keys(lsp.config._configs))
return vim
.iter(config_names)
:unique()
--- @param name string
:filter(function(name)
return name ~= '*'
end)
:totable()
end
--- Key-value pairs used to filter the returned configs.
--- @class vim.lsp.get_configs.Filter
--- @inlinedoc
---
--- If true, only return enabled configs. If false, only return configs that
--- aren't enabled.
--- @field enabled? boolean
---
--- Only return configs which attach to the given filetype.
--- @field filetype? string
--- Get LSP configs.
---
--- Note: Will eagerly evaluate config files in `'runtimepath'` if necessary.
--- @param filter? vim.lsp.get_configs.Filter
--- @return vim.lsp.Config[]: List of |vim.lsp.Config| objects
function lsp.get_configs(filter)
validate('filter', filter, 'table', true)
filter = filter or {}
local configs = {} --- @type vim.lsp.Config[]
local config_names --- @type string[]
if not filter.enabled then
config_names = get_config_names()
else
-- Shortcut filtering enabled configs by directly getting enabled configs
config_names = vim.tbl_keys(lsp._enabled_configs)
end
for _, config_name in ipairs(config_names) do
local config = lsp.config[config_name]
if
config
and (filter.enabled ~= false or not lsp.is_enabled(config_name))
and (
filter.filetype == nil
or (config.filetypes ~= nil and vim.list_contains(config.filetypes, filter.filetype))
)
then
configs[#configs + 1] = config
end
end
return configs
end
local lsp_enable_autocmd_id --- @type integer?
local function validate_cmd(v)

View File

@@ -7114,6 +7114,52 @@ describe('LSP', function()
exec_lua([[vim.lsp.enable('foo', false)]])
eq(false, exec_lua([[return vim.lsp.is_enabled('foo')]]))
end)
it('vim.lsp.get_configs()', function()
exec_lua(function()
vim.lsp.config('foo', {
cmd = { 'foo' },
filetypes = { 'foofile' },
root_markers = { '.foorc' },
})
vim.lsp.config('bar', {
cmd = { 'bar' },
root_markers = { '.barrc' },
})
vim.lsp.enable('foo')
end)
local function names(configs)
local config_names = vim
.iter(configs)
:map(function(config)
return config.name
end)
:totable()
table.sort(config_names)
return config_names
end
-- With no filter, return all configs
eq({ 'bar', 'foo' }, names(exec_lua([[return vim.lsp.get_configs()]])))
-- Confirm `enabled` works
eq({ 'foo' }, names(exec_lua([[return vim.lsp.get_configs { enabled = true }]])))
eq({ 'bar' }, names(exec_lua([[return vim.lsp.get_configs { enabled = false }]])))
-- Confirm `filetype` works
eq({ 'foo' }, names(exec_lua([[return vim.lsp.get_configs { filetype = 'foofile' }]])))
-- Confirm filters combine
eq(
{ 'foo' },
names(exec_lua([[return vim.lsp.get_configs { filetype = 'foofile', enabled = true }]]))
)
eq(
{},
names(exec_lua([[return vim.lsp.get_configs { filetype = 'foofile', enabled = false }]]))
)
end)
end)
describe('vim.lsp.buf.workspace_diagnostics()', function()