dotfiles/nvim/.config/nvim/lua/plugins/lsp.lua
Marty Oehme 091274fd82
nvim: Add custom lazy loading events
Using lazy-events.nvim we can create custom events (such as the LazyVim
equivalent LazyFile event) based on other events (i.e. a grouping), on
globbing files in the current dir, or on arbitrary custom logic.

For now, any plugins which require files to be present to work are
loaded with the 'LazyFile' event.

Any plugins which need to be loaded when opening a directory with vim
are loaded with the 'StartWithDir' event (essentially only neo-tree for
now). Similarly mini.starter is loaded on the `StartScreen` event.

Any plugin which only makes sense to run in a `.git` dir (gitsigns etc)
will only run on the `LazyProject:git` event.
2025-06-21 16:44:39 +02:00

229 lines
6.9 KiB
Lua

local function get_all_servers()
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
return servers
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 = function()
local to_install = {}
local not_enabled = {}
for k, v in pairs(get_all_servers()) do
table.insert(to_install, k)
if v["disable"] and v["disable"] == true then
table.insert(not_enabled, k)
end
end
local tbl = {
ensure_installed = to_install,
automatic_enable = { exclude = not_enabled },
}
return tbl
end,
dependencies = { "williamboman/mason.nvim" },
cmd = { "LspInstall", "LspUninstall" },
},
{ "saghen/blink.cmp", optional = true },
},
event = "LazyFile",
opts = { servers = get_all_servers() },
init = function()
if require("core.util").is_available("which-key") then
require("which-key").add({ "<localleader>l", group = "language" })
end
end,
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, enabled)
if vim.fn.has("nvim-0.11") == 1 then
vim.lsp.config(server_name, config or {})
if enabled == false or vim.lsp.config[server_name]["autostart"] == false then
vim.lsp.enable(server_name, false)
else
vim.lsp.enable(server_name, true)
end
else
require("lspconfig")[server_name].setup(config)
end
end
if vim.fn.executable("nu") == 1 then
register("nushell")
end
-- arduino lsp only works if arduino-cli is installed
if vim.fn.executable("arduino-cli") == 1 then
register("arduino_language_server")
end
-- attach ltex for fitting ft only when spell checking becomes enabled
vim.api.nvim_create_autocmd("User", {
pattern = "SpellEnable",
callback = function()
local mapped = {}
local lang_map = {
en_us = "en-US",
en_gb = "en-GB",
de_de = "de-DE",
}
for _, v in ipairs(vim.opt.spelllang:get()) do
table.insert(mapped, lang_map[v])
end
vim.lsp.config("ltex", {
settings = { ltex = { language = mapped } },
})
-- single-shot setup: Enable for this buffer
-- but instantly disable again globally
vim.lsp.enable("ltex")
vim.lsp.enable("ltex", false)
end,
})
vim.api.nvim_create_autocmd("User", {
pattern = "SpellDisable",
callback = function()
vim.lsp.enable("ltex", false)
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", "[e", "<cmd>lua vim.diagnostic.goto_prev()<cr>", o({ desc = "Previous diagnostic" }))
map("n", "]e", "<cmd>lua vim.diagnostic.goto_next()<cr>", o({ desc = "Next diagnostic" }))
map(
"n",
"[E",
"<cmd>lua vim.diagnostic.goto_prev({severity = vim.diagnostic.severity.ERROR})<cr>",
o({ desc = "Previous error" })
)
map(
"n",
"]E",
"<cmd>lua vim.diagnostic.goto_next({severity = vim.diagnostic.severity.ERROR})<cr>",
o({ desc = "Next error" })
)
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