# Neovim Configuration (LazyVim) A LazyVim-based setup focused on Go and Zig development, with LSP, debugging, test running, task running, AI chat, database tooling, and a which-key cheat sheet. Everything is lazy-loaded so plugins only activate for the filetype or command that needs them. ## Prerequisites - **Neovim >= 0.11** (developed on 0.12.x; 0.11+ is required for the modern `vim.lsp` / `root_markers` API used by some servers) - **Git** - **A Nerd Font** (icons in the explorer, statusline, dadbod UI, etc.) - **Node.js** — TypeScript/JSON/YAML/web LSPs and `prettier` - **Go** — `gopls`, `delve`, `gofumpt`, `goimports` - **Zig** — the `zig` toolchain on `$PATH` (for LSP, build/run, tests, debug) - **Python** _(optional)_ — `pyright` + `ruff` are configured if you use it Most language servers, linters, formatters, and DAP adapters install automatically through Mason on first launch. ## Installation 1. **Back up any existing config:** ```bash mv ~/.config/nvim ~/.config/nvim.bak mv ~/.local/share/nvim ~/.local/share/nvim.bak mv ~/.local/state/nvim ~/.local/state/nvim.bak mv ~/.cache/nvim ~/.cache/nvim.bak ``` 2. **Clone:** ```bash git clone https://git.samoneal.io/sam_oneal/nvim-config.git ~/.config/nvim ``` 3. **Launch Neovim.** lazy.nvim bootstraps itself and installs all plugins. 4. **Install tooling** with `:Mason` (or `:MasonInstall `). Check health with `:checkhealth` and LSP attachment with `:checkhealth lsp`. ## What's included **Base:** [LazyVim](https://www.lazyvim.org/) with leader = ``, localleader = `\`, colorscheme `catppuccin-mocha`. **LazyVim extras enabled** (in `lua/config/lazy.lua`): `lang.go`, `lang.json`, `lang.yaml`, `lang.markdown`, `lang.typescript`, `lang.tailwind`, `dap.core`. **Languages (LSP + Treesitter):** Go, Zig, Lua, TypeScript/JavaScript, HTML, CSS, JSON, YAML, Markdown, SQL, Dockerfile, Python. | Concern | Plugin / tool | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | LSP | nvim-lspconfig + Mason (`gopls`, `zls`, `lua_ls`, `ts_ls`, `html`, `cssls`, `jsonls`, `yamlls`, `marksman`, `sqlls`, `dockerls`, `pyright`/`ruff`) | | Completion | blink.cmp (LazyVim default; SQL routed to dadbod) | | Formatting | conform.nvim via LazyVim's format pipeline | | Debugging | nvim-dap + delve (Go), codelldb (Zig/C/C++) | | Testing | neotest (Go, Python, Zig) with in-buffer pass/fail signs | | Coverage | nvim-coverage (Go) | | Task running | overseer.nvim (+ user templates) | | AI chat | [opencode.nvim](https://github.com/nickjvandyke/opencode.nvim) (provider-agnostic) | | Database | vim-dadbod + dadbod-ui + dadbod-completion | | Git | Neogit + diffview, plus LazyVim's lazygit | | Explorer/picker | snacks.nvim | | Symbols | aerial.nvim | | Diagnostics | trouble.nvim | | Cheat sheet | which-key.nvim (hold ``) | ### AI (opencode.nvim) AI is intentionally **environment-agnostic**: opencode.nvim drives the external [`opencode`](https://opencode.ai) CLI, so the model/provider (Anthropic, OpenAI, local models, etc.) is chosen in opencode's own config rather than here. Install the `opencode` CLI separately and configure your provider there. ### Database (vim-dadbod) Open the UI with `Du`. Add a connection with `Da`. Examples: ``` postgresql://user:pass@localhost:5432/dbname mysql://user:pass@localhost:3306/dbname sqlite:path/to/db.sqlite ``` SQL completion is provided via dadbod inside SQL buffers, and SQL autoformat-on-save is intentionally disabled (format manually with `cf`). ## Key bindings Hold `` (Space) to pop up the **which-key** menu — it lists every group below as a live cheat sheet. Mouse is enabled (`mouse=a`), and the explorer, dadbod UI, Neogit, DAP UI, trouble, aerial, and Overseer pickers are all clickable. > Note the deliberate capitalization: **`T*`** is Terminal (lowercase > `t*` is the Test group), and **`D*`** is Database (lowercase > `d*` is Debug). ### AI (`a`) | Key | Action | | --------------------- | ---------------------------------- | | `aa` | Ask AI about current context (n/v) | | `ax` | Ask AI about selection | | `at` | Toggle AI panel | | `go` | Operator: add range to AI chat | | `goo` | Add current line to AI chat | | `` / `` | Scroll AI chat up/down | ### Code › Go (`cg`) | Key | Action | | -------------- | ------------------------------------- | | `cgb` | Run benchmarks (`-bench=. -benchmem`) | | `cgpc` | CPU profile | | `cgpm` | Memory profile | | `cgpt` | Trace profile | ### Code › Zig (`cz`) | Key | Action | | ------------- | ---------------- | | `czb` | `zig build` | | `czr` | `zig build run` | | `czt` | `zig build test` | ### Tests (neotest, `t`) Works in Go, Python, and Zig buffers; results show as signs in the gutter. | Key | Action | |-----|--------| | `tt` | Run nearest test | | `tf` | Run test file | | `ts` | Toggle test summary | | `to` | Open test output | | `tc` | Show coverage (Go) | ### Debugging (DAP, `d`) Go uses delve; Zig/C/C++ use codelldb. Zig launch configs build with debug symbols first, then run (`Launch (zig build)` prompts for the exe under `zig-out/bin`; `Launch (current file)` compiles the open file). | Key | Action | |-----|--------| | `db` | Toggle breakpoint | | `dB` | Conditional breakpoint | | `dc` | Continue / start | | `dC` | Run to cursor | | `di` | Step into | | `dO` | Step over | | `do` | Step out | | `dj` / `dk` | Stack down / up | | `dr` | Toggle REPL | | `ds` | Session | | `dt` | Terminate | | `dw` | Inspect widget (hover) | | `du` | Toggle DAP UI | | `de` | Eval (n/v) | | `dT` | Debug Go test | | `dL` | Debug last Go test | ### Tasks (Overseer, `o`) | Key | Action | | ------------ | --------------------------------------------------------- | | `or` | Run a task (picker — includes `go:` and `zig:` templates) | | `ot` | Toggle task list | ### Database (`D`) | Key | Action | | ------------ | -------------- | | `Du` | Toggle DB UI | | `Da` | Add connection | | `Df` | Find DB buffer | ### Git (`g`) | Key | Action | | ------------ | ------------------------- | | `gg` | Lazygit (LazyVim default) | | `gn` | Neogit | | `gl` | Lazygit log | | `gf` | Lazygit file history | ### Search / Symbols (`s`) | Key | Action | | ------------ | ------------------------ | | `so` | Symbols outline (aerial) | | `sh` | Incoming calls | | `sc` | Outgoing calls | ### Diagnostics (Trouble, `x`) | Key | Action | | ------------ | --------------------- | | `xx` | Diagnostics | | `xw` | Workspace diagnostics | | `xt` | TODOs | ### Terminal (`T`) | Key | Action | | ------------ | --------------------------- | | `Th` | Terminal (horizontal split) | | `Tv` | Terminal (vertical split) | | `` | Exit terminal mode | ### Explorer & windows | Key | Action | | ------------------------ | ----------------------------- | | `` | Toggle explorer (snacks) | | `` / `` | Open file in vertical split | | `` / `` | Open file in horizontal split | | `` | Navigate windows | | `` | Resize window | ### Formatting (LazyVim pipeline) | Key / Command | Action | | ------------- | -------------------------- | | `cf` | Format buffer | | `uf` | Toggle autoformat (buffer) | | `uF` | Toggle autoformat (global) | | `:LazyFormat` | Format now | ### General editing | Key | Action | | ------------------ | --------------------------- | | `` | Save file | | `` / `` | Prev / next buffer | | `` | Clear search highlight | | `` / `` | Move line/selection down/up | | `<` / `>` (visual) | Indent, keep selection | ## Formatters by file type | File type | Formatter | | --------------- | ---------------------------------------- | | Go | gofumpt, goimports (`-local github.com`) | | Lua | stylua (2-space) | | JS/TS/JSX/TSX | prettier (2-space, single quotes) | | HTML/CSS/SCSS | prettier | | JSON/JSONC/YAML | prettier | | Markdown | prettier | | SQL | sql-formatter (postgresql; manual only) | ## File structure ``` ~/.config/nvim/ ├── init.lua # Entry point → require("config.lazy") ├── lua/ │ ├── config/ │ │ ├── lazy.lua # lazy.nvim bootstrap, LazyVim + extras │ │ ├── options.lua # Neovim options │ │ ├── keymaps.lua # Custom keymaps │ │ └── autocmds.lua # Autocommands (yank hl, ft tweaks, SQL no-autoformat…) │ ├── plugins/ │ │ ├── mason.lua # Tooling installs (LSP/linters/formatters/DAP) │ │ ├── lspconfig.lua # Per-server LSP config (gopls, zls, …) │ │ ├── treesitter.lua # Parsers │ │ ├── formatting.lua # conform.nvim │ │ ├── dap.lua # Debugging (Go via delve, Zig via codelldb) │ │ ├── neotest.lua # Test runner (go/python/zig) │ │ ├── go-coverage.lua # Go coverage signs │ │ ├── overseer.lua # Task runner │ │ ├── ai.lua # opencode.nvim │ │ ├── dadbod.lua # Database │ │ ├── git.lua # Neogit + diffview │ │ ├── editor.lua # snacks tweaks + which-key groups │ │ ├── symbols.lua # aerial │ │ ├── dashboard.lua # trouble opts │ │ ├── devcontainer.lua # nvim-remote-containers │ │ ├── performance.lua # treesitter large-file guard │ │ ├── theme.lua # catppuccin │ │ └── zig.lua # Zig ft plugin (toolchain wired across files) │ └── overseer/template/user/ # go_*, zig_{build,run,test} task templates ``` ## Zig notes - LSP is `zls` (`lua/plugins/lspconfig.lua`), root markers `build.zig` / `build.zig.zon`, with build-on-save and inlay hints. - `zig build test` and project-wide neotest runs require a standard `test` step in your `build.zig`; individual `.zig` files also work. - Debugging needs `codelldb` (`:MasonInstall codelldb` if it doesn't auto-install). Builds use `-O Debug` so symbols are present. ## Troubleshooting - **LSP not attaching:** `:checkhealth lsp` — confirm the server appears and is attached (not just installed in Mason). If `root_dir`/`root_markers` look off, the server may not find your project root. - **Formatting:** `:ConformInfo`. - **Tests not detected:** ensure the Treesitter parser for the language is installed (`:TSInstall `); Zig needs a `test` step in `build.zig`. - **Ctrl+Shift keys not firing:** some terminals don't transmit them; use the plain-Ctrl fallbacks (`` / `` in the explorer). - **Nerd Font icons missing:** install a Nerd Font and set it in your terminal. ## Known issues (in current repo) Two unrelated typos exist in `lua/config/keymaps.lua`'s opencode block (present before these docs): the scroll-up command string reads `sesion.half.page.up` (should be `session.…`), and `at` is mapped in `t` (terminal) mode where `n`/`x` is likely intended. Documented here for accuracy; fix when convenient.