-- LSP attach handler using LspAttach autocmd (NeoVim 0.11+ recommended pattern) vim.api.nvim_create_autocmd('LspAttach', { callback = function(args) local client = vim.lsp.get_client_by_id(args.data.client_id) local bufnr = args.buf -- Enable completion triggered by vim.bo[bufnr].omnifunc = "v:lua.vim.lsp.omnifunc" vim.keymap.set("n", "K", function() vim.lsp.buf.hover() end, { buffer = bufnr, desc = "Hover info" }) vim.keymap.set("n", "", function() vim.lsp.buf.signature_help() end, { buffer = bufnr, desc = "Signature help" }) vim.keymap.set("n", "L", function() vim.lsp.buf.format({ async = true }) end, { buffer = bufnr, desc = "LSP format" }) -- TypeScript/JavaScript specific: organize imports if client and client.name == "ts_ls" then vim.keymap.set("n", "gro", function() client:exec_cmd({ command = "_typescript.organizeImports", arguments = { vim.api.nvim_buf_get_name(0) }, title = "", }) end, { buffer = bufnr, desc = "Organize imports" }) end end, }) local function configure_lsp() -- Setup LSP configurations using vim.lsp.config (NeoVim 0.11+) -- Completion capabilities from blink.cmp (extends Neovim's defaults with the -- snippet/resolve/tag support that the hand-rolled table used to set). local capabilities = require("blink.cmp").get_lsp_capabilities() -- Global config for all LSP servers vim.lsp.config('*', { capabilities = capabilities, }) -- Configure lua-language-server vim.lsp.config('lua_ls', { settings = { Lua = { diagnostics = { -- `vim`/`require` are covered by lazydev.nvim + LuaLS defaults. -- `hs` is the Hammerspoon global, which lazydev doesn't know about. globals = { "hs", }, disable = { "unused-local", }, }, }, }, }) -- Configure bash-language-server vim.lsp.config('bashls', { settings = { bashIde = { enableExplainshellEndpoint = false, explainshellEndpoint = "", globPattern = "*@(.sh|.bash|.zsh|bash*|zsh*)", shfmt = { path = "shfmt", args = { "-i", "2", "-ci", "-sr" }, }, }, }, }) -- Configure typescript-language-server vim.lsp.config('ts_ls', {}) -- Configure astro-language-server vim.lsp.config('astro', {}) -- Configure gopls (Go language server) vim.lsp.config('gopls', { settings = { gopls = { analyses = { unusedparams = true, }, staticcheck = true, gofumpt = true, }, }, }) -- Configure pyright (Python language server) vim.lsp.config('pyright', { settings = { python = { analysis = { autoSearchPaths = true, useLibraryCodeForTypes = true, diagnosticMode = "workspace", }, }, }, }) -- Configure jdtls (Java language server) vim.lsp.config('jdtls', {}) -- Enable all configured servers -- Note: This doesn't force installation, it just enables them if installed vim.lsp.enable({ 'lua_ls', 'bashls', 'ts_ls', 'astro', 'gopls', 'pyright', 'jdtls' }) -- ESLint LSP is temporarily disabled (causes errors with call hierarchy) -- To re-enable, remove the line below and add 'eslint' to vim.lsp.enable() vim.lsp.enable('eslint', false) end return { { "nvim-treesitter/nvim-treesitter", branch = "main", -- Use main branch for Neovim 0.12+ compatibility build = ":TSUpdate", config = function() -- Add nvim-treesitter's runtime directory to runtimepath for queries (folds, indents, etc.) local ts_runtime = vim.fn.stdpath("data") .. "/lazy/nvim-treesitter/runtime" if vim.fn.isdirectory(ts_runtime) == 1 then vim.opt.runtimepath:prepend(ts_runtime) end -- The `main` branch has no `ensure_installed` option, so we install any -- missing parsers ourselves. Add/remove languages here as needed. local TS = require("nvim-treesitter") local ensure_installed = { "astro", "go", "html", "javascript", "lua", "python", "tsx", "typescript", } local installed = TS.get_installed("parsers") local to_install = vim.tbl_filter(function(lang) return not vim.tbl_contains(installed, lang) end, ensure_installed) if #to_install > 0 then TS.install(to_install, { summary = true }) end -- On the `main` branch, treesitter highlighting must be started per -- buffer (it is NOT automatic). Start it whenever a parser is available. vim.api.nvim_create_autocmd("FileType", { group = vim.api.nvim_create_augroup("sz_treesitter_start", { clear = true }), callback = function(args) pcall(vim.treesitter.start, args.buf) end, }) -- Show all available code actions (including organize imports) vim.keymap.set( "n", "ca", vim.lsp.buf.code_action, { noremap = true, silent = true, desc = "Show code actions" } ) vim.keymap.set("v", "", vim.lsp.buf.code_action, { noremap = true, silent = true, desc = "Show code actions" }) end, }, { "williamboman/mason.nvim", main = "mason", opts = { registries = { "github:mason-org/mason-registry", "github:Crashdummyy/mason-registry", -- Roslyn LSP }, }, }, { "williamboman/mason-lspconfig.nvim", main = "mason-lspconfig", opts = { -- Only ensure core language servers are installed on ALL machines -- Other servers will be auto-installed when you open a file of that type ensure_installed = { "lua_ls", "bashls" }, }, }, { "WhoIsSethDaniel/mason-tool-installer.nvim", dependencies = { "williamboman/mason.nvim" }, config = function() require("mason-tool-installer").setup({ -- Core tools - always installed ensure_installed = { "stylua", -- Lua formatting "shfmt", -- Bash formatting "prettier", -- JS/TS/Astro formatting }, -- Optional tools - install with: :MasonToolsInstall -- Or add them here when working on those projects -- "black", -- Python formatting -- "isort", -- Python imports sorting -- "gofumpt", -- Go formatting (stricter than gofmt) -- "goimports", -- Go imports formatting -- "csharpier", -- C# formatting -- "netcoredbg", -- C#/.NET debugging -- "google-java-format", -- Java formatting }) end, }, { "neovim/nvim-lspconfig", config = configure_lsp, }, { "seblj/roslyn.nvim", ft = "cs", dependencies = { "nvim-lua/plenary.nvim", }, opts = { filewatching = "roslyn", -- Disable Razor extension (only needed for Blazor) razor = { enabled = false, }, config = { settings = { ["csharp|inlay_hints"] = { csharp_enable_inlay_hints_for_implicit_object_creation = true, csharp_enable_inlay_hints_for_implicit_variable_types = true, csharp_enable_inlay_hints_for_lambda_parameter_types = true, csharp_enable_inlay_hints_for_types = true, dotnet_enable_inlay_hints_for_indexer_parameters = true, dotnet_enable_inlay_hints_for_literal_parameters = true, dotnet_enable_inlay_hints_for_object_creation_parameters = true, dotnet_enable_inlay_hints_for_other_parameters = true, dotnet_enable_inlay_hints_for_parameters = true, dotnet_suppress_inlay_hints_for_parameters_that_differ_only_by_suffix = true, dotnet_suppress_inlay_hints_for_parameters_that_match_argument_name = true, dotnet_suppress_inlay_hints_for_parameters_that_match_method_intent = true, }, ["csharp|completion"] = { dotnet_show_completion_items_from_unimported_namespaces = true, dotnet_show_name_completion_suggestions = true, }, ["csharp|code_lens"] = { dotnet_enable_references_code_lens = true, dotnet_enable_tests_code_lens = true, }, }, }, }, }, { "stevearc/conform.nvim", event = { "BufReadPre", "BufNewFile" }, config = function() local conform = require("conform") conform.setup({ formatters_by_ft = { astro = { "prettier" }, bash = { "shfmt" }, cs = { "csharpier" }, css = { "prettier" }, go = { "goimports", "gofumpt" }, -- Run goimports then gofumpt html = { "prettier" }, java = { "google-java-format" }, javascript = { "prettier" }, javascriptreact = { "prettier" }, json = { "prettier" }, lua = { "stylua" }, markdown = { "prettier" }, python = { "isort", "black" }, -- Run isort then black typescript = { "prettier" }, typescriptreact = { "prettier" }, }, -- Set to `true` to format on save, or provide a function for conditional formatting -- format_on_save = { -- timeout_ms = 500, -- lsp_fallback = true, -- Use LSP formatting as a fallback if no external formatter is found -- }, }) vim.keymap.set({ "n", "v" }, "l", function() conform.format({ async = true, lsp_format = "fallback" }) end, { desc = "Format file or range" }) end, }, }