biscuit.nvim

Latest release Last commit License Stars

**Batch quickfix actions and LSP formatting across your entire project.** Biscuit loads files into LSP for project-wide diagnostics and lets you batch-apply quickfix actions for specific diagnostic codes (like gopls's `any`, `slicescontains`, etc.) across hundreds of files at once. ## The Problem You have 200 Go files with `interface{}` that should be `any`. Opening each file, waiting for LSP, and applying the quickfix manually would take hours. ## The Solution ```vim :LoadLSP " Load all Go files into LSP :ApplyFixes any " Fix all 200 files in seconds ``` ## Features - **Load files into LSP** — Get diagnostics for your entire project - **Batch quickfix by diagnostic code** — Specify codes like `any`, `slicescontains`, `printf` - **Safe by default** — Only applies quickfix-kind actions, picking the first match - **Batch LSP formatting** — Run `textDocument/formatting` on all loaded buffers - **Dry run mode** — Preview changes before applying ## Requirements - Neovim >= **0.10.0** - `fd` (recommended) or `find` for file discovery - LSP server(s) configured for your filetypes ## Installation ### [lazy.nvim](https://github.com/folke/lazy.nvim) ```lua { "taigrr/biscuit.nvim", cmd = { "LoadLSP", "LoadLSPDir", "ApplyFixes", "ListDiagnosticCodes", "FormatBuffers" }, opts = { codes = { "any", -- interface{} -> any "slicescontains", -- Use slices.Contains "printf", -- Printf format issues }, }, } ``` ## Quick Start ```vim " 1. Open a Go file to set filetype :e main.go " 2. Load all Go files into LSP :LoadLSP " 3. See what diagnostic codes are available :ListDiagnosticCodes " 4. Apply fixes (dry run first) :ApplyFixes! any :ApplyFixes any " 5. Or apply all configured codes :ApplyFixes " 6. Format all loaded buffers :FormatBuffers " 7. Clean up :UnloadHidden ``` ## Commands | Command | Description | |---------|-------------| | `:LoadLSP [dir] [exts...]` | Load files into LSP (uses current filetype if no args) | | `:LoadLSPDir` | Prompt for a directory and load files matching the current filetype | | `:UnloadHidden` | Unload hidden buffers to free resources | | `:UnloadTracked` | Unload buffers loaded by `:LoadLSP` | | `:ApplyFixes[!] [codes...]` | Apply quickfixes for diagnostic codes. `!` for dry run | | `:ListDiagnosticCodes` | List diagnostic codes from loaded buffers | | `:ListConfiguredCodes` | List configured codes | | `:FormatBuffers[!]` | Format all loaded buffers using LSP. `!` for dry run | ## Configuration ```lua require("biscuit").setup({ -- Diagnostic codes to auto-fix (exact match) -- These are the "code" field from LSP diagnostics codes = { "any", -- gopls: interface{} -> any "slicescontains", -- gopls: Use slices.Contains "printf", -- gopls: Printf format specifier issues "unusedwrite", -- gopls: Unused write to variable }, -- Performance options batch_size = 10, -- Files per batch when loading batch_delay = 50, -- Ms between batches max_files = 0, -- Limit files (0 = unlimited) auto_save = true, -- Save after applying actions/formatting -- Directories to skip exclude_dirs = { "node_modules", ".git", "dist", "build", "__pycache__", ".venv", "vendor", ".next", "coverage", }, }) ``` ## Common Diagnostic Codes ### gopls (Go) | Code | Description | |------|-------------| | `any` | Use `any` instead of `interface{}` | | `slicescontains` | Use `slices.Contains` instead of loop | | `printf` | Printf format specifier issues | | `unusedwrite` | Unused write to variable | | `fillstruct` | Fill struct with zero values | | `undeclaredname` | Undeclared name | ### typescript-language-server | Code | Description | |------|-------------| | `6133` | Unused variable | | `6196` | Unused import | Run `:ListDiagnosticCodes` after `:LoadLSP` to see what codes your LSP provides. ## How It Works 1. **Load files** — `:LoadLSP` loads matching files into buffers for LSP analysis 2. **Filter diagnostics** — Finds diagnostics matching your configured codes 3. **Request code actions** — For each matching diagnostic, requests quickfix actions 4. **Apply if unique** — Only applies when exactly ONE quickfix action exists 5. **Auto-save** — Optionally saves modified files ## Health Check ```vim :checkhealth biscuit ``` ## Troubleshooting ### EMFILE: Too many open files (macOS) macOS defaults to a file descriptor limit of 256. Loading hundreds of files will exceed this. Add to your `~/.zshrc` or `~/.bashrc`: ```bash ulimit -n 4096 ``` Biscuit detects low limits and warns you via `:checkhealth biscuit`. You can also reduce `batch_size` or set `max_files` to stay under the limit. ## Tips - **Discover codes**: Run `:ListDiagnosticCodes` to see what your LSP provides - **Start with dry run**: Use `!` to preview changes first - **Clean up**: Run `:UnloadTracked` after batch operations to free memory - **Health check**: Run `:checkhealth biscuit` to verify your setup ## License [0BSD](LICENSE) (c) [Tai Groot](https://github.com/taigrr)