38 KiB
38 KiB
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.mdnoting what was archived
Phase 1.6 — Preserve selected directories
- Keep
undodir,spell,view,UltiSnips,templatesas-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.nvimplugin 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
listcharsandshowbreakvalues - 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+secureinstead of neoconf (neoconf incompatible with Neovim 0.11+ vim.lsp.config API) - Enable
exrcandsecurein settings.lua - Implement
.nvim.lualoader in anLspAttachcallback using the client's actual root directory - Wire
intelephense.environment.includePathsvia.nvim.lua - Create validation workspaces:
WORKSPACE_TEST/andEXTERNAL_TEST/with sample PHP files - Migrate to
vim.lsp.config()andvim.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()tovim.lsp.config()+vim.lsp.enable() - Use
LspAttachfor project-local settings loading (loads from actual root_dir, not cwd)
Phase 3.8 — Validate project-local LSP settings
- Create test workspaces with
.nvim.luafiles - Validate settings load from the attached client's actual root_dir
- Verify
gdworks for external library references viaintelephense.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+secureapproach (zero dependencies, aligned with minimal philosophy) - Implemented secure
.nvim.lualoading duringLspAttach(loads from actual root_dir) - Migrated from deprecated
require('lspconfig')framework tovim.lsp.config()+vim.lsp.enable() - Root detection handled by nvim-lspconfig's native configs (no custom root_dir needed)
- Validated
gdworks for external library symbols viaintelephense.environment.includePaths - Settings schema:
.nvim.luareturns{ lsp = { [server_name] = { settings = {...} } } } - Security:
securemode prompts user to trust.nvim.luafiles before execution - Kept
single_file_support = falseforintelephense - Fixed duplicate
gdresults issue
Phase 3.5 — LSP minimal defaults
- Add
nvim-lspconfigwith 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
LspAttachfrom.nvim.luafiles
Phase 3.6 — LSP server management (Mason)
- Confirm scope and priorities for this subphase
- Add
williamboman/mason.nvimandwilliamboman/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.luawith 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>eor<leader>vkeymaps - use:Ex,:Vexdirectly when needed - Preview behavior: Enter opens file in netrw window,
popens 50/50 horizontal split below
Phase 4.2.1 — PHP gf enhancement
- Add PHP
includeexprfor intelligentgfbehavior (handles__DIR__,__FILE__,dirname(__FILE__)patterns) - Created
after/ftplugin/php.luawith path resolution for WordPress/PHP patterns - Tested and validated with test-gf.php
Phase 4.3 — Telescope setup
- Add
telescope.nvim+telescope-fzf-native.nvimfor 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.nvimfor 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 = trueandsplit = 'belowright' - Floating window preview direction:
preview_split = "right" - Note: Evaluate practicality; can switch to
mini.filesif 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-treesitterwith 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-ufofor 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-lintfor 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>ltto 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.nvimfor 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.luafor modularity and easier maintenance
Phase 10.3 — Templates
- Templates: auto-populate new
.shfrom 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.luawithutils.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-groupsas 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.luato use paper-tonic-modern - Remove old Paper Tonic reference
- Remove colorscheme-overrides.lua (no longer needed)
- Test full config reload
Notes:
lua/plugins/colorscheme.luaalready updated to loadpaper-tonic-modern- Old
paper-tonic/directory moved tolegacy/paper-tonic-original/(archived) - No
colorscheme-overrides.luaexisted (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
.vimtmpbackup artifacts and prevent them from being re-added - Retire the broken
legacy/paper-tonic-originalsymlink - 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_installationoption;lsp.luaremains the sole activation authority - Replace deprecated
client.notify(...)with the 0.12-compatibleclient:notify(...)method for project-local settings - Replace deprecated
vim.loopusage withvim.uv - Replace deprecated diagnostic navigation APIs with the current diagnostic jump API
- Revalidate native
exrcbehavior 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 netrwis needed: Neovim's default runtime wrapper loads netrw automatically - Keep the existing Undotree plugin: Neovim 0.12's built-in
:Undotreeis an inactive optional package, while the configured plugin's richer:UndotreeToggleworkflow still opens cleanly
Phase 13.4 — Install Neovim 0.12.6 alongside 0.11.5
- Download the official
nvim-linux-x86_64.tar.gzrelease 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
currentsymlink (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:
/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, andXDG_STATE_HOMEdirectories - 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
- Run
:checkhealthand review every relevant warning/error - Verify netrw can be loaded and
:Exploreopens 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.luasettings - Validate the current completion stack before activation; native completion migration follows in Phase 13.9
- Validate conform format-on-save and nvim-lint/Ruff diagnostics
- 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 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 — 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/undotreewith the optional nativenvim.undotreepackage while preserving<leader>u - Trial native automatic/LSP completion with explicit selection and built-in snippet expansion
- 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 only after the native workflow passes
- 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
pas 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 the netrw version and its documented
netrw#Call()bridge - Confirm
NetrwGetWord,NetrwBrowseChgDir, andNetrwPreviewremain 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=directorybuffers - 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
pbehavior without changing its familiar purpose - Add a narrowly scoped, buffer-local
CursorMovedautocmd - 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, 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
:KeymapsGuidewith the enhancedplifecycle - 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/*.luafiles. - 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. Removedlua_lsfrom Mason's ensure_installed list inlua/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
.gitdirectory (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+secureapproach. - vim.lsp.config migration: Migrated from deprecated
require('lspconfig')[server].setup()tovim.lsp.config()+vim.lsp.enable()(Neovim 0.11+ native API). - Project config format:
.nvim.luafiles return{ lsp = { [server_name] = { settings = {...} } } }. - Security model:
securemode prompts user to trust.nvim.luafiles before execution (one-time per file hash). - Settings loading: An
LspAttachcallback securely reads project settings from the client's actualroot_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.