From 978970eda6c7f95cef965d48840938ae959b1eaa Mon Sep 17 00:00:00 2001 From: dotfiles-core sync bot <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2026 02:31:07 +0000 Subject: [PATCH 1/2] Squashed 'core/' changes from e9cf907..da15412 da15412 Merge pull request #243 from dotgibson/release/v3.8.0 6db0425 release v3.8.0 b8c9968 Merge pull request #242 from dotgibson/feat/nvim-nvchad-ui-refactor cb7cb39 fix(nvim): address PR review on taplo roots, oi guard, sig cap, bufferline underline 37f5032 feat(nvim): NvChad-styled UI refactor + LSP/fold/root fixes git-subtree-dir: core git-subtree-split: da1541206be1f7e0b36a1592a1a775ae6f2f0bf0 --- CHANGELOG.md | 47 ++++++++++ core.version | 2 +- nvim/lua/gerrrt/cheatsheet.lua | 58 +++++++++--- nvim/lua/gerrrt/config/autocmds.lua | 14 ++- nvim/lua/gerrrt/config/options.lua | 8 +- nvim/lua/gerrrt/plugins/bufferline-nvim.lua | 62 +++++++++++++ nvim/lua/gerrrt/plugins/lualine-nvim.lua | 50 ++++++++++- nvim/lua/gerrrt/plugins/mini-nvim.lua | 5 +- nvim/lua/gerrrt/plugins/nvim-ufo.lua | 10 ++- nvim/lua/gerrrt/plugins/which-key.lua | 15 ++++ nvim/lua/gerrrt/servers/cssls.lua | 7 +- nvim/lua/gerrrt/servers/html.lua | 6 +- nvim/lua/gerrrt/servers/taplo.lua | 12 ++- nvim/lua/gerrrt/utils/diagnostics.lua | 12 ++- nvim/lua/gerrrt/utils/lsp.lua | 99 +++++++++++++++++++-- nvim/lua/gerrrt/utils/ui-highlights.lua | 12 +++ 16 files changed, 371 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe6dce3..9ed29d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,53 @@ commit (`git tag -a vX.Y.Z -m vX.Y.Z`). ## [Unreleased] +## [v3.8.0] - 2026-07-18 + +### Changed + +- **Neovim UI moves to an NvChad-styled statusline + bufferline.** `lualine` now uses a + hand-built theme derived from tokyonight's resolved palette (mode/location render as rounded + accent **pills**, git/cwd as a lighter block, filename on the base run) instead of the bundled + `tokyonight` theme — so the blocks read as opaque islands on the transparent bar and follow + NvChad's structure. `bufferline` gains palette-aware highlights so the active buffer lifts as a + subtle raised block with an accent underline while inactive buffers dim into the bar. Both are + computed at plugin-load (pcall-guarded), so a fresh box falls back to the bundled/auto theming. +- **LSP hover, signature help, and the diagnostic float share one padded, rounded card style.** + Hover/signature pass an explicit rounded border with width/height caps (a huge docstring becomes + a tidy box); the diagnostic float drops its header row and gains a left pad + width cap. + Signature help now also pops **automatically** when you rest inside a function's arguments + (`CursorHoldI`, gated on server support, suppressed while the completion menu is open). +- **`which-key` and the `?` cheatsheet restyled to mirror NvChad's keymap visualizer.** + which-key gets a minimal rounded, padded, left-aligned column popup with NvChad-palette colors + (blue keys, red descriptions, green groups); the cheatsheet's category headings become + full-width accent **pill** bars (cycling colors) with blue keys — both palette-aware, with a + semantic-link fallback on a bare box. + +### Fixed + +- **Neovim `taplo` (TOML) root detection.** `root_markers` listed the glob `"*.toml"`, which + `vim.fs.root`/`vim.fs.find` do not support — so it never matched and taplo always fell back to + `.git`, giving a lone TOML file outside a repo a cwd root. Replaced with real manifest names + (`pyproject.toml`, `Cargo.toml`, `foundry.toml`, `taplo.toml`, `.taplo.toml`, `.git`). +- **Neovim `oi` (organize imports) no longer binds where it can't work or races the + formatter.** A server that ENUMERATES its code-action kinds without `source[.organizeImports]` is + now skipped (so the map no longer silently no-ops on e.g. `lua_ls`); a server that only reports a + bare `true` or a provider table without kinds still gets the map, since it can't be ruled out. The + racy fixed-`50ms` post-format timer is dropped — formatting stays owned by format-on-save and + `cf`. +- **Neovim cursor-restore skips commit buffers.** `gitcommit`/`gitrebase` buffers open at the top + again instead of jumping to a stale mark from a previous commit. +- **Neovim folding has a single owner.** `nvim-ufo` computes folds via its own treesitter+indent + providers, so the global `foldmethod=expr` + treesitter `foldexpr` in `options.lua` was redundant + per-buffer work (UFO never reads `foldexpr`). Dropped it; UFO now owns folding outright. + +### Changed (internal) + +- Deduped the identical `snippetSupport` capability boilerplate in the Neovim `html`/`cssls` + server specs into a shared `utils.lsp.with_snippets` helper; standardized autocmd augroups on an + explicit `{ clear = true }`; corrected a stale comment claiming no `vim.notify`-competing + notifier is installed (fidget is, for LSP progress only — no clash). + ## [v3.7.0] - 2026-07-17 ### Changed diff --git a/core.version b/core.version index 7c69a55..1981190 100644 --- a/core.version +++ b/core.version @@ -1 +1 @@ -3.7.0 +3.8.0 diff --git a/nvim/lua/gerrrt/cheatsheet.lua b/nvim/lua/gerrrt/cheatsheet.lua index 5704421..7c767da 100644 --- a/nvim/lua/gerrrt/cheatsheet.lua +++ b/nvim/lua/gerrrt/cheatsheet.lua @@ -236,21 +236,46 @@ local HL = { footer = "GerrrtCheatFooter", } +-- Number of accent "pill" highlight groups; cards cycle through them by index so adjacent headings +-- get different colors — NvChad's grid look (black text on a vivid accent bar). Populated by +-- define_highlights when the palette is available; 0 on a bare box (headings fall back to plain). +local NPILL = 0 + local function define_highlights() - -- default = true so a user's colorscheme / overrides always win. Linked to semantic groups - -- every theme defines, so it inherits tokyonight here and Just Works on a bare box too. local set = vim.api.nvim_set_hl - set(0, HL.title, { link = "Title", default = true }) - set(0, HL.key, { link = "Constant", default = true }) - set(0, HL.rule, { link = "Comment", default = true }) - set(0, HL.sep, { link = "Comment", default = true }) - set(0, HL.footer, { link = "Comment", default = true }) + -- Prefer the tokyonight palette so the panel reads as NvChad's grid: heading bars in cycling + -- accents, blue keys, dim rules/footer. pcall so a fresh box (tokyonight not loaded) degrades to + -- the old semantic links instead of erroring — the cheatsheet is deps-free by design. + local ok, c = pcall(function() + return require("tokyonight.colors").setup({ style = "storm" }) -- mirror plugins/theme.lua + end) + if ok and type(c) == "table" then + local accents = { c.blue, c.green, c.magenta, c.cyan, c.orange, c.yellow, c.red, c.teal, c.purple } + for i, accent in ipairs(accents) do + -- black (bg_dark) bold text on a vivid accent → the pill heading + set(0, "GerrrtCheatPill" .. i, { fg = c.bg_dark, bg = accent, bold = true }) + end + NPILL = #accents + set(0, HL.title, { fg = c.blue, bold = true }) -- heading fallback (unused when NPILL>0) + set(0, HL.key, { fg = c.blue, bold = true }) + set(0, HL.rule, { fg = c.fg_gutter }) + set(0, HL.sep, { fg = c.comment }) + set(0, HL.footer, { fg = c.comment, italic = true }) + else + -- bare-box fallback: link to semantic groups every colorscheme defines (previous behavior). + NPILL = 0 + set(0, HL.title, { link = "Title", default = true }) + set(0, HL.key, { link = "Constant", default = true }) + set(0, HL.rule, { link = "Comment", default = true }) + set(0, HL.sep, { link = "Comment", default = true }) + set(0, HL.footer, { link = "Comment", default = true }) + end end -- Build one card into an array of "rich lines" ({ text = str, hls = { {s,e,hl}, ... } }) where s/e -- are BYTE offsets local to that line. Multibyte (the ─ rule) is fine because everything downstream -- measures with #str (bytes) for offsets and strdisplaywidth() for padding. -local function build_card(section) +local function build_card(section, idx) local lines = {} local function rich(text, hls) table.insert(lines, { text = text, hls = hls or {} }) @@ -263,9 +288,16 @@ local function build_card(section) end keyw = math.min(keyw, CARD_W - 6) -- never let keys eat the whole card - rich(section[1], { { s = 0, e = #section[1], hl = HL.title } }) - local rule = string.rep("─", CARD_W) - rich(rule, { { s = 0, e = #rule, hl = HL.rule } }) + -- Heading as a full-width PILL: one leading space, title, padded to the card width, and the whole + -- run painted with a cycling accent group (black bold text on a vivid bar) — NvChad's grid look. + -- Falls back to the plain HL.title span on a bare box where no pill groups were defined. + local head = " " .. section[1] + local pad = CARD_W - vim.fn.strdisplaywidth(head) + if pad > 0 then + head = head .. string.rep(" ", pad) + end + local head_hl = NPILL > 0 and ("GerrrtCheatPill" .. ((idx - 1) % NPILL + 1)) or HL.title + rich(head, { { s = 0, e = #head, hl = head_hl } }) for i = 2, #section do local key, desc = section[i][1], section[i][2] @@ -352,8 +384,8 @@ function M.open() ncol = math.min(ncol, #M.sections) local cards = {} - for _, s in ipairs(M.sections) do - table.insert(cards, build_card(s)) + for i, s in ipairs(M.sections) do + table.insert(cards, build_card(s, i)) end local cols, tallest = pack(cards, ncol) diff --git a/nvim/lua/gerrrt/config/autocmds.lua b/nvim/lua/gerrrt/config/autocmds.lua index 3a28b8e..da4ec97 100644 --- a/nvim/lua/gerrrt/config/autocmds.lua +++ b/nvim/lua/gerrrt/config/autocmds.lua @@ -5,10 +5,16 @@ local on_attach = require("gerrrt.utils.lsp").on_attach -- Restore last cursor position when reopening a file -local last_cursor_group = vim.api.nvim_create_augroup("LastCursorGroup", {}) +local last_cursor_group = vim.api.nvim_create_augroup("LastCursorGroup", { clear = true }) vim.api.nvim_create_autocmd("BufReadPost", { group = last_cursor_group, callback = function() + -- Commit/rebase buffers should open at the top (you're writing a new message / editing the + -- todo list), not wherever the cursor last sat in a previous commit — skip the restore there. + local ft = vim.bo.filetype + if ft == "gitcommit" or ft == "gitrebase" then + return + end local mark = vim.api.nvim_buf_get_mark(0, '"') local lcount = vim.api.nvim_buf_line_count(0) if mark[1] > 0 and mark[1] <= lcount then @@ -18,7 +24,7 @@ vim.api.nvim_create_autocmd("BufReadPost", { }) -- Highlight the yanked text for 200ms -local highlight_yank_group = vim.api.nvim_create_augroup("HighlightYank", {}) +local highlight_yank_group = vim.api.nvim_create_augroup("HighlightYank", { clear = true }) vim.api.nvim_create_autocmd("TextYankPost", { group = highlight_yank_group, pattern = "*", @@ -37,7 +43,7 @@ vim.api.nvim_create_autocmd("TextYankPost", { -- Format on save: trim trailing whitespace, then run conform. -- lsp_format = "fallback" means filetypes without a conform formatter still get -- formatted by their LSP (e.g. gopls), and filetypes with neither are left alone. -local lsp_fmt_group = vim.api.nvim_create_augroup("FormatOnSaveGroup", {}) +local lsp_fmt_group = vim.api.nvim_create_augroup("FormatOnSaveGroup", { clear = true }) vim.api.nvim_create_autocmd("BufWritePre", { group = lsp_fmt_group, callback = function(args) @@ -55,7 +61,7 @@ vim.api.nvim_create_autocmd("BufWritePre", { }) -- on attach function shortcuts -local lsp_on_attach_group = vim.api.nvim_create_augroup("LspMappings", {}) +local lsp_on_attach_group = vim.api.nvim_create_augroup("LspMappings", { clear = true }) vim.api.nvim_create_autocmd("LspAttach", { group = lsp_on_attach_group, callback = on_attach, diff --git a/nvim/lua/gerrrt/config/options.lua b/nvim/lua/gerrrt/config/options.lua index 48c18bb..0f7273c 100644 --- a/nvim/lua/gerrrt/config/options.lua +++ b/nvim/lua/gerrrt/config/options.lua @@ -109,8 +109,12 @@ vim.opt.guicursor = { } -- Folding Settings -vim.opt.foldmethod = "expr" -- Use expression for folding -vim.opt.foldexpr = "v:lua.vim.treesitter.foldexpr()" -- Use treesitter for folding +-- Folds are owned by nvim-ufo (plugins/nvim-ufo.lua), which computes them via its own treesitter + +-- indent providers. We deliberately do NOT set foldmethod=expr + a global treesitter foldexpr here: +-- UFO does not read 'foldexpr', so those only added redundant per-buffer fold computation on top of +-- what UFO already does. We keep just the "folds open on open" intent; UFO re-asserts +-- foldlevel/foldlevelstart=99 in its init. To fall back to plain folding, remove nvim-ufo.lua AND +-- restore `foldmethod=expr` + `foldexpr=v:lua.vim.treesitter.foldexpr()` here. vim.opt.foldlevel = 99 -- Keep all folds open by default -- Split Behavior diff --git a/nvim/lua/gerrrt/plugins/bufferline-nvim.lua b/nvim/lua/gerrrt/plugins/bufferline-nvim.lua index 97c7a9c..d3be3a9 100644 --- a/nvim/lua/gerrrt/plugins/bufferline-nvim.lua +++ b/nvim/lua/gerrrt/plugins/bufferline-nvim.lua @@ -23,7 +23,62 @@ -- -- ICONS : diagnostic glyphs use \u{XXXX} escapes (matching utils/diagnostics.lua + lualine) so -- they survive transfer — raw Nerd-Font private-use glyphs get silently stripped. +-- LOOK : palette-aware highlights (build_highlights below) push the line toward NvChad's tabufline: +-- the ACTIVE buffer lifts as a subtle raised block (bg = bg_highlight) with a bright accent +-- underline; inactive buffers dim into the transparent bar (bg = NONE, fg = comment). Colors +-- come from tokyonight's resolved palette so they track the theme, computed at load time (in +-- `config`, when tokyonight is guaranteed loaded) rather than at spec-eval — same reasoning +-- as the hand-built lualine theme. pcall-guarded so a fresh box falls back to bufferline's +-- own auto-theming instead of erroring. -- ================================================================================================ + +-- Build bufferline's `highlights` from the tokyonight palette. Returns nil on a box where +-- tokyonight hasn't loaded, so setup() falls back to bufferline's colorscheme-derived defaults. +local function build_highlights() + local ok, c = pcall(function() + return require("tokyonight.colors").setup({ style = "storm" }) -- mirror plugins/theme.lua + end) + if not ok or type(c) ~= "table" then + return nil + end + local none = "NONE" + local active = { fg = c.fg, bg = c.bg_highlight } -- the raised active-buffer block + local dim = { fg = c.comment, bg = none } -- inactive, blended into the transparent bar + return { + fill = { bg = none }, + background = dim, + buffer_visible = { fg = c.fg_dark, bg = none }, + -- `sp` set here too: for indicator style="underline" bufferline draws the accent line on the + -- selected buffer's own highlight using its `sp`, so pin it to the accent explicitly. + buffer_selected = { fg = c.fg, bg = c.bg_highlight, bold = true, italic = false, sp = c.blue }, + -- thin separators kept hairline-subtle, never heavy dividers + separator = { fg = c.bg_dark, bg = none }, + separator_visible = { fg = c.bg_dark, bg = none }, + separator_selected = { fg = c.bg_dark, bg = c.bg_highlight }, + -- accent underline under the active buffer (indicator style = "underline"). The underline + -- color is taken from `sp`, not `fg` — and because setup deep-merges with bufferline's + -- defaults, an unset `sp` would keep the default and the blue line wouldn't render. Set both. + indicator_selected = { fg = c.blue, sp = c.blue, bg = c.bg_highlight }, + indicator_visible = { fg = none, bg = none }, + -- unsaved dot: green when active (matches lualine/incline), amber otherwise + modified = { fg = c.orange, bg = none }, + modified_visible = { fg = c.orange, bg = none }, + modified_selected = { fg = c.green, bg = c.bg_highlight }, + -- per-buffer diagnostic counts track the gutter/statusline colors + error = { fg = c.red, bg = none }, + error_visible = { fg = c.red, bg = none }, + error_selected = { fg = c.red, bg = c.bg_highlight, bold = true }, + warning = { fg = c.yellow, bg = none }, + warning_visible = { fg = c.yellow, bg = none }, + warning_selected = { fg = c.yellow, bg = c.bg_highlight, bold = true }, + -- tab-mode (mode="tabs") blocks, styled to match the buffer blocks + tab = dim, + tab_selected = active, + tab_separator = { fg = c.bg_dark, bg = none }, + tab_separator_selected = { fg = c.bg_dark, bg = c.bg_highlight }, + } +end + return { "akinsho/bufferline.nvim", version = "*", @@ -95,4 +150,11 @@ return { hover = { enabled = true, delay = 150, reveal = { "close" } }, }, }, + config = function(_, opts) + -- Attach the palette-aware highlights (nil on a fresh box → bufferline uses its own + -- colorscheme defaults) and hand the merged table to setup. Kept in `config` rather than + -- `opts` so build_highlights() runs at bufferline load, when tokyonight is loaded. + opts.highlights = build_highlights() + require("bufferline").setup(opts) + end, } diff --git a/nvim/lua/gerrrt/plugins/lualine-nvim.lua b/nvim/lua/gerrrt/plugins/lualine-nvim.lua index eeca3e4..ed3cb4e 100644 --- a/nvim/lua/gerrrt/plugins/lualine-nvim.lua +++ b/nvim/lua/gerrrt/plugins/lualine-nvim.lua @@ -9,8 +9,14 @@ -- right : search count · attached LSP servers · diagnostics · filetype · cwd · location -- LOOK : the signature NvChad move is the ROUNDED block — half-circle caps (U+E0B6) and -- (U+E0B4) instead of powerline arrows, with NO inner component separators so each --- half reads as one clean run of blocks. Colors come from lualine's tokyonight theme --- (which sets a bg per section), so this stays readable even under transparency. +-- half reads as one clean run of blocks. Colors come from a HAND-BUILT theme derived from +-- tokyonight's resolved palette (build_theme below) — not lualine's bundled tokyonight +-- theme — so the blocks map onto NvChad's structure (accent mode/location PILLS at both +-- ends, a lighter git/cwd block, a base filename run) and each section keeps a solid bg, +-- which is what makes the pills read as opaque islands on the transparent bar. Because it +-- pulls from the same palette tokyonight hands `on_highlights` (utils/ui-highlights.lua), +-- it stays theme- and transparency-aware; swap `style` in plugins/theme.lua and the mirror +-- `style` in build_theme to keep them in sync (both default to "storm"). -- ICONS : All glyphs are written as \u{XXXX} escapes (Nerd Font private-use codepoints), -- NOT raw glyphs. Raw glyphs get silently stripped when text passes through tools -- that don't preserve the private-use area; escapes are plain ASCII in the file and @@ -26,6 +32,44 @@ return { event = "VeryLazy", dependencies = { "nvim-tree/nvim-web-devicons" }, config = function() + -- Build a lualine theme from tokyonight's resolved palette so the statusline mirrors NvChad's + -- St_* block structure: a/z = accent PILLS (mode on the left, cursor location on the right — + -- lualine feeds theme `a` to both), b/y = a lighter git/cwd block, c/x = the base filename + -- run. Mode → accent follows NvChad (Normal=blue, Insert=purple, Visual=cyan, Replace=orange, + -- Command/Terminal=green). Dark text (bg_dark) on the bright accents keeps the pills legible. + -- pcall so a fresh box where tokyonight hasn't loaded falls back to lualine's bundled theme + -- rather than aborting the whole statusline. + local function build_theme() + local ok, c = pcall(function() + -- mirror plugins/theme.lua's `style`; the palette keys used below are style-stable + return require("tokyonight.colors").setup({ style = "storm" }) + end) + if not ok or type(c) ~= "table" then + return "tokyonight" + end + local base, block = c.bg_dark, c.bg_highlight + local function mk(accent) + return { + a = { fg = c.bg_dark, bg = accent, gui = "bold" }, + b = { fg = c.fg_dark, bg = block }, + c = { fg = c.fg, bg = base }, + } + end + return { + normal = mk(c.blue), + insert = mk(c.magenta), -- NvChad Insert pill = purple + visual = mk(c.cyan), + replace = mk(c.orange), + command = mk(c.green), + terminal = mk(c.green), + inactive = { + a = { fg = c.comment, bg = base }, + b = { fg = c.comment, bg = base }, + c = { fg = c.comment, bg = base }, + }, + } + end + -- Show the language servers attached to the current buffer. local function lsp_servers() local clients = vim.lsp.get_clients({ bufnr = 0 }) @@ -47,7 +91,7 @@ return { require("lualine").setup({ options = { - theme = "tokyonight", + theme = build_theme(), icons_enabled = true, globalstatus = true, -- NvChad's rounded blocks: half-circle section caps, and NO component separators diff --git a/nvim/lua/gerrrt/plugins/mini-nvim.lua b/nvim/lua/gerrrt/plugins/mini-nvim.lua index 8521237..9fb19fb 100644 --- a/nvim/lua/gerrrt/plugins/mini-nvim.lua +++ b/nvim/lua/gerrrt/plugins/mini-nvim.lua @@ -73,8 +73,9 @@ return { require("mini.notify").setup({}) -- setup() alone does NOT replace vim.notify — without this line mini.notify is configured -- but unused, and vim.notify(...) calls (e.g. harpoon's "added" toast) still hit Neovim's - -- built-in notifier. make_notify() returns a drop-in replacement. (No competing notifier - -- like noice/fidget is installed, so there's nothing to clash with.) + -- built-in notifier. make_notify() returns a drop-in replacement. fidget.nvim is also + -- installed (plugins/fidget-nvim.lua) but only renders LSP *progress* — it is not a + -- vim.notify backend, so the two don't clash; this owns vim.notify, fidget owns $/progress. vim.notify = require("mini.notify").make_notify() end, } diff --git a/nvim/lua/gerrrt/plugins/nvim-ufo.lua b/nvim/lua/gerrrt/plugins/nvim-ufo.lua index 737a77f..0893b82 100644 --- a/nvim/lua/gerrrt/plugins/nvim-ufo.lua +++ b/nvim/lua/gerrrt/plugins/nvim-ufo.lua @@ -5,10 +5,12 @@ -- contents, `zR`/`zM` open/close all with proper restore, and `zK` peeks the folded lines -- under the cursor in a popup (falling back to LSP hover when there's no fold). Uses the -- treesitter parsers you already install, with an indent fallback for parserless filetypes. --- INTERPLAY: your options.lua sets foldmethod=expr + a treesitter foldexpr and foldlevel=99. UFO --- drives folds through its own providers, so we mirror foldlevel/foldlevelstart=99 here to --- keep everything open on open (UFO requirement). Remove this file to fall straight back to --- your existing treesitter foldexpr — nothing else depends on it. +-- INTERPLAY: UFO is the SOLE fold owner. options.lua sets only foldlevel=99 (folds open on open) and +-- NO LONGER sets foldmethod=expr / a global treesitter foldexpr — UFO computes folds through +-- its own providers below, so that global foldexpr was redundant per-buffer work (UFO never +-- reads 'foldexpr'). We re-assert foldlevel/foldlevelstart=99 here (UFO requirement). To fall +-- back to plain folding, remove this file AND restore foldmethod=expr + +-- foldexpr=v:lua.vim.treesitter.foldexpr() in options.lua. -- ================================================================================================ return { "kevinhwang91/nvim-ufo", diff --git a/nvim/lua/gerrrt/plugins/which-key.lua b/nvim/lua/gerrrt/plugins/which-key.lua index a6bb4d6..12b60e7 100644 --- a/nvim/lua/gerrrt/plugins/which-key.lua +++ b/nvim/lua/gerrrt/plugins/which-key.lua @@ -6,6 +6,21 @@ return { "folke/which-key.nvim", event = "VeryLazy", opts = { + -- NvChad-flavored popup: the classic bottom prompt, but a minimal rounded float with a bit of + -- breathing room and a clean left-aligned column layout — the colors (blue keys, red + -- descriptions, green groups) are set palette-aware in utils/ui-highlights.lua so they track + -- the theme instead of being hardcoded here. + preset = "classic", + win = { + border = "rounded", + padding = { 1, 2 }, -- one row / two cols of inner padding, so entries don't kiss the border + title = true, + title_pos = "center", + }, + layout = { + spacing = 4, -- gap between the key and its description column + align = "left", + }, spec = { { "b", group = "buffer" }, { "c", group = "code / LSP" }, diff --git a/nvim/lua/gerrrt/servers/cssls.lua b/nvim/lua/gerrrt/servers/cssls.lua index 91d0023..dbf8b2d 100644 --- a/nvim/lua/gerrrt/servers/cssls.lua +++ b/nvim/lua/gerrrt/servers/cssls.lua @@ -9,11 +9,8 @@ -- INSTALL: mason — package name "css-lsp" (added to ensure_installed in plugins/conform.lua). -- ================================================================================================ return function(capabilities) - local caps = vim.deepcopy(capabilities) - caps.textDocument = caps.textDocument or {} - caps.textDocument.completion = caps.textDocument.completion or {} - caps.textDocument.completion.completionItem = caps.textDocument.completion.completionItem or {} - caps.textDocument.completion.completionItem.snippetSupport = true + -- css-lsp ships snippet completions; advertise the client capability so they come through. + local caps = require("gerrrt.utils.lsp").with_snippets(capabilities) local lint = { validate = true, lint = { unknownAtRules = "warning" } } diff --git a/nvim/lua/gerrrt/servers/html.lua b/nvim/lua/gerrrt/servers/html.lua index d9b88a8..adc1a92 100644 --- a/nvim/lua/gerrrt/servers/html.lua +++ b/nvim/lua/gerrrt/servers/html.lua @@ -9,11 +9,7 @@ -- ================================================================================================ return function(capabilities) -- html-lsp ships snippet completions; advertise the client capability so they come through. - local caps = vim.deepcopy(capabilities) - caps.textDocument = caps.textDocument or {} - caps.textDocument.completion = caps.textDocument.completion or {} - caps.textDocument.completion.completionItem = caps.textDocument.completion.completionItem or {} - caps.textDocument.completion.completionItem.snippetSupport = true + local caps = require("gerrrt.utils.lsp").with_snippets(capabilities) vim.lsp.config("html", { capabilities = caps, diff --git a/nvim/lua/gerrrt/servers/taplo.lua b/nvim/lua/gerrrt/servers/taplo.lua index 1776887..ce93193 100644 --- a/nvim/lua/gerrrt/servers/taplo.lua +++ b/nvim/lua/gerrrt/servers/taplo.lua @@ -11,6 +11,16 @@ return function(capabilities) capabilities = capabilities, cmd = { "taplo", "lsp", "stdio" }, filetypes = { "toml" }, - root_markers = { "*.toml", ".git" }, + -- Real base names only: vim.fs.root / vim.fs.find do NOT support globs (see neovim + -- runtime/lua/vim/fs.lua — "paths and globs are not supported"). The old "*.toml" never + -- matched, so taplo silently always fell back to .git and a lone TOML file outside a repo + -- got a cwd root. List the TOML manifests this stack actually uses instead. + -- + -- The manifests are NESTED into one inner list so they share EQUAL priority — root at the + -- NEAREST ancestor holding ANY of them. A flat sequential list is priority order (neovim + -- fs.lua: "to indicate 'equal priority', specify items in a nested list"), which in a mixed + -- monorepo would prefer a distant pyproject.toml over a nearer Cargo.toml. `.git` stays a + -- lower-priority fallback for a TOML file that sits in a repo without a manifest above it. + root_markers = { { "pyproject.toml", "Cargo.toml", "foundry.toml", "taplo.toml", ".taplo.toml" }, ".git" }, }) end diff --git a/nvim/lua/gerrrt/utils/diagnostics.lua b/nvim/lua/gerrrt/utils/diagnostics.lua index 384a821..7d1f22c 100644 --- a/nvim/lua/gerrrt/utils/diagnostics.lua +++ b/nvim/lua/gerrrt/utils/diagnostics.lua @@ -27,7 +27,17 @@ M.setup = function() -- survives copy/paste like the other glyphs here. `source` is kept to the float to avoid -- cluttering the end-of-line text when several servers attach. virtual_text = { spacing = 2, prefix = "\u{25cf}" }, -- 25cf BLACK CIRCLE - float = { border = "rounded", source = true }, + -- The diagnostic float (cd, and [d/]d jumps) styled to match the LSP hover/signature + -- cards: rounded border (also the global winborder), no "Diagnostics:" header row (the border + -- is chrome enough), a 2-space prefix so text doesn't kiss the border, and a width cap so a + -- long multi-server message wraps into a tidy padded box instead of a full-width strip. + float = { + border = "rounded", + source = true, + header = "", + prefix = " ", + max_width = 80, + }, }) end diff --git a/nvim/lua/gerrrt/utils/lsp.lua b/nvim/lua/gerrrt/utils/lsp.lua index 697543b..025ad84 100644 --- a/nvim/lua/gerrrt/utils/lsp.lua +++ b/nvim/lua/gerrrt/utils/lsp.lua @@ -9,6 +9,47 @@ -- ================================================================================================ local M = {} +-- Return a COPY of `capabilities` with completionItem.snippetSupport advertised. Servers whose +-- completions ship as snippets (html-lsp, css-lsp) need this or their items arrive as plain text. +-- Extracted here so servers/html.lua and servers/cssls.lua stop hand-rolling the identical +-- deepcopy + nested-table dance (kept them in lockstep was pure drift risk). deepcopy so a server +-- flipping this can never mutate the shared `capabilities` table the other servers were built from. +function M.with_snippets(capabilities) + local caps = vim.deepcopy(capabilities) + caps.textDocument = caps.textDocument or {} + caps.textDocument.completion = caps.textDocument.completion or {} + caps.textDocument.completion.completionItem = caps.textDocument.completion.completionItem or {} + caps.textDocument.completion.completionItem.snippetSupport = true + return caps +end + +-- True when the server's advertised code-action support could include source.organizeImports. +-- A server that enumerates its codeActionKinds without a "source"/"source.organizeImports" kind is +-- ruled out; a bare boolean `true` (no kinds enumerated) is allowed through since we can't tell. +local function offers_organize_imports(client) + local prov = client.server_capabilities and client.server_capabilities.codeActionProvider + if prov == true then + return true + end + if type(prov) == "table" then + local kinds = prov.codeActionKinds + if type(kinds) ~= "table" then + return true -- provider present but kinds unspecified — allow it + end + -- Code-action kinds are HIERARCHICAL, so accept descendants too: some servers advertise a more + -- specific kind (e.g. ruff → "source.organizeImports.ruff") while still honoring a request for + -- the parent "source.organizeImports". Match the bare "source" umbrella, the exact kind, or any + -- "source.organizeImports.*" descendant. + for _, k in ipairs(kinds) do + if k == "source" or k == "source.organizeImports" or vim.startswith(k, "source.organizeImports.") then + return true + end + end + return false + end + return false +end + M.on_attach = function(event) if not event.data then return @@ -49,11 +90,48 @@ M.on_attach = function(event) end -- ── Native LSP (built into Neovim 0.12) ────────────────────────────────── - keymap("n", "K", vim.lsp.buf.hover, opts("Hover documentation")) + -- hover/signature pass an explicit rounded border + size caps. winborder (options.lua) already + -- makes floats rounded globally; passing `border` here is self-documenting AND lets us bound the + -- width/height so a huge docstring becomes a tidy, padded NvChad-style card instead of a wall. + local float_opts = { border = "rounded", max_width = 80, max_height = 25 } + keymap("n", "K", function() + vim.lsp.buf.hover(float_opts) + end, opts("Hover documentation")) keymap("n", "gD", vim.lsp.buf.declaration, opts("Go to declaration")) keymap("n", "rn", vim.lsp.buf.rename, opts("Rename symbol")) keymap({ "n", "v" }, "ca", vim.lsp.buf.code_action, opts("Code action")) - keymap("i", "", vim.lsp.buf.signature_help, opts("Signature help")) + keymap("i", "", function() + vim.lsp.buf.signature_help(float_opts) + end, opts("Signature help")) + + -- Auto signature help while resting in a function's arguments. blink.cmp already surfaces + -- signatures DURING completion; this covers the other case — you've paused typing inside the + -- parens and want the params without reaching for . Gated on server support, and otherwise + -- self-gating (signature_help draws nothing when the cursor isn't inside a signature). Skipped + -- while the blink menu is open so two signature floats never stack. focusable=false so it can't + -- steal the cursor; silent=true swallows the "no signature help" message. Fires after + -- 'updatetime' (300ms, options.lua) of no typing. Per-buffer augroup (clear=true) dedupes across + -- multiple attached clients. + if client:supports_method("textDocument/signatureHelp", bufnr) then + local sig_group = vim.api.nvim_create_augroup("GerrrtLspSignature_" .. bufnr, { clear = true }) + vim.api.nvim_create_autocmd("CursorHoldI", { + group = sig_group, + buffer = bufnr, + callback = function() + local blink_ok, blink = pcall(require, "blink.cmp") + if blink_ok and blink.is_visible() then + return + end + pcall(vim.lsp.buf.signature_help, { + border = "rounded", + max_width = 80, + max_height = 25, -- same bounded card as manual K/ so many-overload sigs stay tidy + focusable = false, + silent = true, + }) + end, + }) + end -- ── Diagnostics (native) ───────────────────────────────────────────────── keymap("n", "cd", vim.diagnostic.open_float, opts("Line diagnostics")) @@ -72,16 +150,23 @@ M.on_attach = function(event) keymap("n", "fs", "FzfLua lsp_document_symbols", opts("Document symbols")) keymap("n", "fw", "FzfLua lsp_workspace_symbols", opts("Workspace symbols")) - -- ── Organize imports (if the server supports it) ───────────────────────── - if client:supports_method("textDocument/codeAction", bufnr) then + -- ── Organize imports (only where the server actually offers it) ────────── + -- The old guard was generic `textDocument/codeAction` support — nearly every server advertises + -- that, so oi bound even for servers (lua_ls, etc.) that have no organizeImports action + -- and would silently no-op. Now we inspect the advertised codeActionKinds: a server that + -- ENUMERATES its kinds without "source[.organizeImports]" is skipped; a server that only reports + -- a bare `true` (kinds unknown) still gets the map, since we can't rule it out. + -- + -- The old body also formatted after a fixed `defer_fn(…, 50)` — racy: 50ms could fire before the + -- import edit applied (formatting stale text) or needlessly late. Dropped entirely; formatting is + -- owned by format-on-save (config/autocmds.lua) and cf, so organizeImports just does the + -- one thing it names. + if offers_organize_imports(client) then keymap("n", "oi", function() vim.lsp.buf.code_action({ context = { only = { "source.organizeImports" }, diagnostics = {} }, apply = true, }) - vim.defer_fn(function() - vim.lsp.buf.format({ bufnr = bufnr }) - end, 50) end, opts("Organize imports")) end end diff --git a/nvim/lua/gerrrt/utils/ui-highlights.lua b/nvim/lua/gerrrt/utils/ui-highlights.lua index 3998e9a..95d0f3a 100644 --- a/nvim/lua/gerrrt/utils/ui-highlights.lua +++ b/nvim/lua/gerrrt/utils/ui-highlights.lua @@ -56,6 +56,18 @@ function M.apply(hl, c) hl.BlinkCmpSignatureHelpBorder = { fg = c.border_highlight, bg = none } hl.BlinkCmpLabelMatch = { fg = c.blue, bold = true } + -- ── which-key: NvChad's palette on the minimal rounded float — blue keys, red descriptions, ── + -- green groups, dim separators. Border/title tint match the LSP + finder floats above so every + -- popup in the config reads as one system. (Layout/border geometry is set in plugins/which-key.lua.) + hl.WhichKey = { fg = c.blue } -- the key itself + hl.WhichKeyGroup = { fg = c.green } -- a +prefix group (e.g. "+git") + hl.WhichKeyDesc = { fg = c.red } -- the action description + hl.WhichKeySeparator = { fg = c.comment } -- the → between key and desc + hl.WhichKeyValue = { fg = c.green } + hl.WhichKeyNormal = { bg = none } + hl.WhichKeyBorder = { fg = c.border_highlight, bg = none } + hl.WhichKeyTitle = { fg = c.blue, bg = none, bold = true } + -- ── gutter: NvChad's dim line numbers with a bright, obvious current line ──────────────── hl.LineNr = { fg = c.fg_gutter } hl.CursorLineNr = { fg = c.orange, bold = true } From b693d5d03677a48b0fbaf9692f7d3b921eeebf15 Mon Sep 17 00:00:00 2001 From: dotfiles-core sync bot <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2026 02:31:07 +0000 Subject: [PATCH 2/2] =?UTF-8?q?chore(core):=20core.lock=20=E2=86=92=20da15?= =?UTF-8?q?41206be1=20(v3.8.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core.lock b/core.lock index e0697b7..263b3d0 100644 --- a/core.lock +++ b/core.lock @@ -1,6 +1,6 @@ # GENERATED by dotfiles-core sync-core.sh — vendored Core provenance (B1). # Regenerate after a manual 'git subtree pull' with: make core-lock -core_version=3.7.0 -core_sha=e9cf907f5849dfae2f3313d840fcbafb3772b83a -core_branch=e9cf907f5849dfae2f3313d840fcbafb3772b83a -core_tag=v3.7.0 +core_version=3.8.0 +core_sha=da1541206be1f7e0b36a1592a1a775ae6f2f0bf0 +core_branch=da1541206be1f7e0b36a1592a1a775ae6f2f0bf0 +core_tag=v3.8.0