local module = {} module.start = function(config) -- Neovim's luaeval sometimes adds a Boolean key to table we need to remove. if type(config.init_options) == "table" and config.init_options[true] ~= nil then config.init_options[true] = nil 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 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 if args.is_notification then -- For notifications we send a request and expect no direct response. local success = client.notify(args.method, args.params) if success then return -1 end return 0 end local success, request_id -- For request 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. success, request_id = client.request( args.method, args.params, ---@diagnostic disable-next-line: param-type-mismatch function(_, result, _, _) vim.fn["ale#lsp#HandleResponse"](client.name, { id = request_id, result = result, }) end, ---@diagnostic disable-next-line: param-type-mismatch -1 ) if success then return request_id end return 0 end return module