From 6aeb4ae913a1659a669a6f68bd2ec77ee3b0e1c2 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 19:07:25 +0000 Subject: [PATCH] tidy up and update documentation --- AGENTS.md | 6 +- CONFORM_MIGRATION.md | 49 +++++----- LOG.md | 10 +- MIGRATION_PLAN.md | 18 ++-- README.md | 10 +- lua/plugins/none-ls.lua.disabled | 158 ------------------------------- 6 files changed, 51 insertions(+), 200 deletions(-) delete mode 100644 lua/plugins/none-ls.lua.disabled diff --git a/AGENTS.md b/AGENTS.md index 2054920..2714d17 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,7 +43,7 @@ This repository is being migrated to a modern, minimal Neovim setup driven by Lu ├── oil.lua ├── ufo.lua ├── gitsigns.lua - ├── none-ls.lua + ├── conform.lua ├── nvim-lint.lua ├── mason.lua ├── mason-lspconfig.lua @@ -87,7 +87,7 @@ This repository is being migrated to a modern, minimal Neovim setup driven by Lu - UX/Editing: `Comment.nvim`, `nvim-surround`, `nvim-autopairs`, `indent-blankline.nvim`, `nvim-ufo`, `undotree`. - Git: `gitsigns.nvim`. - Copilot: `copilot.lua`, `copilot-cmp`. -- Formatting/Linting: `none-ls.nvim`, `nvim-lint`. +- Formatting/Linting: `conform.nvim`, `nvim-lint`. - LSP Management: `mason.nvim`, `mason-lspconfig.nvim`, `mason-tool-installer.nvim`. ## Workflow Requirements to Preserve @@ -115,7 +115,7 @@ This repository is being migrated to a modern, minimal Neovim setup driven by Lu - ✅ Phase 6: UX/Editing (Comment, surround, autopairs, indent guides, UFO, undotree) - ✅ Phase 7: Git integration (Gitsigns) - ✅ Phase 8: Copilot integration (copilot.lua + copilot-cmp) -- ✅ Phase 9: Formatting & Linting (none-ls, nvim-lint, Mason tool installer) +- ✅ Phase 9: Formatting & Linting (conform.nvim, nvim-lint, Mason tool installer) - ✅ Phase 10: Migrate kept behaviors (abbreviations, templates, custom Treesitter queries) - ⏸️ Phase 11: Cleanup & validation (pending) diff --git a/CONFORM_MIGRATION.md b/CONFORM_MIGRATION.md index 50526c9..da0bfd8 100644 --- a/CONFORM_MIGRATION.md +++ b/CONFORM_MIGRATION.md @@ -1,14 +1,15 @@ # Migration Plan: none-ls → conform.nvim **Date:** 2026-01-12 +**Status:** ✅ **COMPLETED** **Reason:** none-ls's phpcbf formatter is buggy (adds blank lines). conform.nvim is more modern, doesn't use LSP overhead, better maintained. ## Current State -**Formatting (none-ls):** +**Formatting (conform.nvim):** - ✅ prettier (JS/TS/CSS/JSON/HTML/Markdown) - ✅ stylua (Lua) -- ❌ phpcbf (PHP) - broken, using custom autocmd workaround +- ✅ phpcbf (PHP) with WordPress standard **Linting (nvim-lint):** - phpcs, eslint_d, markdownlint @@ -19,34 +20,34 @@ ## Migration Checklist ### Phase 1: Setup conform.nvim -- [ ] Create `lua/plugins/conform.lua` -- [ ] Configure formatters: - - [ ] prettier (JS/TS/CSS/JSON/HTML/Markdown) - - [ ] stylua (Lua) - - [ ] phpcbf (PHP) with WordPress standard support -- [ ] Configure format-on-save behavior -- [ ] Set up project-local executable resolution (vendor/bin, node_modules/.bin, Mason, global) -- [ ] Add keymaps for manual formatting (`lf`, `lt`) +- [x] Create `lua/plugins/conform.lua` +- [x] Configure formatters: + - [x] prettier (JS/TS/CSS/JSON/HTML/Markdown) + - [x] stylua (Lua) + - [x] phpcbf (PHP) with WordPress standard support +- [x] Configure format-on-save behavior +- [x] Set up project-local executable resolution (vendor/bin, node_modules/.bin, Mason, global) +- [x] Add keymaps for manual formatting (`lf`, `lt`) ### Phase 2: Remove none-ls -- [ ] Delete `lua/plugins/none-ls.lua` -- [ ] Remove none-ls from lazy-lock.json (happens automatically on `:Lazy sync`) -- [ ] Remove the custom phpcbf autocmd workaround (no longer needed) +- [x] Delete `lua/plugins/none-ls.lua` +- [x] Remove none-ls from lazy-lock.json (happens automatically on `:Lazy sync`) +- [x] Remove the custom phpcbf autocmd workaround (no longer needed) ### Phase 3: Testing -- [ ] Test prettier formatting (JS/TS/CSS files) -- [ ] Test stylua formatting (Lua files) -- [ ] Test phpcbf formatting (PHP files) - **verify NO blank lines added** -- [ ] Test format-on-save toggle (`lt`) -- [ ] Test manual format (`lf`) -- [ ] Test project-local formatter detection -- [ ] Verify phpcs.xml is respected when present +- [x] Test prettier formatting (JS/TS/CSS files) +- [x] Test stylua formatting (Lua files) +- [x] Test phpcbf formatting (PHP files) - **verified NO blank lines added** +- [x] Test format-on-save toggle (`lt`) +- [x] Test manual format (`lf`) +- [x] Test project-local formatter detection +- [x] Verify phpcs.xml is respected when present ### Phase 4: Documentation -- [ ] Update README.md Configuration section -- [ ] Update AGENTS.md Process section (if needed) -- [ ] Update MIGRATION_PLAN.md status -- [ ] Update LOG.md with decision and rationale +- [x] Update README.md Configuration section +- [x] Update AGENTS.md Process section (plugin list, formatting tools) +- [x] Update MIGRATION_PLAN.md status (Phase 9.2) +- [x] Update LOG.md with decision and rationale ## Expected Benefits diff --git a/LOG.md b/LOG.md index c3d0b01..93def00 100644 --- a/LOG.md +++ b/LOG.md @@ -69,7 +69,7 @@ Record every decision here with a short rationale. Append new entries; do not re - **WordPress**: phpcs/phpcbf already installed globally; use phpcs.xml or --standard=WordPress - **Format-on-save**: Enabled by default, toggle with `lt` - **Manual format**: `lf` (buffer), `lf` (visual range) - - **Linting split**: none-ls for formatting only, nvim-lint for diagnostics (none-ls removed linters) + - **Linting split**: conform.nvim for formatting, nvim-lint for diagnostics (replaced none-ls after discovering phpcbf preprocessing bug) - **Python support**: pyright LSP, black formatter, ruff linter, treesitter parser - **Per-filetype indentation**: Explicit settings per filetype to match formatters - PHP: tabs, 2-space display (WordPress standards) @@ -78,6 +78,14 @@ Record every decision here with a short rationale. Append new entries; do not re - Markdown: 2 spaces (Prettier) - Python: 4 spaces (Black/PEP 8) - **Global defaults**: 4 spaces (reasonable baseline for other filetypes) +- 2026-01-12: **conform.nvim migration**: + - **Trigger**: none-ls phpcbf builtin had preprocessing bug that added blank lines before formatting + - **Root cause**: none-ls uses LSP protocol for formatters, adds preprocessing step that corrupted input + - **Investigation**: Created debug wrappers, confirmed phpcbf CLI works correctly, traced issue to none-ls preprocessing + - **Solution**: Migrated to conform.nvim (modern, actively maintained, no LSP overhead) + - **Configuration**: Simplified config using conform's built-in formatters, only customized phpcbf for WordPress standard + - **Benefits**: Simpler code, no custom executable resolution needed, proper stdin/tmpfile handling per formatter + - **Removed**: none-ls.lua deleted (was renamed to .disabled during migration) - 2025-12-07: Kept Behaviors Phase 10: - **Abbreviations**: Common typo corrections (`adn→and`, `waht→what`, `tehn→then`, `functin→function`, `positin→position`) in dedicated `lua/abbreviations.lua` file for modularity - **Templates**: Shell script template (template.sh) auto-loaded via BufNewFile autocmd for `*.sh` files diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md index dc0ba60..e0c82b4 100644 --- a/MIGRATION_PLAN.md +++ b/MIGRATION_PLAN.md @@ -275,14 +275,13 @@ Source of truth for the step-by-step rebuild. Keep this concise and up to date. - [x] Philosophy: Formatters are authoritative source of truth; Neovim settings should match formatter rules per filetype - [x] Note: Phase 9.4 will align Neovim editor settings (tabstop, shiftwidth, expandtab) with formatter configurations -## Phase 9.2 — none-ls setup with project-aware executables -- [x] Add `nvimtools/none-ls.nvim` -- [x] Create helper function to detect project-local executables (node_modules/.bin/, vendor/bin/, Mason bin) -- [x] Configure formatters: - - [x] prettier (project-local first, then Mason, then global) - - [x] phpcbf (project-local first, then global system install - already available) - - [x] stylua (Mason installed) -- [x] Add `mfussenegger/nvim-lint` for linting (none-ls removed most linters from builtins) +## 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) @@ -290,8 +289,9 @@ Source of truth for the step-by-step rebuild. Keep this concise and up to date. - [x] Add format-on-save autocommand with toggle capability (`lt` to toggle) - [x] Add manual format keymaps: `lf` (buffer), `lf` (visual range) - [x] Ensure WordPress coding standards work via phpcs.xml or --standard flag -- [x] Search order: project node_modules/.bin/ → project vendor/bin/ → Mason bin → system PATH +- [x] 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 diff --git a/README.md b/README.md index ad3d45e..14e780b 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ Buffer-local keymaps available when an LSP client attaches: | n | `gI` | LSP: implementation | | n | `K` | LSP hover | -### None-ls (Formatting) `lua/plugins/none-ls.lua` +### Conform (Formatting) `lua/plugins/conform.lua` #### Keymaps @@ -367,7 +367,7 @@ Buffer-local keymaps available when inside a git repository: ### Formatting & Linting -This config uses **none-ls** for formatting and **nvim-lint** for diagnostics. Both prefer project-local tools over global installations. +This config uses **conform.nvim** for formatting and **nvim-lint** for diagnostics. Both prefer project-local tools over global installations. #### Executable Resolution Order @@ -377,15 +377,15 @@ When looking for formatters/linters, the config searches in this priority: 2. **Mason-managed** (`~/.local/share/nvim/mason/bin/`) 3. **System PATH** (globally installed tools) -See `find_executable()` helper in `lua/plugins/none-ls.lua` and `lua/plugins/nvim-lint.lua`. +Conform.nvim handles this resolution automatically via its built-in `util.find_executable()`. nvim-lint uses a custom `find_executable()` helper in `lua/plugins/nvim-lint.lua`. #### Configured Tools -**Formatters** (`lua/plugins/none-ls.lua`): +**Formatters** (`lua/plugins/conform.lua`): - **JavaScript/TypeScript/CSS/JSON/HTML/Markdown**: `prettier` - **PHP**: `phpcbf` (WordPress standard by default) - **Lua**: `stylua` -- **Python**: handled by `ruff` LSP (not none-ls) +- **Python**: handled by `ruff` LSP (not conform) **Linters** (`lua/plugins/nvim-lint.lua`): - **JavaScript/TypeScript**: `eslint_d` diff --git a/lua/plugins/none-ls.lua.disabled b/lua/plugins/none-ls.lua.disabled deleted file mode 100644 index 2383379..0000000 --- a/lua/plugins/none-ls.lua.disabled +++ /dev/null @@ -1,158 +0,0 @@ --- none-ls.nvim: Formatting only (linting moved to nvim-lint) --- Philosophy: Formatters are authoritative. Project-local executables preferred. --- Note: none-ls removed most linters from builtins, so we use nvim-lint for diagnostics - -return { - "nvimtools/none-ls.nvim", - dependencies = { "nvim-lua/plenary.nvim" }, - event = { "BufReadPre", "BufNewFile" }, - config = function() - local null_ls = require("null-ls") - local augroup = vim.api.nvim_create_augroup("LspFormatting", {}) - - -- Helper: Find project-local executable, fallback to global - -- Searches node_modules/.bin/, vendor/bin/, and Mason bin first - local function find_executable(names) - local cwd = vim.fn.getcwd() - local mason_bin = vim.fn.stdpath("data") .. "/mason/bin/" - - -- Try project-local paths first, then Mason, then global - local search_paths = { - cwd .. "/node_modules/.bin/", - cwd .. "/vendor/bin/", - mason_bin, - } - - for _, name in ipairs(names) do - for _, path in ipairs(search_paths) do - local full_path = path .. name - if vim.fn.executable(full_path) == 1 then - return full_path - end - end - - -- Fallback to system PATH - if vim.fn.executable(name) == 1 then - return name - end - end - - return nil - end - - -- Formatters - local formatting = null_ls.builtins.formatting - - -- Note: Diagnostics (linters) moved to nvim-lint plugin - -- Note: Python formatting handled by ruff LSP - - null_ls.setup({ - sources = { - -- Prettier (JS, TS, CSS, SCSS, JSON, Markdown, HTML) - formatting.prettier.with({ - command = find_executable({ "prettier" }), - prefer_local = "node_modules/.bin", - }), - - -- PHP: phpcbf DISABLED - using direct autocmd approach instead (see bottom of file) - -- formatting.phpcbf causes blank line bug even with custom formatters - - -- stylua (Lua) - formatting.stylua.with({ - command = find_executable({ "stylua" }), - }), - }, - - -- Format on save - on_attach = function(client, bufnr) - if client.supports_method("textDocument/formatting") then - vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr }) - vim.api.nvim_create_autocmd("BufWritePre", { - group = augroup, - buffer = bufnr, - callback = function() - -- Only format if format-on-save is enabled (global flag) - if vim.g.format_on_save ~= false then - -- Save view (cursor position, folds, etc.) before formatting - local view = vim.fn.winsaveview() - vim.lsp.buf.format({ bufnr = bufnr }) - -- Restore view after formatting to preserve folds - vim.fn.winrestview(view) - end - end, - }) - end - end, - }) - - -- Format-on-save is enabled by default - vim.g.format_on_save = true - - -- Keymaps - -- Toggle format-on-save - vim.keymap.set("n", "lt", function() - vim.g.format_on_save = not vim.g.format_on_save - local status = vim.g.format_on_save and "enabled" or "disabled" - vim.notify("Format on save " .. status, vim.log.levels.INFO) - end, { desc = "Formatting: Toggle format on save", silent = true, noremap = true }) - - -- Manual format (buffer) - vim.keymap.set("n", "lf", function() - vim.lsp.buf.format({ async = false }) - end, { desc = "Formatting: Format buffer", silent = true, noremap = true }) - - -- Manual format (visual range) - vim.keymap.set("v", "lf", function() - vim.lsp.buf.format({ async = false }) - end, { desc = "Formatting: Format selection", silent = true, noremap = true }) - - -- PHP: Direct phpcbf formatting (bypasses none-ls entirely) - vim.api.nvim_create_autocmd("BufWritePre", { - pattern = "*.php", - callback = function() - if vim.g.format_on_save == false then - return - end - - local bufnr = vim.api.nvim_get_current_buf() - local filepath = vim.api.nvim_buf_get_name(bufnr) - - -- Find phpcbf - local phpcbf = find_executable({ "phpcbf" }) - if not phpcbf then - return - end - - -- Determine standard - local root = vim.fn.getcwd() - local has_project_ruleset = - vim.loop.fs_stat(root .. "/phpcs.xml") - or vim.loop.fs_stat(root .. "/phpcs.xml.dist") - - local cmd = { phpcbf, "-q", "--stdin-path=" .. filepath } - if not has_project_ruleset then - table.insert(cmd, "--standard=WordPress") - end - table.insert(cmd, "-") - - -- Get buffer content - local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) - local input = table.concat(lines, "\n") - - -- Run phpcbf - local result = vim.fn.system(cmd, input) - local exit_code = vim.v.shell_error - - -- Apply result if successful (exit code 0 or 1) - if exit_code == 0 or exit_code == 1 then - local output_lines = vim.split(result, "\n", { plain = true }) - -- Remove trailing empty line if present - if output_lines[#output_lines] == "" then - table.remove(output_lines) - end - vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, output_lines) - end - end, - }) - end, -}