diff --git a/lua/config/autocmds.lua b/lua/config/autocmds.lua index bce4cc2..34573a4 100644 --- a/lua/config/autocmds.lua +++ b/lua/config/autocmds.lua @@ -2,6 +2,10 @@ local augroup = vim.api.nvim_create_augroup local autocmd = vim.api.nvim_create_autocmd +-- ───────────────────────────────────────────────────────────────────────────── +-- General UX +-- ───────────────────────────────────────────────────────────────────────────── + -- Highlight on yank augroup("YankHighlight", { clear = true }) autocmd("TextYankPost", { @@ -72,13 +76,17 @@ autocmd("BufWritePre", { end, }) +-- ───────────────────────────────────────────────────────────────────────────── +-- Filetype-specific settings +-- ───────────────────────────────────────────────────────────────────────────── + -- Set specific options for certain filetypes -augroup("FileTypeSettings", { clear = false }) +augroup("FileTypeSettings", { clear = true }) --- Go files: use tabs +-- Go + Makefile files: use tabs autocmd("FileType", { group = "FileTypeSettings", - pattern = "go", + pattern = { "go", "make" }, callback = function() vim.opt_local.expandtab = false vim.opt_local.tabstop = 4 @@ -86,18 +94,7 @@ autocmd("FileType", { end, }) --- Makefile: use tabs -autocmd("FileType", { - group = "FileTypeSettings", - pattern = "make", - callback = function() - vim.opt_local.expandtab = false - vim.opt_local.tabstop = 4 - vim.opt_local.shiftwidth = 4 - end, -}) - --- Markdown: enable wrap +-- Markdown: soft wrap + spell autocmd("FileType", { group = "FileTypeSettings", pattern = "markdown", @@ -107,6 +104,19 @@ autocmd("FileType", { end, }) +-- SQL family: skip autoformat-on-save (LazyVim's pipeline respects vim.b.autoformat) +autocmd("FileType", { + group = "FileTypeSettings", + pattern = { "sql", "mysql", "plsql" }, + callback = function() + vim.b.autoformat = false + end, +}) + +-- ───────────────────────────────────────────────────────────────────────────── +-- Project / tooling hints +-- ───────────────────────────────────────────────────────────────────────────── + autocmd("DirChanged", { callback = function() if vim.fn.filereadable(".devcontainer/devcontainer.json") == 1 then @@ -122,21 +132,7 @@ autocmd("BufRead", { local name = vim.fn.fnamemodify(args.file, ":t") vim.notify( "Generated " .. name .. ". Use: `go tool pprof -http=:0 " .. name .. "` or `go tool trace " .. name .. "`", - vim.log.levels.INFO) + vim.log.levels.INFO + ) end, }) - --- SQL files --- autocmd("FileType", { --- pattern = { "sql", "mysql", "plsql" }, --- callback = function() --- local cmp = require("cmp") --- cmp.setup.buffer({ --- sources = { --- { name = "vim-dadbod" }, --- { name = "nvim_lsp" }, --- { name = "buffer" }, - -- }, - -- }) - -- end, --- }) diff --git a/lua/config/keymaps.lua b/lua/config/keymaps.lua index 5a2f74b..56a5a06 100644 --- a/lua/config/keymaps.lua +++ b/lua/config/keymaps.lua @@ -1,35 +1,37 @@ -- Custom keymaps local map = vim.keymap.set - --- Terminal keymaps --- th - Open terminal in horizontal split -map("n", "th", function() +-- ───────────────────────────────────────────────────────────────────────────── +-- Terminal +-- ───────────────────────────────────────────────────────────────────────────── +-- LazyVim uses t* for the Test group (neotest). Putting terminal +-- splits under capital T avoids that collision while staying mnemonic. +map("n", "Th", function() vim.cmd("split | terminal") vim.cmd("startinsert") end, { desc = "Terminal (horizontal split)" }) --- tv - Open terminal in vertical split -map("n", "tv", function() +map("n", "Tv", function() vim.cmd("vsplit | terminal") vim.cmd("startinsert") end, { desc = "Terminal (vertical split)" }) --- Terminal mode: Escape to normal mode map("t", "", "", { desc = "Exit terminal mode" }) --- Explorer keymaps (Snacks explorer) --- Ctrl+Shift+e - Toggle explorer -map("n", "", function() Snacks.explorer() end, { desc = "Toggle Explorer" }) -map("i", "", function() Snacks.explorer() end, { desc = "Toggle Explorer" }) +-- ───────────────────────────────────────────────────────────────────────────── +-- Explorer (Snacks) +-- ───────────────────────────────────────────────────────────────────────────── +-- Using Ctrl-Shift-e here matches the README. Note: some terminals +-- (Terminal.app, basic xterm) don't transmit Ctrl+Shift+letter distinctly — +-- if it doesn't fire, use LazyVim's e / fe instead. +map({ "n", "i" }, "", function() + Snacks.explorer() +end, { desc = "Toggle Explorer" }) --- Note: Ctrl+Shift+v and Ctrl+Shift+h for opening files in splits --- are configured in the snacks picker config (lua/plugins/editor.lua) --- as they need to work within the explorer buffer context - --- Additional useful keymaps --- Better window navigation -map("n", "", "h", { desc = "Go to left window" }) +-- ───────────────────────────────────────────────────────────────────────────── +-- Window navigation +-- ───────────────────────────────────────────────────────────────────────────── +map("n", "", "h", { desc = "Go left window" }) map("n", "", "j", { desc = "Go to lower window" }) map("n", "", "k", { desc = "Go to upper window" }) map("n", "", "l", { desc = "Go to right window" }) @@ -40,75 +42,98 @@ map("n", "", "resize -2", { desc = "Decrease window height" }) map("n", "", "vertical resize -2", { desc = "Decrease window width" }) map("n", "", "vertical resize +2", { desc = "Increase window width" }) --- Buffer navigation -map("n", "", "bprevious", { desc = "Prev buffer" }) -map("n", "", "bnext", { desc = "Next buffer" }) +-- ───────────────────────────────────────────────────────────────────────────── +-- Buffers / editing +-- ───────────────────────────────────────────────────────────────────────────── +map("n", "", "bprevious", { desc = "Prev Buffer" }) +map("n", "", "bnext", { desc = "Next Buffer" }) --- Clear search highlight map("n", "", "nohlsearch", { desc = "Clear search highlight" }) - --- Save file map({ "n", "i", "v", "s" }, "", "w", { desc = "Save file" }) --- Better indenting in visual mode -map("v", "<", "", ">gv") +map("v", "<", "", ">gv", { desc = "Indent right, keep selection" }) --- Move lines up/down map("n", "", "m .+1==", { desc = "Move line down" }) map("n", "", "m .-2==", { desc = "Move line up" }) -map("v", "", ":m '>+1gv=gv", { desc = "Move selection down" }) -map("v", "", ":m '<-2gv=gv", { desc = "Move selection up" }) +map("n", "", ":m '>+1gv=gv", { desc = "Move selection down" }) +map("n", "", ":m '<-2gv=gv", { desc = "Move selection up" }) --- Quick access to AI chat +-- ───────────────────────────────────────────────────────────────────────────── +-- AI (Avante) +-- ───────────────────────────────────────────────────────────────────────────── map("n", "aa", "AvanteAsk", { desc = "AI Ask" }) map("v", "aa", "AvanteAsk", { desc = "AI Ask (selection)" }) map("n", "ac", "AvanteChat", { desc = "AI Chat" }) map("n", "at", "AvanteToggle", { desc = "AI Toggle" }) --- Database keymaps -map("n", "db", "DBUIToggle", { desc = "Toggle DB UI" }) -map("n", "da", "DBUIAddConnection", { desc = "Add DB Connection" }) +-- ───────────────────────────────────────────────────────────────────────────── +-- Database (vim-dadbod) +-- ───────────────────────────────────────────────────────────────────────────── +-- Moved off db to free that for the DAP breakpoint default. +map("n", "Du", "DBUIToggle", { desc = "Toggle DB UI" }) +map("n", "Da", "DBUIAddConnection", { desc = "Add DB Connection" }) +map("n", "Df", "DBUIFindBuffer", { desc = "Find DB Buffer" }) --- Neotest -local neotest = require("neotest") - -map("n", "tt", neotest.run.run, { desc = "Run nearest test" }) +-- ───────────────────────────────────────────────────────────────────────────── +-- Tests (neotest) +-- ───────────────────────────────────────────────────────────────────────────── +-- IMPORTANT: do NOT `require("neotest")` at module top — it eager-loads the +-- plugin on every startup. Wrap each binding in a function so the require +-- happens at keypress time, letting Lazy.nvim load on demand. +map("n", "tt", function() + require("neotest").run.run() +end, { desc = "Run nearest test" }) map("n", "tf", function() - neotest.run.run(vim.fn.expand("%")) + require("neotest").run.run(vim.fn.expand("%")) end, { desc = "Run test file" }) - -map("n", "ts", neotest.summary.toggle) -map("n", "to", neotest.output.open) +map("n", "ts", function() + require("neotest").summary.toggle() +end, { desc = "Toggle test summary" }) +map("n", "to", function() + require("neotest").output.open() +end, { desc = "Open test output" }) map("n", "tc", "Coverage", { desc = "Show coverage" }) --- Go benchmark -map("n", "gb", function() +-- ───────────────────────────────────────────────────────────────────────────── +-- Go: benchmarks & profiling +-- ───────────────────────────────────────────────────────────────────────────── +-- Using cg* (Code > Go) to avoid colliding with Git's g group. +map("n", "cgb", function() vim.cmd("!go test -bench=. -benchmem ./...") end, { desc = "Go benchmarks" }) -map("n", "gpc", function() +map("n", "cgpc", function() vim.cmd("!go test -run=^$ -bench=. -cpuprofile cpu.out ./...") end, { desc = "Go CPU profile" }) -map("n", "gpm", function() +map("n", "cgpm", function() vim.cmd("!go test -run=^$ -bench=. -memprofile mem.out ./...") end, { desc = "Go memory profile" }) -map("n", "gpt", function() - vim.cmd("!go test -run=^$ -bench. -trace trace.out ./...") +map("n", "cgpt", function() + vim.cmd("!go test -run=^$ -bench=. -trace trace.out ./...") end, { desc = "Go trace profile" }) +-- ───────────────────────────────────────────────────────────────────────────── -- Overseer +-- ───────────────────────────────────────────────────────────────────────────── map("n", "or", "OverseerRun", { desc = "Run task" }) -map("n", "ot", "OverseerToogle", { desc = "Task list" }) +map("n", "ot", "OverseerToggle", { desc = "Task list" }) --- Aerial +-- ───────────────────────────────────────────────────────────────────────────── +-- Symbols / outline +-- ───────────────────────────────────────────────────────────────────────────── map("n", "so", "AerialToggle", { desc = "Symbols outline" }) map("n", "sh", vim.lsp.buf.incoming_calls, { desc = "Incoming calls" }) map("n", "sc", vim.lsp.buf.outgoing_calls, { desc = "Outgoing calls" }) --- Diagnostics -map("n", "xx", "Trouble diagnostics toggle") -map("n", "xw", "Trouble workspace_diagnostics") -map("n", "xt", "Trouble todo") +-- ───────────────────────────────────────────────────────────────────────────── +-- Diagnostics (Trouble) +-- ───────────────────────────────────────────────────────────────────────────── +map("n", "xx", "Trouble diagnostics toggle", { desc = "Diagnostics (Trouble)" }) +map("n", "xw", "Trouble workspace_diagnostics", { desc = "Workspace diagnostics" }) +map("n", "xt", "Trouble todo", { desc = "TODOs (Trouble)" }) --- Git Support -map("n", "gg", "Neogit", { desc = "Neogit" }) +-- ───────────────────────────────────────────────────────────────────────────── +-- Git +-- ───────────────────────────────────────────────────────────────────────────── +-- LazyVim's gg is lazygit by default; mapping Neogit to gn. +map("n", "gn", "Neogit", { desc = "Neogit" }) diff --git a/lua/plugins/ai.lua b/lua/plugins/ai.lua index 58bf8b2..9c69d18 100644 --- a/lua/plugins/ai.lua +++ b/lua/plugins/ai.lua @@ -3,11 +3,12 @@ -- API keys via environment variables: -- ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY -- Or scoped: AVANTE_ANTHROPIC_API_KEY, AVANTE_OPENAI_API_KEY, etc. +-- +-- Switch provider at runtime with: :AvanteSwitchProvider claude|openai|gemini|copilot|ollama return { { "yetone/avante.nvim", - event = "VeryLazy", version = false, -- Never set to "*" build = "make", dependencies = { @@ -39,6 +40,18 @@ return { ft = { "markdown", "Avante" }, }, }, + cmd = { + "AvanteAsk", + "AvanteChat", + "AvanteToggle", + "AvanteEdit", + "AvanteRefresh", + "AvanteBuild", + "AvanteSwitchProvider", + "AvanteShowRepoMap", + "AvanteClear", + "AvanteFocus", + }, ---@module 'avante' ---@type avante.Config opts = { @@ -67,7 +80,7 @@ return { }, }, azure = { - endpoint = "", -- e.g., "https://.openai.azure.com" + endpoint = "", -- e.g., "https://.openai.azure.com" deployment = "", -- Azure deployment name api_version = "2024-12-01-preview", timeout = 30000, @@ -193,21 +206,4 @@ return { }, }, }, - -- { - -- "zbirenbaum/copilot.lua", - -- cmd = "Copilot", - -- event = "InsertEnter", - -- opts = { - -- suggestion = { - -- enabled = true, - -- auto_trigger = true, - -- keymap = { - -- accept = "", - -- next = "", - -- prev = "", - -- }, - -- }, - -- panel = { enabled = false }, - -- }, - -- } } diff --git a/lua/plugins/dadbod.lua b/lua/plugins/dadbod.lua index 9b1c928..0c6a029 100644 --- a/lua/plugins/dadbod.lua +++ b/lua/plugins/dadbod.lua @@ -1,5 +1,11 @@ -- Database integration with vim-dadbod -- Supports PostgreSQL, MySQL, SQLite, and more +-- All three plugins are lazy-loaded: +-- - vim-dadbod loads on :DB / :DBUI* commands +-- - dadbod-ui loads on its own UI commands +-- dadbod-completion loads on sql/mysql/plsql filetypes +-- +-- Keymaps live in config/keymaps.lua under the D group. return { -- Core dadbod plugin @@ -13,8 +19,8 @@ return { "kristijanhusak/vim-dadbod-ui", cmd = { "DBUI", "DBUIToggle", "DBUIAddConnection", "DBUIFindBuffer" }, dependencies = { - { "tpope/vim-dadbod", lazy = true }, - { "kristijanhusak/vim-dadbod-completion", ft = { 'sql', 'mysql', 'plsql' }, lazy = true }, + "tpope/vim-dadbod", + { "kristijanhusak/vim-dadbod-completion", ft = { "sql", "mysql", "plsql" } }, }, init = function() -- UI configuration @@ -44,19 +50,18 @@ return { -- Completion { - 'saghen/blink.cmp', + "saghen/blink.cmp", opts = { sources = { default = { "lsp", "path", "snippets", "buffer" }, per_filetype = { - sql = { 'snippets', 'dadbod', 'buffer' }, + sql = { "snippets", "dadbod", "buffer" }, }, -- add vim-dadbod-completion to your completion providers providers = { - dadbod = { name = "Dadbod", module = "vim_dadbod_completion.blink"} - } - } - } - } - + dadbod = { name = "Dadbod", module = "vim_dadbod_completion.blink" }, + }, + }, + }, + }, } diff --git a/lua/plugins/dap.lua b/lua/plugins/dap.lua index 9ca5bb4..0955dee 100644 --- a/lua/plugins/dap.lua +++ b/lua/plugins/dap.lua @@ -1,115 +1,209 @@ --- Debug Adapter Protocol (DAP) configuration for Go +-- Debug Adapter Protocol (DAP) configuration for Go & JS -- Uses delve for Go debugging +-- Loads only when a d* key is pressed (see keys = {...} below). return { - -- nvim-dap configuration + -- ───────────────────────────────────────────────────────────────────────── + -- Core DAP + Go adapter + UI extras + -- ───────────────────────────────────────────────────────────────────────── { "mfussenegger/nvim-dap", dependencies = { - -- Go-specific DAP configuration + -- Fold these under nvim-dap so they share its keys-based lazy trigger + -- instead of loading eagerly at startup. + { "rcarriga/nvim-dap-ui", dependencies = { "nvim-neotest/nvim-nio" } }, + "theHamsta/nvim-dap-virtual-text", + "jay-babu/mason-nvim-dap.nvim", { "leoluz/nvim-dap-go", - opts = { - -- Delve configurations - delve = { - -- Path to delve (uses Mason-installed by default) - path = "dlv", - -- Initialize with default args - initialize_timeout_sec = 20, - -- Whether to use debug adapter mode - port = "${port}", - -- Build flags for delve - build_flags = "", - }, - -- DAP configurations for Go - dap_configurations = { - { - type = "go", - name = "Debug", - request = "launch", - program = "${file}", + -- IMPORTANT: opts is a FUNCTION, not a table literal. + -- The attach config references require("dap.utils"), which must not run until + -- nvim-dap is actually loaded. + opts = function() + return { + delve = { + path = "dlv", + initialize_timeout_sec = 20, + port = "${port}", + build_flags = "", }, - { - type = "go", - name = "Debug Package", - request = "launch", - program = "${fileDirname}", + dap_configurations = { + { type = "go", name = "Debug", request = "launch", program = "${file}" }, + { type = "go", name = "Debug Test", request = "launch", program = "${file}" }, + { type = "go", name = "Debug Package", request = "launch", program = "${filDirname}" }, + { + type = "go", + name = "Debug test (go.mod)", + request = "launch", + mode = "test", + program = "./${relativeFileDirname}", + }, + { + type = "go", + name = "Attach", + request = "attach", + mode = "local", + processId = require("dap.utils").pick_process, + }, }, - { - type = "go", - name = "Debug test", - request = "launch", - mode = "test", - program = "${file}", - }, - { - type = "go", - name = "Debug test (go.mod)", - request = "launch", - mode = "test", - program = "./${relativeFileDirname}", - }, - { - type = "go", - name = "Attach", - request = "attach", - mode = "local", - processId = require("dap.utils").pick_process, - }, - }, - }, + } + end, }, }, keys = { - -- Debug keymaps - { "dB", function() require("dap").set_breakpoint(vim.fn.input("Breakpoint condition: ")) end, desc = "Breakpoint Condition" }, - { "db", function() require("dap").toggle_breakpoint() end, desc = "Toggle Breakpoint" }, - { "dc", function() require("dap").continue() end, desc = "Continue" }, - { "dC", function() require("dap").run_to_cursor() end, desc = "Run to Cursor" }, - { "dg", function() require("dap").goto_() end, desc = "Go to Line (No Execute)" }, - { "di", function() require("dap").step_into() end, desc = "Step Into" }, - { "dj", function() require("dap").down() end, desc = "Down" }, - { "dk", function() require("dap").up() end, desc = "Up" }, - { "dl", function() require("dap").run_last() end, desc = "Run Last" }, - { "do", function() require("dap").step_out() end, desc = "Step Out" }, - { "dO", function() require("dap").step_over() end, desc = "Step Over" }, - { "dp", function() require("dap").pause() end, desc = "Pause" }, - { "dr", function() require("dap").repl.toggle() end, desc = "Toggle REPL" }, - { "ds", function() require("dap").session() end, desc = "Session" }, - { "dt", function() require("dap").terminate() end, desc = "Terminate" }, - { "dw", function() require("dap.ui.widgets").hover() end, desc = "Widgets" }, + { + "dB", + function() + require("dap").set_breakpoint(vim.fn.input("Breakpoint condition: ")) + end, + desc = "Breakpoint Condition", + }, + { + "db", + function() + require("dap").toggle_breakpoint() + end, + desc = "Toggle Breakpoint", + }, + { + "dc", + function() + require("dap").continue() + end, + desc = "Continue", + }, + { + "dC", + function() + require("dap").run_to_cursor() + end, + desc = "Run to Cursor", + }, + { + "dg", + function() + require("dap").goto_() + end, + desc = "Go to Line (No Execute)", + }, + { + "di", + function() + require("dap").step_into() + end, + desc = "Step Into", + }, + { + "dj", + function() + require("dap").down() + end, + desc = "Down", + }, + { + "dk", + function() + require("dap").up() + end, + desc = "Up", + }, + { + "dO", + function() + require("dap").step_over() + end, + desc = "Step Over", + }, + { + "dp", + function() + require("dap").pause() + end, + desc = "Pause", + }, + { + "dr", + function() + require("dap").repl.toggle() + end, + desc = "Toggle REPL", + }, + { + "ds", + function() + require("dap").session() + end, + desc = "Session", + }, + { + "dt", + function() + require("dap").terminate() + end, + desc = "Terminate", + }, + { + "dw", + function() + require("dap.ui.widgets").hover() + end, + desc = "Widgets", + }, -- Go-specific - { "dT", function() require("dap-go").debug_test() end, desc = "Debug Go Test" }, - { "dL", function() require("dap-go").debug_last_test() end, desc = "Debug Last Go Test" }, + { + "dT", + function() + require("dap-go").debug_test() + end, + desc = "Debug Go Test", + }, + { + "dL", + function() + require("dap-go").debug_last_test() + end, + desc = "Debug Last Go Test", + }, }, }, - -- DAP UI for better debugging experience + -- ───────────────────────────────────────────────────────────────────────── + -- DAP UI keymaps + auto open/close (the spec itself is loaded as a dep above) + -- ───────────────────────────────────────────────────────────────────────── { "rcarriga/nvim-dap-ui", - dependencies = { - "mfussenegger/nvim-dap", - "nvim-neotest/nvim-nio", - }, keys = { - { "du", function() require("dapui").toggle({}) end, desc = "Dap UI" }, - { "de", function() require("dapui").eval() end, desc = "Eval", mode = { "n", "v" } }, + { + "du", + function() + require("dapui").toggle({}) + end, + desc = "Dap UI", + }, + { + "de", + function() + require("dapui").eval() + end, + desc = "Eval", + mode = { "n", "v" }, + }, }, opts = { layouts = { { elements = { - { id = "scopes", size = 0.25 }, + { id = "scopes", size = 0.25 }, { id = "breakpoints", size = 0.25 }, - { id = "stacks", size = 0.25 }, - { id = "watches", size = 0.25 }, + { id = "stacks", size = 0.25 }, + { id = "watches", size = 0.25 }, }, position = "left", size = 40, }, { elements = { - { id = "repl", size = 0.5 }, + { id = "repl", size = 0.5 }, { id = "console", size = 0.5 }, }, position = "bottom", @@ -122,8 +216,7 @@ return { local dapui = require("dapui") dapui.setup(opts) - - -- Auto open/close DAP UI + -- Auto open/close DAP UI on debug session start/end dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open({}) end @@ -135,17 +228,24 @@ return { end end, }, + + -- ───────────────────────────────────────────────────────────────────────── + -- mason-nvim-dap: install DAP adapters via Mason + -- ───────────────────────────────────────────────────────────────────────── + -- Note: delve and js-debug-adapter are also listed in mason.lua's + -- ensure_installed (under "DAP adapters"). Either source + -- can install them; mason-nvim-dap is preferred when present because it also + -- auto-registers the adapters with nvim-dap. { "jay-babu/mason-nvim-dap.nvim", opts = { - ensure_installed = { - "python", - "delve", - "js" - }, + ensure_installed = { "python", "delve", "js" }, }, }, - -- Virtual text for debugging + + -- ───────────────────────────────────────────────────────────────────────── + -- Inline variable values during debug sessions + -- ───────────────────────────────────────────────────────────────────────── { "theHamsta/nvim-dap-virtual-text", opts = { diff --git a/lua/plugins/editor.lua b/lua/plugins/editor.lua index 7863e5d..84b697d 100644 --- a/lua/plugins/editor.lua +++ b/lua/plugins/editor.lua @@ -1,29 +1,13 @@ -- Editor enhancements - Snacks explorer configuration --- Custom keybindings for file explorer (snacks.nvim built into LazyVim) +-- +-- LazyVim provides snacks (explorer, picker, lazygit, terminal), native +-- commenting (Neovim 0.10+), and mini.hipatterns for color preview. +-- This file only adds project-specific overrides on top. return { - { - "nvim-telescope/telescope.nvim", - opts = { - defaults = { - hidden = true, - file_ignore_patterns = {}, - }, - pickers = { - find_files = { - hidden = true, - no_ignore = true, - no_ignore_parent = true, - }, - live_grep = { - additional_args = function() - return { "--hidden", "--no-ignore" } - end, - }, - }, - }, - }, - -- Configure snacks.nvim explorer (already included in LazyVim) + -- ───────────────────────────────────────────────────────────────────────── + -- Snacks: explorer + picker tweaks + -- ───────────────────────────────────────────────────────────────────────── { "folke/snacks.nvim", opts = { @@ -33,27 +17,22 @@ return { lazygit = { enabled = true, }, - input = { - enabled = true, - }, picker = { enabled = true, ui_select = true, sources = { explorer = { - -- Explorer picker configuration hidden = true, - ignored = true, -- Show files ignored by .gitignore + ignored = true, -- show .gitignore files too follow_file = true, - -- Custom keymaps within the explorer win = { list = { keys = { - -- Ctrl+Shift+v - Open in vertical split + -- Ctrl+Shift versions (work in Kitty/WezTerm/Alacritty with the right config) [""] = { "edit_vsplit", mode = { "n", "i" } }, - -- Ctrl+Shift+h - Open in horizontal split [""] = { "edit_split", mode = { "n", "i" } }, - -- Alternative mappings (in case terminal doesn't send Ctrl+Shift properly) + -- Plain Ctrl fallbacks for terminals that don't transmit + -- the Shift modifier seperately [""] = { "edit_vsplit", mode = { "n", "i" } }, [""] = { "edit_split", mode = { "n", "i" } }, }, @@ -64,30 +43,8 @@ return { }, }, keys = { - -- Ctrl+Shift+e - Toggle explorer - { - "", - function() - Snacks.explorer() - end, - desc = "Toggle Explorer", - }, - -- Alternative binding if terminal doesn't handle Ctrl+Shift - { - "fe", - function() - Snacks.explorer() - end, - desc = "File Explorer", - }, - -- Lazygit - { - "gg", - function() - Snacks.lazygit() - end, - desc = "Lazygit", - }, + -- Optional Lazygit log/file-history shortcuts - LazyVim's gg + -- already opens lazygit itself. { "gl", function() @@ -104,124 +61,29 @@ return { }, }, }, - - -- Which-key for keybinding hints + -- ───────────────────────────────────────────────────────────────────────── + -- Which-key: leader-prefix cheat sheet + -- ───────────────────────────────────────────────────────────────────────── + -- Hold for ~300ms (timeoutlen) and the group menu appears. { "folke/which-key.nvim", opts = { spec = { { "a", group = "AI" }, - { "d", group = "Debug/Database" }, + { "b", group = "Buffer" }, + { "c", group = "Code" }, + { "cg", group = "Go" }, + { "cgp", group = "Profile" }, + { "d", group = "Debug" }, + { "D", group = "Database" }, + { "f", group = "Find" }, { "g", group = "Git" }, - { "t", group = "Terminal" }, - }, - }, - }, - - -- Color preview for CSS, HTML, etc. - { - "NvChad/nvim-colorizer.lua", - event = { "BufReadPre", "BufNewFile" }, - opts = { - filetypes = { - "css", - "scss", - "sass", - "less", - "html", - "javascript", - "typescript", - "javascriptreact", - "typescriptreact", - "vue", - "svelte", - "lua", - "yaml", - "toml", - "conf", - }, - user_default_options = { - RGB = true, - RRGGBB = true, - RRGGBBAA = true, - names = true, - rgb_fn = true, - hsl_fn = true, - css = true, - css_fn = true, - mode = "background", - tailwind = false, - sass = { enable = true, parsers = { "css" } }, - virtualtext = "■", - always_update = true, -- Update color values even if buffer is not focused - }, - }, - }, - - -- Better terminal integration - { - "akinsho/toggleterm.nvim", - version = "*", - opts = { - size = function(term) - if term.direction == "horizontal" then - return 15 - elseif term.direction == "vertical" then - return vim.o.columns * 0.4 - end - end, - open_mapping = [[]], - hide_numbers = true, - shade_filetypes = {}, - shade_terminals = true, - shading_factor = 2, - start_in_insert = true, - insert_mappings = true, - persist_size = true, - direction = "float", - close_on_exit = true, - shell = vim.o.shell, - float_opts = { - border = "curved", - winblend = 0, - highlights = { - border = "Normal", - background = "Normal", - }, - }, - }, - }, - - -- Help comment and uncomment lines - { - "numToStr/Comment.nvim", - opts = { - padding = true, - - -- LHS of toggle mappings in NORMAL mode - toggler = { - line = "gcc", - block = "gbc", - }, - - -- LHS of operator-pending mappings in NORMAL and VISUAL - opleader = { - line = "gc", - block = "gb", - }, - - -- LHS of extra mappings - extra = { - -- Add comment on the line above - above = "gcO", - -- Add comment on the line below - below = "gco", - -- Add comment at the end of line - eol = "gcA", - }, - mappings = { - basic = true, - extra = true, + { "o", group = "Overseer" }, + { "s", group = "Search / Symbols" }, + { "t", group = "Test" }, + { "T", group = "Terminal" }, + { "u", group = "UI / Toggle" }, + { "x", group = "Diagnostics" }, }, }, }, diff --git a/lua/plugins/formatting.lua b/lua/plugins/formatting.lua index 1db03fb..f480676 100644 --- a/lua/plugins/formatting.lua +++ b/lua/plugins/formatting.lua @@ -1,23 +1,22 @@ -- Formatting configuration with conform.nvim -- Autoformat on save with per-filetype formatters +-- +-- LazyVim owns format-on-save: it registers conform via LazyVim.format.register() +-- and triggers a BufWritePre autocmd gated on vim.g.autoformat / vim.b.autoformat. +-- Do NOT set format_on_save here - it bypasses that pipeline. +-- +-- Controls: +-- :LazyFormat format current buffer now +-- cf format buffer (LazyVim default) +-- uf toggle autoformat for this buffer +-- uF toggle autoformat globally +-- +-- Per-filetype skip is done from autocmd.lua via `vim.b.autofmrat = false`. return { { "stevearc/conform.nvim", - event = { "BufWritePre" }, - cmd = { "ConformInfo" }, - keys = { - { - "cf", - function() - require("conform").format({ async = true, lsp_fallback = true }) - end, - mode = { "n", "v" }, - desc = "Format buffer", - }, - }, opts = { - -- Formatters by filetype formatters_by_ft = { -- Go go = { "gofumpt", "goimports" }, @@ -25,7 +24,7 @@ return { -- Lua lua = { "stylua" }, - -- JavaScript/TypeScript + -- Javascript / Typescript javascript = { "prettier" }, javascriptreact = { "prettier" }, typescript = { "prettier" }, @@ -36,102 +35,37 @@ return { css = { "prettier" }, scss = { "prettier" }, - -- Data formats + -- Data json = { "prettier" }, jsonc = { "prettier" }, yaml = { "prettier" }, + toml = { "prettier" }, -- Markdown markdown = { "prettier" }, ["markdown.mdx"] = { "prettier" }, - -- SQL - sql = { "sql_formatter", "sqlfluff", "pg_format" }, - mysql = { "sql_formatter", "sqlfluff" }, - plsql = { "sql_formatter", "sqlfluff", "pg_format" }, - - -- Makefile (no formatter - they require tabs) - -- make = {}, - - -- Fallback for all other filetypes - ["_"] = { "trim_whitespace" }, + -- SQL - manual format only, autoformat skipped in autocmds.lua + sql = { "sql_formatter" }, + mysql = { "sql_formatter" }, + plsql = { "sql_formatter" }, }, - -- Format on save - format_on_save = function(bufnr) - -- Disable for certain filetypes - local ignore_filetypes = { "sql", "mysql", "plsql" } - if vim.tbl_contains(ignore_filetypes, vim.bo[bufnr].filetype) then - return - end - - -- Disable with a global or buffer-local variable - if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then - return - end - - return { - timeout_ms = 3000, - lsp_fallback = true, - } - end, - - -- Formatter options + -- Per-formatter argument tweaks (merged with LazyVim defaults) formatters = { - -- Stylua configuration stylua = { prepend_args = { "--indent-type", "Spaces", "--indent-width", "2" }, }, - - -- Prettier configuration prettier = { prepend_args = { "--tab-width", "2", "--single-quote" }, }, - - -- SQL formatter configuration sql_formatter = { prepend_args = { "--language", "postgresql" }, }, - - -- goimports configuration goimports = { prepend_args = { "-local", "github.com" }, }, }, }, - init = function() - -- Commands to toggle format on save - vim.api.nvim_create_user_command("FormatDisable", function(args) - if args.bang then - -- FormatDisable! will disable formatting just for this buffer - vim.b.disable_autoformat = true - else - vim.g.disable_autoformat = true - end - vim.notify("Autoformat disabled", vim.log.levels.INFO) - end, { - desc = "Disable autoformat-on-save", - bang = true, - }) - - vim.api.nvim_create_user_command("FormatEnable", function() - vim.b.disable_autoformat = false - vim.g.disable_autoformat = false - vim.notify("Autoformat enabled", vim.log.levels.INFO) - end, { - desc = "Re-enable autoformat-on-save", - }) - - vim.api.nvim_create_user_command("FormatToggle", function() - vim.g.disable_autoformat = not vim.g.disable_autoformat - if vim.g.disable_autoformat then - vim.notify("Autoformat disabled", vim.log.levels.INFO) - else - vim.notify("Autoformat enabled", vim.log.levels.INFO) - end - end, { - desc = "Toggle autoformat-on-save", - }) - end, }, } diff --git a/lua/plugins/go-coverage.lua b/lua/plugins/go-coverage.lua index e408886..fc87bf3 100644 --- a/lua/plugins/go-coverage.lua +++ b/lua/plugins/go-coverage.lua @@ -1,6 +1,19 @@ +-- nvim-coverage: show test coverage gutter signs. +-- Loads when opening Go files or when a Coverage* command is invoked. + return { { "andythigpen/nvim-coverage", + ft = "go", + cmd = { + "Coverage", + "CoverageLoad", + "CoverageShow", + "CoverageHide", + "CoverageToggle", + "CoverageClear", + "CoverageSummary", + }, dependencies = "nvim-lua/plenary.nvim", config = true, }, diff --git a/lua/plugins/lsp.lua b/lua/plugins/lspconfig.lua similarity index 64% rename from lua/plugins/lsp.lua rename to lua/plugins/lspconfig.lua index e6190ee..f86ddd0 100644 --- a/lua/plugins/lsp.lua +++ b/lua/plugins/lspconfig.lua @@ -1,8 +1,9 @@ -- LSP Configuration -- Extends LazyVim's built-in LSP support --- Helper function for monorepo root detection --- Finds nearest project marker, with fallback to git root +-- Root detection: nearest project marker, falling back to git root. +-- The require is inside the returned function so lspconfig.util is loaded +-- lazily at attach time, not at module load. local function make_root_detector(markers) return function(fname) local util = require("lspconfig.util") @@ -17,52 +18,15 @@ local function make_root_detector(markers) end return { - -- Mason: manage LSP servers, linters, formatters - { - "mason-org/mason.nvim", - opts = { - ensure_installed = { - -- LSP servers - "gopls", -- Go - "lua-language-server", -- Lua - "typescript-language-server", -- TypeScript/JavaScript - "js-debug-adapter", -- JS - "html-lsp", -- HTML - "css-lsp", -- CSS - "json-lsp", -- JSON - "yaml-language-server", -- YAML - "marksman", -- Markdown - "sqlls", -- SQL - "pyright", --Python - "ruff", --Python - "docker-language-server", -- Docker - "zls", -- Zig - -- Linters - "golangci-lint", - "eslint_d", - "markdownlint", - "hadolint", - "shellcheck", - -- Formatters - "gofumpt", - "goimports", - "prettier", - "stylua", - "sql-formatter", - -- DAP - "delve", -- Go debugger - }, - }, - }, - -- LSP configuration { "neovim/nvim-lspconfig", opts = { servers = { + -- ───────────────────────────────────────────────────────────────── -- Go + -- ───────────────────────────────────────────────────────────────── gopls = { - -- Root detection: find go.work first, then go.mod, then git root root_dir = make_root_detector({ "go.work", "go.mod" }), settings = { gopls = { @@ -98,16 +62,16 @@ return { staticcheck = true, directoryFilters = { "-.git", "-.vscode", "-.idea", "-.vscode-test", "-node_modules" }, semanticTokens = true, - -- Monorepo/workspace support expandWorkspaceToModule = true, experimentalPostfixCompletions = true, - -- Handle multiple modules ["build.standaloneTags"] = { "ignore" }, }, }, }, + -- ───────────────────────────────────────────────────────────────── -- Lua + -- ───────────────────────────────────────────────────────────────── lua_ls = { root_dir = make_root_detector({ ".luarc.json", @@ -120,22 +84,17 @@ return { }), settings = { Lua = { - workspace = { - checkThirdParty = false, - }, - completion = { - callSnippet = "Replace", - }, - diagnostics = { - globals = { "vim" }, - }, + workspace = { checkThirdParty = false }, + completion = { callSnippet = "Replace" }, + diagnostics = { globals = { "vim" } }, }, }, }, - -- TypeScript/JavaScript + -- ───────────────────────────────────────────────────────────────── + -- TypeScript / JavaScript + -- ───────────────────────────────────────────────────────────────── ts_ls = { - -- Root detection: find nearest tsconfig/package.json root_dir = make_root_detector({ "tsconfig.json", "jsconfig.json", "package.json" }), settings = { typescript = { @@ -163,23 +122,22 @@ return { }, }, - -- HTML + -- ───────────────────────────────────────────────────────────────── + -- Web (HTML / CSS / JSON / YAML) + -- ───────────────────────────────────────────────────────────────── html = { root_dir = make_root_detector({ "package.json", ".git" }), filetypes = { "html", "templ" }, }, - -- CSS cssls = { root_dir = make_root_detector({ "package.json", ".git" }), }, - -- JSON jsonls = { root_dir = make_root_detector({ "package.json", ".git" }), }, - -- YAML yamlls = { root_dir = make_root_detector({ "package.json", ".git" }), settings = { @@ -193,60 +151,20 @@ return { }, }, - -- Markdown + -- ───────────────────────────────────────────────────────────────── + -- Markdown / SQL / Docker + -- ───────────────────────────────────────────────────────────────── marksman = { root_dir = make_root_detector({ ".marksman.toml", ".git" }), }, - -- SQL sqlls = { root_dir = make_root_detector({ ".sqllsrc.json", "sqlls.json", ".git" }), }, - -- Docker dockerls = {}, docker_compose_language_server = {}, }, }, }, - - -- Treesitter for better syntax highlighting - { - "nvim-treesitter/nvim-treesitter", - opts = { - ensure_installed = { - "go", - "gomod", - "gowork", - "gosum", - "lua", - "luadoc", - "typescript", - "javascript", - "tsx", - "html", - "css", - "json", - "jsonc", - "yaml", - "markdown", - "markdown_inline", - "ninja", - "sql", - "make", - "vim", - "vimdoc", - "bash", - "regex", - "diff", - "gitcommit", - "git_rebase", - "dockerfile", - "latex", - "typst", - }, - highlight = { enable = true }, - indent = { enable = true }, - }, - }, } diff --git a/lua/plugins/mason.lua b/lua/plugins/mason.lua new file mode 100644 index 0000000..834139b --- /dev/null +++ b/lua/plugins/mason.lua @@ -0,0 +1,43 @@ +return { + -- Mason: manage LSP servers, linters, formatters + { + "mason-org/mason.nvim", + opts = { + ensure_installed = { + + -- LSP servers + "gopls", + "lua-language-server", + "typescript-language-server", + "html-lsp", + "css-lsp", + "json-lsp", + "yaml-language-server", + "marksman", + "sqlls", + "pyright", + "ruff", + "docker-language-server", + "zls", + + -- Linters + "golangci-lint", + "eslint_d", + "markdownlint", + "hadolint", + "shellcheck", + + -- Formatters + "gofumpt", + "goimports", + "prettier", + "stylua", + "sql-formatter", + + -- DAP adapters + "delve", + "js-debug-adapter", + }, + }, + }, +} diff --git a/lua/plugins/neotest.lua b/lua/plugins/neotest.lua index efbe278..37a1ca3 100644 --- a/lua/plugins/neotest.lua +++ b/lua/plugins/neotest.lua @@ -1,6 +1,13 @@ +-- Neotest: test runner with Go and Python adapters. +-- Loads on go/python filetypes, or when require("neotest") is called from a +-- keymap (config.keymap wraps the bindings in `function() require(...) end` +-- precisely so the lazy-load can happen. + return { { "nvim-neotest/neotest", + lazy = true, + ft = { "go", "ptyhon" }, dependencies = { "nvim-neotest/nvim-nio", "nvim-lua/plenary.nvim", diff --git a/lua/plugins/overseer.lua b/lua/plugins/overseer.lua index 979cf64..ed61506 100644 --- a/lua/plugins/overseer.lua +++ b/lua/plugins/overseer.lua @@ -1,8 +1,25 @@ +-- Overseer: task runner. +-- Loads on demand when any Overseer command is invoked. + return { { "stevearc/overseer.nvim", + cmd = { + "OverseerOpen", + "OverseerClose", + "OverseerToggle", + "OverseerSaveBundle", + "OverseerLoadBundle", + "OverseerDeleteBundle", + "OverseerRunCmd", + "OverseerRun", + "OverseerInfo", + "OverseerBuild", + "OverseerQuickAction", + "OverseerTaskAction", + }, opts = { - templates = { "builtin", "user" } + templates = { "builtin", "user" }, }, }, } diff --git a/lua/plugins/treesitter.lua b/lua/plugins/treesitter.lua new file mode 100644 index 0000000..0b59800 --- /dev/null +++ b/lua/plugins/treesitter.lua @@ -0,0 +1,66 @@ +-- Treesitter parsers +-- LazyVim's nvim-treesitter spec uses opts_extend = { "ensure_installed" } +-- so this list is merged with the parsers added by language extras. + +return { + { + "nvim-treesitter/nvim-treesitter", + opts = { + ensure_installed = { + -- Go + "go", + "gomod", + "gowork", + "gosum", + + -- Lua + "lua", + "luadoc", + + -- Typescript / Javascript + "typescript", + "javascript", + "tsx", + + -- Web + "html", + "css", + + -- Data + "json", + "jsonc", + "yaml", + "toml", + + -- Markdown / docs + "markdown", + "markdown_inline", + + -- Build / config + "ninja", + "sql", + "make", + + -- Editor / shell + "vim", + "vimdoc", + "bash", + "regex", + + -- Git + "diff", + "gitcommit", + "git_rebase", + + -- Containers + "dockerfile", + + -- Typesetting + "latex", + "typst", + }, + highlight = { enable = true }, + indent = { enable = true }, + }, + }, +} diff --git a/lua/plugins/zig.lua b/lua/plugins/zig.lua index 3f678d4..a3cb7d9 100644 --- a/lua/plugins/zig.lua +++ b/lua/plugins/zig.lua @@ -1,5 +1,10 @@ +-- Zig language support via the official Codeberg-hosted plugin. +-- Loads only on zig files. + return { - 'https://codeberg.org/ziglang/zig.vim', - lazy = true, - ft = { "zig" }, + { + "https://codeberg.org/ziglang/zig.vim", + lazy = true, + ft = { "zig" }, + }, }