From: Samir Benmendil Date: Sun, 9 Feb 2025 18:09:24 +0000 (+0000) Subject: lazyvim: absorb lspconfig X-Git-Url: https://git.rmz.io/dotfiles.git/commitdiff_plain/77f182ef72eb8bac67c6c5f235e6031b9fff8288 lazyvim: absorb lspconfig --- diff --git a/nvim/lua/plugins/lsp.lua b/nvim/lua/plugins/lsp.lua deleted file mode 100644 index d020443..0000000 --- a/nvim/lua/plugins/lsp.lua +++ /dev/null @@ -1,123 +0,0 @@ -return { - -- lspconfig - { - "neovim/nvim-lspconfig", - ---@class PluginLspOpts - opts = function() - local keys = require("lazyvim.plugins.lsp.keymaps").get() - -- TODO: setup a new mapping for this - -- { "gr", "Telescope lsp_references", desc = "References" }, - keys[#keys+1] = { "gr", false } - - return { - diagnostics = { - underline = true, - update_in_insert = false, - virtual_text = { - spacing = 4, - source = "if_mani", - prefix = "●", - }, - severity_sort = true, - signs = { - text = { - [vim.diagnostic.severity.ERROR] = " ", - [vim.diagnostic.severity.WARN] = " ", - [vim.diagnostic.severity.HINT] = " ", - [vim.diagnostic.severity.INFO] = " ", - }, - }, - }, - inlay_hints = { enabled = false, }, - codelens = { enabled = false, }, - document_highlight = { enabled = true, }, - capabilities = { - workspace = { - fileOperations = { - didRename = true, - willRename = true, - }, - }, - }, - format = { - formatting_options = nil, - timeout_ms = nil, - }, - -- Automatically format on save - -- autoformat = false, - -- LSP Server Settings - ---@type lspconfig.options - servers = { - lua_ls = { - settings = { - Lua = { - workspace = { checkThirdParty = false, }, - codeLens = { enable = true, }, - completion = { callSnippet = "Replace", }, - doc = { privateName = { "^_" }, }, - hint = { - enable = true, - setType = false, - paramType = true, - paramName = "Disable", - semicolon = "Disable", - arrayIndex = "Disable", - }, - diagnostics = { - disable = { "missing-fields", }, - }, - }, - }, - }, - -- Add clangd extensions - -- https://github.com/p00f/clangd_extensions.nvim - }, - -- you can do any additional lsp server setup here - -- return true if you don't want this server to be setup with lspconfig - ---@type table - setup = { - -- example to setup with typescript.nvim - -- tsserver = function(_, opts) - -- require("typescript").setup({ server = opts }) - -- return true - -- end, - -- Specify * to use this function as a fallback for any server - -- ["*"] = function(server, opts) end, - }, - } - end, - }, - - -- cmdline tools and lsp servers - { - - "williamboman/mason.nvim", - cmd = "Mason", - keys = { { "cm", "Mason", desc = "Mason" } }, - opts = { - ensure_installed = { - "stylua", - "shfmt", - -- "flake8", - }, - }, - ---@param opts MasonSettings | {ensure_installed: string[]} - config = function(_, opts) - require("mason").setup(opts) - local mr = require("mason-registry") - local function ensure_installed() - for _, tool in ipairs(opts.ensure_installed) do - local p = mr.get_package(tool) - if not p:is_installed() then - p:install() - end - end - end - if mr.refresh then - mr.refresh(ensure_installed) - else - ensure_installed() - end - end, - }, -} diff --git a/nvim/lua/plugins/lsp/init.lua b/nvim/lua/plugins/lsp/init.lua new file mode 100644 index 0000000..351f060 --- /dev/null +++ b/nvim/lua/plugins/lsp/init.lua @@ -0,0 +1,176 @@ +return { + { "neovim/nvim-lspconfig", + event = "LazyFile", + dependencies = { + "mason.nvim", + { "williamboman/mason-lspconfig.nvim", config = function() end }, -- don't configure yet + }, + ---@class PluginLspOpts + opts = { + ---@type vim.diagnostic.Opts + diagnostics = { + underline = true, + update_in_insert = false, + virtual_text = { + spacing = 4, + source = "if_many", + prefix = "●", + }, + severity_sort = true, + signs = { + text = { + [vim.diagnostic.severity.ERROR] = " ", + [vim.diagnostic.severity.WARN] = " ", + [vim.diagnostic.severity.HINT] = " ", + [vim.diagnostic.severity.INFO] = " ", + }, + }, + }, + inlay_hints = { enabled = false, }, + codelens = { enabled = false, }, + document_highlight = { enabled = true, }, + capabilities = { + workspace = { + fileOperations = { + didRename = true, + willRename = true, + }, + }, + }, + format = { + formatting_options = nil, + timeout_ms = nil, + }, + -- LSP Server Settings + ---@type lspconfig.options + servers = { + lua_ls = { + settings = { + Lua = { + workspace = { checkThirdParty = false, }, + codeLens = { enable = true, }, + completion = { callSnippet = "Replace", }, + doc = { privateName = { "^_" }, }, + hint = { + enable = true, + setType = false, + paramType = true, + paramName = "Disable", + semicolon = "Disable", + arrayIndex = "Disable", + }, + diagnostics = { + disable = { "missing-fields", }, + }, + }, + }, + }, + -- TODO: Add clangd extensions + -- https://github.com/p00f/clangd_extensions.nvim + }, + -- you can do any additional lsp server setup here + -- return true if you don't want this server to be setup with lspconfig + ---@type table + setup = { + -- example to setup with typescript.nvim + -- tsserver = function(_, opts) + -- require("typescript").setup({ server = opts }) + -- return true + -- end, + -- Specify * to use this function as a fallback for any server + -- ["*"] = function(server, opts) end, + }, + }, + ---@param opts PluginLspOpts + config = function(_, opts) + local lspconfig = require('lspconfig') + for server, config in pairs(opts.servers) do + local capabilities = vim.tbl_deep_extend("force", + vim.lsp.protocol.make_client_capabilities() or {}, + require('blink.cmp').get_lsp_capabilities() or {}, + config.capabilities or {} + ) + config.capabilities = capabilities + lspconfig[server].setup(config) + end + + -- setup keymaps + LazyVim.lsp.on_attach(function(client, buffer) + require("plugins.lsp.keymaps").on_attach(client, buffer) + end) + + LazyVim.lsp.setup() + LazyVim.lsp.on_dynamic_capability(require("plugins.lsp.keymaps").on_attach) + -- TODO: should vim.diagnostics be condigured directly? + vim.diagnostic.config(vim.deepcopy(opts.diagnostics)) + + -- get all the servers that are available through mason-lspconfig + local have_mason, mlsp = pcall(require, "mason-lspconfig") + local all_mslp_servers = {} + if have_mason then + all_mslp_servers = vim.tbl_keys(require("mason-lspconfig.mappings.server").lspconfig_to_package) + end + + local ensure_installed = {} ---@type string[] + for server, server_opts in pairs(opts.servers) do + if server_opts then + server_opts = server_opts == true and {} or server_opts + if server_opts.enabled ~= false then + -- run manual setup if mason=false or if this is a server that cannot be installed with mason-lspconfig + if server_opts.mason == false or not vim.tbl_contains(all_mslp_servers, server) then + require("lspconfig")[server].setup(server_opts) + else + ensure_installed[#ensure_installed + 1] = server + end + end + end + end + + if have_mason then + mlsp.setup({ + ensure_installed = vim.tbl_deep_extend( + "force", + ensure_installed, + LazyVim.opts("mason-lspconfig.nvim").ensure_installed or {} + ), + handlers = { setup }, + }) + end + end + }, + { "williamboman/mason.nvim", + cmd = "Mason", + keys = { { "cm", "Mason", desc = "Mason" } }, + build = ":MasonUpdate", + opts_extend = { "ensure_installed" }, + opts = { + ensure_installed = { + "stylua", + "shfmt", + }, + }, + ---@param opts MasonSettings | {ensure_installed: string[]} + config = function(_, opts) + require("mason").setup(opts) + local mr = require("mason-registry") + mr:on("package:install:success", function() + vim.defer_fn(function() + -- trigger FileType event to possibly load this newly installed LSP server + require("lazy.core.handler.event").trigger({ + event = "FileType", + buf = vim.api.nvim_get_current_buf(), + }) + end, 100) + end) + + mr.refresh(function() + for _, tool in ipairs(opts.ensure_installed) do + local p = mr.get_package(tool) + if not p:is_installed() then + p:install() + end + end + end) + end, + }, +} diff --git a/nvim/lua/plugins/lsp/keymaps.lua b/nvim/lua/plugins/lsp/keymaps.lua new file mode 100644 index 0000000..72cfa51 --- /dev/null +++ b/nvim/lua/plugins/lsp/keymaps.lua @@ -0,0 +1,99 @@ +local M = {} + +---@type LazyKeysLspSpec[]|nil +M._keys = nil + +---@alias LazyKeysLspSpec LazyKeysSpec|{has?:string|string[], cond?:fun():boolean} +---@alias LazyKeysLsp LazyKeys|{has?:string|string[], cond?:fun():boolean} + +---@return LazyKeysLspSpec[] +function M.get() + if M._keys then + return M._keys + end + -- stylua: ignore + M._keys = { + { "cl", "LspInfo", desc = "Lsp Info" }, + { "gd", vim.lsp.buf.definition, desc = "Goto Definition", has = "definition" }, + -- { "gr", vim.lsp.buf.references, desc = "References", nowait = true }, + { "gI", vim.lsp.buf.implementation, desc = "Goto Implementation" }, + { "gy", vim.lsp.buf.type_definition, desc = "Goto T[y]pe Definition" }, + { "gD", vim.lsp.buf.declaration, desc = "Goto Declaration" }, + { "K", function() return vim.lsp.buf.hover() end, desc = "Hover" }, + { "gK", function() return vim.lsp.buf.signature_help() end, desc = "Signature Help", has = "signatureHelp" }, + { "", function() return vim.lsp.buf.signature_help() end, mode = "i", desc = "Signature Help", has = "signatureHelp" }, + { "ca", vim.lsp.buf.code_action, desc = "Code Action", mode = { "n", "v" }, has = "codeAction" }, + { "cc", vim.lsp.codelens.run, desc = "Run Codelens", mode = { "n", "v" }, has = "codeLens" }, + { "cC", vim.lsp.codelens.refresh, desc = "Refresh & Display Codelens", mode = { "n" }, has = "codeLens" }, + { "cR", function() Snacks.rename.rename_file() end, desc = "Rename File", mode ={"n"}, has = { "workspace/didRenameFiles", "workspace/willRenameFiles" } }, + { "cr", vim.lsp.buf.rename, desc = "Rename", has = "rename" }, + { "cA", LazyVim.lsp.action.source, desc = "Source Action", has = "codeAction" }, + { "]]", function() Snacks.words.jump(vim.v.count1) end, has = "documentHighlight", + desc = "Next Reference", cond = function() return Snacks.words.is_enabled() end }, + { "[[", function() Snacks.words.jump(-vim.v.count1) end, has = "documentHighlight", + desc = "Prev Reference", cond = function() return Snacks.words.is_enabled() end }, + { "", function() Snacks.words.jump(vim.v.count1, true) end, has = "documentHighlight", + desc = "Next Reference", cond = function() return Snacks.words.is_enabled() end }, + { "", function() Snacks.words.jump(-vim.v.count1, true) end, has = "documentHighlight", + desc = "Prev Reference", cond = function() return Snacks.words.is_enabled() end }, + } + + return M._keys +end + +---@param method string|string[] +function M.has(buffer, method) + if type(method) == "table" then + for _, m in ipairs(method) do + if M.has(buffer, m) then + return true + end + end + return false + end + method = method:find("/") and method or "textDocument/" .. method + local clients = LazyVim.lsp.get_clients({ bufnr = buffer }) + for _, client in ipairs(clients) do + if client.supports_method(method) then + return true + end + end + return false +end + +---@return LazyKeysLsp[] +function M.resolve(buffer) + local Keys = require("lazy.core.handler.keys") + if not Keys.resolve then + return {} + end + local spec = vim.tbl_extend("force", {}, M.get()) + local opts = LazyVim.opts("nvim-lspconfig") + local clients = LazyVim.lsp.get_clients({ bufnr = buffer }) + for _, client in ipairs(clients) do + local maps = opts.servers[client.name] and opts.servers[client.name].keys or {} + vim.list_extend(spec, maps) + end + return Keys.resolve(spec) +end + +function M.on_attach(_, buffer) + local Keys = require("lazy.core.handler.keys") + local keymaps = M.resolve(buffer) + + for _, keys in pairs(keymaps) do + local has = not keys.has or M.has(buffer, keys.has) + local cond = not (keys.cond == false or ((type(keys.cond) == "function") and not keys.cond())) + + if has and cond then + local opts = Keys.opts(keys) + opts.cond = nil + opts.has = nil + opts.silent = opts.silent ~= false + opts.buffer = buffer + vim.keymap.set(keys.mode or "n", keys.lhs, keys.rhs, opts) + end + end +end + +return M diff --git a/nvim/lua/plugins/picker.lua b/nvim/lua/plugins/picker.lua index 347804f..6f69fc2 100644 --- a/nvim/lua/plugins/picker.lua +++ b/nvim/lua/plugins/picker.lua @@ -65,7 +65,7 @@ return { }, { "neovim/nvim-lspconfig", opts = function() - local Keys = require("lazyvim.plugins.lsp.keymaps").get() + local Keys = require("plugins.lsp.keymaps").get() -- stylua: ignore vim.list_extend(Keys, { { "gd", function() Snacks.picker.lsp_definitions() end, desc = "Goto Definition", has = "definition" },