nvim/MIGRATION_PLAN.md

39 KiB
Raw Blame History

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

  • Create AGENTS.md with rules

Phase 1 — Clean & Archive Legacy Config

Phase 1.1 — Confirm scope and priorities

  • Confirm scope and priorities for this phase

Phase 1.2 — Inventory legacy files

  • Inventory existing Vimscript, plugin files, and directories

Phase 1.3 — Create archive location

  • Create legacy/ archive folder within this config

Phase 1.4 — Move legacy files into archive

  • Move legacy init files and plugin directories into legacy/ (do not delete)

Phase 1.5 — Document archived contents

  • Optionally add legacy/README.md noting what was archived

Phase 1.6 — Preserve selected directories

  • Keep undodir, spell, view, UltiSnips, templates as-is for now (review later)
  • Re-enabled persistent undo in settings.lua (2025-01-06)

Phase 2 — Bootstrap

Phase 2.1 — Confirm scope and priorities

  • Confirm scope and priorities for this phase

Phase 2.2 — Scaffold Lua config skeleton

  • 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

  • Bootstrap lazy.nvim plugin manager

Phase 2.4 — Disable unused providers

  • Disable unused providers (ruby, perl, node)

Phase 2.5 — Ensure lazy boots without specs

  • Ensure lazy boots without specs (add empty lua/plugins/init.lua)

Phase 3 — Core Editing & LSP

Phase 3.1 — Confirm scope and priorities

  • Confirm scope and priorities for this phase
  • Decision: PHP LSP = intelephense
  • Decision: Enable Markdown LSP = marksman
  • Decision: Use legacy listchars and showbreak values
  • Decision: Support per‑project config for intelephense.includePaths
  • Decision: No direnv fallback for local config

Phase 3.2 — Non-plugin settings to Lua

  • Port non-plugin settings to Lua (options, listchars, showbreak, spelllang=en_gb)

Phase 3.3 — Core completion stack

  • Add core completion stack: nvim-cmp, cmp-nvim-lsp, cmp-buffer, cmp-path, LuaSnip
  • Disable auto-selection in nvim-cmp (require explicit confirm)

Phase 3.4 — Project‑local configuration (native exrc)

  • Confirm scope and priorities for this subphase
  • Decision: Use native exrc + secure instead of neoconf (neoconf incompatible with Neovim 0.11+ vim.lsp.config API)
  • Enable exrc and secure in settings.lua
  • Implement .nvim.lua loader in an LspAttach callback using the client's actual root directory
  • Wire intelephense.environment.includePaths via .nvim.lua
  • Create validation workspaces: WORKSPACE_TEST/ and EXTERNAL_TEST/ with sample PHP files
  • Migrate to vim.lsp.config() and vim.lsp.enable() (Neovim 0.11+ native API)
  • Verify project settings load correctly from actual root_dir
  • Clean up debug notifications and temporary code

Phase 3.7 — Clean LSP config

  • Remove debug/helper logic from LSP config
  • Migrate from deprecated require('lspconfig')[server].setup() to vim.lsp.config() + vim.lsp.enable()
  • Use LspAttach for project-local settings loading (loads from actual root_dir, not cwd)

Phase 3.8 — Validate project-local LSP settings

  • Create test workspaces with .nvim.lua files
  • Validate settings load from the attached client's actual root_dir
  • Verify gd works for external library references via intelephense.environment.includePaths
  • Clean up debug code and notifications

Phase 3.9 — Native exrc + vim.lsp.config migration (RESOLVED)

  • Discovered neoconf incompatible with Neovim 0.11+ (GitHub issue #116, unresolved since May 2024)
  • Migrated to native exrc + secure approach (zero dependencies, aligned with minimal philosophy)
  • Implemented secure .nvim.lua loading during LspAttach (loads from actual root_dir)
  • Migrated from deprecated require('lspconfig') framework to vim.lsp.config() + vim.lsp.enable()
  • Root detection handled by nvim-lspconfig's native configs (no custom root_dir needed)
  • Validated gd works for external library symbols via intelephense.environment.includePaths
  • Settings schema: .nvim.lua returns { lsp = { [server_name] = { settings = {...} } } }
  • Security: secure mode prompts user to trust .nvim.lua files before execution
  • Kept single_file_support = false for intelephense
  • Fixed duplicate gd results issue

Phase 3.5 — LSP minimal defaults

  • Add nvim-lspconfig with minimal defaults (no over-configuration)
  • Add minimal LSP on-attach keymaps (gd, gr, K, gD, gI)
  • Add global LSP keymaps with fallback in lua/keymaps.lua
  • Intelephense: set single_file_support=false, root detection via nvim-lspconfig defaults
  • Project settings securely loaded during LspAttach from .nvim.lua files

Phase 3.6 — LSP server management (Mason)

  • Confirm scope and priorities for this subphase
  • Add williamboman/mason.nvim and williamboman/mason-lspconfig.nvim
  • Ensure servers installed: lua_ls, tsserver, html, cssls, jsonls, bashls, marksman, intelephense
  • Keep our custom per-server setup; use Mason only for installation

Phase 4 — Navigation

Phase 4.1 — Confirm scope and priorities

  • Confirm scope and priorities for this phase
  • Decision: Skip Neo-tree in favor of netrw for navigation/visual context
  • Decision: Use netrw for project structure visualization and file preview
  • Decision: Telescope as primary "find file" tool
  • Decision: Evaluate Oil.nvim for file manipulation (later removed in Phase 4.5)

Phase 4.2 — Configure netrw

  • Configure netrw with tree view, preview split, and basic settings (no banner, sensible defaults)
  • Created lua/netrw-config.lua with settings: tree view, horizontal preview split below, 50/50 split, human-readable sizes
  • Add netrw keymaps: <leader>te (new tab at current file's directory), <leader>tE (new tab at project root)
  • Decision: No <leader>e or <leader>v keymaps - use :Ex, :Vex directly when needed
  • Preview behavior: Enter opens file in netrw window, p opens 50/50 horizontal split below

Phase 4.2.1 — PHP gf enhancement

  • Add PHP includeexpr for intelligent gf behavior (handles __DIR__, __FILE__, dirname(__FILE__) patterns)
  • Created after/ftplugin/php.lua with path resolution for WordPress/PHP patterns
  • Tested and validated with test-gf.php

Phase 4.3 — Telescope setup

  • Add telescope.nvim + telescope-fzf-native.nvim for fuzzy finding
  • Configure basic pickers: find_files, live_grep, buffers, help_tags, oldfiles, current_buffer_fuzzy_find
  • Add LSP pickers: lsp_document_symbols, lsp_workspace_symbols
  • 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)
  • Minimal UI: dropdown theme for files/buffers, no previewer for quick selection

Phase 4.4 — Oil.nvim evaluation (retired)

  • Add stevearc/oil.nvim for filesystem operations (rename, create, delete, copy, move)
  • Configure to handle buffer name sync on file operations
  • Keep minimal - use only when shell operations would cause buffer issues
  • 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)
  • Preview splits open to the right via vertical = true and split = 'belowright'
  • Floating window preview direction: preview_split = "right"
  • Note: Evaluate practicality; can switch to mini.files if preferred

Phase 4.5 — Return to a netrw-only explorer workflow

  • Remove Oil.nvim after workflow evaluation
  • Use netrw as the sole tree browser and file explorer for consistency on remote systems
  • Keep Telescope as a fuzzy finder rather than a file explorer

Phase 5 — Treesitter

Phase 5.1 — Confirm scope and priorities

  • Confirm scope and priorities for this phase
  • Decision: Focus on languages: PHP, HTML, JavaScript, TypeScript, CSS, Markdown, Lua, Bash, JSON
  • Decision: Enable syntax highlighting and incremental selection (CR to expand, BS to shrink)
  • Decision: Enable textobjects for functions, classes, parameters, conditionals, loops
  • Decision: Enable autotag for HTML/PHP/JS/React files
  • Decision: Disable indent (experimental) to start; can enable per-filetype if stable

Phase 5.2 — Base Treesitter

  • Add nvim-treesitter with incremental selection, highlighting
  • Configure parsers: lua, vim, vimdoc, php, html, javascript, typescript, tsx, css, scss, json, markdown, markdown_inline, bash, regex
  • Enable auto-install for missing parsers
  • Incremental selection keymaps: CR (init/expand), S-CR (scope expand), BS (shrink)
  • Disable for large files (>100KB) to maintain performance

Phase 5.3 — Treesitter textobjects

  • Add nvim-treesitter-textobjects
  • Select keymaps: af/if (function), ac/ic (class), aa/ia (parameter), ai/ii (conditional), al/il (loop), a/ (comment)
  • Move keymaps: ]f/[f (next/prev function start), ]F/[F (function end), similar for classes and parameters
  • Swap keymaps: <leader>a/<leader>A (swap parameter with next/prev)

Phase 5.4 — Treesitter autotag

  • Add nvim-ts-autotag
  • 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

  • Confirm scope and priorities for this phase
  • Decision: No cursorcolumn (as per AGENTS.md)
  • Decision: No auto-window toggling behavior (keep settings static)
  • Decision: No motion plugin (skip leap/flash)
  • Decision: Move undotree to separate Phase 6.8
  • UX settings to add: signcolumn, cursorline, colorcolumn

Phase 6.2 — Core UX settings

  • Add signcolumn=yes (always show gutter for LSP/git)
  • Add cursorline (highlight current line)
  • Add colorcolumn=80,120 (visual guides)
  • No cursorcolumn (excluded as per AGENTS.md)
  • No per-window autocommands (keep simple)

Phase 6.3 — Comment.nvim

  • Add numToStr/Comment.nvim
  • Configure for gcc/gc commenting
  • Add keymaps if needed

Phase 6.4 — Surround

  • Add kylechui/nvim-surround
  • Minimal config with default keymaps (ys, ds, cs)

Phase 6.5 — Autopairs

  • Add windwp/nvim-autopairs
  • Integrate with nvim-cmp

Phase 6.6 — Indent guides

  • Add lukas-reineke/indent-blankline.nvim
  • Configure scope highlighting

Phase 6.7 — Folding (UFO)

  • Add kevinhwang91/nvim-ufo for folding
  • Configure with Treesitter and LSP providers
  • Add fold keymaps (za, zR, zM, etc.)

Phase 6.8 — Undotree

  • Add mbbill/undotree
  • Add keymap to toggle undotree
  • Configure minimal settings

Phase 7 — Git Integration

Phase 7.1 — Confirm scope and priorities

  • Confirm scope and priorities for this phase
  • Decision: Gitsigns with minimal config - signs in gutter, hunk navigation/actions only
  • Decision: Skip markdown rendering plugins entirely

Phase 7.2 — Gitsigns

  • Add lewis6991/gitsigns.nvim
  • Configure signs in gutter (add, change, delete, topdelete, changedelete)
  • Add hunk navigation keymaps (]h/[h for next/prev hunk)
  • Add hunk action keymaps (stage, reset, preview)
  • Keep minimal - no inline blame, no advanced features

Phase 8 — Copilot

Phase 8.1 — Confirm scope and priorities

  • Confirm scope and priorities for this phase
  • Decision: Integrate Copilot into nvim-cmp as completion source
  • Decision: Auto-trigger suggestions as you type
  • Decision: No specific Copilot keymaps needed (use existing cmp keymaps)
  • Decision: Enable for all filetypes
  • Decision: Copilot suggestions appear before LSP suggestions in completion menu
  • Node.js v22.21.1 upgraded and confirmed working

Phase 8.2 — Copilot setup

  • Add zbirenbaum/copilot.lua + zbirenbaum/copilot-cmp
  • Configure copilot.lua with auto-trigger and all filetypes
  • Add copilot-cmp as source to nvim-cmp
  • Set copilot-cmp priority higher than LSP in completion menu
  • 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

  • Confirm scope and priorities for this phase
  • Decision: Formatters - prettier (JS/TS/CSS/JSON/MD/HTML), phpcbf (PHP), stylua (Lua), ruff_format via conform (Python; supersedes the original black plan)
  • Decision: Linters - eslint (JS/TS), phpcs (PHP), markdownlint (Markdown), ruff (Python)
  • Decision: Project-local executables preferred, fallback to global (node_modules/.bin/, vendor/bin/)
  • Decision: phpcs/phpcbf already installed globally with WordPress coding standards - no installation needed
  • Decision: Format-on-save enabled by default with toggle capability
  • Decision: Manual format keymaps: <leader>lf (normal), <leader>lf (visual range)
  • Decision: Respect project config files (.prettierrc, phpcs.xml, .eslintrc, pyproject.toml, etc.)
  • Strategy: Helper function detects project-local executables first, then global
  • WordPress: phpcs.xml in project root or --standard=WordPress flag for WordPress projects
  • Philosophy: Formatters are authoritative source of truth; Neovim settings should match formatter rules per filetype
  • 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

  • Add stevearc/conform.nvim (replaced none-ls due to phpcbf preprocessing bug)
  • Configure formatters (conform has built-in support for all):
    • prettier (auto-detects project-local, Mason, global)
    • phpcbf (auto-detects project-local, global; customized for WordPress standard)
    • stylua (auto-detects project-local, Mason, global)
  • Add mfussenegger/nvim-lint for linting (separate from formatting)
  • Configure linters via nvim-lint:
    • eslint_d (project-local first, then Mason, then global - daemon version for speed)
    • phpcs (project-local first, then global system install - already available)
    • markdownlint (Mason installed)
  • Add format-on-save autocommand with toggle capability (<leader>lt to toggle)
  • Add manual format keymaps: <leader>lf (buffer), <leader>lf (visual range)
  • Ensure WordPress coding standards work via phpcs.xml or --standard flag
  • Search order handled by conform.nvim automatically: project → Mason → system PATH
  • Note: Linting runs on BufEnter, BufWritePost, InsertLeave events
  • Removed none-ls.lua (bug caused blank lines in PHP formatting)

Phase 9.3 — Mason formatter/linter installation

  • Add WhoIsSethDaniel/mason-tool-installer.nvim for automated installation
  • Install via Mason: prettier, eslint_d, markdownlint, stylua, and ruff
  • Note: phpcs/phpcbf already installed globally, skip Mason installation
  • Verify all tools available: Tools installed to ~/.local/share/nvim/mason/bin/

Phase 9.4 — Align Neovim settings with formatter rules

  • Configure per-filetype settings to match formatter defaults
  • PHP: tabs, 2-width display (WordPress standards)
  • JS/TS/CSS/JSON/HTML: spaces, 2-width (Prettier defaults)
  • Lua: spaces, 2-width (common Neovim convention)
  • Markdown: spaces, 2-width (Prettier defaults)
  • Python: spaces, 4-width (Black/PEP 8 defaults)
  • Use autocmds in lua/autocmds.lua for filetype-specific tabstop, shiftwidth, expandtab
  • Set global defaults: 4-width, spaces (reasonable baseline)
  • Goal: Manual editing feels natural and matches what formatter will produce on save
  • Note: All settings kept explicit per-filetype for clarity and maintainability

Phase 10 — Migrate Kept Behaviours

Phase 10.1 — Confirm scope and priorities

  • Confirm scope and priorities for this phase
  • Decision: Port abbreviations to Lua via vim.cmd.iabbrev
  • Decision: Port shell template auto-loading via autocmd + BufReadPost
  • Decision: Whitespace highlighting already handled via listchars (Phase 3.2)
  • Decision: Skip persistent folds (UFO handles folding, no explicit persistence needed)

Phase 10.2 — Abbreviations

  • Abbreviations: adn→and, waht→what, tehn→then, functin→function, positin→position
  • Created dedicated lua/abbreviations.lua for modularity and easier maintenance

Phase 10.3 — Templates

  • Templates: auto-populate new .sh from template
  • Added BufNewFile autocmd for *.sh in lua/autocmds.lua

Phase 10.4 — Whitespace highlighting

  • Whitespace highlighting (modern approach)
  • Note: Already handled via listchars in Phase 3.2 (settings.lua)

Phase 10.5 — Persistent folds

  • Persistent folds (via UFO) -- no need, this is no longer required.
  • Note: UFO handles folding; no explicit persistence mechanism needed

Phase 10.6 — Custom tabline function

  • Implement custom tabline with configurable path shortening (lua/utils.lua)
  • Configure tabline in lua/settings.lua with utils.tabline_full_parents
  • Document tabline configuration in README.md
  • 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

  • Confirm scope and priorities for this phase
  • Decision: Create modern Paper Tonic fork from scratch
  • Decision: Name = "paper-tonic-modern"
  • Decision: Location = inside nvim config initially, move to repo later
  • Decision: Structure = modular Lua colorscheme (colors.lua, groups/*.lua)
  • 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

  • Create lua/paper-tonic-modern/colors.lua
  • Extract all c_* color variables from Paper Tonic
  • Document what each color represents (foreground, background, accent colors)
  • Convert to modern format (keep hex, 256-color, ANSI mappings)

Phase 11.3 — Create colorscheme structure

  • 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)
  • Create helper function for setting highlights
  • Set up colorscheme loading mechanism

Phase 11.4 — Base vim highlight groups

  • Create lua/paper-tonic-modern/groups/editor.lua
  • Map Paper Tonic colors to base groups: Normal, Comment, LineNr, CursorLine, etc.
  • Create lua/paper-tonic-modern/groups/syntax.lua
  • Map to syntax groups: Function, String, Keyword, Identifier, etc.
  • Test with simple Lua file to verify base colors work

Phase 11.5 — Modern TreeSitter highlight groups

  • Create lua/paper-tonic-modern/groups/treesitter.lua
  • 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
  • Use :help treesitter-highlight-groups as reference
  • Test with multiple languages: Lua, PHP, JavaScript, HTML, CSS

Phase 11.6 — Language-specific TreeSitter groups

  • Add language-specific overrides (e.g., @variable.php, @tag.html)
  • Ensure HTML tags, CSS selectors, PHP variables have appropriate colors
  • Test across primary languages (HTML, PHP, JS, CSS, Markdown)
  • 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)

  • Create lua/paper-tonic-modern/groups/semantic.lua
  • 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)
  • Verify cross-language consistency: class names in HTML match CSS
  • 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
  • Test with existing custom queries (after/queries/css/highlights.scm, after/queries/html/highlights.scm)

Phase 11.8 — Plugin highlight groups

  • Create lua/paper-tonic-modern/groups/lsp.lua
  • Define LSP diagnostics: DiagnosticError, DiagnosticWarn, DiagnosticHint, DiagnosticInfo
  • Define LSP UI: LspReferenceText, LspReferenceRead, LspReferenceWrite
  • Create lua/paper-tonic-modern/groups/plugins.lua
  • Add Telescope highlights (TelescopeNormal, TelescopeBorder, TelescopeSelection)
  • Add Gitsigns highlights (GitSignsAdd, GitSignsChange, GitSignsDelete)
  • 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

  • Update lua/plugins/colorscheme.lua to use paper-tonic-modern
  • Remove old Paper Tonic reference
  • Remove colorscheme-overrides.lua (no longer needed)
  • 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

  • Test all primary language parsers (Lua, PHP, HTML, CSS, JS, Markdown)
  • Verify semantic highlight captures for matching CSS/HTML classes and IDs load correctly
  • Check configured plugin UI highlight groups (Telescope, Gitsigns, nvim-cmp)
  • Verify LSP diagnostic highlight groups are defined and distinct
  • 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

  • Confirm scope and priorities for this phase

Phase 12.2 — Retire legacy files

  • Retire tracked .vimtmp backup artifacts and prevent them from being re-added
  • Retire the broken legacy/paper-tonic-original symlink
  • Keep the meaningful legacy Vimscript archive as historical reference

Phase 12.3 — Startup performance

  • 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
  • Remove Oil.nvim and its keymaps, lockfile entry, highlights, and active documentation
  • Fix working directory behavior (disabled autochdir, locked Telescope to project root)
  • Make alternate-buffer jumps skip netrw explorer buffers when returning to the last real file
  • 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)
  • Confirm primary Tree-sitter parsers and HTML/CSS custom queries load
  • Confirm Intelephense attaches with secure project-local includePaths
  • Confirm configured formatter and linter executables resolve

Phase 12.6 — Final polish

  • Review all keymaps and ensure they're documented (README.md + :KeymapsGuide)
  • 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

  • Confirm scope and priorities for this phase
  • Target the current stable release: Neovim 0.12.6
  • Continue using the official standalone Linux archive; do not hand Neovim management to pacman
  • Keep Neovim 0.11.5 available until the upgraded setup has passed normal-use validation
  • Separate the Neovim upgrade, plugin update, and Mason tool update into independently testable steps

Phase 13.2 — Create a clean rollback checkpoint

  • Commit the completed Oil.nvim removal before beginning the upgrade
  • Record the current Neovim version, plugin lockfile, parser state, and startup result
  • Confirm the existing 0.11.5 configuration still passes its automated checks
  • 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
  • Remove deprecated vim.lsp.with() handler wrappers; the shared floating-preview override already supplies the configured border
  • Disable mason-lspconfig's automatic server activation and remove its obsolete automatic_installation option; lsp.lua remains the sole activation authority
  • 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 bundled optional netrw package behaves with the configured explorer workflow
  • Confirm no explicit :packadd netrw is needed: Neovim's default runtime wrapper loads netrw automatically
  • Keep the existing Undotree plugin: Neovim 0.12's built-in :Undotree is an inactive optional package, while the configured plugin's richer :UndotreeToggle workflow still opens cleanly

Phase 13.4 — Install Neovim 0.12.6 alongside 0.11.5

  • Download the official nvim-linux-x86_64.tar.gz release archive
  • Verify the archive against the SHA-256 digest published with the release
  • Install into a versioned directory without replacing 0.11.5
  • Introduce an explicit user-level selector for the active version
  • Activate 0.12.6 through the existing ~/.local/bin PATH entry after verifying both binaries
  • Document rollback by repointing ~/.local/bin/nvim to the 0.11.5 binary

Installed layout:

/opt/neovim/nvim-linux-x86_64/  # 0.11.5 rollback runtime
/opt/neovim/nvim-0.12.6/
~/.local/bin/nvim -> /opt/neovim/nvim-0.12.6/bin/nvim

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 during candidate testing
  • Validate headless startup and capture startup time
  • 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 configured keymaps automatically
  • 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 the current completion stack before activation; native completion migration follows in Phase 13.9
  • Validate conform formatter availability and nvim-lint diagnostics; retain normal-use checks for save-time behavior
  • Validate Treesitter parsers and custom HTML/CSS queries; retain interactive checks for highlighting, text objects, autotag, and folding
  • Validate the Paper Tonic colorscheme, plugin highlights, sessions, persistent undo, and shell templates
  • Perform a PTY-backed PHP smoke test for native completion selection, acceptance, snippets, LSP settings, and diagnostics

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 — Consolidate on Neovim 0.12 native features

  • Keep lazy.nvim as the plugin manager; do not combine the runtime upgrade with a migration to experimental vim.pack
  • Replace mbbill/undotree with the optional native nvim.undotree package while preserving <leader>u
  • Configure native automatic/LSP completion with explicit selection and built-in snippet expansion for its settling trial
  • Enable Copilot inline suggestions with a distinct accept key and hide them while the native completion menu is visible
  • Validate PHP, HTML, JavaScript/TypeScript, Markdown, Lua, paths, snippets, autopairs, and Copilot interactively
  • Remove nvim-cmp, cmp-nvim-lsp, cmp-buffer, cmp-path, LuaSnip, and copilot-cmp from the 0.12 candidate
  • Remove obsolete completion integrations, lockfile entries, highlights, and documentation

Phase 13.10 — 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
  • Keep netrw as the sole tree browser and file explorer
  • Keep p as the preview entry point so stock-netrw muscle memory remains useful on remote systems
  • Once a native preview window exists, update it automatically as the netrw cursor moves
  • Close the preview automatically when a file is opened for editing or netrw is left
  • Treat the existence of the preview window as state; avoid a separate global preview-mode flag

Target workflow:

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 bundled netrw v184 and its documented netrw#Call() bridge
  • Confirm NetrwGetWord, NetrwBrowseChgDir, NetrwTreePath, 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 directory startup behavior: a real UI opens the configured netrw tree normally
  • 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:

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, and large directories
  • Test rapid cursor movement for flicker, stale timers, and excess filesystem work
  • Prevent Tree-sitter parser races during rapid cross-language file preview replacement and restore highlighting on edit
  • Hide diagnostics in file previews and restore their prior per-buffer state on edit
  • Suppress remaining Vim syntax and indent-guide decoration in file previews, restoring prior state on edit
  • 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 dynamic :KeymapsGuide mapping descriptions 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.