summaryrefslogtreecommitdiff
path: root/lua/lvim/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'lua/lvim/lsp')
-rw-r--r--lua/lvim/lsp/config.lua29
-rw-r--r--lua/lvim/lsp/handlers.lua70
-rw-r--r--lua/lvim/lsp/init.lua14
-rw-r--r--lua/lvim/lsp/manager.lua67
-rw-r--r--lua/lvim/lsp/null-ls/code_actions.lua81
-rw-r--r--lua/lvim/lsp/null-ls/formatters.lua35
-rw-r--r--lua/lvim/lsp/null-ls/init.lua2
-rw-r--r--lua/lvim/lsp/null-ls/linters.lua30
-rw-r--r--lua/lvim/lsp/null-ls/services.lua14
-rw-r--r--lua/lvim/lsp/templates.lua3
-rw-r--r--lua/lvim/lsp/utils.lua11
11 files changed, 235 insertions, 121 deletions
diff --git a/lua/lvim/lsp/config.lua b/lua/lvim/lsp/config.lua
index ce7ed891..1fbaf3a9 100644
--- a/lua/lvim/lsp/config.lua
+++ b/lua/lvim/lsp/config.lua
@@ -4,16 +4,31 @@ return {
signs = {
active = true,
values = {
- { name = "LspDiagnosticsSignError", text = "ï™™" },
- { name = "LspDiagnosticsSignWarning", text = "" },
- { name = "LspDiagnosticsSignHint", text = "ï µ" },
- { name = "LspDiagnosticsSignInformation", text = "" },
+ { name = "DiagnosticSignError", text = "ï™™" },
+ { name = "DiagnosticSignWarn", text = "" },
+ { name = "DiagnosticSignHint", text = "ï µ" },
+ { name = "DiagnosticSignInfo", text = "" },
},
},
virtual_text = true,
update_in_insert = false,
underline = true,
severity_sort = true,
+ float = {
+ focusable = false,
+ style = "minimal",
+ border = "rounded",
+ source = "always",
+ header = "",
+ prefix = "",
+ format = function(d)
+ local t = vim.deepcopy(d)
+ if d.code then
+ t.message = string.format("%s [%s]", t.message, t.code):gsub("1. ", "")
+ end
+ return t.message
+ end,
+ },
},
document_highlight = true,
code_lens_refresh = true,
@@ -40,10 +55,13 @@ return {
},
null_ls = {
setup = {},
+ config = {},
},
override = {
"angularls",
"ansiblels",
+ "ccls",
+ "csharp_ls",
"denols",
"ember",
"emmet_ls",
@@ -54,10 +72,13 @@ return {
"ltex",
"phpactor",
"pylsp",
+ "quick_lint_js",
"rome",
"sorbet",
"sqlls",
"sqls",
+ "solang",
+ "spectral",
"stylelint_lsp",
"tailwindcss",
"tflint",
diff --git a/lua/lvim/lsp/handlers.lua b/lua/lvim/lsp/handlers.lua
index 27ce8589..45f73e91 100644
--- a/lua/lvim/lsp/handlers.lua
+++ b/lua/lvim/lsp/handlers.lua
@@ -9,42 +9,10 @@ function M.setup()
underline = lvim.lsp.diagnostics.underline,
update_in_insert = lvim.lsp.diagnostics.update_in_insert,
severity_sort = lvim.lsp.diagnostics.severity_sort,
+ float = lvim.lsp.diagnostics.float,
}
- if vim.fn.has "nvim-0.5.1" > 0 then
- vim.lsp.handlers["textDocument/publishDiagnostics"] = function(_, result, ctx, _)
- local uri = result.uri
- local bufnr = vim.uri_to_bufnr(uri)
- if not bufnr then
- return
- end
-
- local diagnostics = result.diagnostics
- local ok, vim_diag = pcall(require, "vim.diagnostic")
- if ok then
- -- FIX: why can't we just use vim.diagnostic.get(buf_id)?
- config.signs = true
- for i, diagnostic in ipairs(diagnostics) do
- local rng = diagnostic.range
- diagnostics[i].lnum = rng["start"].line
- diagnostics[i].end_lnum = rng["end"].line
- diagnostics[i].col = rng["start"].character
- diagnostics[i].end_col = rng["end"].character
- end
- local namespace = vim.lsp.diagnostic.get_namespace(ctx.client_id)
-
- vim_diag.set(namespace, bufnr, diagnostics, config)
- if not vim.api.nvim_buf_is_loaded(bufnr) then
- return
- end
- vim_diag.show(namespace, bufnr, diagnostics, config)
- else
- vim.lsp.diagnostic.save(diagnostics, bufnr, ctx.client_id)
- if not vim.api.nvim_buf_is_loaded(bufnr) then
- return
- end
- vim.lsp.diagnostic.display(diagnostics, bufnr, ctx.client_id, config)
- end
- end
+ if vim.fn.has "nvim-0.6" == 1 then
+ vim.diagnostic.config(config)
else
vim.lsp.handlers["textDocument/publishDiagnostics"] = function(_, _, params, client_id, _)
local uri = params.uri
@@ -60,27 +28,29 @@ function M.setup()
end
vim.lsp.diagnostic.display(diagnostics, bufnr, client_id, config)
end
- end
-
- vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {
- border = lvim.lsp.popup_border,
- })
- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, {
- border = lvim.lsp.popup_border,
- })
-end
+ vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {
+ border = lvim.lsp.popup_border,
+ })
-local function split_by_chunk(text, chunkSize)
- local s = {}
- for i = 1, #text, chunkSize do
- s[#s + 1] = text:sub(i, i + chunkSize - 1)
+ vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, {
+ border = lvim.lsp.popup_border,
+ })
end
- return s
end
function M.show_line_diagnostics()
- -- TODO: replace all this with vim.diagnostic.show_position_diagnostics()
+ if vim.fn.has "nvim-0.6" == 1 then
+ return vim.diagnostic.open_float(0, { scope = "line" })
+ end
+
+ local function split_by_chunk(text, chunkSize)
+ local s = {}
+ for i = 1, #text, chunkSize do
+ s[#s + 1] = text:sub(i, i + chunkSize - 1)
+ end
+ return s
+ end
local diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
local severity_highlight = {
"LspDiagnosticsFloatingError",
diff --git a/lua/lvim/lsp/init.lua b/lua/lvim/lsp/init.lua
index d00f75c6..68a64d6c 100644
--- a/lua/lvim/lsp/init.lua
+++ b/lua/lvim/lsp/init.lua
@@ -137,10 +137,10 @@ function M.get_common_opts()
end
local LSP_DEPRECATED_SIGN_MAP = {
- ["LspDiagnosticsSignError"] = "DiagnosticSignError",
- ["LspDiagnosticsSignWarning"] = "DiagnosticSignWarn",
- ["LspDiagnosticsSignHint"] = "DiagnosticSignHint",
- ["LspDiagnosticsSignInformation"] = "DiagnosticSignInfo",
+ ["DiagnosticSignError"] = "LspDiagnosticsSignError",
+ ["DiagnosticSignWarn"] = "LspDiagnosticsSignWarning",
+ ["DiagnosticSignHint"] = "LspDiagnosticsSignHint",
+ ["DiagnosticSignInfo"] = "LspDiagnosticsSignInformation",
}
function M.setup()
@@ -151,11 +151,11 @@ function M.setup()
return
end
- local is_neovim_nightly = vim.fn.has "nvim-0.5.1" > 0
+ local is_neovim_5 = vim.fn.has "nvim-0.6" ~= 1
for _, sign in ipairs(lvim.lsp.diagnostics.signs.values) do
local lsp_sign_name = LSP_DEPRECATED_SIGN_MAP[sign.name]
- if is_neovim_nightly and lsp_sign_name then
+ if is_neovim_5 and lsp_sign_name then
vim.fn.sign_define(lsp_sign_name, { texthl = lsp_sign_name, text = sign.text, numhl = lsp_sign_name })
end
vim.fn.sign_define(sign.name, { texthl = sign.name, text = sign.text, numhl = sign.name })
@@ -171,7 +171,7 @@ function M.setup()
require("lvim.lsp.null-ls").setup()
- require("lvim.utils").toggle_autoformat()
+ require("lvim.core.autocmds").configure_format_on_save()
end
return M
diff --git a/lua/lvim/lsp/manager.lua b/lua/lvim/lsp/manager.lua
index dbb7b87f..7a35f1ff 100644
--- a/lua/lvim/lsp/manager.lua
+++ b/lua/lvim/lsp/manager.lua
@@ -45,48 +45,65 @@ local function buf_try_add(server_name, bufnr)
require("lspconfig")[server_name].manager.try_add_wrapper(bufnr)
end
+-- check if the manager autocomd has already been configured since some servers can take a while to initialize
+-- this helps guarding against a data-race condition where a server can get configured twice
+-- which seems to occur only when attaching to single-files
+local function client_is_configured(server_name, ft)
+ ft = ft or vim.bo.filetype
+ local active_autocmds = vim.split(vim.fn.execute("autocmd FileType " .. ft), "\n")
+ for _, result in ipairs(active_autocmds) do
+ if result:match(server_name) then
+ return true
+ end
+ end
+ return false
+end
+
---Setup a language server by providing a name
---@param server_name string name of the language server
---@param user_config table [optional] when available it will take predence over any default configurations
function M.setup(server_name, user_config)
vim.validate { name = { server_name, "string" } }
- if lvim_lsp_utils.is_client_active(server_name) then
+ if lvim_lsp_utils.is_client_active(server_name) or client_is_configured(server_name) then
+ Log:debug(string.format("[%q] is already configured. Ignoring repeated setup call.", server_name))
return
end
- local servers = require "nvim-lsp-installer.servers"
local config = resolve_config(server_name, user_config)
+
+ local servers = require "nvim-lsp-installer.servers"
local server_available, requested_server = servers.get_server(server_name)
- if server_available then
- local install_notification = false
-
- if not requested_server:is_installed() then
- if lvim.lsp.automatic_servers_installation then
- Log:debug "Automatic server installation detected"
- requested_server:install()
- install_notification = true
- else
- Log:debug(requested_server.name .. " is not managed by the automatic installer")
- end
- end
+ local is_overridden = vim.tbl_contains(lvim.lsp.override, server_name)
- requested_server:on_ready(function()
- if install_notification then
- vim.notify(string.format("Installation complete for [%s] server", requested_server.name), vim.log.levels.INFO)
- end
- install_notification = false
- requested_server:setup(config)
- end)
- else
- -- since it may not be installed, don't attempt to configure the LSP unless there is a custom provider
- local has_custom_provider, _ = pcall(require, "lvim/lsp/providers/" .. server_name)
- if has_custom_provider then
+ if not server_available or is_overridden then
+ pcall(function()
require("lspconfig")[server_name].setup(config)
buf_try_add(server_name)
+ end)
+ return
+ end
+
+ local install_notification = false
+
+ if not requested_server:is_installed() then
+ if lvim.lsp.automatic_servers_installation then
+ Log:debug "Automatic server installation detected"
+ requested_server:install()
+ install_notification = true
+ else
+ Log:debug(requested_server.name .. " is not managed by the automatic installer")
end
end
+
+ requested_server:on_ready(function()
+ if install_notification then
+ vim.notify(string.format("Installation complete for [%s] server", requested_server.name), vim.log.levels.INFO)
+ end
+ install_notification = false
+ requested_server:setup(config)
+ end)
end
return M
diff --git a/lua/lvim/lsp/null-ls/code_actions.lua b/lua/lvim/lsp/null-ls/code_actions.lua
new file mode 100644
index 00000000..ff59fabf
--- /dev/null
+++ b/lua/lvim/lsp/null-ls/code_actions.lua
@@ -0,0 +1,81 @@
+local M = {}
+
+local null_ls = require "null-ls"
+local services = require "lvim.lsp.null-ls.services"
+local Log = require "lvim.core.log"
+
+local METHOD = null_ls.methods.CODE_ACTION
+
+local is_registered = function(name)
+ local query = {
+ name = name,
+ method = METHOD,
+ }
+ return require("null-ls.sources").is_registered(query)
+end
+
+function M.list_registered_providers(filetype)
+ local registered_providers = services.list_registered_providers_names(filetype)
+ return registered_providers[METHOD] or {}
+end
+
+function M.list_available(filetype)
+ local availables = require("null-ls.sources").get_available(filetype, METHOD)
+ local actors = vim.tbl_map(function(src)
+ return src.name
+ end, availables)
+ table.sort(actors)
+ return actors
+end
+
+function M.list_configured(actions_configs)
+ local actors, errors = {}, {}
+
+ for _, config in ipairs(actions_configs) do
+ vim.validate {
+ ["config.name"] = { config.name, "string" },
+ }
+
+ local name = config.name:gsub("-", "_")
+ local actor = null_ls.builtins.code_actions[name]
+
+ if not actor then
+ Log:error("Not a valid code_actions: " .. config.name)
+ errors[name] = {} -- Add data here when necessary
+ elseif is_registered(config.name) then
+ Log:trace "Skipping registering the source more than once"
+ else
+ local command
+ if actor._opts.command then
+ command = services.find_command(actor._opts.command)
+ end
+ if not command and actor._opts.command ~= nil then
+ Log:warn("Not found: " .. actor._opts.command)
+ errors[name] = {} -- Add data here when necessary
+ else
+ Log:debug("Using code_actions: " .. (command or config.name))
+ table.insert(
+ actors,
+ actor.with {
+ command = command, -- could be nil
+ extra_args = config.args,
+ filetypes = config.filetypes,
+ }
+ )
+ end
+ end
+ end
+
+ return { supported = actors, unsupported = errors }
+end
+
+function M.setup(actions_configs)
+ if vim.tbl_isempty(actions_configs) then
+ return
+ end
+
+ local actions = M.list_configured(actions_configs)
+ null_ls.register { sources = actions.supported }
+end
+
+return M
diff --git a/lua/lvim/lsp/null-ls/formatters.lua b/lua/lvim/lsp/null-ls/formatters.lua
index 20939039..b2e191c5 100644
--- a/lua/lvim/lsp/null-ls/formatters.lua
+++ b/lua/lvim/lsp/null-ls/formatters.lua
@@ -4,6 +4,14 @@ local null_ls = require "null-ls"
local services = require "lvim.lsp.null-ls.services"
local Log = require "lvim.core.log"
+local is_registered = function(name)
+ local query = {
+ name = name,
+ method = require("null-ls").methods.FORMATTING,
+ }
+ return require("null-ls.sources").is_registered(query)
+end
+
function M.list_registered_providers(filetype)
local null_ls_methods = require "null-ls.methods"
local formatter_method = null_ls_methods.internal["FORMATTING"]
@@ -30,24 +38,29 @@ function M.list_configured(formatter_configs)
local formatters, errors = {}, {}
for _, fmt_config in ipairs(formatter_configs) do
- local formatter_name = fmt_config.exe:gsub("-", "_")
- local formatter = null_ls.builtins.formatting[formatter_name]
+ local name = fmt_config.exe:gsub("-", "_")
+ local formatter = null_ls.builtins.formatting[name]
if not formatter then
Log:error("Not a valid formatter: " .. fmt_config.exe)
- errors[fmt_config.exe] = {} -- Add data here when necessary
+ errors[name] = {} -- Add data here when necessary
+ elseif is_registered(fmt_config.exe) then
+ Log:trace "Skipping registering the source more than once"
else
local formatter_cmd = services.find_command(formatter._opts.command)
if not formatter_cmd then
Log:warn("Not found: " .. formatter._opts.command)
- errors[fmt_config.exe] = {} -- Add data here when necessary
+ errors[name] = {} -- Add data here when necessary
else
Log:debug("Using formatter: " .. formatter_cmd)
- formatters[fmt_config.exe] = formatter.with {
- command = formatter_cmd,
- extra_args = fmt_config.args,
- filetypes = fmt_config.filetypes,
- }
+ table.insert(
+ formatters,
+ formatter.with {
+ command = formatter_cmd,
+ extra_args = fmt_config.args,
+ filetypes = fmt_config.filetypes,
+ }
+ )
end
end
end
@@ -60,8 +73,8 @@ function M.setup(formatter_configs)
return
end
- local formatters_by_ft = M.list_configured(formatter_configs)
- null_ls.register { sources = formatters_by_ft.supported }
+ local formatters = M.list_configured(formatter_configs)
+ null_ls.register { sources = formatters.supported }
end
return M
diff --git a/lua/lvim/lsp/null-ls/init.lua b/lua/lvim/lsp/null-ls/init.lua
index 5e8c6b11..f5e820e8 100644
--- a/lua/lvim/lsp/null-ls/init.lua
+++ b/lua/lvim/lsp/null-ls/init.lua
@@ -9,7 +9,7 @@ function M:setup()
return
end
- null_ls.config()
+ null_ls.config(lvim.lsp.null_ls.config)
local default_opts = require("lvim.lsp").get_common_opts()
if vim.tbl_isempty(lvim.lsp.null_ls.setup or {}) then
diff --git a/lua/lvim/lsp/null-ls/linters.lua b/lua/lvim/lsp/null-ls/linters.lua
index ced4bf34..6a793d26 100644
--- a/lua/lvim/lsp/null-ls/linters.lua
+++ b/lua/lvim/lsp/null-ls/linters.lua
@@ -4,6 +4,14 @@ local null_ls = require "null-ls"
local services = require "lvim.lsp.null-ls.services"
local Log = require "lvim.core.log"
+local is_registered = function(name)
+ local query = {
+ name = name,
+ method = require("null-ls").methods.DIAGNOSTICS,
+ }
+ return require("null-ls.sources").is_registered(query)
+end
+
function M.list_registered_providers(filetype)
local null_ls_methods = require "null-ls.methods"
local linter_method = null_ls_methods.internal["DIAGNOSTICS"]
@@ -21,6 +29,7 @@ function M.list_available(filetype)
table.insert(linters, provider.name)
end
end
+
table.sort(linters)
return linters
end
@@ -29,24 +38,29 @@ function M.list_configured(linter_configs)
local linters, errors = {}, {}
for _, lnt_config in pairs(linter_configs) do
- local linter_name = lnt_config.exe:gsub("-", "_")
- local linter = null_ls.builtins.diagnostics[linter_name]
+ local name = lnt_config.exe:gsub("-", "_")
+ local linter = null_ls.builtins.diagnostics[name]
if not linter then
Log:error("Not a valid linter: " .. lnt_config.exe)
errors[lnt_config.exe] = {} -- Add data here when necessary
+ elseif is_registered(lnt_config.exe) then
+ Log:trace "Skipping registering the source more than once"
else
local linter_cmd = services.find_command(linter._opts.command)
if not linter_cmd then
Log:warn("Not found: " .. linter._opts.command)
- errors[lnt_config.exe] = {} -- Add data here when necessary
+ errors[name] = {} -- Add data here when necessary
else
Log:debug("Using linter: " .. linter_cmd)
- linters[lnt_config.exe] = linter.with {
- command = linter_cmd,
- extra_args = lnt_config.args,
- filetypes = lnt_config.filetypes,
- }
+ table.insert(
+ linters,
+ linter.with {
+ command = linter_cmd,
+ extra_args = lnt_config.args,
+ filetypes = lnt_config.filetypes,
+ }
+ )
end
end
end
diff --git a/lua/lvim/lsp/null-ls/services.lua b/lua/lvim/lsp/null-ls/services.lua
index 9cb29f49..9151cc39 100644
--- a/lua/lvim/lsp/null-ls/services.lua
+++ b/lua/lvim/lsp/null-ls/services.lua
@@ -46,15 +46,13 @@ function M.find_command(command)
end
function M.list_registered_providers_names(filetype)
- local u = require "null-ls.utils"
- local c = require "null-ls.config"
+ local s = require "null-ls.sources"
+ local available_sources = s.get_available(filetype)
local registered = {}
- for method, source in pairs(c.get()._methods) do
- for name, filetypes in pairs(source) do
- if u.filetype_matches(filetypes, filetype) then
- registered[method] = registered[method] or {}
- table.insert(registered[method], name)
- end
+ for _, source in ipairs(available_sources) do
+ for method in pairs(source.methods) do
+ registered[method] = registered[method] or {}
+ table.insert(registered[method], source.name)
end
end
return registered
diff --git a/lua/lvim/lsp/templates.lua b/lua/lvim/lsp/templates.lua
index 33c75a6e..3478f4fb 100644
--- a/lua/lvim/lsp/templates.lua
+++ b/lua/lvim/lsp/templates.lua
@@ -19,8 +19,7 @@ end
---@param server_name string name of a valid language server, e.g. pyright, gopls, tsserver, etc.
---@param dir string the full path to the desired directory
function M.generate_ftplugin(server_name, dir)
- local has_custom_provider, _ = pcall(require, "lvim/lsp/providers/" .. server_name)
- if vim.tbl_contains(lvim.lsp.override, server_name) and not has_custom_provider then
+ if vim.tbl_contains(lvim.lsp.override, server_name) then
return
end
diff --git a/lua/lvim/lsp/utils.lua b/lua/lvim/lsp/utils.lua
index 7cc8f54f..df3846ce 100644
--- a/lua/lvim/lsp/utils.lua
+++ b/lua/lvim/lsp/utils.lua
@@ -22,22 +22,23 @@ function M.get_active_clients_by_ft(filetype)
end
function M.get_client_capabilities(client_id)
+ local client
if not client_id then
local buf_clients = vim.lsp.buf_get_clients()
- for _, buf_client in ipairs(buf_clients) do
+ for _, buf_client in pairs(buf_clients) do
if buf_client.name ~= "null-ls" then
- client_id = buf_client.id
+ client = buf_client
break
end
end
+ else
+ client = vim.lsp.get_client_by_id(tonumber(client_id))
end
- if not client_id then
+ if not client then
error "Unable to determine client_id"
return
end
- local client = vim.lsp.get_client_by_id(tonumber(client_id))
-
local enabled_caps = {}
for capability, status in pairs(client.resolved_capabilities) do
if status == true then