nvim/MIGRATION_PLAN.md
2026-10-28 14:58:06 +00:00

686 lines
37 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)
- [x] Re-enabled persistent undo in settings.lua (2025-01-06)
## 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 per‑project 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`
- [x] Disable auto-selection in nvim-cmp (require explicit confirm)
## Phase 3.4 — Project‑local 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 an `LspAttach` callback using the client's actual root directory
- [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 `LspAttach` 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 the attached client's actual root_dir
- [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 secure `.nvim.lua` loading during `LspAttach` (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 securely loaded during `LspAttach` 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: Evaluate Oil.nvim for file manipulation (later removed in Phase 4.5)
## 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 evaluation (retired)
- [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 4.5 — Return to a netrw-only explorer workflow
- [x] Remove Oil.nvim after workflow evaluation
- [x] Use netrw as the sole tree browser and file explorer for consistency on remote systems
- [x] Keep Telescope as a fuzzy finder rather than a file explorer
## 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), ruff_format via conform (Python; supersedes the original black plan)
- [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 — conform.nvim setup with project-aware executables
- [x] Add `stevearc/conform.nvim` (replaced none-ls due to phpcbf preprocessing bug)
- [x] Configure formatters (conform has built-in support for all):
- [x] prettier (auto-detects project-local, Mason, global)
- [x] phpcbf (auto-detects project-local, global; customized for WordPress standard)
- [x] stylua (auto-detects project-local, Mason, global)
- [x] Add `mfussenegger/nvim-lint` for linting (separate from formatting)
- [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 handled by conform.nvim automatically: project → Mason → system PATH
- [x] Note: Linting runs on BufEnter, BufWritePost, InsertLeave events
- [x] Removed none-ls.lua (bug caused blank lines in PHP formatting)
## 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, and 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 10.6 — Custom tabline function
- [x] Implement custom tabline with configurable path shortening (`lua/utils.lua`)
- [x] Configure tabline in `lua/settings.lua` with `utils.tabline_full_parents`
- [x] Document tabline configuration in `README.md`
- [x] Show N parent directories in full, shorten earlier paths to first letter
## Phase 11 — Colorscheme: Modern Paper Tonic
## Phase 11.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
- [x] Decision: Create modern Paper Tonic fork from scratch
- [x] Decision: Name = "paper-tonic-modern"
- [x] Decision: Location = inside nvim config initially, move to repo later
- [x] Decision: Structure = modular Lua colorscheme (colors.lua, groups/*.lua)
- [x] Phased approach:
- Phase 11.2-11.4: Extract palette and base groups
- Phase 11.5-11.6: General TreeSitter highlights (modern @* groups)
- Phase 11.7: Semantic highlighting (custom CSS/HTML captures)
- Phase 11.8: Plugin support (LSP, Telescope, Gitsigns, etc.)
## Phase 11.2 — Extract Paper Tonic color palette
- [x] Create `lua/paper-tonic-modern/colors.lua`
- [x] Extract all `c_*` color variables from Paper Tonic
- [x] Document what each color represents (foreground, background, accent colors)
- [x] Convert to modern format (keep hex, 256-color, ANSI mappings)
## Phase 11.3 — Create colorscheme structure
- [x] Create directory structure:
- `colors/paper-tonic-modern.lua` (entry point)
- `lua/paper-tonic-modern/init.lua` (main logic)
- `lua/paper-tonic-modern/colors.lua` (palette)
- `lua/paper-tonic-modern/groups/` (highlight group definitions)
- [x] Create helper function for setting highlights
- [x] Set up colorscheme loading mechanism
## Phase 11.4 — Base vim highlight groups
- [x] Create `lua/paper-tonic-modern/groups/editor.lua`
- [x] Map Paper Tonic colors to base groups: Normal, Comment, LineNr, CursorLine, etc.
- [x] Create `lua/paper-tonic-modern/groups/syntax.lua`
- [x] Map to syntax groups: Function, String, Keyword, Identifier, etc.
- [x] Test with simple Lua file to verify base colors work
## Phase 11.5 — Modern TreeSitter highlight groups
- [x] Create `lua/paper-tonic-modern/groups/treesitter.lua`
- [x] Map modern `@*` groups to Paper Tonic colors:
- Core: @variable, @function, @keyword, @string, @number, @boolean, @comment
- Types: @type, @property, @field, @parameter
- Operators: @operator, @punctuation.delimiter, @punctuation.bracket
- Language constructs: @keyword.function, @keyword.return, @keyword.conditional
- [x] Use `:help treesitter-highlight-groups` as reference
- [x] Test with multiple languages: Lua, PHP, JavaScript, HTML, CSS
## Phase 11.6 — Language-specific TreeSitter groups
- [x] Add language-specific overrides (e.g., @variable.php, @tag.html)
- [x] Ensure HTML tags, CSS selectors, PHP variables have appropriate colors
- [x] Test across primary languages (HTML, PHP, JS, CSS, Markdown)
- [x] Added comprehensive language-specific groups:
- CSS/SCSS: properties, variables, functions (green c2 tones)
- HTML: tags, attributes, delimiters (blue c3)
- PHP: variables, functions, methods, built-ins (primary brownish-red)
- JavaScript/TypeScript: variables, functions, properties, JSX tags (primary + blue for JSX)
- Lua: variables, functions, built-ins (primary)
- Markdown: headings, links, code blocks (primary for structure)
- JSON: properties, strings, numbers (neutral)
- Bash: variables, functions, built-ins (primary)
## Phase 11.7 — Semantic highlighting (custom captures)
- [x] Create `lua/paper-tonic-modern/groups/semantic.lua`
- [x] Define your custom semantic captures:
- @CssClassName (for CSS .class and HTML class="" - same color)
- @CssIdentifier (for CSS #id and HTML id="" - same color)
- @DataAttribute, @DataAttributeValue (HTML data-* attributes)
- @cssPseudoClass, @cssNestingSelector, @CssUniversalSelector (CSS-specific)
- [x] Verify cross-language consistency: class names in HTML match CSS
- [x] Implemented semantic highlights:
- CSS: @CssClassName (green c2), @CssIdentifier (strong green c2_strong), @cssPseudoClass (weak green c2_weak)
- CSS: @cssNestingSelector (strong green), @CssUniversalSelector (SpecialChar)
- CSS: @CssProp (primary_weak), @CssPropertyValue (bold), @CssUnit (Delimiter)
- HTML: @ClassNameAttribute, @IdAttribute, @DataAttribute (all fg_weak for attribute names)
- HTML: @DataAttributeValue (Function)
- Cross-language: class values and #id values in HTML use same colors as CSS selectors
- [x] Test with existing custom queries (after/queries/css/highlights.scm, after/queries/html/highlights.scm)
## Phase 11.8 — Plugin highlight groups
- [x] Create `lua/paper-tonic-modern/groups/lsp.lua`
- [x] Define LSP diagnostics: DiagnosticError, DiagnosticWarn, DiagnosticHint, DiagnosticInfo
- [x] Define LSP UI: LspReferenceText, LspReferenceRead, LspReferenceWrite
- [x] Create `lua/paper-tonic-modern/groups/plugins.lua`
- [x] Add Telescope highlights (TelescopeNormal, TelescopeBorder, TelescopeSelection)
- [x] Add Gitsigns highlights (GitSignsAdd, GitSignsChange, GitSignsDelete)
- [x] Add nvim-cmp highlights (CmpItemKind*, CmpItemMenu, etc.)
**Notes**:
- LSP diagnostics use red (error), orange (warn), blue (info), green (hint)
- LSP references use cyan backgrounds with varying intensity (text/read/write)
- LSP semantic tokens link to TreeSitter groups for consistency
- Telescope uses neutral colors with primary accent for matching and selection
- Gitsigns uses status colors: green (add), blue (change), red (delete)
- nvim-cmp uses primary tones for kinds, bold for matching text
- indent-blankline and ufo use subtle grays for non-intrusive UI
## Phase 11.9 — Update plugin configuration
- [x] Update `lua/plugins/colorscheme.lua` to use paper-tonic-modern
- [x] Remove old Paper Tonic reference
- [x] Remove colorscheme-overrides.lua (no longer needed)
- [x] Test full config reload
**Notes**:
- `lua/plugins/colorscheme.lua` already updated to load `paper-tonic-modern`
- Old `paper-tonic/` directory moved to `legacy/paper-tonic-original/` (archived)
- No `colorscheme-overrides.lua` existed (not needed with modern implementation)
- Full config reload tested: Normal ✓, DiagnosticError ✓, TelescopeNormal ✓, GitSignsAdd ✓
- Colorscheme loads correctly: `vim.g.colors_name = 'paper-tonic-modern'`
## Phase 11.10 — Validation and polish
- [x] Test all primary language parsers (Lua, PHP, HTML, CSS, JS, Markdown)
- [x] Verify semantic highlight captures for matching CSS/HTML classes and IDs load correctly
- [x] Check configured plugin UI highlight groups (Telescope, Gitsigns, nvim-cmp)
- [x] Verify LSP diagnostic highlight groups are defined and distinct
- [x] Document color scheme in README
## Phase 11.11 — Extract to separate repo (later)
- [ ] Move to `~/projects/paper-tonic-modern/`
- [ ] Update plugin config to point to new location
- [ ] Add proper README, LICENSE, screenshots
- [ ] Consider publishing to GitHub
## Phase 12 — Cleanup & Validation
## Phase 12.1 — Confirm scope and priorities
- [x] Confirm scope and priorities for this phase
## Phase 12.2 — Retire legacy files
- [x] Retire tracked `.vimtmp` backup artifacts and prevent them from being re-added
- [x] Retire the broken `legacy/paper-tonic-original` symlink
- [x] Keep the meaningful legacy Vimscript archive as historical reference
## Phase 12.3 — Startup performance
- [x] Validate startup performance (clean headless launch in approximately 45 ms on 2026-10-27)
## Phase 12.4 — Navigation validation
- [ ] Validate Telescope navigation + LSP jumps
- [ ] Validate netrw browsing and preview splits
- [x] Remove Oil.nvim and its keymaps, lockfile entry, highlights, and active documentation
- [x] Fix working directory behavior (disabled autochdir, locked Telescope to project root)
- [x] Make alternate-buffer jumps skip netrw explorer buffers when returning to the last real file
- [x] Guard alternate-buffer tracking against unset or invalid buffer numbers
## Phase 12.5 — Language tooling validation
- [ ] Validate HTML/PHP/JS/Markdown tooling
- [ ] Verify LSP (gd, gr, K, diagnostics)
- [ ] Verify formatting (prettier, phpcbf, stylua)
- [ ] Verify linting (eslint_d, phpcs, markdownlint)
- [x] Confirm primary Tree-sitter parsers and HTML/CSS custom queries load
- [x] Confirm Intelephense attaches with secure project-local includePaths
- [x] Confirm configured formatter and linter executables resolve
## Phase 12.6 — Final polish
- [x] Review all keymaps and ensure they're documented (README.md + :KeymapsGuide)
- [x] Update LOG.md with final architecture and usage
- [ ] Clean up any remaining debug code or temporary comments
## Phase 13 — Controlled Neovim 0.12 Upgrade
## Phase 13.1 — Confirm scope and upgrade policy
- [x] Confirm scope and priorities for this phase
- [x] Target the current stable release: Neovim 0.12.6
- [x] Continue using the official standalone Linux archive; do not hand Neovim management to pacman
- [x] Keep Neovim 0.11.5 available until the upgraded setup has passed normal-use validation
- [x] Separate the Neovim upgrade, plugin update, and Mason tool update into independently testable steps
## Phase 13.2 — Create a clean rollback checkpoint
- [x] Commit the completed Oil.nvim removal before beginning the upgrade
- [x] Record the current Neovim version, plugin lockfile, parser state, and startup result
- [x] Confirm the existing 0.11.5 configuration still passes its automated checks
- [x] Do not remove or overwrite the working 0.11.5 installation
## Phase 13.3 — Audit 0.12 compatibility before switching
- [ ] Review the Neovim 0.12 breaking changes against the active configuration
- [x] Remove deprecated `vim.lsp.with()` handler wrappers; the shared floating-preview override already supplies the configured border
- [x] Disable mason-lspconfig's automatic server activation and remove its obsolete `automatic_installation` option; `lsp.lua` remains the sole activation authority
- [x] Replace deprecated `client.notify(...)` with the 0.12-compatible `client:notify(...)` method for project-local settings
- [ ] Replace deprecated `vim.loop` usage with `vim.uv`
- [ ] Replace deprecated diagnostic navigation APIs with the current diagnostic jump API
- [ ] Revalidate native `exrc` behavior now that 0.12 searches parent directories
- [ ] Verify `vim.secure.read()` behavior in the project-local LSP settings loader
- [ ] Determine how Neovim 0.12's built-in directory buffers interact with opt-in legacy netrw
- [ ] Explicitly load netrw with `:packadd netrw` from Lua if 0.12 no longer loads it automatically
- [ ] Check for conflicts or redundancy with Neovim 0.12's built-in `:Undotree`; do not remove the plugin without a separate decision
## Phase 13.4 — Install Neovim 0.12.6 alongside 0.11.5
- [x] Download the official `nvim-linux-x86_64.tar.gz` release archive
- [x] Verify the archive against the SHA-256 digest published with the release
- [x] Install into a versioned directory without replacing 0.11.5
- [ ] Introduce an explicit `current` symlink (or equivalent atomic selector) for the active version
- [ ] Update the external PATH configuration only after both versioned binaries are verified
- [ ] Document the one-command rollback to 0.11.5
Proposed layout:
```text
/opt/neovim/nvim-0.11.5/
/opt/neovim/nvim-0.12.6/
/opt/neovim/current -> nvim-0.11.5
```
## Phase 13.5 — Test 0.12 with the existing plugin lockfile
- [ ] Use isolated `XDG_DATA_HOME`, `XDG_CACHE_HOME`, and `XDG_STATE_HOME` directories
- [ ] Install the currently locked plugin revisions into the isolated data directory
- [ ] Keep the production plugin and Mason directories untouched
- [ ] Validate headless startup and capture startup time
- [x] Run `:checkhealth` and review every relevant warning/error
- [ ] Verify netrw can be loaded and `:Explore` opens the expected tree browser
- [ ] Fix core/config compatibility issues before updating plugins
## Phase 13.6 — Update plugins in isolation
- [ ] Use a temporary branch or worktree based on the rollback checkpoint
- [ ] Run lazy.nvim updates only inside the isolated 0.12 test environment
- [ ] Review every lockfile change before adopting it
- [ ] Prioritize lazy.nvim, nvim-lspconfig, Treesitter, Telescope, Mason integration, completion, formatting, linting, UFO, and Copilot compatibility
- [ ] Rebuild or update Treesitter parsers for the 0.12 runtime and verify custom queries
- [ ] Keep Mason-managed language servers, formatters, and linters pinned during this step
- [ ] Adopt the tested lockfile only after all plugin-level regressions are resolved
## Phase 13.7 — Validate the complete 0.12 candidate
- [ ] Validate startup, lazy loading, commands, and keymaps
- [ ] Validate netrw tree browsing, previews, alternate-buffer behavior, and file operations
- [ ] Validate Telescope files, grep, buffers, and LSP symbol pickers
- [ ] Validate LSP attachment, `gd`, `gr`, `K`, diagnostics, and project-local `.nvim.lua` settings
- [ ] Validate nvim-cmp, LuaSnip, and Copilot completion
- [ ] Validate conform format-on-save and nvim-lint/Ruff diagnostics
- [ ] Validate Treesitter highlighting, text objects, autotag, folding, and custom HTML/CSS queries
- [ ] Validate the Paper Tonic colorscheme, plugin highlights, sessions, persistent undo, and shell templates
- [ ] Perform interactive smoke tests in the PHP validation workspace
## Phase 13.8 — Activate with rollback available
- [ ] Switch the active-version selector from 0.11.5 to 0.12.6
- [ ] Repeat a short smoke test through the normal shell/PATH entrypoint
- [ ] Keep 0.11.5 and its rollback instructions available during a settling period
- [ ] Update AGENTS.md, README.md, and LOG.md with the final supported version and upgrade procedure
- [ ] Remove 0.11.5 only after an explicit later decision
## Phase 13.9 — Update external tools separately
- [ ] Review Mason package updates only after Neovim and plugin revisions are stable
- [ ] Update language servers, formatters, and linters in small groups
- [ ] Revalidate PHP, JavaScript/TypeScript, HTML/CSS, Markdown, Lua, and Python after each group
## Phase 14 — Netrw Live Preview and Directory Summaries
## Phase 14.1 — Confirm the final interaction model
- [ ] Confirm scope and priorities for this phase after the 0.12 upgrade
- [x] Keep netrw as the sole tree browser and file explorer
- [x] Keep `p` as the preview entry point so stock-netrw muscle memory remains useful on remote systems
- [x] Once a native preview window exists, update it automatically as the netrw cursor moves
- [x] Close the preview automatically when a file is opened for editing or netrw is left
- [x] Treat the existence of the preview window as state; avoid a separate global preview-mode flag
Target workflow:
```text
p on file/directory open preview and begin live updates
j / k replace preview with the selected entry
<CR>, o, v, t open the file and close the preview
:pclose / <C-w>z close the preview and stop live updates
```
## Phase 14.2 — Revalidate netrw integration on Neovim 0.12
- [ ] Confirm the netrw version and its documented `netrw#Call()` bridge
- [ ] Confirm `NetrwGetWord`, `NetrwBrowseChgDir`, and `NetrwPreview` remain available
- [ ] Confirm tree paths, symlinks, directory markers, local paths, and URI paths are classified correctly
- [ ] Resolve any interaction with Neovim 0.12's built-in `filetype=directory` buffers
- [ ] Keep a graceful stock-netrw fallback if enhanced preview setup cannot be installed
## Phase 14.3 — Implement live preview lifecycle
- [ ] Extend netrw's buffer-local `p` behavior without changing its familiar purpose
- [ ] Add a narrowly scoped, buffer-local `CursorMoved` autocmd
- [ ] Debounce preview updates by approximately 75–100 ms
- [ ] Update only when a preview window exists in the current tab
- [ ] Continue using netrw's native preview machinery for regular files
- [ ] Close the preview when leaving the netrw buffer to edit a file
- [ ] Cancel and close pending timers when the netrw buffer is wiped out
- [ ] Avoid focus changes: keep the cursor in the tree while previews update
## Phase 14.4 — Add read-only directory summaries
- [ ] Show an informational scratch-buffer preview when the selected entry is a directory
- [ ] List immediate children only; do not calculate recursive directory sizes
- [ ] Sort directories first and files second, alphabetically within each group
- [ ] Show entry type, human-readable file size, modification time, and name
- [ ] Show a summary count and total size for immediate files
- [ ] Cap output at approximately 200 entries and show a truncation notice
- [ ] Handle empty, missing, unreadable, and rapidly changing directories without errors
- [ ] Display symlinks distinctly without recursively following directory links
- [ ] Mirror netrw's hidden-file visibility where practical
- [ ] Support local filesystems when Neovim itself is running over SSH
- [ ] Fall back to native behavior for remote netrw URI schemes if directory enumeration is unavailable
Example directory preview:
```text
lua/plugins/
Type Size Modified Name
dir — 2026-10-27 11:42 helpers/
file 4.8K 2026-10-27 13:10 lsp.lua
file 3.2K 2026-10-27 12:55 telescope.lua
1 directory, 2 files, 8.0K
```
## Phase 14.5 — Test preview behavior
- [ ] Test file → file, file → directory, directory → file, and directory → directory movement
- [ ] Test directory expand/collapse while a preview exists
- [ ] Test manual preview closure and confirm cursor movement no longer previews
- [ ] Test `<CR>`, horizontal split, vertical split, and tab selection cleanup
- [ ] Test separate preview windows across tabpages
- [ ] Test spaces, Unicode, symlinks, unreadable entries, deleted entries, and large directories
- [ ] Test rapid cursor movement for flicker, stale timers, and excess filesystem work
- [ ] Test the workflow in an actual SSH session
- [ ] Confirm stock netrw remains understandable when this configuration is unavailable
## Phase 14.6 — Document and finalize
- [ ] Update README.md and `:KeymapsGuide` with the enhanced `p` lifecycle
- [ ] Record implementation decisions and limitations in LOG.md
- [ ] Mark Phase 12.4 netrw preview validation complete after interactive use
---
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`. **RESOLVED**: Installed lua-language-server via system package manager (pacman) instead of Mason. Removed `lua_ls` from Mason's ensure_installed list in `lua/plugins/mason-lspconfig.lua`.
- 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**: An `LspAttach` callback securely reads project settings from the client's actual `root_dir` (not cwd), ensuring correct behavior across different workspace structures.
- **Phase 4 Navigation Strategy**: Use built-in netrw as the sole tree browser and file explorer so the workflow transfers to remote systems. Telescope remains the fuzzy finder. Oil.nvim was evaluated and removed because maintaining a second explorer split muscle memory without providing the preferred tree view.