nvim/MIGRATION_PLAN.md

379 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Neovim Migration Checklist
Source of truth for the step-by-step rebuild. Keep this concise and up to date. See details in `neovim-migration-guide.md`.
## Phase 0 — Ground Rules
- [x] Create AGENTS.md with rules
## Phase 1 — Clean & Archive Legacy Config
## Phase 1.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
## Phase 1.2 — Inventory legacy files
- [x] Inventory existing Vimscript, plugin files, and directories
## Phase 1.3 — Create archive location
- [x] Create `legacy/` archive folder within this config
## Phase 1.4 — Move legacy files into archive
- [x] Move legacy init files and plugin directories into `legacy/` (do not delete)
## Phase 1.5 — Document archived contents
- [x] Optionally add `legacy/README.md` noting what was archived
## Phase 1.6 — Preserve selected directories
- [x] Keep `undodir`, `spell`, `view`, `UltiSnips`, `templates` as-is for now (review later)
## Phase 2 — Bootstrap
## Phase 2.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
## Phase 2.2 — Scaffold Lua config skeleton
- [x] Scaffold Lua config skeleton (`init.lua`, `lua/settings.lua`, `lua/keymaps.lua`, `lua/autocmds.lua`, `lua/utils.lua`, `lua/plugins/`)
## Phase 2.3 — Bootstrap lazy.nvim
- [x] Bootstrap `lazy.nvim` plugin manager
## Phase 2.4 — Disable unused providers
- [x] Disable unused providers (ruby, perl, node)
## Phase 2.5 — Ensure lazy boots without specs
- [x] Ensure lazy boots without specs (add empty `lua/plugins/init.lua`)
## Phase 3 — Core Editing & LSP
## Phase 3.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
- [x] Decision: PHP LSP = `intelephense`
- [x] Decision: Enable Markdown LSP = `marksman`
- [x] Decision: Use legacy `listchars` and `showbreak` values
- [x] Decision: Support perproject config for `intelephense.includePaths`
- [x] Decision: No direnv fallback for local config
## Phase 3.2 — Non-plugin settings to Lua
- [x] Port non-plugin settings to Lua (options, listchars, showbreak, spelllang=en_gb)
## Phase 3.3 — Core completion stack
- [x] Add core completion stack: `nvim-cmp`, `cmp-nvim-lsp`, `cmp-buffer`, `cmp-path`, `LuaSnip`
## Phase 3.4 — Projectlocal configuration (native exrc)
- [x] Confirm scope and priorities for this subphase
- [x] Decision: Use native `exrc` + `secure` instead of neoconf (neoconf incompatible with Neovim 0.11+ vim.lsp.config API)
- [x] Enable `exrc` and `secure` in settings.lua
- [x] Implement `.nvim.lua` loader in LSP config using `on_new_config` hook
- [x] Wire `intelephense.environment.includePaths` via `.nvim.lua`
- [x] Create validation workspaces: `WORKSPACE_TEST/` and `EXTERNAL_TEST/` with sample PHP files
- [x] Migrate to `vim.lsp.config()` and `vim.lsp.enable()` (Neovim 0.11+ native API)
- [x] Verify project settings load correctly from actual root_dir
- [x] Clean up debug notifications and temporary code
## Phase 3.7 — Clean LSP config
- [x] Remove debug/helper logic from LSP config
- [x] Migrate from deprecated `require('lspconfig')[server].setup()` to `vim.lsp.config()` + `vim.lsp.enable()`
- [x] Use `on_new_config` hook for project-local settings loading (loads from actual root_dir, not cwd)
## Phase 3.8 — Validate project-local LSP settings
- [x] Create test workspaces with `.nvim.lua` files
- [x] Validate settings load from actual root_dir via `on_new_config` hook
- [x] Verify `gd` works for external library references via `intelephense.environment.includePaths`
- [x] Clean up debug code and notifications
## Phase 3.9 — Native exrc + vim.lsp.config migration (RESOLVED)
- [x] Discovered neoconf incompatible with Neovim 0.11+ (GitHub issue #116, unresolved since May 2024)
- [x] Migrated to native `exrc` + `secure` approach (zero dependencies, aligned with minimal philosophy)
- [x] Implemented `.nvim.lua` loader using `on_new_config` hook (loads from actual root_dir)
- [x] Migrated from deprecated `require('lspconfig')` framework to `vim.lsp.config()` + `vim.lsp.enable()`
- [x] Root detection handled by nvim-lspconfig's native configs (no custom root_dir needed)
- [x] Validated `gd` works for external library symbols via `intelephense.environment.includePaths`
- [x] Settings schema: `.nvim.lua` returns `{ lsp = { [server_name] = { settings = {...} } } }`
- [x] Security: `secure` mode prompts user to trust `.nvim.lua` files before execution
- [x] Kept `single_file_support = false` for `intelephense`
- [x] Fixed duplicate `gd` results issue
## Phase 3.5 — LSP minimal defaults
- [x] Add `nvim-lspconfig` with minimal defaults (no over-configuration)
- [x] Add minimal LSP on-attach keymaps (gd, gr, K, gD, gI)
- [x] Add global LSP keymaps with fallback in `lua/keymaps.lua`
- [x] Intelephense: set `single_file_support=false`, root detection via nvim-lspconfig defaults
- [x] Project settings loaded via `on_new_config` hook from `.nvim.lua` files
## Phase 3.6 — LSP server management (Mason)
- [x] Confirm scope and priorities for this subphase
- [x] Add `williamboman/mason.nvim` and `williamboman/mason-lspconfig.nvim`
- [x] Ensure servers installed: `lua_ls`, `tsserver`, `html`, `cssls`, `jsonls`, `bashls`, `marksman`, `intelephense`
- [x] Keep our custom per-server setup; use Mason only for installation
## Phase 4 — Navigation
## Phase 4.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
- [x] Decision: Skip Neo-tree in favor of netrw for navigation/visual context
- [x] Decision: Use netrw for project structure visualization and file preview
- [x] Decision: Telescope as primary "find file" tool
- [x] Decision: Add Oil.nvim for file manipulation (handles buffer sync on rename/move/delete)
- [x] Note: Oil.nvim is for evaluation; alternative is mini.files if too heavy
## Phase 4.2 — Configure netrw
- [x] Configure netrw with tree view, preview split, and basic settings (no banner, sensible defaults)
- [x] Created `lua/netrw-config.lua` with settings: tree view, horizontal preview split below, 50/50 split, human-readable sizes
- [x] Add netrw keymaps: `<leader>te` (new tab at current file's directory), `<leader>tE` (new tab at project root)
- [x] Decision: No `<leader>e` or `<leader>v` keymaps - use `:Ex`, `:Vex` directly when needed
- [x] Preview behavior: Enter opens file in netrw window, `p` opens 50/50 horizontal split below
## Phase 4.2.1 — PHP gf enhancement
- [x] Add PHP `includeexpr` for intelligent `gf` behavior (handles `__DIR__`, `__FILE__`, `dirname(__FILE__)` patterns)
- [x] Created `after/ftplugin/php.lua` with path resolution for WordPress/PHP patterns
- [x] Tested and validated with test-gf.php
## Phase 4.3 — Telescope setup
- [x] Add `telescope.nvim` + `telescope-fzf-native.nvim` for fuzzy finding
- [x] Configure basic pickers: `find_files`, `live_grep`, `buffers`, `help_tags`, `oldfiles`, `current_buffer_fuzzy_find`
- [x] Add LSP pickers: `lsp_document_symbols`, `lsp_workspace_symbols`
- [x] Keymaps configured:
- `<leader>ff` - Find files
- `<leader>fg` - Live grep (search text)
- `<leader>fb` - Find buffers (with `<C-d>` to delete)
- `<leader>fh` - Find help
- `<leader>fr` - Recent files
- `<leader>/` - Search current buffer
- `<leader>fs` - Document symbols (LSP)
- `<leader>fS` - Workspace symbols (LSP)
- [x] Minimal UI: dropdown theme for files/buffers, no previewer for quick selection
## Phase 4.4 — Oil.nvim for file manipulation
- [x] Add `stevearc/oil.nvim` for filesystem operations (rename, create, delete, copy, move)
- [x] Configure to handle buffer name sync on file operations
- [x] Keep minimal - use only when shell operations would cause buffer issues
- [x] Keymaps configured:
- `<leader>fo` - Open Oil file browser (regular buffer)
- `<leader>fO` - Open Oil in floating window (with floating previews)
- `<C-p>` in Oil - Preview files (vertical split to right for regular, floating window for float mode)
- [x] Preview splits open to the right via `vertical = true` and `split = 'belowright'`
- [x] Floating window preview direction: `preview_split = "right"`
- [x] Note: Evaluate practicality; can switch to `mini.files` if preferred
## Phase 5 — Treesitter
## Phase 5.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
- [x] Decision: Focus on languages: PHP, HTML, JavaScript, TypeScript, CSS, Markdown, Lua, Bash, JSON
- [x] Decision: Enable syntax highlighting and incremental selection (CR to expand, BS to shrink)
- [x] Decision: Enable textobjects for functions, classes, parameters, conditionals, loops
- [x] Decision: Enable autotag for HTML/PHP/JS/React files
- [x] Decision: Disable indent (experimental) to start; can enable per-filetype if stable
## Phase 5.2 — Base Treesitter
- [x] Add `nvim-treesitter` with incremental selection, highlighting
- [x] Configure parsers: lua, vim, vimdoc, php, html, javascript, typescript, tsx, css, scss, json, markdown, markdown_inline, bash, regex
- [x] Enable auto-install for missing parsers
- [x] Incremental selection keymaps: CR (init/expand), S-CR (scope expand), BS (shrink)
- [x] Disable for large files (>100KB) to maintain performance
## Phase 5.3 — Treesitter textobjects
- [x] Add `nvim-treesitter-textobjects`
- [x] Select keymaps: `af`/`if` (function), `ac`/`ic` (class), `aa`/`ia` (parameter), `ai`/`ii` (conditional), `al`/`il` (loop), `a/` (comment)
- [x] Move keymaps: `]f`/`[f` (next/prev function start), `]F`/`[F` (function end), similar for classes and parameters
- [x] Swap keymaps: `<leader>a`/`<leader>A` (swap parameter with next/prev)
## Phase 5.4 — Treesitter autotag
- [x] Add `nvim-ts-autotag`
- [x] Enable auto-close, auto-rename, and close-on-slash for HTML/PHP/JS/React/TS/Vue files
## Phase 6 — UX / Editing
## Phase 6.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
- [x] Decision: No cursorcolumn (as per AGENTS.md)
- [x] Decision: No auto-window toggling behavior (keep settings static)
- [x] Decision: No motion plugin (skip leap/flash)
- [x] Decision: Move undotree to separate Phase 6.8
- [x] UX settings to add: signcolumn, cursorline, colorcolumn
## Phase 6.2 — Core UX settings
- [x] Add signcolumn=yes (always show gutter for LSP/git)
- [x] Add cursorline (highlight current line)
- [x] Add colorcolumn=80,120 (visual guides)
- [x] No cursorcolumn (excluded as per AGENTS.md)
- [x] No per-window autocommands (keep simple)
## Phase 6.3 — Comment.nvim
- [x] Add `numToStr/Comment.nvim`
- [x] Configure for gcc/gc commenting
- [x] Add keymaps if needed
## Phase 6.4 — Surround
- [x] Add `kylechui/nvim-surround`
- [x] Minimal config with default keymaps (ys, ds, cs)
## Phase 6.5 — Autopairs
- [x] Add `windwp/nvim-autopairs`
- [x] Integrate with nvim-cmp
## Phase 6.6 — Indent guides
- [x] Add `lukas-reineke/indent-blankline.nvim`
- [x] Configure scope highlighting
## Phase 6.7 — Folding (UFO)
- [x] Add `kevinhwang91/nvim-ufo` for folding
- [x] Configure with Treesitter and LSP providers
- [x] Add fold keymaps (za, zR, zM, etc.)
## Phase 6.8 — Undotree
- [x] Add `mbbill/undotree`
- [x] Add keymap to toggle undotree
- [x] Configure minimal settings
## Phase 7 — Git Integration
## Phase 7.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
- [x] Decision: Gitsigns with minimal config - signs in gutter, hunk navigation/actions only
- [x] Decision: Skip markdown rendering plugins entirely
## Phase 7.2 — Gitsigns
- [x] Add `lewis6991/gitsigns.nvim`
- [x] Configure signs in gutter (add, change, delete, topdelete, changedelete)
- [x] Add hunk navigation keymaps (]h/[h for next/prev hunk)
- [x] Add hunk action keymaps (stage, reset, preview)
- [x] Keep minimal - no inline blame, no advanced features
## Phase 8 — Copilot
## Phase 8.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
- [x] Decision: Integrate Copilot into nvim-cmp as completion source
- [x] Decision: Auto-trigger suggestions as you type
- [x] Decision: No specific Copilot keymaps needed (use existing cmp keymaps)
- [x] Decision: Enable for all filetypes
- [x] Decision: Copilot suggestions appear before LSP suggestions in completion menu
- [x] Node.js v22.21.1 upgraded and confirmed working
## Phase 8.2 — Copilot setup
- [x] Add `zbirenbaum/copilot.lua` + `zbirenbaum/copilot-cmp`
- [x] Configure copilot.lua with auto-trigger and all filetypes
- [x] Add copilot-cmp as source to nvim-cmp
- [x] Set copilot-cmp priority higher than LSP in completion menu
- [x] Note: Copilot initializes on first InsertEnter; enter/exit insert mode once before using `:Copilot auth`
## Phase 9 — Formatting & Linting
## Phase 9.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
- [x] Decision: Formatters - prettier (JS/TS/CSS/JSON/MD/HTML), phpcbf (PHP), stylua (Lua), black (Python)
- [x] Decision: Linters - eslint (JS/TS), phpcs (PHP), markdownlint (Markdown), ruff (Python)
- [x] Decision: Project-local executables preferred, fallback to global (node_modules/.bin/, vendor/bin/)
- [x] Decision: phpcs/phpcbf already installed globally with WordPress coding standards - no installation needed
- [x] Decision: Format-on-save enabled by default with toggle capability
- [x] Decision: Manual format keymaps: `<leader>lf` (normal), `<leader>lf` (visual range)
- [x] Decision: Respect project config files (.prettierrc, phpcs.xml, .eslintrc, pyproject.toml, etc.)
- [x] Strategy: Helper function detects project-local executables first, then global
- [x] WordPress: phpcs.xml in project root or --standard=WordPress flag for WordPress projects
- [x] Philosophy: Formatters are authoritative source of truth; Neovim settings should match formatter rules per filetype
- [x] Note: Phase 9.4 will align Neovim editor settings (tabstop, shiftwidth, expandtab) with formatter configurations
## Phase 9.2 — none-ls setup with project-aware executables
- [x] Add `nvimtools/none-ls.nvim`
- [x] Create helper function to detect project-local executables (node_modules/.bin/, vendor/bin/, Mason bin)
- [x] Configure formatters:
- [x] prettier (project-local first, then Mason, then global)
- [x] phpcbf (project-local first, then global system install - already available)
- [x] stylua (Mason installed)
- [x] Add `mfussenegger/nvim-lint` for linting (none-ls removed most linters from builtins)
- [x] Configure linters via nvim-lint:
- [x] eslint_d (project-local first, then Mason, then global - daemon version for speed)
- [x] phpcs (project-local first, then global system install - already available)
- [x] markdownlint (Mason installed)
- [x] Add format-on-save autocommand with toggle capability (`<leader>lt` to toggle)
- [x] Add manual format keymaps: `<leader>lf` (buffer), `<leader>lf` (visual range)
- [x] Ensure WordPress coding standards work via phpcs.xml or --standard flag
- [x] Search order: project node_modules/.bin/ → project vendor/bin/ → Mason bin → system PATH
- [x] Note: Linting runs on BufEnter, BufWritePost, InsertLeave events
## Phase 9.3 — Mason formatter/linter installation
- [x] Add `WhoIsSethDaniel/mason-tool-installer.nvim` for automated installation
- [x] Install via Mason: prettier, eslint_d, markdownlint, stylua, black, ruff
- [x] Note: phpcs/phpcbf already installed globally, skip Mason installation
- [x] Verify all tools available: Tools installed to ~/.local/share/nvim/mason/bin/
## Phase 9.4 — Align Neovim settings with formatter rules
- [x] Configure per-filetype settings to match formatter defaults
- [x] PHP: tabs, 2-width display (WordPress standards)
- [x] JS/TS/CSS/JSON/HTML: spaces, 2-width (Prettier defaults)
- [x] Lua: spaces, 2-width (common Neovim convention)
- [x] Markdown: spaces, 2-width (Prettier defaults)
- [x] Python: spaces, 4-width (Black/PEP 8 defaults)
- [x] Use autocmds in lua/autocmds.lua for filetype-specific tabstop, shiftwidth, expandtab
- [x] Set global defaults: 4-width, spaces (reasonable baseline)
- [x] Goal: Manual editing feels natural and matches what formatter will produce on save
- [x] Note: All settings kept explicit per-filetype for clarity and maintainability
## Phase 10 — Migrate Kept Behaviours
## Phase 10.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
- [x] Decision: Port abbreviations to Lua via vim.cmd.iabbrev
- [x] Decision: Port shell template auto-loading via autocmd + BufReadPost
- [x] Decision: Whitespace highlighting already handled via listchars (Phase 3.2)
- [x] Decision: Skip persistent folds (UFO handles folding, no explicit persistence needed)
## Phase 10.2 — Abbreviations
- [x] Abbreviations: `adn→and`, `waht→what`, `tehn→then`, `functin→function`, `positin→position`
- [x] Created dedicated `lua/abbreviations.lua` for modularity and easier maintenance
## Phase 10.3 — Templates
- [x] Templates: auto-populate new `.sh` from template
- [x] Added BufNewFile autocmd for *.sh in lua/autocmds.lua
## Phase 10.4 — Whitespace highlighting
- [x] Whitespace highlighting (modern approach)
- [x] Note: Already handled via listchars in Phase 3.2 (settings.lua)
## Phase 10.5 — Persistent folds
- [x] Persistent folds (via UFO) -- no need, this is no longer required.
- [x] Note: UFO handles folding; no explicit persistence mechanism needed
## Phase 11 — Cleanup & Validation
## Phase 11.1 — Confirm scope and priorities
- [ ] Confirm scope and priorities for this phase
## Phase 11.2 — Retire legacy files
- [ ] Retire legacy Vimscript files (keep for reference until verified)
## Phase 11.3 — Startup performance
- [ ] Validate startup performance (no errors, fast launch)
## Phase 11.4 — Tab workflow validation
- [ ] Validate tab-per-context workflow with Neo-tree (if used)
## Phase 11.5 — Navigation validation
- [ ] Validate Telescope navigation + LSP jumps
## Phase 11.6 — Language tooling validation
- [ ] Validate HTML/PHP/JS/Markdown tooling
---
Notes:
- Keep plugin specs minimal; configure in their own `lua/plugins/*.lua` files.
- Avoid adding plugins not listed in the guide unless explicitly requested.
- Prefer simple defaults; only add settings that clearly improve workflow.
- Plugin approval policy: unlisted plugins may be proposed, but must be explicitly confirmed before installation.
Known Issues / Follow-ups:
- lua-language-server (lua_ls) from Mason failed to start due to missing shared library `libbfd-2.38-system.so`. Options:
- Install lua-language-server via system package manager compatible with your distro.
- Provide the required `libbfd` or adjust symlink to match expected soname.
- Skip lua_ls for now; validation done with other servers (jsonls, intelephense).
- LSP root detection: In some cases, the parent repository is picked as the root (e.g., when a workspace lives inside another repo). Workaround: create an empty `.git` directory (or a marker like `.nvimroot`) in the intended workspace root to pin the project root for LSPs.
Decisions & Changes:
- **Neoconf dropped**: folke/neoconf.nvim incompatible with Neovim 0.11+ vim.lsp.config API (GitHub issue #116, open since May 2024, no resolution). Migrated to native `exrc` + `secure` approach.
- **vim.lsp.config migration**: Migrated from deprecated `require('lspconfig')[server].setup()` to `vim.lsp.config()` + `vim.lsp.enable()` (Neovim 0.11+ native API).
- **Project config format**: `.nvim.lua` files return `{ lsp = { [server_name] = { settings = {...} } } }`.
- **Security model**: `secure` mode prompts user to trust `.nvim.lua` files before execution (one-time per file hash).
- **Settings loading**: `on_new_config` hook loads project settings from actual `root_dir` (not cwd), ensuring correct behavior across different workspace structures.
- **Phase 4 Navigation Strategy**: Skipped Neo-tree in favor of built-in netrw for visual context and project structure browsing. Telescope for fuzzy finding. Oil.nvim for file manipulation (evaluation phase; alternative is mini.files). Rationale: User needs visual context and preview, not per-tab roots or complex tree features. File manipulation primarily done in shell, but Oil.nvim handles buffer sync issues when renaming/moving files.