return { { "neovim/nvim-lspconfig", opts = { servers = { eslint = { settings = { -- helps eslint find the eslintrc when it's placed in a subfolder instead of the cwd root workingDirectory = { mode = "auto" }, }, }, pyright = {}, ruff_lsp = {}, neocmake = {}, dockerls = {}, docker_compose_language_service = {}, gopls = { keys = { -- Workaround for the lack of a DAP strategy in neotest-go: https://github.com/nvim-neotest/neotest-go/issues/12 { "td", "lua require('dap-go').debug_test()", desc = "Debug Nearest (Go)" }, }, settings = { gopls = { gofumpt = true, codelenses = { gc_details = false, generate = true, regenerate_cgo = true, run_govulncheck = true, test = true, tidy = true, upgrade_dependency = true, vendor = true, }, hints = { assignVariableTypes = true, compositeLiteralFields = true, compositeLiteralTypes = true, constantValues = true, functionTypeParameters = true, parameterNames = true, rangeVariableTypes = true, }, analyses = { fieldalignment = true, nilness = true, unusedparams = true, unusedwrite = true, useany = true, }, usePlaceholders = true, completeUnimported = true, staticcheck = true, directoryFilters = { "-.git", "-.vscode", "-.idea", "-.vscode-test", "-node_modules" }, semanticTokens = true, }, }, }, jdtls = { -- stylua: ignore keys = { { "co", function() require("jdtls").organize_imports() end, desc = "Organize Imports", }, { "cR", function() require("jdtls").rename_file() end, desc = "Rename File", }, { "cxv", function() require("jdtls").extract_variable() end, desc = "Extract Variable", }, { "cxv", function() require("jdtls").extract_variable({ visual = true }) end, mode = "v", desc = "Extract Variable", }, { "cxc", function() require("jdtls").extract_constant() end, desc = "Extract Constant", }, { "cxc", function() require("jdtls").extract_constant({ visual = true }) end, mode = "v", desc = "Extract Constant", }, { "cxm", function() require("jdtls").extract_method({ visual = true }) end, mode = "v", desc = "Extract Method", }, }, }, rust_analyzer = { keys = { { "K", "RustHoverActions", desc = "Hover Actions (Rust)" }, { "cR", "RustCodeAction", desc = "Code Action (Rust)" }, { "dr", "RustDebuggables", desc = "Run Debuggables (Rust)" }, }, settings = { ["rust-analyzer"] = { cargo = { allFeatures = true, loadOutDirsFromCheck = true, runBuildScripts = true, }, -- Add clippy lints for Rust. checkOnSave = { allFeatures = true, command = "clippy", extraArgs = { "--no-deps" }, }, procMacro = { enable = true, ignored = { ["async-trait"] = { "async_trait" }, ["napi-derive"] = { "napi" }, ["async-recursion"] = { "async_recursion" }, }, }, }, }, }, taplo = { keys = { { "K", function() if vim.fn.expand("%:t") == "Cargo.toml" and require("crates").popup_available() then require("crates").show_popup() else vim.lsp.buf.hover() end end, desc = "Show Crate Documentation", }, }, }, }, tsserver = { keys = { { "co", "TypescriptOrganizeImports", desc = "Organize Imports" }, { "cR", "TypescriptRenameFile", desc = "Rename File" }, }, settings = { typescript = { format = { indentSize = vim.o.shiftwidth, convertTabsToSpaces = vim.o.expandtab, tabSize = vim.o.tabstop, }, }, javascript = { format = { indentSize = vim.o.shiftwidth, convertTabsToSpaces = vim.o.expandtab, tabSize = vim.o.tabstop, }, }, completions = { completeFunctionCalls = true, }, }, }, }, setup = { rust_analyzer = function(_, opts) local rust_tools_opts = require("lazyvim.util").opts("rust-tools.nvim") require("rust-tools").setup(vim.tbl_deep_extend("force", rust_tools_opts or {}, { server = opts })) return true end, ruff_lsp = function() require("lazyvim.util").on_attach(function(client, _) if client.name == "ruff_lsp" then -- Disable hover in favor of Pyright client.server_capabilities.hoverProvider = false end end) end, gopls = function(_, opts) -- workaround for gopls not supporting semanticTokensProvider -- https://github.com/golang/go/issues/54531#issuecomment-1464982242 require("lazyvim.util").on_attach(function(client, _) if client.name == "gopls" then if not client.server_capabilities.semanticTokensProvider then local semantic = client.config.capabilities.textDocument.semanticTokens client.server_capabilities.semanticTokensProvider = { full = true, legend = { tokenTypes = semantic.tokenTypes, tokenModifiers = semantic.tokenModifiers, }, range = true, } end end end) -- end workaround end, jdtls = function() -- The lspconfig configuration for jdtls contains two useful items: -- 1. The list of filetypes on which to match. -- 2. Custom method for finding the root for a java project. local lsp_config = require("lspconfig.server_configurations.jdtls").default_config local find_java_project_root = lsp_config.root_dir local filetypes = lsp_config.filetypes -- Attach jdtls for the proper filetypes (i.e. java). -- Existing server will be reused if the root_dir matches. vim.api.nvim_create_autocmd("FileType", { group = vim.api.nvim_create_augroup("MyJdtls", { clear = true }), pattern = filetypes, callback = function() local fname = vim.api.nvim_buf_get_name(0) local root_dir = find_java_project_root(fname) local project_name = root_dir and vim.fs.basename(root_dir) local cmd = { "jdtls" } if project_name then local jdtls_cache_dir = vim.fs.joinpath(vim.fn.stdpath("cache"), "jdtls", project_name) vim.list_extend(cmd, { "-configuration", vim.fs.joinpath(jdtls_cache_dir, "config"), "-data", vim.fs.joinpath(jdtls_cache_dir, "workspace"), }) end require("jdtls").start_or_attach({ cmd = cmd, root_dir = root_dir, }) require("which-key").register({ c = { x = { name = "Extract" } } }, { prefix = "" }) end, }) return true -- avoid duplicate servers end, tsserver = function(_, opts) require("typescript").setup({ server = opts }) return true end, eslint = function() vim.api.nvim_create_autocmd("BufWritePre", { callback = function(event) if not require("lazyvim.plugins.lsp.format").enabled() then -- exit early if autoformat is not enabled return end local client = vim.lsp.get_active_clients({ bufnr = event.buf, name = "eslint" })[1] if client then local diag = vim.diagnostic.get(event.buf, { namespace = vim.lsp.diagnostic.get_namespace(client.id) }) if #diag > 0 then vim.cmd("EslintFixAll") end end end, }) end, }, }, }