feat(ui): modernize nvim with fzf-lua, snacks dashboard, and p10k-style statusline

- Replace Snacks picker with fzf-lua for LSP navigation (gd, gr, gi, gt)
- Add fzf-lua plugin with LSP-optimized settings
- Fix Mason inconsistencies (add eslint, gopls to ensure_installed)
- Replace alpha-nvim with Snacks dashboard (shared config)
- Create dashboard_config.lua for DRY dashboard settings
- Modernize lualine with p10k-rainbow style and solid backgrounds
- Enhance bufferline with LSP diagnostics and modern styling
- Update noice with centered cmdline/search and modern icons
- Add global rounded borders for floating windows
- Improve indent-blankline with scope highlighting
- Add return-to-dashboard on last buffer close
- Create performance_monitor module

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-12 18:59:46 -05:00
parent 2382297a6d
commit 89cba7056e
14 changed files with 998 additions and 224 deletions

View File

@@ -0,0 +1,66 @@
-- Shared dashboard configuration for alpha-nvim and snacks.dashboard
local M = {}
M.header = {
" ██████╗ █████╗ ██████╗ ██████╗ ██████╗ ███████╗██╗ ██╗ ",
" █╔════╝██╔══██╗██╔══██╗██╔════╝ ██╔══██╗██╔════╝██║ ██║ ",
" █║ ███████║██████╔╝██║ ███╗██║ ██║█████╗ ██║ ██║ ",
" █║ ██╔══██║██╔══██╗██║ ██║██║ ██║██╔══╝ ╚██╗ ██╔╝ ",
" ██████╗██║ ██║██║ ██║╚██████╔╝██████╔╝███████╗ ╚████╔╝ ",
" ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═══╝ ",
" ",
" ███╗ ██╗███████╗ ██████╗ ██╗ ██╗██╗███╗ ███╗ ",
" ████╗ ██║██╔════╝██╔═══██╗██║ ██║██║████╗ ████║ ",
" ██╔██╗ ██║█████╗ ██║ ██║██║ ██║██║██╔████╔██║ ",
" ██║╚██╗██║██╔══╝ ██║ ██║╚██╗ ██╔╝██║██║╚██╔╝██║ ",
" ██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║ ",
" ╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ ",
}
-- Menu items (single column)
M.menu_items = {
{ key = "f", icon = " ", desc = "Find File", action = ":FzfLua files" },
{ key = "n", icon = " ", desc = "New File", action = ":ene | startinsert" },
{ key = "g", icon = " ", desc = "Find Text", action = ":FzfLua live_grep" },
{ key = "r", icon = " ", desc = "Recent Files", action = ":FzfLua oldfiles" },
{ key = "t", icon = " ", desc = "File Tree", action = ":NvimTreeToggle" },
{ key = "L", icon = "󰒲 ", desc = "Lazy", action = ":Lazy" },
{ key = "m", icon = " ", desc = "Mason", action = ":Mason" },
{ key = "q", icon = " ", desc = "Quit", action = ":qa" },
}
-- Get header as string (for snacks)
function M.get_header_string()
return table.concat(M.header, "\n")
end
-- Helper to convert menu items to snacks format
local function to_snacks_keys(items)
local keys = {}
for _, item in ipairs(items) do
table.insert(keys, {
icon = item.icon,
key = item.key,
desc = item.desc,
action = item.action,
})
end
return keys
end
-- Get snacks dashboard keys format
function M.get_snacks_keys()
return to_snacks_keys(M.menu_items)
end
-- Get alpha buttons (requires alpha dashboard module)
function M.get_alpha_buttons(dashboard)
local buttons = {}
for _, item in ipairs(M.menu_items) do
local cmd = item.action:gsub("^:", "<cmd>") .. "<CR>"
table.insert(buttons, dashboard.button(item.key, item.icon .. " " .. item.desc, cmd))
end
return buttons
end
return M

View File

@@ -0,0 +1,112 @@
local M = {}
-- Check overall Neovim performance
function M.check_performance()
local stats = {}
-- Startup time
stats.startup = vim.fn.reltimefloat(vim.fn.reltime(vim.g.start_time or vim.fn.reltime()))
-- Memory usage
local mem = collectgarbage("count")
stats.memory_kb = math.floor(mem)
stats.memory_mb = string.format("%.2f", mem / 1024)
-- Buffer count
stats.buffers = #vim.fn.getbufinfo({ buflisted = 1 })
-- Loaded plugins (lazy.nvim)
local lazy_ok, lazy = pcall(require, "lazy")
if lazy_ok then
local lazy_stats = lazy.stats()
stats.plugins_loaded = lazy_stats.loaded
stats.plugins_total = lazy_stats.count
stats.startup_ms = string.format("%.2f", lazy_stats.startuptime)
end
-- Active LSP clients
stats.lsp_clients = #vim.lsp.get_clients()
-- Display results
local lines = {
"Performance Stats",
string.rep("-", 40),
string.format("Memory Usage: %s MB (%d KB)", stats.memory_mb, stats.memory_kb),
string.format("Open Buffers: %d", stats.buffers),
string.format("Active LSP Clients: %d", stats.lsp_clients),
}
if stats.plugins_loaded then
table.insert(lines, string.format("Plugins: %d/%d loaded", stats.plugins_loaded, stats.plugins_total))
table.insert(lines, string.format("Startup Time: %s ms", stats.startup_ms))
end
vim.notify(table.concat(lines, "\n"), vim.log.levels.INFO, { title = "Performance" })
end
-- Check LSP health for current buffer
function M.check_lsp_health()
local clients = vim.lsp.get_clients({ bufnr = 0 })
if #clients == 0 then
vim.notify("No LSP clients attached to this buffer", vim.log.levels.WARN, { title = "LSP Health" })
return
end
local lines = {
"LSP Health Report",
string.rep("-", 40),
}
for _, client in ipairs(clients) do
table.insert(lines, "")
table.insert(lines, string.format("Client: %s (id: %d)", client.name, client.id))
table.insert(lines, string.format(" Root: %s", client.config.root_dir or "N/A"))
-- Check capabilities
local caps = {}
if client.server_capabilities.completionProvider then
table.insert(caps, "completion")
end
if client.server_capabilities.hoverProvider then
table.insert(caps, "hover")
end
if client.server_capabilities.definitionProvider then
table.insert(caps, "definition")
end
if client.server_capabilities.referencesProvider then
table.insert(caps, "references")
end
if client.server_capabilities.documentFormattingProvider then
table.insert(caps, "formatting")
end
if client.server_capabilities.codeActionProvider then
table.insert(caps, "code_actions")
end
table.insert(lines, string.format(" Capabilities: %s", table.concat(caps, ", ")))
end
-- Diagnostics summary
local diagnostics = vim.diagnostic.get(0)
local counts = { errors = 0, warnings = 0, hints = 0, info = 0 }
for _, d in ipairs(diagnostics) do
if d.severity == vim.diagnostic.severity.ERROR then
counts.errors = counts.errors + 1
elseif d.severity == vim.diagnostic.severity.WARN then
counts.warnings = counts.warnings + 1
elseif d.severity == vim.diagnostic.severity.HINT then
counts.hints = counts.hints + 1
elseif d.severity == vim.diagnostic.severity.INFO then
counts.info = counts.info + 1
end
end
table.insert(lines, "")
table.insert(lines, string.format("Diagnostics: %d errors, %d warnings, %d hints, %d info",
counts.errors, counts.warnings, counts.hints, counts.info))
vim.notify(table.concat(lines, "\n"), vim.log.levels.INFO, { title = "LSP Health" })
end
return M

View File

@@ -2,26 +2,26 @@
local keymap = vim.keymap
-- =============================================================================
-- LSP NAVIGATION (FUNCTION NAVIGATION)
-- LSP NAVIGATION (FUNCTION NAVIGATION) - Using fzf-lua
-- =============================================================================
-- Primary LSP navigation
keymap.set("n", "gd", "<cmd>lua require('snacks.picker').lsp_definitions()<cr>", { desc = "Go to definition" })
keymap.set("n", "gi", "<cmd>lua require('snacks.picker').lsp_implementations()<cr>", { desc = "Go to implementation" })
keymap.set("n", "gr", "<cmd>lua require('snacks.picker').lsp_references()<cr>", { desc = "Show references" })
keymap.set("n", "gt", "<cmd>lua require('snacks.picker').lsp_type_definitions()<cr>", { desc = "Go to type definition" })
keymap.set("n", "gd", "<cmd>FzfLua lsp_definitions<cr>", { desc = "Go to definition" })
keymap.set("n", "gi", "<cmd>FzfLua lsp_implementations<cr>", { desc = "Go to implementation" })
keymap.set("n", "gr", "<cmd>FzfLua lsp_references<cr>", { desc = "Show references" })
keymap.set("n", "gt", "<cmd>FzfLua lsp_typedefs<cr>", { desc = "Go to type definition" })
-- Symbol search
keymap.set("n", "<leader>ds", "<cmd>lua require('snacks.picker').lsp_document_symbols()<cr>", { desc = "Document symbols" })
keymap.set("n", "<leader>ws", "<cmd>lua require('snacks.picker').lsp_workspace_symbols()<cr>", { desc = "Workspace symbols" })
keymap.set("n", "<leader>ds", "<cmd>FzfLua lsp_document_symbols<cr>", { desc = "Document symbols" })
keymap.set("n", "<leader>ws", "<cmd>FzfLua lsp_workspace_symbols<cr>", { desc = "Workspace symbols" })
-- Code actions and documentation
keymap.set("n", "<leader>ca", "<cmd>lua vim.lsp.buf.code_action()<cr>", { desc = "Code actions" })
keymap.set("n", "<leader>ca", "<cmd>FzfLua lsp_code_actions<cr>", { desc = "Code actions" })
keymap.set("n", "<leader>rn", "<cmd>lua vim.lsp.buf.rename()<cr>", { desc = "Rename" })
keymap.set("n", "K", "<cmd>lua vim.lsp.buf.hover()<cr>", { desc = "Hover documentation" })
-- Diagnostics
keymap.set("n", "<leader>D", "<cmd>lua require('snacks.picker').diagnostics()<cr>", { desc = "Show diagnostics" })
keymap.set("n", "<leader>D", "<cmd>FzfLua diagnostics_workspace<cr>", { desc = "Show diagnostics" })
keymap.set("n", "<leader>dd", "<cmd>lua vim.diagnostic.open_float()<cr>", { desc = "Line diagnostics" })
keymap.set("n", "[d", "<cmd>lua vim.diagnostic.goto_prev()<cr>", { desc = "Previous diagnostic" })
keymap.set("n", "]d", "<cmd>lua vim.diagnostic.goto_next()<cr>", { desc = "Next diagnostic" })

View File

@@ -242,3 +242,57 @@ vim.api.nvim_create_autocmd("FileType", {
end
end,
})
-- =============================================================================
-- RETURN TO DASHBOARD ON LAST BUFFER CLOSE
-- =============================================================================
-- When closing the last buffer, return to dashboard instead of quitting
vim.api.nvim_create_autocmd("BufDelete", {
callback = function()
local bufs = vim.tbl_filter(function(b)
return vim.api.nvim_buf_is_valid(b)
and vim.bo[b].buflisted
and vim.api.nvim_buf_get_name(b) ~= ""
end, vim.api.nvim_list_bufs())
-- If this is the last listed buffer, open dashboard
if #bufs <= 1 then
vim.schedule(function()
local ok, snacks = pcall(require, "snacks")
if ok and snacks.dashboard then
snacks.dashboard()
end
end)
end
end,
})
-- =============================================================================
-- MODERN UI: GLOBAL ROUNDED BORDERS
-- =============================================================================
-- Rounded border style for all floating windows
local border = "rounded"
-- Override default floating window border
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { border = border })
vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, { border = border })
-- Diagnostic floating window border
vim.diagnostic.config({
float = {
border = border,
source = "always",
header = "",
prefix = "",
},
})
-- Set global float border highlight
vim.api.nvim_set_hl(0, "FloatBorder", { fg = "#7aa2f7", bg = "NONE" })
vim.api.nvim_set_hl(0, "NormalFloat", { bg = "#1a1b26" })
-- Modern cursor line (subtle highlight)
vim.api.nvim_set_hl(0, "CursorLine", { bg = "#292e42" })
vim.api.nvim_set_hl(0, "CursorLineNr", { fg = "#7aa2f7", bold = true })