dotfiles/nvim/.config/nvim/lua/plugins/lsp.lua
Marty Oehme d2432e0039
nvim: Extend lsp configuration with multiple declarations
So far if LSP declarations in 'core/languages.lua' were duplicated (e.g.
the 'astro' filetype and the 'vue' filetype both declare the
'typescript' lsp), one would simply overwrite the other. That is fine as
long as there is no custom setup, but if there is one of them is lost.

This commit changes it so that the different configurations extend each
other. The 'latter' one still overwrites the former but this is just
what must happen if there should be conflicting setups. 

May increase loading time slightly.
2025-06-06 15:58:43 +02:00

288 lines
9.1 KiB
Lua

local servers = {}
for _, lang in pairs(Languages) do
if not lang.lsp then
goto continue
end
for name, conf in pairs(lang.lsp) do
servers[name] = vim.tbl_deep_extend("force", servers[name] or {}, conf)
end
::continue::
end
local lsp = {
{ -- pretty lsp 'peek' menus
"DNLHC/glance.nvim",
opts = { border = { enable = true }, theme = { enable = true, mode = "auto" } },
cmd = { "Glance" },
},
{
"neovim/nvim-lspconfig",
dependencies = {
{
"williamboman/mason-lspconfig.nvim",
opts = { automatic_installation = true },
dependencies = { "williamboman/mason.nvim" },
cmd = { "LspInstall", "LspUninstall" },
},
{ "saghen/blink.cmp", optional = true },
},
event = { "BufReadPost", "BufNewFile", "BufWritePre" },
opts = { servers = servers },
config = function(_, lspconfig_opts)
-- Display diagnostics as virtual text only if not in insert mode
-- /r/neovim/comments/12inp4c/disable_diagnostics_virtual_text_when_in_insert/jqqifwk/
vim.diagnostic.config({ virtual_text = true })
vim.api.nvim_create_autocmd("InsertEnter", {
callback = function()
vim.diagnostic.config({ virtual_text = false })
end,
})
vim.api.nvim_create_autocmd("InsertLeave", {
callback = function()
vim.diagnostic.config({ virtual_text = true })
end,
})
vim.fn.sign_define("DiagnosticSignError", { text = "", texthl = "DiagnosticSignError" })
vim.fn.sign_define("DiagnosticSignWarn", { text = "", texthl = "DiagnosticSignWarn" })
vim.fn.sign_define("DiagnosticSignInfo", { text = "", texthl = "DiagnosticSignInfo" })
vim.fn.sign_define("DiagnosticSignHint", { text = "", texthl = "DiagnosticSignHint" })
local function register(server_name, config)
if vim.fn.has("nvim-0.11") == 1 then
vim.lsp.config(server_name, config)
else
require("lspconfig")[server_name].setup(config)
end
end
for server_name, config in pairs(lspconfig_opts.servers) do
register(server_name, config)
if vim.fn.has("nvim-0.11") == 0 then
config.capabilities = require("blink.cmp").get_lsp_capabilities(config.capabilities)
end
end
register("marksman", {
filetypes = { "markdown", "quarto" },
on_attach = function(client, _)
-- TODO: for some reason this stays true even after rootdir switch?
if client.config.in_zk_notebook then
local default_handler = vim.diagnostic.handlers.virtual_text
vim.diagnostic.handlers.virtual_text = {
show = function(namespace, bufnrr, diagnostics, opts)
for i, diagnostic in ipairs(diagnostics) do
if
diagnostic.source
== "Marksman" -- You need to check what the correct value should be here
then
table.remove(diagnostics, i)
end
end
default_handler.show(namespace, bufnrr, diagnostics, opts)
end,
hide = function(...)
default_handler.hide(...)
end,
}
default_handler = vim.diagnostic.handlers.signs
vim.diagnostic.handlers.signs = {
show = function(namespace, bufnrr, diagnostics, opts)
for i, diagnostic in ipairs(diagnostics) do
if
diagnostic.source
== "Marksman" -- You need to check what the correct value should be here
then
table.remove(diagnostics, i)
end
end
default_handler.show(namespace, bufnrr, diagnostics, opts)
end,
hide = function(...)
default_handler.hide(...)
end,
}
end
end,
on_new_config = function(conf, new_root)
if require("lspconfig.util").root_pattern(".zk")(new_root) then
conf.in_zk_notebook = true
else
conf.in_zk_notebook = false
end
end,
})
local python_path
-- ensure python virtualenv is determined automatically on lsp start
-- we primarily use pyright for cmp lsp completion & hover info
register("basedpyright", {
on_attach = function(client, _)
require("core.util").set_python_env()
if python_path == nil then
python_path, _ = vim.fn.expand(require("core.util").get_python_venv_bin(client.config.root_dir))
end
client.config.settings.python = {} or client.config.settings.python
client.config.settings.python.pythonPath = python_path
end,
settings = {
-- disable imports and linting since, using ruff for it
pyright = {
disableOrganizeImports = true,
},
python = {
analysis = {
-- ignore all files, use ruff for linting
ignore = { "*" },
},
},
},
})
register("ruff", {
on_attach = function(client, _)
require("core.util").set_python_env()
client.server_capabilities.hoverProvider = false -- we use pyright for hover info
if python_path == nil then
python_path, _ = vim.fn.expand(require("core.util").get_python_venv_bin(client.config.root_dir))
end
client.config.settings.python = {} or client.config.settings.python
client.config.settings.python.pythonPath = python_path
end,
})
-- set up arduino with the help of arduino.nvim plugin
if require("core.util").is_available("arduino") then
register("arduino_language_server", {
on_new_config = require("arduino").on_new_config,
})
end
-- attach ltex for fitting ft only when spell checking becomes enabled
vim.api.nvim_create_autocmd("User", {
pattern = "SpellEnable",
callback = function()
register("ltex", {
on_attach = function(_, _)
if require("core.util").is_available("ltex_extra") then
require("ltex_extra").setup()
end
end,
settings = {
ltex = {
language = vim.o.spelllang,
},
},
})
vim.cmd("LspStart ltex")
end,
})
end,
keys = { { "<leader>vs", ":LspInfo<cr>", desc = "LspInfo" } },
},
}
vim.api.nvim_create_autocmd("LspAttach", {
desc = "LSP actions",
callback = function(event)
local o = function(add_opts)
return vim.tbl_extend("force", { buffer = event.buf }, add_opts)
end
local map = vim.keymap.set
map("n", "K", "<cmd>lua vim.lsp.buf.hover()<cr>", o({ desc = "Hover definition" }))
map("n", "[d", "<cmd>lua vim.diagnostic.goto_prev()<cr>", o({ desc = "Previous diagnostic" }))
map("n", "]d", "<cmd>lua vim.diagnostic.goto_next()<cr>", o({ desc = "Next diagnostic" }))
map(
"n",
"[D",
"<cmd>lua vim.diagnostic.goto_prev({severity = vim.diagnostic.severity.ERROR})<cr>",
o({ desc = "Previous error" })
)
map(
"n",
"]D",
"<cmd>lua vim.diagnostic.goto_next({severity = vim.diagnostic.severity.ERROR})<cr>",
o({ desc = "Next error" })
)
if require("core.util").is_available("which-key") then
require("which-key").add({ "<localleader>l", group = "language" })
end
map("n", "<localleader>ld", "<cmd>lua vim.diagnostic.open_float()<cr>", o({ desc = "Show line diagnostics" }))
map("n", "<localleader>la", "<cmd>lua vim.lsp.buf.code_action()<cr>", o({ desc = "Codeactions" }))
map("n", "<localleader>ln", "<cmd>lua vim.lsp.buf.rename()<cr>", o({ desc = "Rename element" }))
map("n", "<localleader>lc", "<cmd>lua vim.lsp.buf.declaration()<cr>", o({ desc = "Declaration" }))
map("n", "<localleader>ls", "<cmd>lua vim.lsp.buf.signature_help()<cr>", o({ desc = "Signature help" }))
map("n", "<localleader>lI", function()
vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled())
end, o({ desc = "Toggle inlay hints" }))
map("n", "<localleader>lD", function()
vim.diagnostic.enable(not vim.diagnostic.is_enabled())
end, o({ desc = "Toggle Diagnostics" }))
-- FIXME: Will be re-enabled with insert-mode autocmd above
map("n", "<localleader>lo", function()
local c = vim.diagnostic.config() or {}
vim.diagnostic.config({ virtual_text = not c["virtual_text"] })
end, o({ desc = "Toggle virtual diag text" }))
if vim.fn.has("nvim-0.11") == true then -- new feature https://gpanders.com/blog/whats-new-in-neovim-0-11/#virtual-lines
map("n", "<localleader>lO", function()
local c = vim.diagnostic.config() or {}
vim.diagnostic.config({ virtual_lines = not c["virtual_lines"] })
end, o({ desc = "Toggle virtual diag lines" }))
end
local pref = function(glances, telescope, fallback)
if glances and vim.fn.exists(":Glance") > 0 then
return glances
elseif telescope and vim.fn.exists(":Telescope") > 0 then
return telescope
else
return fallback
end
end
map(
"n",
"<localleader>lr",
pref(
"<cmd>Glance references<cr>",
"<cmd>Telescope lsp_references<cr>",
"<cmd>lua vim.lsp.buf.references()<cr>"
),
o({ desc = "References" })
)
map(
"n",
"<localleader>lf",
pref(
"<cmd>Glance definitions<cr>",
"<cmd>Telescope lsp_definitions<cr>",
"<cmd>lua vim.lsp.buf.definition()<cr>"
),
o({ desc = "Definition" })
)
map(
"n",
"<localleader>lt",
pref(
"<cmd>Glance type_definitions<cr>",
"<cmd>Telescope lsp_type_definitions<cr>",
"<cmd>lua vim.lsp.buf.type_definition()<cr>"
),
o({ desc = "Type definition" })
)
map(
"n",
"<localleader>lm",
pref(
"<cmd>Glance implementations<cr>",
"<cmd>Telescope lsp_implementations<cr>",
"<cmd>lua vim.lsp.buf.implementation()<cr>"
),
o({ desc = "Implementation" })
)
end,
})
return lsp