mirror of
https://github.com/neovim/neovim
synced 2025-07-15 16:51:49 +00:00
fix(lsp): handle non-existent configs in lsp.config/enable
This commit is contained in:
committed by
Lewis Russell
parent
c63e794b10
commit
ed07167261
@ -374,26 +374,31 @@ lsp.config = setmetatable({ _configs = {} }, {
|
||||
validate('name', name, 'string')
|
||||
|
||||
local rconfig = lsp._enabled_configs[name] or {}
|
||||
self._configs[name] = self._configs[name] or {}
|
||||
|
||||
if not rconfig.resolved_config then
|
||||
-- Resolve configs from lsp/*.lua
|
||||
-- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites.
|
||||
local rtp_config = {} ---@type vim.lsp.Config
|
||||
local rtp_config --- @type vim.lsp.Config?
|
||||
for _, v in ipairs(api.nvim_get_runtime_file(('lsp/%s.lua'):format(name), true)) do
|
||||
local config = assert(loadfile(v))() ---@type any?
|
||||
if type(config) == 'table' then
|
||||
rtp_config = vim.tbl_deep_extend('force', rtp_config, config)
|
||||
--- @type vim.lsp.Config?
|
||||
rtp_config = vim.tbl_deep_extend('force', rtp_config or {}, config)
|
||||
else
|
||||
log.warn(string.format('%s does not return a table, ignoring', v))
|
||||
end
|
||||
end
|
||||
|
||||
if not rtp_config and not self._configs[name] then
|
||||
log.warn(string.format('%s does not have a configuration', name))
|
||||
return
|
||||
end
|
||||
|
||||
rconfig.resolved_config = vim.tbl_deep_extend(
|
||||
'force',
|
||||
lsp.config._configs['*'] or {},
|
||||
rtp_config,
|
||||
lsp.config._configs[name] or {}
|
||||
rtp_config or {},
|
||||
self._configs[name] or {}
|
||||
)
|
||||
rconfig.resolved_config.name = name
|
||||
end
|
||||
@ -424,6 +429,50 @@ lsp.config = setmetatable({ _configs = {} }, {
|
||||
|
||||
local lsp_enable_autocmd_id --- @type integer?
|
||||
|
||||
local function validate_cmd(v)
|
||||
if type(v) == 'table' then
|
||||
if vim.fn.executable(v[1]) == 0 then
|
||||
return false, v[1] .. ' is not executable'
|
||||
end
|
||||
return true
|
||||
end
|
||||
return type(v) == 'function'
|
||||
end
|
||||
|
||||
--- @param config vim.lsp.Config
|
||||
local function validate_config(config)
|
||||
validate('cmd', config.cmd, validate_cmd, 'expected function or table with executable command')
|
||||
validate('reuse_client', config.reuse_client, 'function', true)
|
||||
validate('filetypes', config.filetypes, 'table', true)
|
||||
end
|
||||
|
||||
--- @param bufnr integer
|
||||
--- @param name string
|
||||
--- @param config vim.lsp.Config
|
||||
local function can_start(bufnr, name, config)
|
||||
local config_ok, err = pcall(validate_config, config)
|
||||
if not config_ok then
|
||||
log.error(('cannot start %s due to config error: %s'):format(name, err))
|
||||
return false
|
||||
end
|
||||
|
||||
if config.filetypes and not vim.tbl_contains(config.filetypes, vim.bo[bufnr].filetype) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- @param bufnr integer
|
||||
--- @param config vim.lsp.Config
|
||||
local function start_config(bufnr, config)
|
||||
return vim.lsp.start(config, {
|
||||
bufnr = bufnr,
|
||||
reuse_client = config.reuse_client,
|
||||
_root_markers = config.root_markers,
|
||||
})
|
||||
end
|
||||
|
||||
--- @param bufnr integer
|
||||
local function lsp_enable_callback(bufnr)
|
||||
-- Only ever attach to buffers that represent an actual file.
|
||||
@ -431,32 +480,9 @@ local function lsp_enable_callback(bufnr)
|
||||
return
|
||||
end
|
||||
|
||||
--- @param config vim.lsp.Config
|
||||
local function can_start(config)
|
||||
if config.filetypes and not vim.tbl_contains(config.filetypes, vim.bo[bufnr].filetype) then
|
||||
return false
|
||||
elseif type(config.cmd) == 'table' and vim.fn.executable(config.cmd[1]) == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--- @param config vim.lsp.Config
|
||||
local function start(config)
|
||||
return vim.lsp.start(config, {
|
||||
bufnr = bufnr,
|
||||
reuse_client = config.reuse_client,
|
||||
_root_markers = config.root_markers,
|
||||
})
|
||||
end
|
||||
|
||||
for name in vim.spairs(lsp._enabled_configs) do
|
||||
local config = lsp.config[name]
|
||||
validate('cmd', config.cmd, { 'function', 'table' })
|
||||
validate('cmd', config.reuse_client, 'function', true)
|
||||
|
||||
if can_start(config) then
|
||||
if config and can_start(bufnr, name, config) then
|
||||
-- Deepcopy config so changes done in the client
|
||||
-- do not propagate back to the enabled configs.
|
||||
config = vim.deepcopy(config)
|
||||
@ -466,11 +492,11 @@ local function lsp_enable_callback(bufnr)
|
||||
config.root_dir(bufnr, function(root_dir)
|
||||
config.root_dir = root_dir
|
||||
vim.schedule(function()
|
||||
start(config)
|
||||
start_config(bufnr, config)
|
||||
end)
|
||||
end)
|
||||
else
|
||||
start(config)
|
||||
start_config(bufnr, config)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -45,6 +45,11 @@ function log.get_filename()
|
||||
return logfilename
|
||||
end
|
||||
|
||||
--- @param s string
|
||||
function log._set_filename(s)
|
||||
logfilename = s
|
||||
end
|
||||
|
||||
--- @type file*?, string?
|
||||
local logfile, openerr
|
||||
|
||||
|
@ -6370,5 +6370,35 @@ describe('LSP', function()
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
it('validates config on attach', function()
|
||||
local tmp1 = t.tmpname(true)
|
||||
exec_lua(function()
|
||||
vim.lsp.log._set_filename(fake_lsp_logfile)
|
||||
end)
|
||||
|
||||
local function test_cfg(cfg, err)
|
||||
exec_lua(function()
|
||||
vim.lsp.config['foo'] = {}
|
||||
vim.lsp.config('foo', cfg)
|
||||
vim.lsp.enable('foo')
|
||||
vim.cmd.edit(assert(tmp1))
|
||||
vim.bo.filetype = 'foo'
|
||||
end)
|
||||
|
||||
retry(nil, 1000, function()
|
||||
assert_log(err, fake_lsp_logfile)
|
||||
end)
|
||||
end
|
||||
|
||||
test_cfg({
|
||||
cmd = { 'lolling' },
|
||||
}, 'cannot start foo due to config error: .* lolling is not executable')
|
||||
|
||||
test_cfg({
|
||||
cmd = { 'cat' },
|
||||
filetypes = true,
|
||||
}, 'cannot start foo due to config error: .* filetypes: expected table, got boolean')
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
Reference in New Issue
Block a user