updated lsp to default to mason

This commit is contained in:
Walter Jenkins 2025-11-14 13:28:15 -06:00
parent 4564d7093e
commit 80b5d665fe
3 changed files with 142 additions and 201 deletions

117
init.lua
View File

@ -1,92 +1,109 @@
-- ~/.config/nvim/init.lua
require 'core.options' -- Load general options require 'core.options' -- Load general options
require 'core.keymaps' -- Load general keymaps require 'core.keymaps' -- Load general keymaps
require 'core.snippets' -- Custom code snippets require 'core.snippets' -- Custom code snippets
-- Install package manager (lazy.nvim) -- Install package manager
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim' local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim'
if not vim.loop.fs_stat(lazypath) then if not vim.loop.fs_stat(lazypath) then
vim.fn.system({ vim.fn.system {
'git', 'clone', '--filter=blob:none', 'git',
'clone',
'--filter=blob:none',
'https://github.com/folke/lazy.nvim.git', 'https://github.com/folke/lazy.nvim.git',
'--branch=stable', lazypath, '--branch=stable', -- latest stable release
}) lazypath,
}
end end
vim.opt.rtp:prepend(lazypath) vim.opt.rtp:prepend(lazypath)
-- Filetypes
vim.filetype.add({ 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 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 = { local themes = {
quantum = 'plugins.themes.quantum', quantum = 'plugins.themes.quantum',
nord = 'plugins.themes.nord', nord = 'plugins.themes.nord',
onedark = 'plugins.themes.onedark', onedark = 'plugins.themes.onedark',
} }
local theme_module = themes[env_var_nvim_theme] or themes[default_color_scheme]
-- Plugins -- Setup plugins
require('lazy').setup({ require('lazy').setup({
require(theme_module), require(themes[env_var_nvim_theme]),
require 'core.ui', require 'core.ui',
-- Load mason early so tools are ready for LSP configs require 'plugins.aerial',
require 'plugins.mason', require 'plugins.autoformat',
require 'plugins.flash',
-- Core dev UX require 'plugins.autocompletion',
require 'plugins.treesitter',
require 'plugins.telescope',
require 'plugins.lualine',
require 'plugins.bufferline', 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.indent-blankline',
require 'plugins.neo-tree', require 'plugins.neo-tree',
require 'plugins.misc',
require 'plugins.mason',
require 'plugins.snack',
require 'plugins.telescope',
require 'plugins.toggleterm', require 'plugins.toggleterm',
require 'plugins.treesitter',
require 'plugins.vim-tmux-navigator', require 'plugins.vim-tmux-navigator',
require 'plugins.zellij', 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 = { 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 { icons = vim.g.have_nerd_font and {} or {
cmd = '', config = '🛠', event = '📅', ft = '📂', init = '', cmd = '',
keys = '🗝', plugin = '🔌', runtime = '💻', require = '🌙', config = '🛠',
source = '📄', start = '🚀', task = '📌', lazy = '💤 ', 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 function file_exists(file)
local f = io.open(file, 'r') local f = io.open(file, 'r')
if f then f:close(); return true end if f then
f:close()
return true
else
return false return false
end end
end
-- local session_file = '.session.vim' -- Path to the session file
-- if file_exists(session_file) then vim.cmd('source ' .. session_file) end 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 -- vim: ts=2 sts=2 sw=2 et

View File

@ -2,162 +2,75 @@
return { return {
{ {
"neovim/nvim-lspconfig", "neovim/nvim-lspconfig",
lazy = false,
config = function() config = function()
local lspconfig = require("lspconfig") ---------------------------------------------------------------------------
local util = require("lspconfig.util") -- LSP client utilities
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
-- ==============================
vim.api.nvim_create_user_command("LspClients", function() vim.api.nvim_create_user_command("LspClients", function()
-- new API: vim.lsp.get_clients
local clients = vim.lsp.get_clients() local clients = vim.lsp.get_clients()
local counts = {} local client_counts = {}
for _, c in ipairs(clients) do
counts[c.name] = (counts[c.name] or 0) + 1 for _, client in ipairs(clients) do
client_counts[client.name] = (client_counts[client.name] or 0) + 1
end end
print("=== Active LSP Clients ===") print("=== Active LSP Clients ===")
for name, n in pairs(counts) do for name, count in pairs(client_counts) do
local dup = n > 1 and " ⚠️ DUPLICATE" or "" local status = count > 1 and " ⚠️ DUPLICATE" or ""
print(string.format("%s: %d client(s)%s", name, n, dup)) print(string.format("%s: %d client(s)%s", name, count, status))
end 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() vim.api.nvim_create_user_command("LspKillDuplicates", function()
-- only worry about duplicate gopls, since thats your main concern
local gopls_clients = vim.lsp.get_clients({ name = "gopls" }) local gopls_clients = vim.lsp.get_clients({ name = "gopls" })
if #gopls_clients <= 1 then if #gopls_clients <= 1 then
print("No duplicate gopls clients found") print("No duplicate gopls clients found")
return return
end 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) local function has_hover(bufnr)
for _, c in pairs(vim.lsp.get_active_clients({ bufnr = bufnr })) do local clients = vim.lsp.get_clients({ bufnr = bufnr })
if c.server_capabilities and c.server_capabilities.hoverProvider then return true end for _, c in pairs(clients) do
if c.server_capabilities and c.server_capabilities.hoverProvider then
return true
end
end end
return false return false
end end
@ -172,11 +85,16 @@ return {
vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, desc = desc }) vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, desc = desc })
end end
-- K = hover (safe)
buf_map("n", "K", function() 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") local ok, saga_hover = pcall(require, "lspsaga.hover")
if ok and saga_hover and saga_hover.render_hover_doc then 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 else
pcall(vim.lsp.buf.hover) pcall(vim.lsp.buf.hover)
end end

View File

@ -5,11 +5,14 @@ return {
dependencies = { dependencies = {
"williamboman/mason.nvim", "williamboman/mason.nvim",
"jay-babu/mason-null-ls.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() config = function()
local null_ls = require("null-ls") 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({ null_ls.setup({
sources = { sources = {
-- Go -- Go
@ -17,21 +20,24 @@ return {
null_ls.builtins.formatting.golines, -- optional null_ls.builtins.formatting.golines, -- optional
null_ls.builtins.diagnostics.golangci_lint, -- if installed null_ls.builtins.diagnostics.golangci_lint, -- if installed
-- Web (keep only what you use) -- Web
null_ls.builtins.formatting.prettierd, null_ls.builtins.formatting.prettierd,
null_ls.builtins.diagnostics.eslint_d, eslint_d,
}, },
}) })
require("mason-null-ls").setup({ require("mason-null-ls").setup({
ensure_installed = { "gofumpt", "golines", "golangci-lint", "prettierd", "eslint_d" }, ensure_installed = { "gofumpt", "golines", "golangci-lint", "prettierd", "eslint_d" },
automatic_installation = true, automatic_installation = true,
-- leaving automatic_setup at default (false) so it doesn't double-register sources
}) })
-- (optional) format on save for Go -- (optional) format on save for Go
vim.api.nvim_create_autocmd("BufWritePre", { vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.go", pattern = "*.go",
callback = function() vim.lsp.buf.format({ async = false }) end, callback = function()
vim.lsp.buf.format({ async = false })
end,
}) })
end, end,
} }