# Neovim Rebuild — Decisions & Reference Authoritative notes for the Neovim migration. Use this alongside `MIGRATION_PLAN.md` and `neovim-migration-guide.md`. - Migration plan: MIGRATION_PLAN.md - Guide and requirements: neovim-migration-guide.md ## Decisions Log Record every decision here with a short rationale. Append new entries; do not rewrite history. - 2025-12-06: PHP LSP = `intelephense` (good PHP ecosystem support; integrates includePaths). - 2025-12-06: Enable Markdown LSP = `marksman` (lightweight, good MD features). - 2025-12-06: Use legacy `listchars` and `showbreak` values (preserve muscle memory). - 2025-12-06: No direnv fallback for local config (keep setup simple and repo-driven). - 2025-12-06: Project-local config = layered JSON `.nvim.json` + `.nvim.local.json` with `.nvimroot` as workspace boundary marker (multi-repo friendly). - 2025-12-06: Switch to `folke/neoconf.nvim` for project-local configuration (supersedes custom layered JSON loader); use `.neoconf.json` at workspace root. - 2025-12-06: Use `mason.nvim` + `mason-lspconfig.nvim` to install/manage LSP servers; keep custom lspconfig setup; include `.neoconf.json` in root detection. - For PHP validation inside this repo: we require `.neoconf.json` to attach `intelephense` to avoid the repo’s `.git` being chosen as the LSP root. - 2025-12-06: Plugin approval policy — other plugins are allowed, but do not install any plugin not listed without explicit confirmation. - 2025-12-07: **CRITICAL REGRESSION FIX**: Neovim 0.11+ does NOT support `on_new_config` callback in `vim.lsp.config()`. Project-local settings (`.nvim.lua`) must be loaded via `LspAttach` autocmd instead. The `on_new_config` hook was silently ignored, causing empty settings and breaking goto-definition for external includes. Solution: Use `LspAttach` to load project settings, merge into `client.settings`, and send `workspace/didChangeConfiguration` notification. - 2025-12-07: Native `exrc` + `.nvim.lua` finalized as project-local config approach (replaces neoconf, which is incompatible with Neovim 0.11+ native LSP API). Security via `vim.opt.secure = true`. - 2025-12-07: Navigation Phase 4 decisions: - Skip Neo-tree in favor of netrw for project visualization - Use Telescope as primary "find file" tool with fuzzy finding - Add Oil.nvim for file manipulation (rename/move/delete with buffer sync) - netrw for tree view and preview splits; Oil for operations that would break buffer names - PHP `gf` enhancement via `includeexpr` for WordPress/PHP path resolution - 2025-12-07: Treesitter Phase 5 decisions: - Focus on core languages: PHP, HTML, JavaScript, TypeScript, CSS, Markdown, Lua, Bash, JSON - Enable syntax highlighting with performance safeguard (disable for files >100KB) - Incremental selection: CR to expand, BS to shrink, S-CR for scope expansion - Textobjects for functions (`af`/`if`), classes (`ac`/`ic`), parameters (`aa`/`ia`), conditionals, loops, comments - Movement keymaps: `]f`/`[f` for next/prev function, `]c`/`[c` for classes, `]a`/`[a` for parameters - Parameter swapping: `a`/`A` to swap with next/prev parameter - Auto-tag for HTML/PHP/JS/React/TS/Vue files (auto-close, auto-rename, close-on-slash) - Indent disabled initially (experimental); can enable per-filetype if stable - 2025-12-07: UX/Editing Phase 6 decisions: - Enable signcolumn=yes (always show for LSP diagnostics, git signs) - Enable cursorline (highlight current line) - Enable colorcolumn=80,120 (visual guides at 80 and 120 chars) - NO cursorcolumn (as per AGENTS.md) - NO auto-window toggling behavior (keep settings static, no per-window autocmds) - NO motion plugin (skip leap/flash for simplicity) - Undotree moved to separate phase (6.8) - Comment.nvim for commenting (gcc/gc/gbc/gb keymaps) - nvim-surround for text objects (ys/ds/cs operators) - nvim-autopairs for auto-closing brackets/quotes with Treesitter and cmp integration - indent-blankline for visual indent guides with scope highlighting - nvim-ufo for enhanced folding with Treesitter/LSP providers (zR/zM/K for peek) - undotree for visual undo history (u to toggle) - 2025-12-07: Git integration Phase 7: - Gitsigns with minimal config for signs in gutter (add, change, delete markers) - Hunk navigation: `]h`/`[h` for next/prev hunk - Hunk actions: stage, reset, preview - NO inline blame or advanced features (keep minimal) - 2025-12-07: Copilot Phase 8: - Integrated via copilot.lua + copilot-cmp (completion source) - Auto-trigger suggestions as you type - Copilot suggestions appear before LSP in completion menu (higher priority) - Enabled for all filetypes - No specific Copilot keymaps (use existing cmp keymaps) - Node.js v22.21.1 confirmed working - 2025-12-07: Formatting & Linting Phase 9: - **Philosophy**: Formatters are authoritative; Neovim settings match formatter output - **Formatters**: prettier (JS/TS/CSS/JSON/MD/HTML), phpcbf (PHP/WordPress), stylua (Lua), black (Python) - **Linters**: eslint_d (JS/TS), phpcs (PHP/WordPress), markdownlint (Markdown), ruff (Python) - **Strategy**: Project-local executables first (node_modules/.bin/, vendor/bin/), then Mason, then system PATH - **WordPress**: phpcs/phpcbf already installed globally; use phpcs.xml or --standard=WordPress - **Format-on-save**: Enabled by default, toggle with `lt` - **Manual format**: `lf` (buffer), `lf` (visual range) - **Linting split**: none-ls for formatting only, nvim-lint for diagnostics (none-ls removed linters) - **Python support**: pyright LSP, black formatter, ruff linter, treesitter parser - **Per-filetype indentation**: Explicit settings per filetype to match formatters - PHP: tabs, 2-space display (WordPress standards) - JS/TS/CSS/JSON/HTML: 2 spaces (Prettier) - Lua: 2 spaces (common convention) - Markdown: 2 spaces (Prettier) - Python: 4 spaces (Black/PEP 8) - **Global defaults**: 4 spaces (reasonable baseline for other filetypes) - 2025-12-07: Kept Behaviors Phase 10: - **Abbreviations**: Common typo corrections (`adn→and`, `waht→what`, `tehn→then`, `functin→function`, `positin→position`) in dedicated `lua/abbreviations.lua` file for modularity - **Templates**: Shell script template (template.sh) auto-loaded via BufNewFile autocmd for `*.sh` files - **Whitespace highlighting**: Already handled via `listchars` in Phase 3.2 (settings.lua) - **Persistent folds**: Not needed; UFO handles folding without explicit persistence mechanism ## Project-Local Configuration (design) Context: Projects can contain multiple git repositories (e.g., WordPress site with theme + multiple plugins). We need a way to set per-project `intelephense.includePaths` and similar without globalizing settings. Options considered - Layered JSON files (recommended): `.nvim.json` (committed) + `.nvim.local.json` (gitignored) - Walk upward from current file; collect and merge configs until reaching a boundary marker or filesystem root. - Boundary marker: `.nvimroot` to define a workspace that can encompass multiple repos. - Merge order: top → bottom; nearest wins on conflicts; arrays replace by default unless we later add a merge strategy. - Pros: safe (data-only), portable, reviewable in PRs. - Cons: less dynamic than Lua. - Layered Lua files: `.nvim.lua` + `.nvim.local.lua` - Pros: maximum flexibility (computed paths). - Cons: executes code from the repo; security gates required. - Env/direnv: rejected (decision 2025-12-06). - Central registry file in `~/.config/nvim/`: possible later for private overrides, but not primary. Chosen approach (confirmed) - Use `folke/neoconf.nvim` with `.neoconf.json` at the workspace root (or project roots) to supply LSP/plugin settings. Multi-repo: keep a single `.neoconf.json` at the parent workspace folder and open Neovim from there (or use a rooter later if needed). Example `.neoconf.json` ``` { "lspconfig": { "intelephense": { "settings": { "intelephense": { "environment": { "includePaths": [ "wp-content/themes/my-theme", "wp-content/plugins/custom-plugin" ] } } } } } } ``` - Files recognized per directory level: - `.nvim.json`: checked into VCS; shared per-project defaults. - `.nvim.local.json`: gitignored; machine-specific overrides. - Loader behavior: 1. Start at the buffer’s directory; ascend toward root. 2. Stop at directory containing `.nvimroot` (if found) or at filesystem root. 3. At each level, if `.nvim.json` or `.nvim.local.json` exists, parse and stage for merge. 4. Merge staged configs in ascending order; nearest directory applies last. - Path resolution: - Relative paths resolve against the topmost boundary (i.e., `.nvimroot` directory if present; otherwise the highest directory in which a config was found). This allows referencing sibling repos (e.g., theme and plugins) from a single top-level site folder. Example `.nvim.json` ``` { "lsp": { "php": { "intelephense": { "includePaths": [ "wp-content/themes/my-theme", "wp-content/plugins/custom-plugin", "vendor/some-package/src" ] } } } } ``` Intended integration - A helper module `lua/local_config.lua` will expose: - `load(start_dir) -> table` collecting and merging configs - `get(path, default)` for reads, e.g., `get({"lsp","php","intelephense","includePaths"}, {})` - LSP wiring (in Phase 3.5): - Read includePaths via the helper and pass to `settings.intelephense.environment.includePaths` in `lspconfig.intelephense.setup{}`. Open items to confirm - Confirm JSON as the format (vs Lua) for project-local config. - Confirm `.nvimroot` as the workspace boundary marker name. - Confirm array merge behavior (replace vs concatenate). Initial proposal: replace. ## LSP Scope (initial) - Core servers: `lua_ls`, `tsserver`, `html`, `cssls`, `jsonls`, `bashls` - PHP: `intelephense` (decision) - Markdown: `marksman` (decision) - Keep configuration minimal; defer language-specific tuning. ## Text & Editing Settings - `spelllang = en_gb` - `listchars` and `showbreak` will reuse legacy values. - `completeopt = menu,menuone,noselect` for `nvim-cmp`. ## Process Reminders - After any change or decision, update this README and MIGRATION_PLAN.md. - Keep subphases small and verify each step in isolation.