dotfiles/nvim/lua/lsp_conf.lua
2026-06-02 08:20:14 +02:00

210 lines
6.3 KiB
Lua

local utils = require("utils")
vim.api.nvim_create_autocmd("LspAttach", {
group = vim.api.nvim_create_augroup("lsp_buf_conf", { clear = true }),
callback = function(event_context)
local client = vim.lsp.get_client_by_id(event_context.data.client_id)
-- vim.print(client.name, client.server_capabilities)
if not client then
return
end
local bufnr = event_context.buf
-- Mappings.
local map = function(mode, l, r, opts)
opts = opts or {}
opts.silent = true
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
map("n", "gd", function()
vim.lsp.buf.definition {
on_list = function(options)
-- custom logic to avoid showing multiple definition when you use this style of code:
-- `local M.my_fn_name = function() ... end`.
-- See also post here: https://www.reddit.com/r/neovim/comments/19cvgtp/any_way_to_remove_redundant_definition_in_lua_file/
-- vim.print(options.items)
local unique_defs = {}
local def_loc_hash = {}
-- each item in options.items contain the location info for a definition provided by LSP server
for _, def_location in pairs(options.items) do
-- use filename and line number to uniquelly indentify a definition,
-- we do not expect/want multiple definition in single line!
local hash_key = def_location.filename .. def_location.lnum
if not def_loc_hash[hash_key] then
def_loc_hash[hash_key] = true
table.insert(unique_defs, def_location)
end
end
options.items = unique_defs
-- set the location list
---@diagnostic disable-next-line: param-type-mismatch
vim.fn.setloclist(0, {}, " ", options)
-- open the location list when we have more than 1 definitions found,
-- otherwise, jump directly to the definition
if #options.items > 1 then
vim.cmd.lopen()
else
vim.cmd([[silent! lfirst]])
end
end,
}
end, { desc = "go to definition" })
map("n", "<C-]>", vim.lsp.buf.definition)
map("n", "K", function()
vim.lsp.buf.hover {
border = "single",
max_height = 20,
max_width = 130,
close_events = { "CursorMoved", "BufLeave", "WinLeave", "LSPDetach" },
}
end)
map("n", "<C-k>", vim.lsp.buf.signature_help)
map("n", "<space>rn", vim.lsp.buf.rename, { desc = "varialbe rename" })
map("n", "<space>ca", vim.lsp.buf.code_action, { desc = "LSP code action" })
map("n", "<space>wa", vim.lsp.buf.add_workspace_folder, { desc = "add workspace folder" })
map("n", "<space>wr", vim.lsp.buf.remove_workspace_folder, { desc = "remove workspace folder" })
map("n", "<space>wl", function()
vim.print(vim.lsp.buf.list_workspace_folders())
end, { desc = "list workspace folder" })
-- Set some key bindings conditional on server capabilities
-- Disable ruff hover feature in favor of Pyright
if client.name == "ruff" then
client.server_capabilities.hoverProvider = false
end
end,
nested = true,
desc = "Configure buffer keymap and behavior based on LSP",
})
-- Enable lsp servers when they are available
local capabilities = require("lsp_utils").get_default_capabilities()
-- `*` will set default config for all lsp
vim.lsp.config("*", {
capabilities = capabilities,
flags = {
debounce_text_changes = 500,
},
})
vim.lsp.config("basedpyright", {
capabilities = capabilities,
offset_encoding = "utf-16",
settings = {
python = {
analysis = {
typeCheckingMode = "strict",
},
},
},
})
vim.lsp.config("ruff", {
capabilities = capabilities,
offset_encoding = "utf-8",
})
-- A mapping from lsp server name to the executable name
local enabled_lsp_servers = {
basedpyright = "basedpyright-langserver",
ruff = "ruff",
lua_ls = "lua-language-server",
-- clangd = "clangd",
vimls = "vim-language-server",
bashls = "bash-language-server",
yamlls = "yaml-language-server",
gopls = "gopls",
-- the server can be install via homebrew: brew install golangci-lint-langserver
-- golangci-lint also needs to be installed: https://github.com/golangci/golangci-lint
golangci_lint_ls = "golangci-lint-langserver",
-- to install codebook, run `brew install codebook-lsp`
-- codebook = "codebook-lsp"
}
for server_name, lsp_executable in pairs(enabled_lsp_servers) do
if utils.executable(lsp_executable) then
vim.lsp.enable(server_name)
else
local msg = string.format(
"Executable '%s' for server '%s' not found! Server will not be enabled",
lsp_executable,
server_name
)
vim.notify(msg, vim.log.levels.WARN, { title = "Nvim-config" })
end
end
-- LSP related command
vim.api.nvim_create_user_command("LspInfo", "checkhealth vim.lsp", {
desc = "Show LSP Info",
})
vim.api.nvim_create_user_command("LspLog", function(_)
local log_path = vim.lsp.log.get_filename()
vim.cmd(string.format("edit %s", log_path))
end, {
desc = "Show LSP log",
})
vim.api.nvim_create_user_command("LspRestart", "lsp restart", {
desc = "Restart LSP",
})
--- show LSP progress (works on Ghostty)
vim.api.nvim_create_autocmd("LspProgress", {
callback = function(ev)
local value = ev.data.params.value
vim.api.nvim_echo({ { value.message or "done" } }, false, {
id = "lsp." .. ev.data.client_id,
kind = "progress",
source = "vim.lsp",
title = value.title,
status = value.kind ~= "end" and "running" or "success",
percent = value.percentage,
})
end,
})
-- this controls the LSP inlayHints behavior
vim.g.lsp_inlay_hint_enabled = false
local update_inlayhint = function(enable)
-- Some LSP server supports inlay hint, but disable this feature by default, so you may need to
-- enable inlay hint in the LSP server config.
vim.lsp.inlay_hint.enable(enable)
end
vim.api.nvim_create_user_command("LspInlayHints", function(context)
-- vim.print("context", context)
if context["args"] == "enable" then
vim.g.lsp_inlay_hint_enabled = true
end
if context["args"] == "disable" then
vim.g.lsp_inlay_hint_enabled = false
end
update_inlayhint(vim.g.lsp_inlay_hint_enabled)
end, {
bang = false,
nargs = 1,
force = true,
desc = "Toggle LSP inlayHints",
complete = function()
return { "enable", "disable" }
end,
})