mirror of
https://github.com/dense-analysis/ale.git
synced 2026-09-30 23:52:22 +08:00
- A stale or cancelled textDocument/diagnostic request can be answered with a nil result and no error, which is valid per the LSP specification. Guard against it so the handler does nothing rather than indexing the nil result.
217 lines
6.7 KiB
Lua
217 lines
6.7 KiB
Lua
local module = {}
|
|
|
|
local function remove_invalid_lsp_table_keys(value, seen)
|
|
if type(value) ~= "table" then
|
|
return value
|
|
end
|
|
|
|
seen = seen or {}
|
|
|
|
if seen[value] then
|
|
return value
|
|
end
|
|
|
|
seen[value] = true
|
|
|
|
for key, child in pairs(value) do
|
|
local key_type = type(key)
|
|
|
|
if key_type ~= "number" and key_type ~= "string" then
|
|
value[key] = nil
|
|
else
|
|
remove_invalid_lsp_table_keys(child, seen)
|
|
end
|
|
end
|
|
|
|
return value
|
|
end
|
|
|
|
module.start = function(config)
|
|
-- Neovim's luaeval sometimes adds a Boolean key to table we need to remove.
|
|
remove_invalid_lsp_table_keys(config.init_options)
|
|
|
|
-- ensure init_options uses empty_dict if empty
|
|
if type(config.init_options) == "table"
|
|
and next(config.init_options) == nil
|
|
and getmetatable(config.init_options) == nil
|
|
then
|
|
config.init_options = vim.empty_dict()
|
|
end
|
|
|
|
-- If configuring LSP via a socket connection, then generate the cmd
|
|
-- using vim.lsp.rpc.connect(), as defined in Neovim documentation.
|
|
if config.host then
|
|
local cmd_func = vim.lsp.rpc.connect(config.host, config.port)
|
|
config.host = nil
|
|
config.port = nil
|
|
|
|
-- Wrap the cmd function so we don't throw errors back to the user
|
|
-- if the connection to an address fails to start.
|
|
--
|
|
-- We will separately log in ALE that we failed to start a connection.
|
|
--
|
|
-- In older Neovim versions TCP connections do not function if supplied
|
|
-- a hostname instead of an address.
|
|
config.cmd = function(dispatch)
|
|
local success, result = pcall(cmd_func, dispatch)
|
|
|
|
if success then
|
|
return result
|
|
end
|
|
|
|
return nil
|
|
end
|
|
end
|
|
|
|
config.handlers = {
|
|
-- Override Neovim's handling of diagnostics to run through ALE's
|
|
-- functions so all of the functionality in ALE works.
|
|
["textDocument/publishDiagnostics"] = function(err, result, _, _)
|
|
if err == nil then
|
|
vim.fn["ale#lsp_linter#HandleLSPDiagnostics"](
|
|
config.name,
|
|
result.uri,
|
|
result.diagnostics
|
|
)
|
|
end
|
|
end,
|
|
-- Handle pull model diagnostic data.
|
|
["textDocument/diagnostic"] = function(err, result, request, _)
|
|
if err == nil and result ~= nil then
|
|
local diagnostics
|
|
|
|
if result.kind == "unchanged" then
|
|
diagnostics = "unchanged"
|
|
else
|
|
diagnostics = result.items
|
|
end
|
|
|
|
vim.fn["ale#lsp_linter#HandleLSPDiagnostics"](
|
|
config.name,
|
|
request.params.textDocument.uri,
|
|
diagnostics
|
|
)
|
|
end
|
|
end,
|
|
-- When the pull model is enabled we have to handle and return
|
|
-- some kind of data for a server diagnostic refresh request.
|
|
["workspace/diagnostic/refresh"] = function()
|
|
return {}
|
|
end,
|
|
}
|
|
|
|
config.on_init = function(client, _)
|
|
-- Tell ALE about server capabilities as soon as we can.
|
|
-- This will inform ALE commands what can be done with each server,
|
|
-- such as "go to definition" support, etc.
|
|
vim.fn["ale#lsp#UpdateCapabilities"](
|
|
config.name,
|
|
client.server_capabilities
|
|
)
|
|
|
|
-- Neovim calls `on_init` before marking a client as active, meaning
|
|
-- we can't get a client via get_client_by_id until after `on_init` is
|
|
-- called. By deferring execution of calling the init callbacks we
|
|
-- can only call them after the client becomes available, which
|
|
-- will make notifications for configuration changes work, etc.
|
|
vim.defer_fn(function()
|
|
vim.fn["ale#lsp#CallInitCallbacks"](config.name)
|
|
end, 0)
|
|
end
|
|
|
|
config.get_language_id = function(bufnr, _)
|
|
return vim.fn["ale#lsp#GetLanguage"](config.name, bufnr)
|
|
end
|
|
|
|
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
|
|
|
-- Language servers like Pyright do not enable the diagnostics pull model
|
|
-- unless dynamicRegistration is enabled for diagnostics.
|
|
if capabilities.textDocument.diagnostic ~= nil then
|
|
capabilities.textDocument.diagnostic.dynamicRegistration = true
|
|
config.capabilities = capabilities
|
|
end
|
|
|
|
---@diagnostic disable-next-line: missing-fields
|
|
return vim.lsp.start(config, {
|
|
attach = false,
|
|
silent = true,
|
|
})
|
|
end
|
|
|
|
module.buf_attach = function(args)
|
|
return vim.lsp.buf_attach_client(args.bufnr, args.client_id)
|
|
end
|
|
|
|
module.buf_detach = function(args)
|
|
return vim.lsp.buf_detach_client(args.bufnr, args.client_id)
|
|
end
|
|
|
|
-- Send a message to an LSP server.
|
|
-- Notifications do not need to be handled.
|
|
--
|
|
-- Returns -1 when a message is sent, but no response is expected
|
|
-- 0 when the message is not sent and
|
|
-- >= 1 with the message ID when a response is expected.
|
|
module.send_message = function(args)
|
|
local client = vim.lsp.get_client_by_id(args.client_id)
|
|
|
|
if client == nil then
|
|
return 0
|
|
end
|
|
|
|
local params = remove_invalid_lsp_table_keys(args.params)
|
|
|
|
if args.is_notification then
|
|
local success
|
|
|
|
if vim.version().minor >= 11 then
|
|
-- Supporting Neovim 0.11+
|
|
---@diagnostic disable-next-line
|
|
success = client.notify(client, args.method, params)
|
|
else
|
|
-- Supporting Neovim 0.10 and below
|
|
---@diagnostic disable-next-line
|
|
success = client.notify(args.method, params)
|
|
end
|
|
|
|
-- For notifications we send a request and expect no direct response.
|
|
if success then
|
|
return -1
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
local success, request_id
|
|
local handle_func = function(_, result, _, _)
|
|
vim.fn["ale#lsp#HandleResponse"](client.name, {
|
|
id = request_id,
|
|
result = result,
|
|
})
|
|
end
|
|
|
|
if vim.version().minor >= 11 then
|
|
-- Supporting Neovim 0.11+
|
|
|
|
-- We send a request and handle the response.
|
|
--
|
|
-- We set the bufnr to -1 to prevent Neovim from flushing anything, as ALE
|
|
-- already flushes changes to files before sending requests.
|
|
---@diagnostic disable-next-line
|
|
success, request_id = client.request(client, args.method, params, handle_func, -1)
|
|
else
|
|
-- Supporting Neovim 0.10 and below
|
|
---@diagnostic disable-next-line
|
|
success, request_id = client.request(args.method, params, handle_func, -1)
|
|
end
|
|
|
|
if success then
|
|
return request_id
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
return module
|