From 80b5d665fe72c3e2f80c566a4af2f7ccd92278e6 Mon Sep 17 00:00:00 2001 From: Walter Jenkins Date: Fri, 14 Nov 2025 13:28:15 -0600 Subject: [PATCH] updated lsp to default to mason --- init.lua | 121 ++++++++++++++---------- lua/plugins/lsp.lua | 204 ++++++++++++---------------------------- lua/plugins/none-ls.lua | 18 ++-- 3 files changed, 142 insertions(+), 201 deletions(-) diff --git a/init.lua b/init.lua index 2e6d9d33..4ccba6b8 100644 --- a/init.lua +++ b/init.lua @@ -1,92 +1,109 @@ --- ~/.config/nvim/init.lua require 'core.options' -- Load general options require 'core.keymaps' -- Load general keymaps require 'core.snippets' -- Custom code snippets --- Install package manager (lazy.nvim) -local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim' +-- Install package manager +local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim' if not vim.loop.fs_stat(lazypath) then - vim.fn.system({ - 'git', 'clone', '--filter=blob:none', + vim.fn.system { + 'git', + 'clone', + '--filter=blob:none', 'https://github.com/folke/lazy.nvim.git', - '--branch=stable', lazypath, - }) + '--branch=stable', -- latest stable release + lazypath, + } end vim.opt.rtp:prepend(lazypath) - --- Filetypes vim.filetype.add({ - extension = { templ = 'templ' }, + extension = { + templ = "templ", + } }) --- Theme selection (robust against unknown NVIM_THEME) +-- Import color theme based on environment variable NVIM_THEME local default_color_scheme = 'quantum' -local env_var_nvim_theme = os.getenv('NVIM_THEME') or default_color_scheme +local env_var_nvim_theme = os.getenv 'NVIM_THEME' or default_color_scheme + +-- Define a table of theme modules local themes = { quantum = 'plugins.themes.quantum', - nord = 'plugins.themes.nord', + nord = 'plugins.themes.nord', onedark = 'plugins.themes.onedark', } -local theme_module = themes[env_var_nvim_theme] or themes[default_color_scheme] --- Plugins +-- Setup plugins require('lazy').setup({ - require(theme_module), + require(themes[env_var_nvim_theme]), require 'core.ui', - -- Load mason early so tools are ready for LSP configs - require 'plugins.mason', - - -- Core dev UX - require 'plugins.treesitter', - require 'plugins.telescope', - require 'plugins.lualine', + require 'plugins.aerial', + require 'plugins.autoformat', + require 'plugins.flash', + require 'plugins.autocompletion', require 'plugins.bufferline', + require 'plugins.comment', + require 'plugins.conform', + require 'plugins.database', + require 'plugins.debug', + require 'plugins.gitsigns', + require 'plugins.harpoon', + require 'plugins.lazygit', + require 'plugins.lsp', + require 'plugins.lualine', + require 'plugins.none-ls', require 'plugins.indent-blankline', require 'plugins.neo-tree', + require 'plugins.misc', + require 'plugins.mason', + require 'plugins.snack', + require 'plugins.telescope', require 'plugins.toggleterm', + require 'plugins.treesitter', require 'plugins.vim-tmux-navigator', require 'plugins.zellij', - require 'plugins.flash', - require 'plugins.comment', - require 'plugins.harpoon', - require 'plugins.gitsigns', - require 'plugins.lazygit', - require 'plugins.aerial', - require 'plugins.misc', - -- LSP & companions - require 'plugins.autocompletion', - require 'plugins.lsp', - require 'plugins.none-ls', -- none-ls/null-ls sources & setup - require 'plugins.autoformat', -- your autoformat-on-save/idle logic - - -- Optional: pick one formatter stack. If you keep Conform, - -- ensure it doesn't also format Go on save to avoid double-format. - require 'plugins.conform', - - -- Debugging / DB (as you had) - require 'plugins.debug', - require 'plugins.database', }, { ui = { + -- If you have a Nerd Font, set icons to an empty table which will use the + -- default lazy.nvim defined Nerd Font icons otherwise define a unicode icons table icons = vim.g.have_nerd_font and {} or { - cmd = '⌘', config = 'πŸ› ', event = 'πŸ“…', ft = 'πŸ“‚', init = 'βš™', - keys = 'πŸ—', plugin = 'πŸ”Œ', runtime = 'πŸ’»', require = 'πŸŒ™', - source = 'πŸ“„', start = 'πŸš€', task = 'πŸ“Œ', lazy = 'πŸ’€ ', + cmd = '⌘', + config = 'πŸ› ', + event = 'πŸ“…', + ft = 'πŸ“‚', + init = 'βš™', + keys = 'πŸ—', + plugin = 'πŸ”Œ', + runtime = 'πŸ’»', + require = 'πŸŒ™', + source = 'πŸ“„', + start = 'πŸš€', + task = 'πŸ“Œ', + lazy = 'πŸ’€ ', }, }, }) --- (Optional) tiny helper if you ever want to source a session file +-- Function to check if a file exists local function file_exists(file) local f = io.open(file, 'r') - if f then f:close(); return true end - return false + if f then + f:close() + return true + else + return false + end end --- local session_file = '.session.vim' --- if file_exists(session_file) then vim.cmd('source ' .. session_file) end +-- Path to the session file +local session_file = '.session.vim' +-- Check if the session file exists in the current directory +-- if file_exists(session_file) then +-- -- Source the session file +-- vim.cmd('source ' .. session_file) +-- end + +-- The line beneath this is called `modeline`. See `:help modeline` -- vim: ts=2 sts=2 sw=2 et - diff --git a/lua/plugins/lsp.lua b/lua/plugins/lsp.lua index 016b536e..a91d1fb3 100644 --- a/lua/plugins/lsp.lua +++ b/lua/plugins/lsp.lua @@ -2,162 +2,75 @@ return { { "neovim/nvim-lspconfig", - lazy = false, config = function() - local lspconfig = require("lspconfig") - local util = require("lspconfig.util") - local configs = require("lspconfig.configs") - - -- Recognize .templ files - vim.filetype.add({ extension = { templ = "templ" } }) - - -- ============================== - -- gopls - -- ============================== - lspconfig.gopls.setup({ - root_dir = function(fname) - return util.root_pattern("go.work", "go.mod", ".git")(fname) - or util.path.dirname(fname) - end, - handlers = { - ["textDocument/signatureHelp"] = function(err, result, ctx, config) - if err and err.message and err.message:find("cannot get type") then - return - end - return vim.lsp.handlers["textDocument/signatureHelp"](err, result, ctx, config) - end, - }, - 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", - "-dist","-build","-out","-coverage","-tmp","-.cache", - }, - semanticTokens = true, - memoryMode = "DegradeClosed", - symbolMatcher = "FastFuzzy", - ["ui.completion.experimentalPostfixCompletions"] = false, - }, - }, - }) - - -- ============================== - -- TypeScript / JavaScript (ts_ls OR tsserver fallback) - -- ============================== - local ts_server = lspconfig.ts_ls or lspconfig.tsserver - if ts_server then - ts_server.setup({}) - end - - -- ============================== - -- Astro (guard if missing) - -- ============================== - if lspconfig.astro then - local function get_typescript_lib() - local mason_ts = vim.fs.normalize( - "~/.local/share/nvim/mason/packages/typescript-language-server/node_modules/typescript/lib" - ) - if vim.fn.isdirectory(mason_ts) == 1 then return mason_ts end - - local global_ts = (vim.fn.system("npm root -g"):gsub("\n", "")) .. "/typescript/lib" - if vim.fn.isdirectory(global_ts) == 1 then return global_ts end - - return vim.fs.normalize( - "~/.local/share/nvim/mason/packages/astro-language-server/node_modules/typescript/lib" - ) - end - - lspconfig.astro.setup({ - init_options = { typescript = { tsdk = get_typescript_lib() } }, - }) - end - - -- ============================== - -- templ (register config if missing) - -- ============================== - if not configs.templ then - configs.templ = { - default_config = { - cmd = { "templ", "lsp" }, - filetypes = { "templ" }, - root_dir = util.root_pattern("go.mod", ".git"), - single_file_support = true, - }, - } - end - lspconfig.templ.setup({}) - - -- ============================== - -- Utilities - -- ============================== + --------------------------------------------------------------------------- + -- LSP client utilities + --------------------------------------------------------------------------- vim.api.nvim_create_user_command("LspClients", function() + -- new API: vim.lsp.get_clients local clients = vim.lsp.get_clients() - local counts = {} - for _, c in ipairs(clients) do - counts[c.name] = (counts[c.name] or 0) + 1 + local client_counts = {} + + for _, client in ipairs(clients) do + client_counts[client.name] = (client_counts[client.name] or 0) + 1 end + print("=== Active LSP Clients ===") - for name, n in pairs(counts) do - local dup = n > 1 and " ⚠️ DUPLICATE" or " βœ…" - print(string.format("%s: %d client(s)%s", name, n, dup)) + for name, count in pairs(client_counts) do + local status = count > 1 and " ⚠️ DUPLICATE" or " βœ…" + print(string.format("%s: %d client(s)%s", name, count, status)) end - if next(counts) == nil then print("No active LSP clients") end - end, {}) + + if next(client_counts) == nil then + print("No active LSP clients") + end + end, { desc = "Show active LSP clients and detect duplicates" }) vim.api.nvim_create_user_command("LspKillDuplicates", function() + -- only worry about duplicate gopls, since that’s your main concern local gopls_clients = vim.lsp.get_clients({ name = "gopls" }) if #gopls_clients <= 1 then print("No duplicate gopls clients found") return end - local keep, kill = nil, {} - for _, c in ipairs(gopls_clients) do - local cnt = 0 - if c.config.settings and c.config.settings.gopls then - for _ in pairs(c.config.settings.gopls) do cnt = cnt + 1 end - end - if cnt > 0 and not keep then keep = c else table.insert(kill, c) end - end - for _, c in ipairs(kill) do - print(("Killing duplicate gopls client (id: %d)"):format(c.id)) - c.stop(true) - end - if keep then print(("Kept gopls client (id: %d) with settings"):format(keep.id)) end - end, {}) + local client_to_keep = nil + local clients_to_kill = {} + + for _, client in ipairs(gopls_clients) do + local settings_count = 0 + if client.config.settings and client.config.settings.gopls then + for _ in pairs(client.config.settings.gopls) do + settings_count = settings_count + 1 + end + end + + if settings_count > 0 and not client_to_keep then + client_to_keep = client + else + table.insert(clients_to_kill, client) + end + end + + for _, client in ipairs(clients_to_kill) do + print(string.format("Killing duplicate gopls client (id: %d)", client.id)) + client.stop(true) + end + + if client_to_keep then + print(string.format("Kept gopls client (id: %d) with settings", client_to_keep.id)) + end + end, { desc = "Kill duplicate gopls clients" }) + + --------------------------------------------------------------------------- + -- Hover safety + keymaps + --------------------------------------------------------------------------- local function has_hover(bufnr) - for _, c in pairs(vim.lsp.get_active_clients({ bufnr = bufnr })) do - if c.server_capabilities and c.server_capabilities.hoverProvider then return true end + local clients = vim.lsp.get_clients({ bufnr = bufnr }) + for _, c in pairs(clients) do + if c.server_capabilities and c.server_capabilities.hoverProvider then + return true + end end return false end @@ -172,11 +85,16 @@ return { vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, desc = desc }) end + -- K = hover (safe) buf_map("n", "K", function() - if not has_hover(bufnr) then return end + if not has_hover(bufnr) then + return + end local ok, saga_hover = pcall(require, "lspsaga.hover") if ok and saga_hover and saga_hover.render_hover_doc then - pcall(function() saga_hover:render_hover_doc() end) + pcall(function() + saga_hover:render_hover_doc() + end) else pcall(vim.lsp.buf.hover) end diff --git a/lua/plugins/none-ls.lua b/lua/plugins/none-ls.lua index adea9a69..48c58785 100644 --- a/lua/plugins/none-ls.lua +++ b/lua/plugins/none-ls.lua @@ -5,33 +5,39 @@ return { dependencies = { "williamboman/mason.nvim", "jay-babu/mason-null-ls.nvim", - "nvimtools/none-ls-extras.nvim", -- optional + "nvimtools/none-ls-extras.nvim", -- required for eslint_d now }, config = function() local null_ls = require("null-ls") + -- eslint_d now comes from none-ls-extras, NOT null_ls.builtins + local eslint_d = require("none-ls.diagnostics.eslint_d") + null_ls.setup({ sources = { -- Go null_ls.builtins.formatting.gofumpt, - null_ls.builtins.formatting.golines, -- optional - null_ls.builtins.diagnostics.golangci_lint, -- if installed + null_ls.builtins.formatting.golines, -- optional + null_ls.builtins.diagnostics.golangci_lint, -- if installed - -- Web (keep only what you use) + -- Web null_ls.builtins.formatting.prettierd, - null_ls.builtins.diagnostics.eslint_d, + eslint_d, }, }) require("mason-null-ls").setup({ ensure_installed = { "gofumpt", "golines", "golangci-lint", "prettierd", "eslint_d" }, automatic_installation = true, + -- leaving automatic_setup at default (false) so it doesn't double-register sources }) -- (optional) format on save for Go vim.api.nvim_create_autocmd("BufWritePre", { pattern = "*.go", - callback = function() vim.lsp.buf.format({ async = false }) end, + callback = function() + vim.lsp.buf.format({ async = false }) + end, }) end, }