From a0926e273ea8ee763aaed3d5d2937f51c33754ca Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 5 Jan 2026 22:10:39 +0000 Subject: [PATCH 01/53] fix wordpress linter/formatter mismatch --- lua/plugins/none-ls.lua | 21 +++++++++++++-------- lua/plugins/nvim-lint.lua | 30 +++++++++++++++--------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/lua/plugins/none-ls.lua b/lua/plugins/none-ls.lua index 31bc519..96736bb 100644 --- a/lua/plugins/none-ls.lua +++ b/lua/plugins/none-ls.lua @@ -59,15 +59,20 @@ return { command = find_executable({ "phpcbf" }), prefer_local = "vendor/bin", -- Respects phpcs.xml in project root or uses WordPress standard - extra_args = function() - local phpcs_xml = vim.fn.findfile("phpcs.xml", ".;") - if phpcs_xml == "" then - phpcs_xml = vim.fn.findfile("phpcs.xml.dist", ".;") + extra_args = function(params) + local root = params.root or vim.fn.getcwd() + + -- Check for project ruleset + local has_project_ruleset = + vim.loop.fs_stat(root .. "/phpcs.xml") + or vim.loop.fs_stat(root .. "/phpcs.xml.dist") + + if has_project_ruleset then + return {} -- Let project ruleset control standard end - if phpcs_xml == "" then - return { "--standard=WordPress" } - end - return {} + + -- No project ruleset: use WordPress to match nvim-lint + return { "--standard=WordPress" } end, }), diff --git a/lua/plugins/nvim-lint.lua b/lua/plugins/nvim-lint.lua index adcd264..eb5a948 100644 --- a/lua/plugins/nvim-lint.lua +++ b/lua/plugins/nvim-lint.lua @@ -47,21 +47,21 @@ return { -- Configure phpcs for WordPress standards lint.linters.phpcs.cmd = find_executable({ "phpcs" }) or "phpcs" - lint.linters.phpcs.args = { - "-q", - "--report=json", - function() - local phpcs_xml = vim.fn.findfile("phpcs.xml", ".;") - if phpcs_xml == "" then - phpcs_xml = vim.fn.findfile("phpcs.xml.dist", ".;") - end - if phpcs_xml == "" then - return "--standard=WordPress" - end - return nil - end, - "-", -- stdin - } + + -- Build args dynamically based on project ruleset presence + -- Note: This runs once at config load, checks cwd for phpcs.xml + local cwd = vim.fn.getcwd() + local has_project_ruleset = + vim.loop.fs_stat(cwd .. "/phpcs.xml") + or vim.loop.fs_stat(cwd .. "/phpcs.xml.dist") + + local phpcs_args = { "-q", "--report=json" } + if not has_project_ruleset then + table.insert(phpcs_args, "--standard=WordPress") + end + table.insert(phpcs_args, "-") -- stdin + + lint.linters.phpcs.args = phpcs_args -- Configure eslint_d to use project-local first lint.linters.eslint_d.cmd = find_executable({ "eslint_d", "eslint" }) or "eslint_d" From 9679e2b5622fdce71c432f10ae18eda940ab780d Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 5 Jan 2026 22:12:37 +0000 Subject: [PATCH 02/53] Update README for formatting and linter configuration Add detailed instructions for configuring formatters and linters, including executable resolution order and project-specific overrides. --- AGENTS.md | 1 + README.md | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 08ba23f..2054920 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -201,6 +201,7 @@ Was caused by settings loading at wrong time; fixed by using `LspAttach` autocmd - After any config or plugin change, immediately update `MIGRATION_PLAN.md` (check off items, add notes, or adjust next steps). - After any change or decision, also update `LOG.md:1` (decisions log and design docs). - **After adding/removing/editing any keymaps**, immediately update `README.md` with the change (add new entries, remove deleted mappings, or update descriptions). +- **After changing formatter/linter configuration, standards, or tool resolution**, immediately update the "Configuration" section in `README.md` (update tool lists, override instructions, or behavior descriptions). - At the start of each phase, confirm scope and priorities for that phase. - Execute phases via subphases (`N.x`), where each bullet under a phase is its own implement-and-test step (e.g., Phase 3.1, 3.2, 3.3, 3.4). - Record decisions and rationale in `LOG.md:1` as they're made. diff --git a/README.md b/README.md index 45d1cf5..82dd858 100644 --- a/README.md +++ b/README.md @@ -304,3 +304,98 @@ Buffer-local keymaps available when inside a git repository: | Mode | Key | Description | | --- | --- | --- | | insert | `` | Fast wrap the previous text with a pair | + +## Configuration + +### Formatting & Linting + +This config uses **none-ls** for formatting and **nvim-lint** for diagnostics. Both prefer project-local tools over global installations. + +#### Executable Resolution Order + +When looking for formatters/linters, the config searches in this priority: + +1. **Project-local** (e.g., `node_modules/.bin/`, `vendor/bin/`) +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`. + +#### Configured Tools + +**Formatters** (`lua/plugins/none-ls.lua`): +- **JavaScript/TypeScript/CSS/JSON/HTML/Markdown**: `prettier` +- **PHP**: `phpcbf` (WordPress standard by default) +- **Lua**: `stylua` +- **Python**: handled by `ruff` LSP (not none-ls) + +**Linters** (`lua/plugins/nvim-lint.lua`): +- **JavaScript/TypeScript**: `eslint_d` +- **PHP**: `phpcs` (WordPress standard by default) +- **Markdown**: `markdownlint` (disabled by default; use `:MarkdownLintEnable`) +- **Python**: handled by `ruff` LSP (not nvim-lint) + +#### Project-Specific Overrides + +Both formatters and linters respect project-level configuration files: + +**PHP** (phpcbf + phpcs): +- Create `phpcs.xml` or `phpcs.xml.dist` in your project root +- When present, this file controls the coding standard (e.g., PSR-12, WordPress, custom) +- When absent, defaults to `--standard=WordPress` + +Example `phpcs.xml`: +```xml + + + Project-specific PHP rules + + + + + + + + + + +``` + +**JavaScript/TypeScript** (prettier + eslint): +- Project config files: `.prettierrc`, `.eslintrc.json`, `eslint.config.js`, etc. +- Global fallback: `~/.eslintrc.json` (when no project config exists) + +**Lua** (stylua): +- Project config: `stylua.toml` or `.stylua.toml` +- Falls back to stylua defaults if not present + +**Markdown** (markdownlint): +- Project config: `.markdownlint.json`, `.markdownlintrc` + +#### Changing Standards Per-Project + +To switch a PHP project from WordPress to PSR-12: + +1. Create `phpcs.xml` in project root: + ```xml + + + + + ``` + +2. Restart Neovim (or reload config: `:source $MYVIMRC`) + +Both `phpcs` and `phpcbf` will now use PSR-12 rules in that project. + +#### Format-on-Save + +- **Enabled by default** for all supported filetypes +- **Toggle**: `lt` (see keymaps below) +- **Manual format**: `lf` (buffer or visual selection) + +#### Linting Behavior + +- **Automatic**: Runs on `BufEnter`, `BufWritePost`, `InsertLeave` +- **Per-filetype**: Configured in `lint.linters_by_ft` table +- **Markdown**: Opt-in only (`:MarkdownLintEnable` / `:MarkdownLintDisable`) \ No newline at end of file From bc9758dbe189b2a1b20a641263eb1b8df03182cf Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 6 Jan 2026 17:59:01 +0000 Subject: [PATCH 03/53] enable persistant undo history --- LOG.md | 1 + MIGRATION_PLAN.md | 1 + lua/settings.lua | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/LOG.md b/LOG.md index f274dac..c3d0b01 100644 --- a/LOG.md +++ b/LOG.md @@ -83,6 +83,7 @@ Record every decision here with a short rationale. Append new entries; do not re - **Templates**: Shell script template (template.sh) auto-loaded via BufNewFile autocmd for `*.sh` files - **Whitespace highlighting**: Already handled via `listchars` in Phase 3.2 (settings.lua) - **Persistent folds**: Not needed; UFO handles folding without explicit persistence mechanism + - **Persistent undo**: Re-enabled 2025-01-06 via `undofile=true` and `undodir` in settings.lua (preserves undo history across sessions) - 2025-12-11: Colorscheme Phase 11.2: - **Color palette extraction**: Extracted all 45 color definitions from original Paper Tonic colorscheme - **Structure**: Created `lua/paper-tonic-modern/colors.lua` with comprehensive documentation diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md index 665be63..dc0ba60 100644 --- a/MIGRATION_PLAN.md +++ b/MIGRATION_PLAN.md @@ -24,6 +24,7 @@ Source of truth for the step-by-step rebuild. Keep this concise and up to date. ## 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 diff --git a/lua/settings.lua b/lua/settings.lua index 8b9eb77..d44a895 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -49,6 +49,10 @@ vim.opt.shiftwidth = 4 -- Indent by 4 vim.opt.softtabstop = 4 -- Backspace removes 4 spaces vim.opt.expandtab = true -- Use spaces by default (overridden per-filetype) +-- Persistent undo +vim.opt.undofile = true -- Enable persistent undo +vim.opt.undodir = vim.fn.stdpath("config") .. "/undodir" -- Store undo files in config directory + -- LSP diagnostics configuration vim.diagnostic.config({ virtual_text = { From 05154e59ef654ef34c306759724ff0c510bbf1f8 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 14:52:59 +0000 Subject: [PATCH 04/53] Add default keymaps for netrw navigation Includes keybindings for navigation, file operations, and batch operations to enhance user experience with netrw. --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/README.md b/README.md index 82dd858..58239a2 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,57 @@ Core keymaps available globally (not plugin-specific). These provide fallbacks f ### `lua/netrw-config.lua` +#### Custom Keymaps + | Mode | Key | Description | | --- | --- | --- | | n | `te` | Open netrw in a new tab rooted at current file directory | | n | `tE` | Open netrw in a new tab rooted at project cwd | +#### Default Netrw Keymaps + +##### Navigation & View + +| Key | Description | +| --- | --- | +| `` | Open file/directory under cursor | +| `-` | Go up to parent directory | +| `gh` | Toggle hidden files visibility (dotfiles) | +| `i` | Cycle through view types (thin/long/wide/tree) | +| `s` | Cycle sort order (name/time/size/extension) | +| `r` | Reverse current sort order | +| `I` | Toggle netrw banner (help text) | + +##### Preview & Splits + +| Key | Description | +| --- | --- | +| `p` | Preview file in horizontal split (50% window size) | +| `P` | Open file in previous window | +| `o` | Open file in horizontal split below | +| `v` | Open file in vertical split | + +##### File Operations + +| Key | Description | +| --- | --- | +| `%` | Create new file (prompts for name) | +| `d` | Create new directory (prompts for name) | +| `D` | Delete file/directory under cursor | +| `R` | Rename/move file under cursor | + +##### Marking & Batch Operations + +| Key | Description | +| --- | --- | +| `mf` | Mark file (for batch operations) | +| `mr` | Mark files using regex pattern | +| `mu` | Unmark all marked files | +| `mt` | Set mark target directory (for move/copy destination) | +| `mc` | Copy marked files to target directory | +| `mm` | Move marked files to target directory | +| `qf` | Display file info | + ## Plugin Reference ### LSP `lua/plugins/lsp.lua` From d876425956ca3e008d618b18ea3b9939014f97b4 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 15:18:10 +0000 Subject: [PATCH 05/53] Update README and keymaps for netrw navigation Add command variants for netrw and update keymaps to improve navigation experience. Store initial working directory as project root for better symlink handling. --- README.md | 16 ++++++++++++++-- lua/netrw-config.lua | 17 +++++++++++------ lua/settings.lua | 4 ++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 58239a2..ad3d45e 100644 --- a/README.md +++ b/README.md @@ -52,12 +52,24 @@ Core keymaps available globally (not plugin-specific). These provide fallbacks f ### `lua/netrw-config.lua` +#### `:Ex` Command Variants + +| Command | Opens netrw at | +| --- | --- | +| `:Ex` | Current working directory (`:pwd`) | +| `:Ex .` | Current file's directory | +| `:Ex %:h` | Current file's directory (explicit) | +| `:Ex /path/to/dir` | Specific path | + #### Custom Keymaps | Mode | Key | Description | | --- | --- | --- | -| n | `te` | Open netrw in a new tab rooted at current file directory | -| n | `tE` | Open netrw in a new tab rooted at project cwd | +| n | `nt` | Open netrw in new tab at current file's directory | +| n | `nT` | Open netrw in new tab at project root | +| n | `nr` | Open netrw in current window at project root | + +**Note:** Project root is stored as the initial working directory when Neovim starts. This allows navigation back to the project root even after following symlinks to external directories. #### Default Netrw Keymaps diff --git a/lua/netrw-config.lua b/lua/netrw-config.lua index 8f8870d..b4f2087 100644 --- a/lua/netrw-config.lua +++ b/lua/netrw-config.lua @@ -40,17 +40,22 @@ vim.g.netrw_sizestyle = 'H' -- Netrw file explorer keymaps -- Open netrw in new tab at current file's directory -map('n', 'te', function() +map('n', 'nt', function() local current_file_dir = vim.fn.expand('%:p:h') vim.cmd('tabnew') vim.cmd('Explore ' .. vim.fn.fnameescape(current_file_dir)) -end, { desc = 'Netrw: Tab explore (current file dir)', silent = true }) +end, { desc = 'Netrw: Tab at current file dir', silent = true }) --- Open netrw in new tab at project root (cwd) -map('n', 'tE', function() +-- Open netrw in new tab at project root (stored at startup) +map('n', 'nT', function() vim.cmd('tabnew') - vim.cmd('Explore ' .. vim.fn.fnameescape(vim.fn.getcwd())) -end, { desc = 'Netrw: Tab explore (project root)', silent = true }) + vim.cmd('Explore ' .. vim.fn.fnameescape(vim.g.project_root)) +end, { desc = 'Netrw: Tab at project root', silent = true }) + +-- Open netrw in current window at project root (stored at startup) +map('n', 'nr', function() + vim.cmd('Explore ' .. vim.fn.fnameescape(vim.g.project_root)) +end, { desc = 'Netrw: Open at project root', silent = true }) -- Enable line numbers in netrw buffers vim.api.nvim_create_autocmd('FileType', { pattern = 'netrw', diff --git a/lua/settings.lua b/lua/settings.lua index d44a895..1a76db1 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -5,6 +5,10 @@ vim.g.loaded_ruby_provider = 0 vim.g.loaded_perl_provider = 0 vim.g.loaded_node_provider = 0 +-- Store initial working directory as project root +-- Used by netrw navigation to return to project root after following symlinks +vim.g.project_root = vim.fn.getcwd() + -- Enable project-local configuration files vim.opt.exrc = true -- Load .nvim.lua from project root vim.opt.secure = true -- Prompt before loading untrusted files From 9768ed395e758633296a38d694b2bba892fcda49 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 15:32:23 +0000 Subject: [PATCH 06/53] Update netrw configuration for directory handling Separate current and browsing directories to prevent LSP and symlink navigation issues. Allow directory operations on symlinks for better usability. --- lua/netrw-config.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/netrw-config.lua b/lua/netrw-config.lua index b4f2087..c22bba5 100644 --- a/lua/netrw-config.lua +++ b/lua/netrw-config.lua @@ -20,9 +20,9 @@ vim.g.netrw_alto = 0 -- 50% split when pressing 'p' vim.g.netrw_winsize = 50 --- Keep the current directory and browsing directory synced --- This makes netrw respect your current working directory -vim.g.netrw_keepdir = 0 +-- Keep the current directory and browsing directory separate +-- This prevents netrw from changing pwd and breaking LSP/symlink navigation +vim.g.netrw_keepdir = 1 -- Open files in the same window (replace netrw buffer) -- Options: 0=same window, 1=horizontal split, 2=vertical split, 3=new tab, 4=previous window From d28af9a31e55225d013b5a61d687c6f1cf37553e Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 16:28:26 +0000 Subject: [PATCH 07/53] Update netrw configuration to keep directories separate Set netrw_keepdir to 0 to prevent changing the working directory, ensuring LSP and symlink navigation remain functional. --- lua/netrw-config.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lua/netrw-config.lua b/lua/netrw-config.lua index c22bba5..7855ba6 100644 --- a/lua/netrw-config.lua +++ b/lua/netrw-config.lua @@ -20,9 +20,7 @@ vim.g.netrw_alto = 0 -- 50% split when pressing 'p' vim.g.netrw_winsize = 50 --- Keep the current directory and browsing directory separate --- This prevents netrw from changing pwd and breaking LSP/symlink navigation -vim.g.netrw_keepdir = 1 +vim.g.netrw_keepdir = 0 -- Open files in the same window (replace netrw buffer) -- Options: 0=same window, 1=horizontal split, 2=vertical split, 3=new tab, 4=previous window From 9916849e010a5884a98a7cb887a8eb9030896b5b Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 17:49:30 +0000 Subject: [PATCH 08/53] update lazy-lock.json --- lazy-lock.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lazy-lock.json b/lazy-lock.json index 98a2d44..6db4427 100644 --- a/lazy-lock.json +++ b/lazy-lock.json @@ -5,27 +5,27 @@ "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" }, "cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" }, "copilot-cmp": { "branch": "master", "commit": "15fc12af3d0109fa76b60b5cffa1373697e261d1" }, - "copilot.lua": { "branch": "master", "commit": "efe563802a550b7f1b7743b007987e97cba22718" }, - "gitsigns.nvim": { "branch": "main", "commit": "5813e4878748805f1518cee7abb50fd7205a3a48" }, + "copilot.lua": { "branch": "master", "commit": "5ace9ecd0db9a7a6c14064e4ce4ede5b800325f3" }, + "gitsigns.nvim": { "branch": "main", "commit": "42d6aed4e94e0f0bbced16bbdcc42f57673bd75e" }, "indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" }, - "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" }, - "mason-lspconfig.nvim": { "branch": "main", "commit": "0b9bb925c000ae649ff7e7149c8cd00031f4b539" }, + "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" }, + "mason-lspconfig.nvim": { "branch": "main", "commit": "fe661093f4b05136437b531e7f959af2a2ae66c8" }, "mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" }, - "mason.nvim": { "branch": "main", "commit": "57e5a8addb8c71fb063ee4acda466c7cf6ad2800" }, - "none-ls.nvim": { "branch": "main", "commit": "5abf61927023ea83031753504adb19630ba80eef" }, - "nvim-autopairs": { "branch": "master", "commit": "7a2c97cccd60abc559344042fefb1d5a85b3e33b" }, - "nvim-cmp": { "branch": "main", "commit": "d97d85e01339f01b842e6ec1502f639b080cb0fc" }, - "nvim-lint": { "branch": "master", "commit": "ebe535956106c60405b02220246e135910f6853d" }, - "nvim-lspconfig": { "branch": "master", "commit": "9c923997123ff9071198ea3b594d4c1931fab169" }, - "nvim-surround": { "branch": "main", "commit": "fcfa7e02323d57bfacc3a141f8a74498e1522064" }, + "mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" }, + "none-ls.nvim": { "branch": "main", "commit": "1fcf9cbf9acf893455c6cee792537aa709de62cb" }, + "nvim-autopairs": { "branch": "master", "commit": "c2a0dd0d931d0fb07665e1fedb1ea688da3b80b4" }, + "nvim-cmp": { "branch": "main", "commit": "85bbfad83f804f11688d1ab9486b459e699292d6" }, + "nvim-lint": { "branch": "master", "commit": "ca6ea12daf0a4d92dc24c5c9ae22a1f0418ade37" }, + "nvim-lspconfig": { "branch": "master", "commit": "92ee7d42320edfbb81f3cad851314ab197fa324a" }, + "nvim-surround": { "branch": "main", "commit": "1098d7b3c34adcfa7feb3289ee434529abd4afd1" }, "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, "nvim-treesitter-textobjects": { "branch": "master", "commit": "5ca4aaa6efdcc59be46b95a3e876300cfead05ef" }, "nvim-ts-autotag": { "branch": "main", "commit": "c4ca798ab95b316a768d51eaaaee48f64a4a46bc" }, - "nvim-ufo": { "branch": "main", "commit": "72d54c31079d38d8dfc5456131b1d0fb5c0264b0" }, - "oil.nvim": { "branch": "master", "commit": "cbcb3f997f6f261c577b943ec94e4ef55108dd95" }, + "nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" }, + "oil.nvim": { "branch": "master", "commit": "d278dc40f9de9980868a0a55fa666fba5e6aeacb" }, "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, "promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" }, "telescope-fzf-native.nvim": { "branch": "main", "commit": "6fea601bd2b694c6f2ae08a6c6fab14930c60e2c" }, "telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" }, - "undotree": { "branch": "master", "commit": "0f1c9816975b5d7f87d5003a19c53c6fd2ff6f7f" } + "undotree": { "branch": "master", "commit": "178d19e00a643f825ea11d581b1684745d0c4eda" } } From 15305641175a4d23562b924b7cc5b287297a771d Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 18:26:51 +0000 Subject: [PATCH 09/53] bypass none-ls when formatting php --- lua/plugins/none-ls.lua | 74 +++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/lua/plugins/none-ls.lua b/lua/plugins/none-ls.lua index 96736bb..2383379 100644 --- a/lua/plugins/none-ls.lua +++ b/lua/plugins/none-ls.lua @@ -54,28 +54,9 @@ return { prefer_local = "node_modules/.bin", }), - -- PHP CodeSniffer (phpcbf) - WordPress standards - formatting.phpcbf.with({ - command = find_executable({ "phpcbf" }), - prefer_local = "vendor/bin", - -- Respects phpcs.xml in project root or uses WordPress standard - extra_args = function(params) - local root = params.root or vim.fn.getcwd() - - -- Check for project ruleset - local has_project_ruleset = - vim.loop.fs_stat(root .. "/phpcs.xml") - or vim.loop.fs_stat(root .. "/phpcs.xml.dist") - - if has_project_ruleset then - return {} -- Let project ruleset control standard - end - - -- No project ruleset: use WordPress to match nvim-lint - return { "--standard=WordPress" } - end, - }), - + -- 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" }), @@ -124,5 +105,54 @@ return { 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, } From d3d2932e08936cdf0a64f70259cec5bbded5cae3 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 18:41:45 +0000 Subject: [PATCH 10/53] add plan to migrate from none-ls to conform --- CONFORM_MIGRATION.md | 71 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 CONFORM_MIGRATION.md diff --git a/CONFORM_MIGRATION.md b/CONFORM_MIGRATION.md new file mode 100644 index 0000000..50526c9 --- /dev/null +++ b/CONFORM_MIGRATION.md @@ -0,0 +1,71 @@ +# Migration Plan: none-ls → conform.nvim + +**Date:** 2026-01-12 +**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):** +- ✅ prettier (JS/TS/CSS/JSON/HTML/Markdown) +- ✅ stylua (Lua) +- ❌ phpcbf (PHP) - broken, using custom autocmd workaround + +**Linting (nvim-lint):** +- phpcs, eslint_d, markdownlint + +**LSP (lspconfig):** +- Language features (autocomplete, goto-def, hover, etc.) + +## 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`) + +### 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) + +### 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 + +### 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 + +## Expected Benefits + +1. **No more blank line bug** - conform calls phpcbf cleanly without none-ls's buggy preprocessing +2. **No LSP overhead** - formatters run as simple shell commands +3. **Better maintained** - conform.nvim is actively developed +4. **Cleaner architecture** - clear separation: LSP (features), nvim-lint (diagnostics), conform (formatting) +5. **Easier debugging** - simpler execution path, better error messages + +## Rollback Plan + +If conform.nvim has issues: +1. Restore `lua/plugins/none-ls.lua` from git +2. Keep the custom phpcbf autocmd for PHP +3. Run `:Lazy sync` to reinstall none-ls + +## Notes + +- conform.nvim has built-in support for all our formatters (prettier, stylua, phpcbf) +- It handles executable resolution similarly to our `find_executable()` helper +- Format-on-save can be toggled per-buffer or globally +- Visual range formatting supported out of the box From 16004b6117d9713e93f2edf2f6e4a2419668170c Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 18:57:13 +0000 Subject: [PATCH 11/53] replace none-ls with conform --- lazy-lock.json | 2 +- lua/plugins/conform.lua | 108 ++++++++++++++++++ .../{none-ls.lua => none-ls.lua.disabled} | 0 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 lua/plugins/conform.lua rename lua/plugins/{none-ls.lua => none-ls.lua.disabled} (100%) diff --git a/lazy-lock.json b/lazy-lock.json index 6db4427..768d060 100644 --- a/lazy-lock.json +++ b/lazy-lock.json @@ -4,6 +4,7 @@ "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" }, "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" }, "cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" }, + "conform.nvim": { "branch": "master", "commit": "238f542a118984a88124fc915d5b981680418707" }, "copilot-cmp": { "branch": "master", "commit": "15fc12af3d0109fa76b60b5cffa1373697e261d1" }, "copilot.lua": { "branch": "master", "commit": "5ace9ecd0db9a7a6c14064e4ce4ede5b800325f3" }, "gitsigns.nvim": { "branch": "main", "commit": "42d6aed4e94e0f0bbced16bbdcc42f57673bd75e" }, @@ -12,7 +13,6 @@ "mason-lspconfig.nvim": { "branch": "main", "commit": "fe661093f4b05136437b531e7f959af2a2ae66c8" }, "mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" }, "mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" }, - "none-ls.nvim": { "branch": "main", "commit": "1fcf9cbf9acf893455c6cee792537aa709de62cb" }, "nvim-autopairs": { "branch": "master", "commit": "c2a0dd0d931d0fb07665e1fedb1ea688da3b80b4" }, "nvim-cmp": { "branch": "main", "commit": "85bbfad83f804f11688d1ab9486b459e699292d6" }, "nvim-lint": { "branch": "master", "commit": "ca6ea12daf0a4d92dc24c5c9ae22a1f0418ade37" }, diff --git a/lua/plugins/conform.lua b/lua/plugins/conform.lua new file mode 100644 index 0000000..4dfc32b --- /dev/null +++ b/lua/plugins/conform.lua @@ -0,0 +1,108 @@ +-- conform.nvim: Modern formatting without LSP overhead +-- Replaces none-ls for all formatting tasks + +return { + "stevearc/conform.nvim", + event = { "BufReadPre", "BufNewFile" }, + config = function() + local conform = require("conform") + + -- 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/" + + 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 + + if vim.fn.executable(name) == 1 then + return name + end + end + + return nil + end + + conform.setup({ + formatters_by_ft = { + -- JavaScript, TypeScript, CSS, SCSS, JSON, HTML, Markdown + javascript = { "prettier" }, + javascriptreact = { "prettier" }, + typescript = { "prettier" }, + typescriptreact = { "prettier" }, + css = { "prettier" }, + scss = { "prettier" }, + html = { "prettier" }, + json = { "prettier" }, + jsonc = { "prettier" }, + markdown = { "prettier" }, + + -- PHP + php = { "phpcbf" }, + + -- Lua + lua = { "stylua" }, + }, + + -- Custom formatter definitions with executable resolution + formatters = { + prettier = { + command = find_executable({ "prettier" }) or "prettier", + }, + phpcbf = { + -- Extend built-in phpcbf to add WordPress standard + prepend_args = { "--standard=WordPress" }, + }, + stylua = { + command = find_executable({ "stylua" }) or "stylua", + }, + }, + + -- Format on save + format_on_save = function(bufnr) + -- Check global flag + if vim.g.format_on_save == false then + return nil + end + + return { + timeout_ms = 500, + lsp_fallback = false, -- Don't use LSP formatting + } + 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() + conform.format({ async = false, lsp_fallback = false }) + end, { desc = "Formatting: Format buffer", silent = true, noremap = true }) + + -- Manual format (visual range) + vim.keymap.set("v", "lf", function() + conform.format({ async = false, lsp_fallback = false }) + end, { desc = "Formatting: Format selection", silent = true, noremap = true }) + end, +} diff --git a/lua/plugins/none-ls.lua b/lua/plugins/none-ls.lua.disabled similarity index 100% rename from lua/plugins/none-ls.lua rename to lua/plugins/none-ls.lua.disabled From 530a65a81a8fd96a4c854fada6b6c31300544356 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 18:58:16 +0000 Subject: [PATCH 12/53] simplify conform config --- lua/plugins/conform.lua | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/lua/plugins/conform.lua b/lua/plugins/conform.lua index 4dfc32b..7bc9af5 100644 --- a/lua/plugins/conform.lua +++ b/lua/plugins/conform.lua @@ -7,34 +7,6 @@ return { config = function() local conform = require("conform") - -- 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/" - - 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 - - if vim.fn.executable(name) == 1 then - return name - end - end - - return nil - end - conform.setup({ formatters_by_ft = { -- JavaScript, TypeScript, CSS, SCSS, JSON, HTML, Markdown @@ -56,18 +28,12 @@ return { lua = { "stylua" }, }, - -- Custom formatter definitions with executable resolution + -- Formatter customization formatters = { - prettier = { - command = find_executable({ "prettier" }) or "prettier", - }, + -- Add WordPress coding standard to phpcbf phpcbf = { - -- Extend built-in phpcbf to add WordPress standard prepend_args = { "--standard=WordPress" }, }, - stylua = { - command = find_executable({ "stylua" }) or "stylua", - }, }, -- Format on save From 6aeb4ae913a1659a669a6f68bd2ec77ee3b0e1c2 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 19:07:25 +0000 Subject: [PATCH 13/53] 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, -} From 4e9e22da00680053d9a6161dfe46bad78c85b5df Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 19:24:41 +0000 Subject: [PATCH 14/53] fix working directory issues --- LOG.md | 1 + MIGRATION_PLAN.md | 1 + README.md | 10 ++++++++++ lua/netrw-config.lua | 4 +++- lua/plugins/telescope.lua | 3 +++ lua/settings.lua | 3 +++ 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/LOG.md b/LOG.md index 93def00..4716ef8 100644 --- a/LOG.md +++ b/LOG.md @@ -26,6 +26,7 @@ Record every decision here with a short rationale. Append new entries; do not re - Add Oil.nvim for file manipulation (rename/move/delete with buffer sync) - netrw for tree view and preview splits; Oil for operations that would break buffer names - PHP `gf` enhancement via `includeexpr` for WordPress/PHP path resolution + - **Working directory locked to project root**: Disabled `autochdir`, set `netrw_keepdir = 1`, captured initial cwd in `vim.g.project_root`, configured Telescope to always search from project root regardless of current buffer - 2025-12-07: Treesitter Phase 5 decisions: - Focus on core languages: PHP, HTML, JavaScript, TypeScript, CSS, Markdown, Lua, Bash, JSON - Enable syntax highlighting with performance safeguard (disable for files >100KB) diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md index e0c82b4..56fe7d0 100644 --- a/MIGRATION_PLAN.md +++ b/MIGRATION_PLAN.md @@ -474,6 +474,7 @@ Source of truth for the step-by-step rebuild. Keep this concise and up to date. - [ ] Validate Telescope navigation + LSP jumps - [ ] Validate netrw browsing and preview splits - [ ] Validate Oil.nvim file operations +- [x] Fix working directory behavior (disabled autochdir, locked Telescope to project root) ## Phase 12.5 — Language tooling validation - [ ] Validate HTML/PHP/JS/Markdown tooling diff --git a/README.md b/README.md index 14e780b..9378af2 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,16 @@ Living reference for session management, keymaps, commands, and plugin-specific features in this config. +## Working Directory Behavior + +The working directory (`:pwd`) is locked to the directory where you opened Neovim and does NOT change when switching files: + +- `autochdir` is disabled (working directory stays fixed at project root) +- `netrw_keepdir = 1` prevents netrw from changing directory when browsing +- Project root is captured at startup in `vim.g.project_root` (used by Telescope and netrw) +- Telescope searches from the initial project root regardless of current buffer +- Use `:cd ` to manually change directory if needed (affects Telescope search scope) + ## Session Management Session support is automatic but user-controlled: diff --git a/lua/netrw-config.lua b/lua/netrw-config.lua index 7855ba6..de3e558 100644 --- a/lua/netrw-config.lua +++ b/lua/netrw-config.lua @@ -20,7 +20,9 @@ vim.g.netrw_alto = 0 -- 50% split when pressing 'p' vim.g.netrw_winsize = 50 -vim.g.netrw_keepdir = 0 +-- Keep working directory unchanged when browsing (1 = don't change directory) +-- Setting to 1 prevents netrw from changing vim's working directory +vim.g.netrw_keepdir = 1 -- Open files in the same window (replace netrw buffer) -- Options: 0=same window, 1=horizontal split, 2=vertical split, 3=new tab, 4=previous window diff --git a/lua/plugins/telescope.lua b/lua/plugins/telescope.lua index 997a60c..5ff9b49 100644 --- a/lua/plugins/telescope.lua +++ b/lua/plugins/telescope.lua @@ -19,6 +19,9 @@ return { telescope.setup({ defaults = { + -- Always search from project root (where Neovim was opened) + cwd = vim.g.project_root, + -- Minimal UI, keep it clean prompt_prefix = '> ', selection_caret = '> ', diff --git a/lua/settings.lua b/lua/settings.lua index 1a76db1..19f96fe 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -9,6 +9,9 @@ vim.g.loaded_node_provider = 0 -- Used by netrw navigation to return to project root after following symlinks vim.g.project_root = vim.fn.getcwd() +-- Prevent automatic directory changes when switching files +vim.opt.autochdir = false -- Keep working directory at project root + -- Enable project-local configuration files vim.opt.exrc = true -- Load .nvim.lua from project root vim.opt.secure = true -- Prompt before loading untrusted files From 5e3a14596d290bec7f80ddde838dfd3844503f83 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 21:31:37 +0000 Subject: [PATCH 15/53] add quickfix and location list mappings for gitsigns Added key mappings to send all hunks to the quickfix list and buffer hunks to the location list for improved navigation and management of git changes. --- README.md | 2 ++ lua/plugins/gitsigns.lua | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 9378af2..3cfde91 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,8 @@ Buffer-local keymaps available when inside a git repository: | n | `hp` | Preview hunk | | n | `hd` | Diff against index | | n | `hD` | Diff against previous commit (`~`) | +| n | `hq` | Send all hunks to quickfix list | +| n | `hl` | Send buffer hunks to location list | | o/x | `ih` | Text object: select git hunk | ### Telescope `lua/plugins/telescope.lua` diff --git a/lua/plugins/gitsigns.lua b/lua/plugins/gitsigns.lua index cb7fa2f..cc9de28 100644 --- a/lua/plugins/gitsigns.lua +++ b/lua/plugins/gitsigns.lua @@ -49,6 +49,10 @@ return { map('n', 'hp', gs.preview_hunk, { desc = 'Gitsigns: Preview hunk' }) map('n', 'hd', gs.diffthis, { desc = 'Gitsigns: Diff this' }) map('n', 'hD', function() gs.diffthis('~') end, { desc = 'Gitsigns: Diff this ~' }) + + -- Quickfix/Location list + map('n', 'hq', function() gs.setqflist('all') end, { desc = 'Gitsigns: All hunks to quickfix' }) + map('n', 'hl', function() gs.setloclist(0) end, { desc = 'Gitsigns: Buffer hunks to loclist' }) -- Text object map({ 'o', 'x' }, 'ih', ':Gitsigns select_hunk', { desc = 'Gitsigns: Select hunk' }) From 7d2c85417dd8938b8305a8c7dbf064cf6c939f6f Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 21:52:50 +0000 Subject: [PATCH 16/53] fix auto session load timing --- lua/autocmds.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lua/autocmds.lua b/lua/autocmds.lua index d66f1ac..1618e06 100644 --- a/lua/autocmds.lua +++ b/lua/autocmds.lua @@ -106,18 +106,27 @@ local session_aug = vim.api.nvim_create_augroup("SessionManagement", { clear = t vim.api.nvim_create_autocmd("VimEnter", { group = session_aug, pattern = "*", - nested = true, -- Allow other autocmds to fire after loading session + once = true, callback = function() -- Only auto-load if: -- 1. Session.vim exists in cwd -- 2. No files were specified on command line (vim.fn.argc() == 0) -- 3. Not started with nvim -S (check if we already loaded a session) if vim.fn.argc() == 0 and vim.fn.filereadable("Session.vim") == 1 and vim.v.this_session == "" then - vim.cmd("source Session.vim") + vim.cmd("silent! source Session.vim") end end, }) +-- Ensure filetype is detected for current buffer after session load +vim.api.nvim_create_autocmd("SessionLoadPost", { + group = session_aug, + pattern = "*", + callback = function() + vim.cmd("filetype detect") + end, +}) + -- Auto-save session on exit, but ONLY if Session.vim already exists vim.api.nvim_create_autocmd("VimLeavePre", { group = session_aug, From 87ea788dee6e4aae2eaf3575ab14f38a61d883f7 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 12 Jan 2026 22:08:43 +0000 Subject: [PATCH 17/53] Implement custom tabline with configurable path shortening Add a custom tabline function to enhance path display in Neovim. Users can configure the number of full parent directories and the length of shortened directory names for better context and manageability. --- LOG.md | 1 + MIGRATION_PLAN.md | 6 ++++ README.md | 31 +++++++++++++++++ lua/settings.lua | 20 +++++++++++ lua/utils.lua | 89 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+) diff --git a/LOG.md b/LOG.md index 4716ef8..6222096 100644 --- a/LOG.md +++ b/LOG.md @@ -9,6 +9,7 @@ Authoritative notes for the Neovim migration. Use this alongside `MIGRATION_PLAN Record every decision here with a short rationale. Append new entries; do not rewrite history. +- 2025-01-12: **Custom Tabline Function**: Implemented configurable custom tabline to replace Neovim's hardcoded path shortening (e.g., `a/p/file.txt`). Function in `lua/utils.lua` allows controlling: (1) number of full parent directories via `utils.tabline_full_parents` (default: 1), and (2) shortening length via `utils.tabline_shorten_length` (default: 3 characters). Example: with defaults, `/path/to/my/project/src/file.txt` becomes `pat/to/my/project/src/file.txt`. User preference for seeing enough context in shortened paths while keeping tab width manageable. - 2025-12-06: PHP LSP = `intelephense` (good PHP ecosystem support; integrates includePaths). - 2025-12-06: Enable Markdown LSP = `marksman` (lightweight, good MD features). - 2025-12-06: Use legacy `listchars` and `showbreak` values (preserve muscle memory). diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md index 56fe7d0..001dc84 100644 --- a/MIGRATION_PLAN.md +++ b/MIGRATION_PLAN.md @@ -336,6 +336,12 @@ Source of truth for the step-by-step rebuild. Keep this concise and up to date. - [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 diff --git a/README.md b/README.md index 3cfde91..763fdc7 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,37 @@ Living reference for session management, keymaps, commands, and plugin-specific features in this config. +## Configuration Options + +### Tabline Display + +Custom tabline shows intelligent path shortening with two configurable options. + +**Location:** `lua/settings.lua` (configured via `utils.tabline_full_parents` and `utils.tabline_shorten_length`) + +**Configuration Options:** + +1. **`utils.tabline_full_parents`** (default: `1`) - Number of parent directories to show in full +2. **`utils.tabline_shorten_length`** (default: `3`) - Number of characters to show for shortened directories + +**Examples:** + +With `full_parents = 1, shorten_length = 3`: +- `/path/to/my/project/src/file.txt` → `pat/to/my/project/src/file.txt` + +With `full_parents = 2, shorten_length = 3`: +- `/path/to/my/project/src/file.txt` → `pat/to/my/project/src/file.txt` + +With `full_parents = 1, shorten_length = 1`: +- `/path/to/my/project/src/file.txt` → `p/t/m/project/src/file.txt` + +With `full_parents = 0, shorten_length = 3`: +- `/path/to/my/project/src/file.txt` → `pat/to/my/pro/src/file.txt` + +The last N parent directories are shown in full, earlier directories are shortened to the specified number of characters. The filename itself is always shown in full. + +**To customize:** Edit `utils.tabline_full_parents` and `utils.tabline_shorten_length` values in `lua/settings.lua` + ## Working Directory Behavior The working directory (`:pwd`) is locked to the directory where you opened Neovim and does NOT change when switching files: diff --git a/lua/settings.lua b/lua/settings.lua index 19f96fe..8f2cc40 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -16,6 +16,26 @@ vim.opt.autochdir = false -- Keep working directory at project root vim.opt.exrc = true -- Load .nvim.lua from project root vim.opt.secure = true -- Prompt before loading untrusted files +-- Custom tabline configuration +-- Load utils module for custom tabline function +local utils = require('utils') + +-- Configure number of parent directories to show in full (default: 1) +-- Examples: +-- 1: /path/to/my/project/src/file.txt → pat/to/my/project/src/file.txt +-- 2: /path/to/my/project/src/file.txt → pat/to/my/project/src/file.txt +utils.tabline_full_parents = 1 + +-- Configure number of characters to show for shortened directories (default: 3) +-- Examples: +-- 3: /path/to/my → pat/to/my +-- 1: /path/to/my → p/t/m +utils.tabline_shorten_length = 4 + +-- Use custom tabline function +vim.opt.tabline = '%!v:lua.require("utils").custom_tabline()' +vim.opt.showtabline = 1 -- Show tabline only when there are 2+ tabs + -- Phase 3.2: non-plugin settings (legacy values where specified) -- Completion UI for nvim-cmp vim.opt.completeopt = { "menu", "menuone", "noselect" } diff --git a/lua/utils.lua b/lua/utils.lua index 419cde8..00d090c 100644 --- a/lua/utils.lua +++ b/lua/utils.lua @@ -6,5 +6,94 @@ function M.safe_require(name) return nil end +-- Number of parent directories to show in full in the tabline +-- The rest will be shortened according to tabline_shorten_length +-- Example: with full_parents = 1, /path/to/my/project/src/file.txt becomes pat/to/my/project/src/file.txt +-- Example: with full_parents = 2, it becomes pat/to/my/project/src/file.txt +M.tabline_full_parents = 1 + +-- Number of characters to show for shortened directory names in the tabline +-- Example: with shorten_length = 3, /path/to/my becomes pat/to/my +-- Example: with shorten_length = 1, /path/to/my becomes p/t/m +M.tabline_shorten_length = 3 + +-- Custom tabline function +-- Shows configurable number of full parent directories, shortens the rest +function M.custom_tabline() + local tabline = '' + local num_tabs = vim.fn.tabpagenr('$') + + for i = 1, num_tabs do + local buflist = vim.fn.tabpagebuflist(i) + local winnr = vim.fn.tabpagewinnr(i) + local bufnr = buflist[winnr] + local bufname = vim.fn.bufname(bufnr) + local bufmodified = vim.fn.getbufvar(bufnr, "&modified") + + -- Highlight for the tab + if i == vim.fn.tabpagenr() then + tabline = tabline .. '%#TabLineSel#' + else + tabline = tabline .. '%#TabLine#' + end + + -- Tab number + tabline = tabline .. ' ' .. i .. ' ' + + -- Format the filename with smart path shortening + local filename + if bufname == '' then + filename = '[No Name]' + else + -- Get the full path relative to cwd if possible + local path = vim.fn.fnamemodify(bufname, ':~:.') + + -- Split path into components + local parts = vim.split(path, '/', { plain = true }) + + if #parts > M.tabline_full_parents + 1 then + -- We have enough parts to do smart shortening + local result = {} + + -- Shorten the leading directories (all but the last full_parents + filename) + local num_to_shorten = #parts - M.tabline_full_parents - 1 + for j = 1, num_to_shorten do + table.insert(result, parts[j]:sub(1, M.tabline_shorten_length)) + end + + -- Add the full parent directories + for j = num_to_shorten + 1, #parts - 1 do + table.insert(result, parts[j]) + end + + -- Add the filename + table.insert(result, parts[#parts]) + + filename = table.concat(result, '/') + else + -- Path is short enough, just use it as-is + filename = path + end + end + + -- Add modified flag + if bufmodified == 1 then + filename = filename .. ' [+]' + end + + tabline = tabline .. filename .. ' ' + end + + -- Fill the rest with TabLineFill + tabline = tabline .. '%#TabLineFill#%T' + + -- Right-align: show tab page count if more than one tab + if num_tabs > 1 then + tabline = tabline .. '%=%#TabLine# ' .. num_tabs .. ' tabs ' + end + + return tabline +end + return M From f178b292f8374a2fa54e3128de7ea64b38dad52e Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 15:23:35 +0000 Subject: [PATCH 18/53] reorder readme sections --- README.md | 84 +++++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 763fdc7..167f6b4 100644 --- a/README.md +++ b/README.md @@ -2,47 +2,6 @@ Living reference for session management, keymaps, commands, and plugin-specific features in this config. -## Configuration Options - -### Tabline Display - -Custom tabline shows intelligent path shortening with two configurable options. - -**Location:** `lua/settings.lua` (configured via `utils.tabline_full_parents` and `utils.tabline_shorten_length`) - -**Configuration Options:** - -1. **`utils.tabline_full_parents`** (default: `1`) - Number of parent directories to show in full -2. **`utils.tabline_shorten_length`** (default: `3`) - Number of characters to show for shortened directories - -**Examples:** - -With `full_parents = 1, shorten_length = 3`: -- `/path/to/my/project/src/file.txt` → `pat/to/my/project/src/file.txt` - -With `full_parents = 2, shorten_length = 3`: -- `/path/to/my/project/src/file.txt` → `pat/to/my/project/src/file.txt` - -With `full_parents = 1, shorten_length = 1`: -- `/path/to/my/project/src/file.txt` → `p/t/m/project/src/file.txt` - -With `full_parents = 0, shorten_length = 3`: -- `/path/to/my/project/src/file.txt` → `pat/to/my/pro/src/file.txt` - -The last N parent directories are shown in full, earlier directories are shortened to the specified number of characters. The filename itself is always shown in full. - -**To customize:** Edit `utils.tabline_full_parents` and `utils.tabline_shorten_length` values in `lua/settings.lua` - -## Working Directory Behavior - -The working directory (`:pwd`) is locked to the directory where you opened Neovim and does NOT change when switching files: - -- `autochdir` is disabled (working directory stays fixed at project root) -- `netrw_keepdir = 1` prevents netrw from changing directory when browsing -- Project root is captured at startup in `vim.g.project_root` (used by Telescope and netrw) -- Telescope searches from the initial project root regardless of current buffer -- Use `:cd ` to manually change directory if needed (affects Telescope search scope) - ## Session Management Session support is automatic but user-controlled: @@ -499,4 +458,45 @@ Both `phpcs` and `phpcbf` will now use PSR-12 rules in that project. - **Automatic**: Runs on `BufEnter`, `BufWritePost`, `InsertLeave` - **Per-filetype**: Configured in `lint.linters_by_ft` table -- **Markdown**: Opt-in only (`:MarkdownLintEnable` / `:MarkdownLintDisable`) \ No newline at end of file +- **Markdown**: Opt-in only (`:MarkdownLintEnable` / `:MarkdownLintDisable`) + +## Configuration Options + +### Tabline Display + +Custom tabline shows intelligent path shortening with two configurable options. + +**Location:** `lua/settings.lua` (configured via `utils.tabline_full_parents` and `utils.tabline_shorten_length`) + +**Configuration Options:** + +1. **`utils.tabline_full_parents`** (default: `1`) - Number of parent directories to show in full +2. **`utils.tabline_shorten_length`** (default: `3`) - Number of characters to show for shortened directories + +**Examples:** + +With `full_parents = 1, shorten_length = 3`: +- `/path/to/my/project/src/file.txt` → `pat/to/my/project/src/file.txt` + +With `full_parents = 2, shorten_length = 3`: +- `/path/to/my/project/src/file.txt` → `pat/to/my/project/src/file.txt` + +With `full_parents = 1, shorten_length = 1`: +- `/path/to/my/project/src/file.txt` → `p/t/m/project/src/file.txt` + +With `full_parents = 0, shorten_length = 3`: +- `/path/to/my/project/src/file.txt` → `pat/to/my/pro/src/file.txt` + +The last N parent directories are shown in full, earlier directories are shortened to the specified number of characters. The filename itself is always shown in full. + +**To customize:** Edit `utils.tabline_full_parents` and `utils.tabline_shorten_length` values in `lua/settings.lua` + +## Working Directory Behavior + +The working directory (`:pwd`) is locked to the directory where you opened Neovim and does NOT change when switching files: + +- `autochdir` is disabled (working directory stays fixed at project root) +- `netrw_keepdir = 1` prevents netrw from changing directory when browsing +- Project root is captured at startup in `vim.g.project_root` (used by Telescope and netrw) +- Telescope searches from the initial project root regardless of current buffer +- Use `:cd ` to manually change directory if needed (affects Telescope search scope) \ No newline at end of file From 82fc5b1953ae1fbf94d27f86baba435ce2badb94 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 15:25:28 +0000 Subject: [PATCH 19/53] remove git hunks to location list mapping --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 167f6b4..ad07b0f 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,6 @@ Buffer-local keymaps available when inside a git repository: | n | `hd` | Diff against index | | n | `hD` | Diff against previous commit (`~`) | | n | `hq` | Send all hunks to quickfix list | -| n | `hl` | Send buffer hunks to location list | | o/x | `ih` | Text object: select git hunk | ### Telescope `lua/plugins/telescope.lua` From 1e91693cf55c24b9fdbdec6d2bc5c3016e1a98d0 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 15:30:40 +0000 Subject: [PATCH 20/53] Add keymap for Git files to quickfix list Introduced a new keymap `gf` to send modified, deleted, and untracked Git files to the quickfix list with their status indicators. This enhances the Git workflow by complementing existing mappings. --- LOG.md | 1 + README.md | 1 + lua/keymaps.lua | 51 ++++++++++++++++++++++++++++++++++++++++ lua/plugins/gitsigns.lua | 1 - 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/LOG.md b/LOG.md index 6222096..59cb29f 100644 --- a/LOG.md +++ b/LOG.md @@ -9,6 +9,7 @@ Authoritative notes for the Neovim migration. Use this alongside `MIGRATION_PLAN Record every decision here with a short rationale. Append new entries; do not rewrite history. +- 2025-01-13: **Git files to quickfix**: Added `gf` keymap to send all modified, deleted, and untracked Git files to quickfix list with status indicators. Uses `git status --porcelain` to parse file status (Modified, Deleted, Added, Untracked, etc.) and displays it in the quickfix text column. Complements existing `hq` (Gitsigns hunks to quickfix) with file-level Git status workflow. - 2025-01-12: **Custom Tabline Function**: Implemented configurable custom tabline to replace Neovim's hardcoded path shortening (e.g., `a/p/file.txt`). Function in `lua/utils.lua` allows controlling: (1) number of full parent directories via `utils.tabline_full_parents` (default: 1), and (2) shortening length via `utils.tabline_shorten_length` (default: 3 characters). Example: with defaults, `/path/to/my/project/src/file.txt` becomes `pat/to/my/project/src/file.txt`. User preference for seeing enough context in shortened paths while keeping tab width manageable. - 2025-12-06: PHP LSP = `intelephense` (good PHP ecosystem support; integrates includePaths). - 2025-12-06: Enable Markdown LSP = `marksman` (lightweight, good MD features). diff --git a/README.md b/README.md index ad07b0f..5a1f849 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Core keymaps available globally (not plugin-specific). These provide fallbacks f | n | `]d` | Diagnostics: next item | Uses `vim.diagnostic.goto_next` | | n | `xd` | Diagnostic float | Opens hover window for cursor diagnostic | | n | `xt` | Toggle diagnostics | Flips `vim.diagnostic.enable()` | +| n | `gf` | Git: Changed files → quickfix | Lists all modified/deleted/untracked files with status | | n | `hi` | Highlight inspector | Shows highlight/capture stack under cursor | ### `lua/netrw-config.lua` diff --git a/lua/keymaps.lua b/lua/keymaps.lua index 595335d..382ca7c 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -50,6 +50,57 @@ map('n', 'xt', function() vim.diagnostic.enable(not vim.diagnostic.is_enabled()) end, { desc = 'Diagnostics: Toggle display', silent = true }) +-- Git: Modified, deleted, and untracked files to quickfix +map('n', 'gf', function() + -- Use git status --porcelain to get all changes with status indicators + -- Format: "XY filename" where X=index status, Y=worktree status + -- Status codes: M=modified, D=deleted, A=added, ??=untracked, etc. + local handle = io.popen('git status --porcelain 2>/dev/null') + if not handle then + vim.notify('Failed to run git status', vim.log.levels.ERROR) + return + end + + local result = handle:read('*a') + handle:close() + + if result == '' then + vim.notify('No git changes found', vim.log.levels.INFO) + return + end + + local qf_list = {} + local status_map = { + ['M'] = 'Modified', + ['A'] = 'Added', + ['D'] = 'Deleted', + ['R'] = 'Renamed', + ['C'] = 'Copied', + ['U'] = 'Unmerged', + ['?'] = 'Untracked', + } + + for line in result:gmatch('[^\n]+') do + -- Parse porcelain format: "XY filename" or "XY original -> renamed" + local index_status = line:sub(1, 1) + local work_status = line:sub(2, 2) + local filename = line:sub(4) -- Skip "XY " prefix + + -- Determine status text (worktree takes precedence over index) + local status_code = work_status ~= ' ' and work_status or index_status + local status_text = status_map[status_code] or 'Changed' + + table.insert(qf_list, { + filename = filename, + lnum = 1, + text = status_text, + }) + end + + vim.fn.setqflist(qf_list, 'r') + vim.cmd('copen') +end, { desc = 'Git: Changed files to quickfix (with status)', silent = true }) + -- Debug: Show highlight group and color under cursor map('n', 'hi', function() local cursor_pos = vim.api.nvim_win_get_cursor(0) diff --git a/lua/plugins/gitsigns.lua b/lua/plugins/gitsigns.lua index cc9de28..c8777fc 100644 --- a/lua/plugins/gitsigns.lua +++ b/lua/plugins/gitsigns.lua @@ -52,7 +52,6 @@ return { -- Quickfix/Location list map('n', 'hq', function() gs.setqflist('all') end, { desc = 'Gitsigns: All hunks to quickfix' }) - map('n', 'hl', function() gs.setloclist(0) end, { desc = 'Gitsigns: Buffer hunks to loclist' }) -- Text object map({ 'o', 'x' }, 'ih', ':Gitsigns select_hunk', { desc = 'Gitsigns: Select hunk' }) From 1b71a3052053513856533236bb6a6dfbad7667e9 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 15:34:28 +0000 Subject: [PATCH 21/53] Update keymap for Git files in quickfix list Changed keymap from `gf` to `gg` for sending modified, deleted, and untracked Git files to the quickfix list. Added utility function to handle Git status parsing. --- LOG.md | 2 +- README.md | 2 +- lua/keymaps.lua | 53 ++++++------------------------------------------- lua/utils.lua | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 49 deletions(-) diff --git a/LOG.md b/LOG.md index 59cb29f..d050ca1 100644 --- a/LOG.md +++ b/LOG.md @@ -9,7 +9,7 @@ Authoritative notes for the Neovim migration. Use this alongside `MIGRATION_PLAN Record every decision here with a short rationale. Append new entries; do not rewrite history. -- 2025-01-13: **Git files to quickfix**: Added `gf` keymap to send all modified, deleted, and untracked Git files to quickfix list with status indicators. Uses `git status --porcelain` to parse file status (Modified, Deleted, Added, Untracked, etc.) and displays it in the quickfix text column. Complements existing `hq` (Gitsigns hunks to quickfix) with file-level Git status workflow. +- 2025-01-13: **Git files to quickfix**: Added `gg` keymap to send all modified, deleted, and untracked Git files to quickfix list with status indicators. Uses `git status --porcelain` to parse file status (Modified, Deleted, Added, Untracked, etc.) and displays it in the quickfix text column. Implementation in `utils.git_changed_files()`. Complements existing `hq` (Gitsigns hunks to quickfix) with file-level Git status workflow. - 2025-01-12: **Custom Tabline Function**: Implemented configurable custom tabline to replace Neovim's hardcoded path shortening (e.g., `a/p/file.txt`). Function in `lua/utils.lua` allows controlling: (1) number of full parent directories via `utils.tabline_full_parents` (default: 1), and (2) shortening length via `utils.tabline_shorten_length` (default: 3 characters). Example: with defaults, `/path/to/my/project/src/file.txt` becomes `pat/to/my/project/src/file.txt`. User preference for seeing enough context in shortened paths while keeping tab width manageable. - 2025-12-06: PHP LSP = `intelephense` (good PHP ecosystem support; integrates includePaths). - 2025-12-06: Enable Markdown LSP = `marksman` (lightweight, good MD features). diff --git a/README.md b/README.md index 5a1f849..c483d7b 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Core keymaps available globally (not plugin-specific). These provide fallbacks f | n | `]d` | Diagnostics: next item | Uses `vim.diagnostic.goto_next` | | n | `xd` | Diagnostic float | Opens hover window for cursor diagnostic | | n | `xt` | Toggle diagnostics | Flips `vim.diagnostic.enable()` | -| n | `gf` | Git: Changed files → quickfix | Lists all modified/deleted/untracked files with status | +| n | `gg` | Git: Changed files → quickfix | Lists all modified/deleted/untracked files with status | | n | `hi` | Highlight inspector | Shows highlight/capture stack under cursor | ### `lua/netrw-config.lua` diff --git a/lua/keymaps.lua b/lua/keymaps.lua index 382ca7c..13484c6 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -51,54 +51,13 @@ map('n', 'xt', function() end, { desc = 'Diagnostics: Toggle display', silent = true }) -- Git: Modified, deleted, and untracked files to quickfix -map('n', 'gf', function() - -- Use git status --porcelain to get all changes with status indicators - -- Format: "XY filename" where X=index status, Y=worktree status - -- Status codes: M=modified, D=deleted, A=added, ??=untracked, etc. - local handle = io.popen('git status --porcelain 2>/dev/null') - if not handle then - vim.notify('Failed to run git status', vim.log.levels.ERROR) - return +map('n', 'gg', function() + local utils = require('utils') + local qf_list = utils.git_changed_files() + if qf_list then + vim.fn.setqflist(qf_list, 'r') + vim.cmd('copen') end - - local result = handle:read('*a') - handle:close() - - if result == '' then - vim.notify('No git changes found', vim.log.levels.INFO) - return - end - - local qf_list = {} - local status_map = { - ['M'] = 'Modified', - ['A'] = 'Added', - ['D'] = 'Deleted', - ['R'] = 'Renamed', - ['C'] = 'Copied', - ['U'] = 'Unmerged', - ['?'] = 'Untracked', - } - - for line in result:gmatch('[^\n]+') do - -- Parse porcelain format: "XY filename" or "XY original -> renamed" - local index_status = line:sub(1, 1) - local work_status = line:sub(2, 2) - local filename = line:sub(4) -- Skip "XY " prefix - - -- Determine status text (worktree takes precedence over index) - local status_code = work_status ~= ' ' and work_status or index_status - local status_text = status_map[status_code] or 'Changed' - - table.insert(qf_list, { - filename = filename, - lnum = 1, - text = status_text, - }) - end - - vim.fn.setqflist(qf_list, 'r') - vim.cmd('copen') end, { desc = 'Git: Changed files to quickfix (with status)', silent = true }) -- Debug: Show highlight group and color under cursor diff --git a/lua/utils.lua b/lua/utils.lua index 00d090c..16aec51 100644 --- a/lua/utils.lua +++ b/lua/utils.lua @@ -17,6 +17,57 @@ M.tabline_full_parents = 1 -- Example: with shorten_length = 1, /path/to/my becomes p/t/m M.tabline_shorten_length = 3 +-- Get all modified, deleted, and untracked Git files with status +-- Returns a list suitable for setqflist() or nil on error +function M.git_changed_files() + -- Use git status --porcelain to get all changes with status indicators + -- Format: "XY filename" where X=index status, Y=worktree status + -- Status codes: M=modified, D=deleted, A=added, ??=untracked, etc. + local handle = io.popen('git status --porcelain 2>/dev/null') + if not handle then + vim.notify('Failed to run git status', vim.log.levels.ERROR) + return nil + end + + local result = handle:read('*a') + handle:close() + + if result == '' then + vim.notify('No git changes found', vim.log.levels.INFO) + return nil + end + + local qf_list = {} + local status_map = { + ['M'] = 'Modified', + ['A'] = 'Added', + ['D'] = 'Deleted', + ['R'] = 'Renamed', + ['C'] = 'Copied', + ['U'] = 'Unmerged', + ['?'] = 'Untracked', + } + + for line in result:gmatch('[^\n]+') do + -- Parse porcelain format: "XY filename" or "XY original -> renamed" + local index_status = line:sub(1, 1) + local work_status = line:sub(2, 2) + local filename = line:sub(4) -- Skip "XY " prefix + + -- Determine status text (worktree takes precedence over index) + local status_code = work_status ~= ' ' and work_status or index_status + local status_text = status_map[status_code] or 'Changed' + + table.insert(qf_list, { + filename = filename, + lnum = 1, + text = status_text, + }) + end + + return qf_list +end + -- Custom tabline function -- Shows configurable number of full parent directories, shortens the rest function M.custom_tabline() From 70d28cbb3f1d40ba0124a75e5a4f9e8a84e78748 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 15:39:03 +0000 Subject: [PATCH 22/53] Update quickfix mapping to open location list Enhance the quickfix mapping to open the location list after setting all hunks, improving user experience. --- lua/plugins/gitsigns.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lua/plugins/gitsigns.lua b/lua/plugins/gitsigns.lua index c8777fc..0205547 100644 --- a/lua/plugins/gitsigns.lua +++ b/lua/plugins/gitsigns.lua @@ -51,7 +51,10 @@ return { map('n', 'hD', function() gs.diffthis('~') end, { desc = 'Gitsigns: Diff this ~' }) -- Quickfix/Location list - map('n', 'hq', function() gs.setqflist('all') end, { desc = 'Gitsigns: All hunks to quickfix' }) + map('n', 'hq', function() + gs.setqflist('all') + vim.cmd('copen') + end, { desc = 'Gitsigns: All hunks to quickfix' }) -- Text object map({ 'o', 'x' }, 'ih', ':Gitsigns select_hunk', { desc = 'Gitsigns: Select hunk' }) From 10f61ab93909a49eb2a706f1fc5df50364e1c91e Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 16:05:44 +0000 Subject: [PATCH 23/53] Add highlight info display under cursor Implement a function to show highlight group and color information in a floating window when the cursor is positioned over text. --- lua/keymaps.lua | 132 +--------------------------------------------- lua/utils.lua | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 131 deletions(-) diff --git a/lua/keymaps.lua b/lua/keymaps.lua index 13484c6..3203a3f 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -62,135 +62,5 @@ end, { desc = 'Git: Changed files to quickfix (with status)', silent = true }) -- Debug: Show highlight group and color under cursor map('n', 'hi', function() - local cursor_pos = vim.api.nvim_win_get_cursor(0) - local row, col = cursor_pos[1] - 1, cursor_pos[2] - - -- Get all highlight groups at cursor position - local ts_hl = vim.treesitter.get_captures_at_pos(0, row, col) - local synID = vim.fn.synID(row + 1, col + 1, 1) - local synName = vim.fn.synIDattr(synID, 'name') - local synTrans = vim.fn.synIDattr(vim.fn.synIDtrans(synID), 'name') - - -- Helper to resolve highlight links - local function resolve_hl(name) - local hl = vim.api.nvim_get_hl(0, { name = name }) - local max_depth = 10 - local depth = 0 - while hl.link and depth < max_depth do - name = hl.link - hl = vim.api.nvim_get_hl(0, { name = name }) - depth = depth + 1 - end - return hl, name - end - - local lines = { - '=== Highlight Info Under Cursor ===', - '', - 'Position: row=' .. row .. ' col=' .. col, - '', - } - - -- TreeSitter captures - if #ts_hl > 0 then - table.insert(lines, 'TreeSitter Captures:') - for _, capture in ipairs(ts_hl) do - local cap_name = '@' .. capture.capture - local hl, resolved_name = resolve_hl(cap_name) - - table.insert(lines, string.format(' %s', cap_name)) - if resolved_name ~= cap_name then - table.insert(lines, string.format(' → resolves to: %s', resolved_name)) - end - if hl.fg then - table.insert(lines, string.format(' fg: #%06x', hl.fg)) - end - if hl.bg then - table.insert(lines, string.format(' bg: #%06x', hl.bg)) - end - - local styles = {} - if hl.bold then table.insert(styles, 'bold') end - if hl.italic then table.insert(styles, 'italic') end - if hl.underline then table.insert(styles, 'underline') end - if #styles > 0 then - table.insert(lines, ' style: ' .. table.concat(styles, ', ')) - end - end - table.insert(lines, '') - end - - -- Syntax group - if synName ~= '' then - table.insert(lines, 'Syntax Group: ' .. synName) - if synTrans ~= synName and synTrans ~= '' then - table.insert(lines, 'Translates to: ' .. synTrans) - end - - local hl, resolved_name = resolve_hl(synTrans ~= '' and synTrans or synName) - if hl.fg then - table.insert(lines, string.format(' fg: #%06x', hl.fg)) - end - if hl.bg then - table.insert(lines, string.format(' bg: #%06x', hl.bg)) - end - table.insert(lines, '') - end - - -- Final applied highlight (use TreeSitter if available, otherwise syntax) - local final_hl_name = nil - if #ts_hl > 0 then - final_hl_name = '@' .. ts_hl[1].capture - elseif synTrans ~= '' then - final_hl_name = synTrans - elseif synName ~= '' then - final_hl_name = synName - end - - if final_hl_name then - local final_hl, final_resolved = resolve_hl(final_hl_name) - table.insert(lines, 'Applied Highlight: ' .. final_resolved) - if final_hl.fg then - table.insert(lines, string.format(' fg: #%06x', final_hl.fg)) - else - table.insert(lines, ' fg: NONE') - end - if final_hl.bg then - table.insert(lines, string.format(' bg: #%06x', final_hl.bg)) - else - table.insert(lines, ' bg: NONE') - end - - local styles = {} - if final_hl.bold then table.insert(styles, 'bold') end - if final_hl.italic then table.insert(styles, 'italic') end - if final_hl.underline then table.insert(styles, 'underline') end - if final_hl.undercurl then table.insert(styles, 'undercurl') end - if #styles > 0 then - table.insert(lines, ' style: ' .. table.concat(styles, ', ')) - end - end - - -- Show in a floating window - local buf = vim.api.nvim_create_buf(false, true) - vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) - - local width = 0 - for _, line in ipairs(lines) do - width = math.max(width, #line) - end - width = math.min(width + 2, vim.o.columns - 4) - - local height = #lines - local opts = { - relative = 'cursor', - width = width, - height = height, - row = 1, - col = 0, - style = 'minimal', - border = 'rounded', - } - - vim.api.nvim_open_win(buf, false, opts) + require('utils').show_highlight_info() end, { desc = 'Debug: Show highlight group and color under cursor', silent = true }) diff --git a/lua/utils.lua b/lua/utils.lua index 16aec51..b5d352e 100644 --- a/lua/utils.lua +++ b/lua/utils.lua @@ -68,6 +68,142 @@ function M.git_changed_files() return qf_list end +-- Show highlight group and color information under cursor +-- Returns nothing, displays results in a floating window +function M.show_highlight_info() + local cursor_pos = vim.api.nvim_win_get_cursor(0) + local row, col = cursor_pos[1] - 1, cursor_pos[2] + + -- Get all highlight groups at cursor position + local ts_hl = vim.treesitter.get_captures_at_pos(0, row, col) + local synID = vim.fn.synID(row + 1, col + 1, 1) + local synName = vim.fn.synIDattr(synID, 'name') + local synTrans = vim.fn.synIDattr(vim.fn.synIDtrans(synID), 'name') + + -- Helper to resolve highlight links + local function resolve_hl(name) + local hl = vim.api.nvim_get_hl(0, { name = name }) + local max_depth = 10 + local depth = 0 + while hl.link and depth < max_depth do + name = hl.link + hl = vim.api.nvim_get_hl(0, { name = name }) + depth = depth + 1 + end + return hl, name + end + + local lines = { + '=== Highlight Info Under Cursor ===', + '', + 'Position: row=' .. row .. ' col=' .. col, + '', + } + + -- TreeSitter captures + if #ts_hl > 0 then + table.insert(lines, 'TreeSitter Captures:') + for _, capture in ipairs(ts_hl) do + local cap_name = '@' .. capture.capture + local hl, resolved_name = resolve_hl(cap_name) + + table.insert(lines, string.format(' %s', cap_name)) + if resolved_name ~= cap_name then + table.insert(lines, string.format(' → resolves to: %s', resolved_name)) + end + if hl.fg then + table.insert(lines, string.format(' fg: #%06x', hl.fg)) + end + if hl.bg then + table.insert(lines, string.format(' bg: #%06x', hl.bg)) + end + + local styles = {} + if hl.bold then table.insert(styles, 'bold') end + if hl.italic then table.insert(styles, 'italic') end + if hl.underline then table.insert(styles, 'underline') end + if #styles > 0 then + table.insert(lines, ' style: ' .. table.concat(styles, ', ')) + end + end + table.insert(lines, '') + end + + -- Syntax group + if synName ~= '' then + table.insert(lines, 'Syntax Group: ' .. synName) + if synTrans ~= synName and synTrans ~= '' then + table.insert(lines, 'Translates to: ' .. synTrans) + end + + local hl, resolved_name = resolve_hl(synTrans ~= '' and synTrans or synName) + if hl.fg then + table.insert(lines, string.format(' fg: #%06x', hl.fg)) + end + if hl.bg then + table.insert(lines, string.format(' bg: #%06x', hl.bg)) + end + table.insert(lines, '') + end + + -- Final applied highlight (use TreeSitter if available, otherwise syntax) + local final_hl_name = nil + if #ts_hl > 0 then + final_hl_name = '@' .. ts_hl[1].capture + elseif synTrans ~= '' then + final_hl_name = synTrans + elseif synName ~= '' then + final_hl_name = synName + end + + if final_hl_name then + local final_hl, final_resolved = resolve_hl(final_hl_name) + table.insert(lines, 'Applied Highlight: ' .. final_resolved) + if final_hl.fg then + table.insert(lines, string.format(' fg: #%06x', final_hl.fg)) + else + table.insert(lines, ' fg: NONE') + end + if final_hl.bg then + table.insert(lines, string.format(' bg: #%06x', final_hl.bg)) + else + table.insert(lines, ' bg: NONE') + end + + local styles = {} + if final_hl.bold then table.insert(styles, 'bold') end + if final_hl.italic then table.insert(styles, 'italic') end + if final_hl.underline then table.insert(styles, 'underline') end + if final_hl.undercurl then table.insert(styles, 'undercurl') end + if #styles > 0 then + table.insert(lines, ' style: ' .. table.concat(styles, ', ')) + end + end + + -- Show in a floating window + local buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) + + local width = 0 + for _, line in ipairs(lines) do + width = math.max(width, #line) + end + width = math.min(width + 2, vim.o.columns - 4) + + local height = #lines + local opts = { + relative = 'cursor', + width = width, + height = height, + row = 1, + col = 0, + style = 'minimal', + border = 'rounded', + } + + vim.api.nvim_open_win(buf, false, opts) +end + -- Custom tabline function -- Shows configurable number of full parent directories, shortens the rest function M.custom_tabline() From bd021716ac4b43ee2b5e823af0c239fffbd41fa7 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 16:16:04 +0000 Subject: [PATCH 24/53] Add quickfix and location list keymaps Introduce keymaps for opening and closing quickfix and location list windows to enhance navigation and usability in Neovim. --- README.md | 4 ++++ lua/keymaps.lua | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index c483d7b..cd898ac 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,10 @@ Core keymaps available globally (not plugin-specific). These provide fallbacks f | n | `gr` | LSP references placeholder | `` so LSP buffers can override cleanly | | n | `gI` | LSP implementation placeholder | `` so LSP buffers can override cleanly | | n | `K` | Keyword help fallback | Uses `keywordprg` (e.g., `man`) when LSP hover is unavailable | +| n | `co` | Quickfix: Open | Opens quickfix window | +| n | `cc` | Quickfix: Close | Closes quickfix window | +| n | `lo` | Location list: Open | Opens location list window | +| n | `lc` | Location list: Close | Closes location list window | | n | `xx` | Diagnostics → location list | Populates current buffer diagnostics | | n | `xX` | Diagnostics → quickfix | Populates project-wide diagnostics | | n | `xe` | Diagnostics → buffer errors | Location list filtered to errors | diff --git a/lua/keymaps.lua b/lua/keymaps.lua index 3203a3f..2dbf084 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -34,6 +34,12 @@ map('n', 'K', function() end end, { desc = 'Vim: Hover/Help (keywordprg fallback)', silent = true }) +-- Quickfix and Location list keymaps +map('n', 'co', 'copen', { desc = 'Quickfix: Open', silent = true }) +map('n', 'cc', 'cclose', { desc = 'Quickfix: Close', silent = true }) +map('n', 'lo', 'lopen', { desc = 'Location list: Open', silent = true }) +map('n', 'lc', 'lclose', { desc = 'Location list: Close', silent = true }) + -- Diagnostic keymaps map('n', 'xx', vim.diagnostic.setloclist, { desc = 'Diagnostics: Buffer diagnostics (location list)', silent = true }) map('n', 'xX', vim.diagnostic.setqflist, { desc = 'Diagnostics: All diagnostics (quickfix)', silent = true }) From 96fcac9aa077d140f7e8364e679ad764dcc529bd Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 17:41:48 +0000 Subject: [PATCH 25/53] Trigger BufReadPost for loaded buffers on session load Ensure Treesitter/LSP attach by executing BufReadPost for all loaded buffers when a session is loaded. --- lua/autocmds.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lua/autocmds.lua b/lua/autocmds.lua index 1618e06..27e9c8b 100644 --- a/lua/autocmds.lua +++ b/lua/autocmds.lua @@ -123,7 +123,14 @@ vim.api.nvim_create_autocmd("SessionLoadPost", { group = session_aug, pattern = "*", callback = function() - vim.cmd("filetype detect") + -- Trigger BufReadPost for all loaded buffers to ensure Treesitter/LSP attach + vim.schedule(function() + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_is_loaded(buf) and vim.bo[buf].buftype == "" then + vim.api.nvim_exec_autocmds("BufReadPost", { buffer = buf }) + end + end + end) end, }) From 80d6db97cf9917e9067a9d37c7b6476d8f0b3095 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 19:03:09 +0000 Subject: [PATCH 26/53] Add borders and background to floating windows --- LOG.md | 1 + lua/paper-tonic-modern/groups/editor.lua | 4 +- lua/settings.lua | 48 +++++++++++++++++++++++- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/LOG.md b/LOG.md index d050ca1..80518f2 100644 --- a/LOG.md +++ b/LOG.md @@ -9,6 +9,7 @@ Authoritative notes for the Neovim migration. Use this alongside `MIGRATION_PLAN Record every decision here with a short rationale. Append new entries; do not rewrite history. +- 2025-01-13: **Popup borders for visibility**: Added rounded borders to all floating windows to prevent them from blending into the background. Changed `NormalFloat` and `FloatBorder` backgrounds from transparent (`c.NONE`) to `c.bg_ui` (slightly off-white) in colorscheme. Configured LSP handlers for hover and signature help to use `border = "rounded"`. Diagnostics already had borders configured. This provides clear visual separation between popups and main editor content. - 2025-01-13: **Git files to quickfix**: Added `gg` keymap to send all modified, deleted, and untracked Git files to quickfix list with status indicators. Uses `git status --porcelain` to parse file status (Modified, Deleted, Added, Untracked, etc.) and displays it in the quickfix text column. Implementation in `utils.git_changed_files()`. Complements existing `hq` (Gitsigns hunks to quickfix) with file-level Git status workflow. - 2025-01-12: **Custom Tabline Function**: Implemented configurable custom tabline to replace Neovim's hardcoded path shortening (e.g., `a/p/file.txt`). Function in `lua/utils.lua` allows controlling: (1) number of full parent directories via `utils.tabline_full_parents` (default: 1), and (2) shortening length via `utils.tabline_shorten_length` (default: 3 characters). Example: with defaults, `/path/to/my/project/src/file.txt` becomes `pat/to/my/project/src/file.txt`. User preference for seeing enough context in shortened paths while keeping tab width manageable. - 2025-12-06: PHP LSP = `intelephense` (good PHP ecosystem support; integrates includePaths). diff --git a/lua/paper-tonic-modern/groups/editor.lua b/lua/paper-tonic-modern/groups/editor.lua index 339b184..c5d4825 100644 --- a/lua/paper-tonic-modern/groups/editor.lua +++ b/lua/paper-tonic-modern/groups/editor.lua @@ -9,8 +9,8 @@ return { -- ============================================================================ Normal = { fg = c.fg, bg = c.bg }, - NormalFloat = { fg = c.fg, bg = c.NONE }, - FloatBorder = { fg = c.fg_stronger, bg = c.NONE }, + NormalFloat = { fg = c.fg, bg = c.bg_ui }, + FloatBorder = { fg = c.fg_weaker, bg = c.bg_ui }, -- ============================================================================ -- Cursor & Line Highlighting diff --git a/lua/settings.lua b/lua/settings.lua index 8f2cc40..8c27bfb 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -95,9 +95,55 @@ vim.diagnostic.config({ update_in_insert = false, -- Don't update diagnostics while typing severity_sort = true, -- Sort by severity (errors first) float = { - border = "rounded", + border = "single", source = "always", -- Show diagnostic source header = "", prefix = "", }, }) + +-- Configure LSP floating windows with borders and padding +-- Custom border with box-drawing characters flush to window edges +-- Using heavy/light box-drawing characters positioned at cell edges +local border = { + { "🭽", "FloatBorder" }, -- top-left corner + { "▔", "FloatBorder" }, -- top horizontal line (at top edge) + { "🭾", "FloatBorder" }, -- top-right corner + { "▕", "FloatBorder" }, -- right vertical line (at right edge) + { "🭿", "FloatBorder" }, -- bottom-right corner + { "▁", "FloatBorder" }, -- bottom horizontal line (at bottom edge) + { "🭼", "FloatBorder" }, -- bottom-left corner + { "▏", "FloatBorder" }, -- left vertical line (at left edge) +} + +-- Override the default open_floating_preview to add padding by modifying content +local orig_util_open_floating_preview = vim.lsp.util.open_floating_preview +function vim.lsp.util.open_floating_preview(contents, syntax, opts, ...) + opts = opts or {} + opts.border = opts.border or border + + -- Add padding by wrapping content with empty lines and spacing + if type(contents) == "table" and #contents > 0 then + -- Add empty lines at top and bottom + table.insert(contents, 1, "") + table.insert(contents, 1, "") + table.insert(contents, "") + table.insert(contents, "") + + -- Add horizontal padding (spaces) to each content line + for i = 3, #contents - 2 do + if type(contents[i]) == "string" then + contents[i] = " " .. contents[i] .. " " + end + end + end + + return orig_util_open_floating_preview(contents, syntax, opts, ...) +end + +vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { + border = border, +}) +vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, { + border = border, +}) From 093ff136755ebb6b1d88dc4f9b953d72cea36f42 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 19:26:23 +0000 Subject: [PATCH 27/53] Enhance completion and documentation window borders Updated the configuration for completion and documentation windows to include custom borders and highlighting for improved visibility. --- lua/plugins/cmp.lua | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lua/plugins/cmp.lua b/lua/plugins/cmp.lua index bb3cbc7..a738fea 100644 --- a/lua/plugins/cmp.lua +++ b/lua/plugins/cmp.lua @@ -34,8 +34,32 @@ return { preselect = cmp.PreselectMode.None, completion = { completeopt = "menu,menuone,noinsert,noselect" }, window = { - completion = cmp.config.window.bordered(), - documentation = cmp.config.window.bordered(), + completion = cmp.config.window.bordered({ + border = { + { "🭽", "FloatBorder" }, + { "▔", "FloatBorder" }, + { "🭾", "FloatBorder" }, + { "▕", "FloatBorder" }, + { "🭿", "FloatBorder" }, + { "▁", "FloatBorder" }, + { "🭼", "FloatBorder" }, + { "▏", "FloatBorder" }, + }, + winhighlight = "Normal:NormalFloat,FloatBorder:FloatBorder", + }), + documentation = cmp.config.window.bordered({ + border = { + { "🭽", "FloatBorder" }, + { "▔", "FloatBorder" }, + { "🭾", "FloatBorder" }, + { "▕", "FloatBorder" }, + { "🭿", "FloatBorder" }, + { "▁", "FloatBorder" }, + { "🭼", "FloatBorder" }, + { "▏", "FloatBorder" }, + }, + winhighlight = "Normal:NormalFloat,FloatBorder:FloatBorder", + }), }, } end, From ad110b0a4ac3d3c9fdd802c5fce9d6df9380a33e Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 19:33:28 +0000 Subject: [PATCH 28/53] Update NormalFloat and FloatBorder color settings --- lua/paper-tonic-modern/groups/editor.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/paper-tonic-modern/groups/editor.lua b/lua/paper-tonic-modern/groups/editor.lua index c5d4825..06e6ae8 100644 --- a/lua/paper-tonic-modern/groups/editor.lua +++ b/lua/paper-tonic-modern/groups/editor.lua @@ -9,8 +9,8 @@ return { -- ============================================================================ Normal = { fg = c.fg, bg = c.bg }, - NormalFloat = { fg = c.fg, bg = c.bg_ui }, - FloatBorder = { fg = c.fg_weaker, bg = c.bg_ui }, + NormalFloat = { fg = c.fg, bg = c.bg }, + FloatBorder = { fg = c.diag_hint, bg = c.bg }, -- ============================================================================ -- Cursor & Line Highlighting From 341236833de06563513e913827f518dcc4b3ae63 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 19:45:42 +0000 Subject: [PATCH 29/53] Update color settings for UI elements and completion menu - Added darker cyan for UI elements in diagnostics. - Adjusted FloatBorder and Pmenu colors for better visibility. --- lua/paper-tonic-modern/colors.lua | 1 + lua/paper-tonic-modern/groups/editor.lua | 13 +++++++------ lua/paper-tonic-modern/groups/plugins.lua | 3 ++- lua/plugins/cmp.lua | 4 ++-- lua/settings.lua | 6 ++++++ 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lua/paper-tonic-modern/colors.lua b/lua/paper-tonic-modern/colors.lua index a0a9cf7..546180b 100644 --- a/lua/paper-tonic-modern/colors.lua +++ b/lua/paper-tonic-modern/colors.lua @@ -104,6 +104,7 @@ M.diag_error = {'#ff0066', 197, 'red'} -- Hot pink-red (screams "error!") M.diag_warn = {'#ff6600', 202, 'red'} -- Fluorescent orange (warnings) M.diag_info = {'#00ccff', 45, 'cyan'} -- Bright fluorescent cyan (info - more prominent) M.diag_hint = {'#66e0ff', 81, 'cyan'} -- Softer fluorescent cyan (hint - less prominent) +M.diag_hint_dark = {'#0099cc', 38, 'cyan'} -- Darker cyan for UI elements (readable on white) -- LSP Diagnostic backgrounds - Light tinted versions for highlighting code M.bg_diag_error = {'#ffe6f0', 224, 'white'} -- Very light pink (for error backgrounds) diff --git a/lua/paper-tonic-modern/groups/editor.lua b/lua/paper-tonic-modern/groups/editor.lua index 06e6ae8..8c4dfce 100644 --- a/lua/paper-tonic-modern/groups/editor.lua +++ b/lua/paper-tonic-modern/groups/editor.lua @@ -10,7 +10,7 @@ return { Normal = { fg = c.fg, bg = c.bg }, NormalFloat = { fg = c.fg, bg = c.bg }, - FloatBorder = { fg = c.diag_hint, bg = c.bg }, + FloatBorder = { fg = c.diag_hint_dark, bg = c.bg }, -- ============================================================================ -- Cursor & Line Highlighting @@ -66,11 +66,12 @@ return { -- Popup Menu (Completion) -- ============================================================================ - Pmenu = { fg = c.fg, bg = c.bg_ui }, - PmenuSel = { fg = c.fg_strong, bg = c.bg_ui, bold = true }, - PmenuSbar = 'Pmenu', - PmenuThumb = 'Pmenu', - WildMenu = { fg = c.fg_strong, bg = c.bg_ui, bold = true }, + Pmenu = { fg = c.diag_hint_dark, bg = c.bg }, + PmenuSel = { fg = c.fg_strong, bg = c.bg_hl, bold = true }, + PmenuSbar = { fg = c.NONE, bg = c.bg_hl }, + PmenuThumb = { fg = c.NONE, bg = c.fg_weaker }, + PmenuBorder = { fg = c.diag_hint_dark, bg = c.bg }, + WildMenu = { fg = c.fg_strong, bg = c.bg_hl, bold = true }, -- ============================================================================ -- Folds diff --git a/lua/paper-tonic-modern/groups/plugins.lua b/lua/paper-tonic-modern/groups/plugins.lua index 87e4a0e..25711f9 100644 --- a/lua/paper-tonic-modern/groups/plugins.lua +++ b/lua/paper-tonic-modern/groups/plugins.lua @@ -63,10 +63,11 @@ return { -- ============================================================================ -- Menu - CmpItemMenu = { fg = c.fg_weak, italic = true }, + CmpItemAbbr = { fg = c.fg }, CmpItemAbbrMatch = { fg = c.primary, bold = true }, CmpItemAbbrMatchFuzzy = { fg = c.primary_weak }, CmpItemAbbrDeprecated = { fg = c.fg_weak, strikethrough = true }, + CmpItemMenu = { fg = c.fg_weak, italic = true }, -- Kind icons/labels CmpItemKindDefault = { fg = c.fg }, diff --git a/lua/plugins/cmp.lua b/lua/plugins/cmp.lua index a738fea..03702bd 100644 --- a/lua/plugins/cmp.lua +++ b/lua/plugins/cmp.lua @@ -45,7 +45,7 @@ return { { "🭼", "FloatBorder" }, { "▏", "FloatBorder" }, }, - winhighlight = "Normal:NormalFloat,FloatBorder:FloatBorder", + winhighlight = "Normal:Normal,FloatBorder:FloatBorder,CursorLine:PmenuSel", }), documentation = cmp.config.window.bordered({ border = { @@ -58,7 +58,7 @@ return { { "🭼", "FloatBorder" }, { "▏", "FloatBorder" }, }, - winhighlight = "Normal:NormalFloat,FloatBorder:FloatBorder", + winhighlight = "Normal:Normal,FloatBorder:FloatBorder", }), }, } diff --git a/lua/settings.lua b/lua/settings.lua index 8c27bfb..66ba0b8 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -40,6 +40,12 @@ vim.opt.showtabline = 1 -- Show tabline only when there are 2+ tabs -- Completion UI for nvim-cmp vim.opt.completeopt = { "menu", "menuone", "noselect" } +-- Built-in completion popup style (for Ctrl-X Ctrl-N/F completions) +-- Use floating window with border for native completion +vim.opt.pumblend = 0 -- No transparency +-- Note: Native completion (pumvisible) doesn't support custom borders like LSP floats +-- The Pmenu* highlight groups control its appearance + -- Spelling vim.opt.spelllang = { "en_gb" } From e450617eb615db458f2cd716e2e5da1a8c4a8e89 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 19:54:44 +0000 Subject: [PATCH 30/53] Enable autoindent and smartindent for PHP and general use This change re-enables autoindent and smartindent options in both PHP and general settings to improve code formatting and maintain consistency across file types. --- lua/autocmds.lua | 6 ++++++ lua/settings.lua | 2 ++ 2 files changed, 8 insertions(+) diff --git a/lua/autocmds.lua b/lua/autocmds.lua index 27e9c8b..b177413 100644 --- a/lua/autocmds.lua +++ b/lua/autocmds.lua @@ -25,6 +25,12 @@ vim.api.nvim_create_autocmd("FileType", { vim.opt_local.tabstop = 2 -- Display tabs as 2 spaces wide vim.opt_local.shiftwidth = 2 -- Indent/outdent by 2 columns (one tab) vim.opt_local.softtabstop = 2 -- Tab key inserts 2 columns (one tab) + + -- Fix: Neovim's built-in PHP indent sets indentexpr but breaks autoindent + -- Re-enable autoindent/smartindent and clear the broken indentexpr + vim.opt_local.autoindent = true + vim.opt_local.smartindent = true + vim.opt_local.indentexpr = "" -- Clear GetPhpIndent(), use smartindent instead end, }) diff --git a/lua/settings.lua b/lua/settings.lua index 66ba0b8..c744ed7 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -81,6 +81,8 @@ vim.opt.tabstop = 4 -- Display tabs as 4 spaces vim.opt.shiftwidth = 4 -- Indent by 4 vim.opt.softtabstop = 4 -- Backspace removes 4 spaces vim.opt.expandtab = true -- Use spaces by default (overridden per-filetype) +vim.opt.autoindent = true -- Copy indent from current line when starting new line +vim.opt.smartindent = true -- Smart autoindenting (C-like programs, PHP, etc.) -- Persistent undo vim.opt.undofile = true -- Enable persistent undo From 486dbef96a0204a409c287861f4925cace310a1b Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 19:59:34 +0000 Subject: [PATCH 31/53] Enable PHP autoindent and smartindent settings Re-enable autoindent and smartindent for PHP files to ensure proper indentation according to WordPress coding standards. --- lua/autocmds.lua | 6 ------ lua/plugins/treesitter.lua | 7 +++++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lua/autocmds.lua b/lua/autocmds.lua index b177413..27e9c8b 100644 --- a/lua/autocmds.lua +++ b/lua/autocmds.lua @@ -25,12 +25,6 @@ vim.api.nvim_create_autocmd("FileType", { vim.opt_local.tabstop = 2 -- Display tabs as 2 spaces wide vim.opt_local.shiftwidth = 2 -- Indent/outdent by 2 columns (one tab) vim.opt_local.softtabstop = 2 -- Tab key inserts 2 columns (one tab) - - -- Fix: Neovim's built-in PHP indent sets indentexpr but breaks autoindent - -- Re-enable autoindent/smartindent and clear the broken indentexpr - vim.opt_local.autoindent = true - vim.opt_local.smartindent = true - vim.opt_local.indentexpr = "" -- Clear GetPhpIndent(), use smartindent instead end, }) diff --git a/lua/plugins/treesitter.lua b/lua/plugins/treesitter.lua index b34fba5..e09ba3d 100644 --- a/lua/plugins/treesitter.lua +++ b/lua/plugins/treesitter.lua @@ -58,9 +58,12 @@ return { }, }, - -- Indentation (experimental, disable if issues) + -- Indentation: Enable for languages with good indent queries + -- PHP requires this because GetPhpIndent() depends on Vim syntax (disabled by Treesitter) indent = { - enable = false, -- Start disabled; can enable per-filetype if stable + enable = true, + -- Disable for languages with poor/incomplete indent queries if needed + disable = {}, }, -- Textobjects From 442d10363b4f8f3961978ba3e4a7bca6ed7f22f3 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 20:07:14 +0000 Subject: [PATCH 32/53] Update fold highlight colors for better visibility Changed Folded and CursorLineFold colors to enhance contrast and improve user experience. Updated UfoFoldedFg and UfoCursorFoldedLine for consistency with new color scheme. --- lua/paper-tonic-modern/groups/editor.lua | 3 ++- lua/paper-tonic-modern/groups/plugins.lua | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lua/paper-tonic-modern/groups/editor.lua b/lua/paper-tonic-modern/groups/editor.lua index 8c4dfce..5c33094 100644 --- a/lua/paper-tonic-modern/groups/editor.lua +++ b/lua/paper-tonic-modern/groups/editor.lua @@ -77,7 +77,8 @@ return { -- Folds -- ============================================================================ - Folded = { fg = c.fg_strong, bold = true }, + Folded = { fg = c.diag_hint_dark, bg = c.NONE, bold = true }, + CursorLineFold = { fg = c.diag_hint_dark, bg = c.bg_hl, bold = true }, FoldColumn = { fg = c.fg_weak }, -- ============================================================================ diff --git a/lua/paper-tonic-modern/groups/plugins.lua b/lua/paper-tonic-modern/groups/plugins.lua index 25711f9..fd847fe 100644 --- a/lua/paper-tonic-modern/groups/plugins.lua +++ b/lua/paper-tonic-modern/groups/plugins.lua @@ -151,8 +151,8 @@ return { -- ============================================================================ UfoFoldedBg = { bg = c.bg_hl_weak }, - UfoFoldedFg = { fg = c.fg_weak }, - UfoCursorFoldedLine = { bg = c.bg_hl, fg = c.fg }, + UfoFoldedFg = { fg = c.diag_hint_dark }, + UfoCursorFoldedLine = { bg = c.bg_hl, fg = c.diag_hint_dark }, UfoPreviewBorder = { fg = c.fg_weak }, UfoPreviewNormal = { bg = c.bg_ui }, UfoPreviewCursorLine = { bg = c.bg_hl }, From 51e5c2a60253a4119cfb845e8b77d213a6dc2ac2 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 20:12:02 +0000 Subject: [PATCH 33/53] Add fold support for comment blocks in PHP Introduce a new query file to enable folding of comment blocks, including docblocks, enhancing code readability and organization. --- after/queries/php/folds.scm | 4 ++++ lua/plugins/ufo.lua | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 after/queries/php/folds.scm diff --git a/after/queries/php/folds.scm b/after/queries/php/folds.scm new file mode 100644 index 0000000..57f8cae --- /dev/null +++ b/after/queries/php/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks (including /** */ docblocks) +(comment) @fold diff --git a/lua/plugins/ufo.lua b/lua/plugins/ufo.lua index 5920173..097a13e 100644 --- a/lua/plugins/ufo.lua +++ b/lua/plugins/ufo.lua @@ -26,7 +26,7 @@ return { -- Open folds when searching open_fold_hl_timeout = 150, close_fold_kinds_for_ft = { - default = { 'imports', 'comment' }, + default = { 'imports' }, }, preview = { win_config = { From b53515e4923b7fde8e197e38f10ae93bcb16b1a7 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 20:14:28 +0000 Subject: [PATCH 34/53] Add fold support for comment blocks in various languages This commit introduces new fold support for comment blocks in Bash, CSS, HTML, JavaScript, Lua, Python, SCSS, TSX, TypeScript, and Vim, enhancing code readability and organization. --- after/queries/bash/folds.scm | 4 ++++ after/queries/css/folds.scm | 4 ++++ after/queries/html/folds.scm | 4 ++++ after/queries/javascript/folds.scm | 8 ++++++++ after/queries/lua/folds.scm | 4 ++++ after/queries/python/folds.scm | 4 ++++ after/queries/scss/folds.scm | 4 ++++ after/queries/tsx/folds.scm | 4 ++++ after/queries/typescript/folds.scm | 4 ++++ after/queries/vim/folds.scm | 4 ++++ 10 files changed, 44 insertions(+) create mode 100644 after/queries/bash/folds.scm create mode 100644 after/queries/css/folds.scm create mode 100644 after/queries/html/folds.scm create mode 100644 after/queries/javascript/folds.scm create mode 100644 after/queries/lua/folds.scm create mode 100644 after/queries/python/folds.scm create mode 100644 after/queries/scss/folds.scm create mode 100644 after/queries/tsx/folds.scm create mode 100644 after/queries/typescript/folds.scm create mode 100644 after/queries/vim/folds.scm diff --git a/after/queries/bash/folds.scm b/after/queries/bash/folds.scm new file mode 100644 index 0000000..30a154e --- /dev/null +++ b/after/queries/bash/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks +(comment) @fold diff --git a/after/queries/css/folds.scm b/after/queries/css/folds.scm new file mode 100644 index 0000000..30a154e --- /dev/null +++ b/after/queries/css/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks +(comment) @fold diff --git a/after/queries/html/folds.scm b/after/queries/html/folds.scm new file mode 100644 index 0000000..30a154e --- /dev/null +++ b/after/queries/html/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks +(comment) @fold diff --git a/after/queries/javascript/folds.scm b/after/queries/javascript/folds.scm new file mode 100644 index 0000000..c56348f --- /dev/null +++ b/after/queries/javascript/folds.scm @@ -0,0 +1,8 @@ +;; extends + +;; Fold comment blocks (including consecutive single-line comments) +(comment) @fold + +;; Fold consecutive single-line comments as a block +((comment) @fold + (#match? @fold "^//")) diff --git a/after/queries/lua/folds.scm b/after/queries/lua/folds.scm new file mode 100644 index 0000000..30a154e --- /dev/null +++ b/after/queries/lua/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks +(comment) @fold diff --git a/after/queries/python/folds.scm b/after/queries/python/folds.scm new file mode 100644 index 0000000..30a154e --- /dev/null +++ b/after/queries/python/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks +(comment) @fold diff --git a/after/queries/scss/folds.scm b/after/queries/scss/folds.scm new file mode 100644 index 0000000..30a154e --- /dev/null +++ b/after/queries/scss/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks +(comment) @fold diff --git a/after/queries/tsx/folds.scm b/after/queries/tsx/folds.scm new file mode 100644 index 0000000..30a154e --- /dev/null +++ b/after/queries/tsx/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks +(comment) @fold diff --git a/after/queries/typescript/folds.scm b/after/queries/typescript/folds.scm new file mode 100644 index 0000000..30a154e --- /dev/null +++ b/after/queries/typescript/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks +(comment) @fold diff --git a/after/queries/vim/folds.scm b/after/queries/vim/folds.scm new file mode 100644 index 0000000..30a154e --- /dev/null +++ b/after/queries/vim/folds.scm @@ -0,0 +1,4 @@ +;; extends + +;; Fold comment blocks +(comment) @fold From b9ddeee54ede2f3901291375977fa8ff0750bfc4 Mon Sep 17 00:00:00 2001 From: ray Date: Tue, 13 Jan 2026 20:27:43 +0000 Subject: [PATCH 35/53] Add search behavior settings for improved usability Configure ignorecase, smartcase, incsearch, and hlsearch options to enhance search functionality in the editor. --- lua/settings.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lua/settings.lua b/lua/settings.lua index c744ed7..4d6af08 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -49,6 +49,12 @@ vim.opt.pumblend = 0 -- No transparency -- Spelling vim.opt.spelllang = { "en_gb" } +-- Search behavior +vim.opt.ignorecase = true -- Case-insensitive search by default +vim.opt.smartcase = true -- Case-sensitive when search contains uppercase letters +vim.opt.incsearch = true -- Show matches as you type +vim.opt.hlsearch = true -- Highlight all search matches + -- Keyword characters (add $ for PHP/shell variables, - for CSS/HTML/config files) vim.opt.iskeyword:append("$") vim.opt.iskeyword:append("-") From ee01977e762aae013f84ff232e37753ca88e2068 Mon Sep 17 00:00:00 2001 From: ray Date: Sun, 18 Jan 2026 13:11:40 +0000 Subject: [PATCH 36/53] Improve hunk navigation with auto-centering behavior Added a delay to center the cursor after moving to the next or previous hunk, enhancing the navigation experience. --- lua/plugins/gitsigns.lua | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lua/plugins/gitsigns.lua b/lua/plugins/gitsigns.lua index 0205547..11f0c63 100644 --- a/lua/plugins/gitsigns.lua +++ b/lua/plugins/gitsigns.lua @@ -25,16 +25,27 @@ return { vim.keymap.set(mode, l, r, opts) end - -- Hunk navigation + -- Hunk navigation (includes zt to center after movement) + -- Note: gs.next_hunk/prev_hunk are async, so zt needs a delay to center after cursor moves map('n', ']h', function() - if vim.wo.diff then return ']c' end - vim.schedule(function() gs.next_hunk() end) + if vim.wo.diff then return ']czt' end + vim.schedule(function() + gs.next_hunk() + end) + vim.defer_fn(function() + vim.cmd('normal! zt') + end, 50) return '' end, { expr = true, desc = 'Gitsigns: Next hunk' }) map('n', '[h', function() - if vim.wo.diff then return '[c' end - vim.schedule(function() gs.prev_hunk() end) + if vim.wo.diff then return '[czt' end + vim.schedule(function() + gs.prev_hunk() + end) + vim.defer_fn(function() + vim.cmd('normal! zt') + end, 50) return '' end, { expr = true, desc = 'Gitsigns: Previous hunk' }) From d2ff40df16ff2cac172dcbe36e0ba246a914abf0 Mon Sep 17 00:00:00 2001 From: ray Date: Sun, 18 Jan 2026 13:20:15 +0000 Subject: [PATCH 37/53] update spell --- spell/en.utf-8.add | 1 + spell/en.utf-8.add.spl | Bin 9639 -> 9650 bytes 2 files changed, 1 insertion(+) diff --git a/spell/en.utf-8.add b/spell/en.utf-8.add index 26ad56b..4143d2a 100644 --- a/spell/en.utf-8.add +++ b/spell/en.utf-8.add @@ -773,3 +773,4 @@ Tengo/! Trengo Hostinger's hPanel +reauthoring diff --git a/spell/en.utf-8.add.spl b/spell/en.utf-8.add.spl index b293aeb84e5445cea4c06950076d702f8c2f2b88..f891df9ecc7eeb4a55c7d2f445c503826ea914a4 100644 GIT binary patch delta 99 zcmZ4Py~&#|%+t5HAT=k)=syF4(A155H6kp`$tk6idqtEvOBgd485vld^Ea;-S;NTa zwK+yCosqG3@)7ZNMy<_05zj3bl#jUmd?o7Gx@xD sJEQXEaEUdHjINVeq%;@>ChJP+F&b`;k%|N=o4ieWHRI&XNiqSP0Q|@sHUIzs From 8e2b71c7f14c26d2eafe0f680c969ce582c7a1db Mon Sep 17 00:00:00 2001 From: ray Date: Sun, 18 Jan 2026 13:24:50 +0000 Subject: [PATCH 38/53] Add navigation mode feature plan documentation Introduce a comprehensive plan for a custom navigation mode in Neovim, detailing user experience, technical implementation, and visual feedback options. --- NAVIGATION_MODE_PLAN.md | 216 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 NAVIGATION_MODE_PLAN.md diff --git a/NAVIGATION_MODE_PLAN.md b/NAVIGATION_MODE_PLAN.md new file mode 100644 index 0000000..608f3cd --- /dev/null +++ b/NAVIGATION_MODE_PLAN.md @@ -0,0 +1,216 @@ +# Navigation Mode Feature Plan + +## Concept +A custom navigation mode where keypresses are automatically prefixed with `[` or `]`, making it easier to navigate using Neovim's bracket-based navigation pairs without repeatedly typing brackets. + +## User Experience + +### Entry +- `z[` - Enter navigation mode with `[` prefix active (backward) +- `z]` - Enter navigation mode with `]` prefix active (forward) + +### In Mode +- All letter keys (`a-z`, `A-Z`) get prefixed with current bracket +- Examples: + - Press `c` → executes `[c` or `]c` (git hunks) + - Press `d` → executes `[d` or `]d` (diagnostics) + - Press `m` → executes `[m` or `]m` (methods) + - Press `f` → executes `[f` or `]f` (functions) + - Press `b` → executes `[b` or `]b` (buffers) + - Press `q` → executes `[q` or `]q` (quickfix) + +### Toggle Prefix +- `[` - Switch to `[` prefix (backward navigation) +- `]` - Switch to `]` prefix (forward navigation) + +### Exit +- `` - Exit navigation mode and restore normal mappings + +## Technical Implementation + +### State Management +```lua +local NavMode = { + active = false, + prefix = '[', -- '[' or ']' +} +``` + +### Key Remapping Strategy +1. Store original mappings for letters a-z, A-Z +2. On mode entry, create new mappings: `key → prefix .. key` +3. On mode exit, restore original mappings +4. On prefix toggle, update all mappings with new prefix + +### Keys to Remap +- Lowercase: `a-z` (26 keys) +- Uppercase: `A-Z` (26 keys) +- Total: 52 keys dynamically remapped + +### Common Bracket Navigation Pairs +- `[c`/`]c` - Previous/next git hunk (gitsigns) +- `[d`/`]d` - Previous/next diagnostic +- `[b`/`]b` - Previous/next buffer (custom mapping) +- `[q`/`]q` - Previous/next quickfix item +- `[l`/`]l` - Previous/next location list item +- `[m`/`]m` - Previous/next method (treesitter textobjects) +- `[f`/`]f` - Previous/next function (treesitter textobjects) +- `[p`/`]p` - Previous/next parameter (treesitter textobjects) +- `[t`/`]t` - Previous/next tag + +## Implementation Details + +### Module Structure +Create `lua/navigation-mode.lua`: + +```lua +local M = {} + +local state = { + active = false, + prefix = '[', + stored_mappings = {}, +} + +local LETTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + +function M.enter(prefix) + if state.active then return end + + state.prefix = prefix or '[' + state.active = true + + -- Store original mappings and create prefixed ones + for i = 1, #LETTERS do + local key = LETTERS:sub(i, i) + -- Store original mapping (if exists) + -- Create new mapping: key → prefix .. key + vim.keymap.set('n', key, state.prefix .. key, { noremap = true, silent = true }) + end + + -- Special mappings for [ and ] to toggle prefix + vim.keymap.set('n', '[', function() M.set_prefix('[') end, { noremap = true, silent = true }) + vim.keymap.set('n', ']', function() M.set_prefix(']') end, { noremap = true, silent = true }) + + -- Exit mapping + vim.keymap.set('n', '', function() M.exit() end, { noremap = true, silent = true }) + + -- TODO: Set visual feedback (statusline, notification, etc.) +end + +function M.set_prefix(new_prefix) + if not state.active then return end + state.prefix = new_prefix + + -- Remap all letters with new prefix + for i = 1, #LETTERS do + local key = LETTERS:sub(i, i) + vim.keymap.set('n', key, state.prefix .. key, { noremap = true, silent = true }) + end + + -- TODO: Update visual feedback +end + +function M.exit() + if not state.active then return end + + -- Restore original mappings + for i = 1, #LETTERS do + local key = LETTERS:sub(i, i) + vim.keymap.del('n', key) + -- Restore stored mapping if it existed + end + + -- Clean up special mappings + vim.keymap.del('n', '[') + vim.keymap.del('n', ']') + vim.keymap.del('n', '') + + state.active = false + + -- TODO: Clear visual feedback +end + +return M +``` + +### Integration in keymaps.lua +```lua +-- Navigation mode +vim.keymap.set('n', 'z[', function() + require('navigation-mode').enter('[') +end, { desc = 'Enter navigation mode (backward)' }) + +vim.keymap.set('n', 'z]', function() + require('navigation-mode').enter(']') +end, { desc = 'Enter navigation mode (forward)' }) +``` + +## Visual Feedback Options (TODO) + +Need to decide on one or more: + +1. **Statusline indicator**: Show `[NAV ←]` or `[NAV →]` in statusline +2. **Notification**: Brief message on mode entry/exit +3. **Command line**: `echo` message showing current prefix +4. **Cursor highlight**: Change cursor color/shape +5. **Virtual text**: Floating indicator in corner of window + +## Open Questions + +1. **Mapping conflicts**: How to handle if a letter already has a mapping? + - Overwrite temporarily? + - Skip that letter? + - Warn user? + +2. **Buffer-local mappings**: Should mode respect buffer-local mappings? + - Store and restore per-buffer? + - Global mode only? + +3. **Visual feedback**: Which approach is clearest without being intrusive? + +4. **Number keys**: Should `0-9` also be prefixed? + - Useful for some navigation pairs + - But might conflict with counts + +5. **Operators**: Should `d`, `c`, `y` still work as operators or only as navigation? + - Current plan: They become navigation only while in mode + - Trade-off: Can't delete/change while navigating + +## Implementation Phases + +1. **Core functionality** (essential) + - Mode enter/exit + - Key remapping for a-z + - Prefix toggle + - Basic state management + +2. **Visual feedback** (important) + - Choose and implement feedback mechanism + - Ensure clarity when mode is active + +3. **Edge cases** (polish) + - Handle existing mappings gracefully + - Buffer-local mapping preservation + - Mode timeout/auto-exit? + +4. **Documentation** (completion) + - Update README.md with keymaps + - Add examples of useful navigation pairs + - Consider adding to help docs + +## Estimated Complexity +- **Core**: ~100-150 lines of Lua +- **Visual feedback**: +20-50 lines depending on approach +- **Edge case handling**: +30-50 lines +- **Total**: 150-250 lines + +## Testing Checklist +- [ ] Enter mode with `z[` and `z]` +- [ ] Verify letter keys are prefixed correctly +- [ ] Toggle between `[` and `]` prefix +- [ ] Exit cleanly with `` +- [ ] No residual mappings after exit +- [ ] Works across different buffers +- [ ] Visual feedback is clear +- [ ] Common navigation pairs work (c, d, m, f, q) From 2a516e353bd178aa4f0a14f5f3cbc4524a1d8572 Mon Sep 17 00:00:00 2001 From: ray Date: Sun, 18 Jan 2026 13:38:42 +0000 Subject: [PATCH 39/53] Enhance navigation with repeat and reverse functionality Add two new commands for bracket navigation: repeat last and reverse last navigation. This reduces repetitive typing and improves user experience when navigating through diagnostics, spelling, and quickfix items. --- NAVIGATION_MODE_PLAN.md | 565 ++++++++++++++++++++++++++++++---------- 1 file changed, 434 insertions(+), 131 deletions(-) diff --git a/NAVIGATION_MODE_PLAN.md b/NAVIGATION_MODE_PLAN.md index 608f3cd..d0f21db 100644 --- a/NAVIGATION_MODE_PLAN.md +++ b/NAVIGATION_MODE_PLAN.md @@ -1,7 +1,163 @@ -# Navigation Mode Feature Plan +# Navigation Enhancement Plans -## Concept -A custom navigation mode where keypresses are automatically prefixed with `[` or `]`, making it easier to navigate using Neovim's bracket-based navigation pairs without repeatedly typing brackets. +**Status**: Two complementary approaches being considered (not mutually exclusive) + +Both approaches aim to reduce repetitive typing when navigating with Neovim's bracket-based pairs (`[c`, `]d`, etc.). + +--- + +## Option 1: Repeat/Reverse Last Bracket Navigation + +### Concept +Two simple commands that remember and replay the last `[x` or `]x` navigation: +- **Repeat**: Execute the same navigation again (e.g., `]d` → `.` → `.` → `.`) +- **Reverse**: Execute the opposite direction (e.g., after `]d`, press `,` → `[d`) + +Similar to `;` and `,` for repeating/reversing `f/F/t/T` motions. + +### User Experience + +```vim +" Example 1: Scanning diagnostics +]d " Next diagnostic +. " Next diagnostic (repeat) +. " Next diagnostic (repeat) +. " Next diagnostic (repeat) +, " Previous diagnostic (reverse, oops went too far) + +" Example 2: Checking spelling +]s " Next misspelling +. " Next misspelling +. " Next misspelling + +" Example 3: Quickfix workflow +]q " Next quickfix item +. " Next quickfix +. " Next quickfix +``` + +### When This Works Best +- Repeatedly navigating **same type**: diagnostics, spelling, quickfix, location list +- Linear scanning through items of one kind +- Quick corrections when you overshoot (reverse) + +### Implementation + +**Module**: `lua/bracket-repeat.lua` + +```lua +local M = {} + +-- Track last bracket navigation +local last_nav = { + prefix = nil, -- '[' or ']' + key = nil, -- 'c', 'd', 'm', 's', 'q', etc. +} + +--- Setup wrapper mappings to track bracket navigation +function M.setup() + -- Common bracket pairs to track + local pairs = { + 'c', -- Git hunks (gitsigns) + 'd', -- Diagnostics + 's', -- Spelling + 'q', -- Quickfix + 'l', -- Location list + 't', -- Tags + 'm', -- Methods (treesitter) + 'f', -- Functions (treesitter) + 'p', -- Parameters (treesitter) + } + + for _, key in ipairs(pairs) do + -- Wrap [x to track + vim.keymap.set('n', '[' .. key, function() + last_nav.prefix = '[' + last_nav.key = key + return '[' .. key + end, { expr = true, silent = true }) + + -- Wrap ]x to track + vim.keymap.set('n', ']' .. key, function() + last_nav.prefix = ']' + last_nav.key = key + return ']' .. key + end, { expr = true, silent = true }) + end +end + +--- Repeat last bracket navigation +function M.repeat_last() + if not last_nav.prefix or not last_nav.key then + vim.notify('No bracket navigation to repeat', vim.log.levels.WARN) + return + end + vim.cmd('normal! ' .. last_nav.prefix .. last_nav.key) +end + +--- Reverse last bracket navigation (flip direction) +function M.reverse_last() + if not last_nav.prefix or not last_nav.key then + vim.notify('No bracket navigation to reverse', vim.log.levels.WARN) + return + end + local opposite = last_nav.prefix == '[' and ']' or '[' + vim.cmd('normal! ' .. opposite .. last_nav.key) + -- Update tracking to reflect the reversal + last_nav.prefix = opposite +end + +return M +``` + +**Integration**: `init.lua` or `lua/keymaps.lua` + +```lua +-- Setup tracking +require('bracket-repeat').setup() + +-- Keybindings (choose one option) + +-- Option A: Override ; and , (loses f/F/t/T repeat, but very ergonomic) +vim.keymap.set('n', ';', function() require('bracket-repeat').repeat_last() end, + { desc = 'Repeat bracket navigation' }) +vim.keymap.set('n', ',', function() require('bracket-repeat').reverse_last() end, + { desc = 'Reverse bracket navigation' }) + +-- Option B: Use z prefix (keeps ; and , for f/t motions) +vim.keymap.set('n', 'z.', function() require('bracket-repeat').repeat_last() end, + { desc = 'Repeat bracket navigation' }) +vim.keymap.set('n', 'z,', function() require('bracket-repeat').reverse_last() end, + { desc = 'Reverse bracket navigation' }) + +-- Option C: Use leader +vim.keymap.set('n', '.', function() require('bracket-repeat').repeat_last() end, + { desc = 'Repeat bracket navigation' }) +vim.keymap.set('n', ',', function() require('bracket-repeat').reverse_last() end, + { desc = 'Reverse bracket navigation' }) +``` + +### Pros & Cons + +**Pros:** +- ✅ Very simple (~40 lines) +- ✅ No mode switching, stays in normal mode +- ✅ Familiar pattern (like `;`/`,` for `f`/`t`) +- ✅ Works with natural workflow +- ✅ Easy to add more tracked pairs + +**Cons:** +- ⚠️ Only handles one "type" at a time (can't easily switch between `]d` and `]c`) +- ⚠️ Requires calling `.setup()` to track pairs +- ⚠️ Might want `;`/`,` for `f`/`t` repeat (depends on keybinding choice) + +### Estimated Complexity +- **Total**: ~40 lines +- **Time**: 15-30 minutes + +--- + +## Option 2: Navigation Mode (getchar Loop) ## User Experience @@ -24,28 +180,43 @@ A custom navigation mode where keypresses are automatically prefixed with `[` or - `]` - Switch to `]` prefix (forward navigation) ### Exit -- `` - Exit navigation mode and restore normal mappings +- `` - Exit navigation mode and return to normal editing + +### When This Works Best +- Switching between **different types** of navigation (`]d` → `]c` → `]m`) +- Want visual feedback showing current direction +- Prefer a dedicated "navigation state" +- Mixed workflow: `]q` to quickfix, then multiple `]c` for hunks (stay in mode, switch keys) + +### Example Workflow + +```vim +" Mixed navigation scenario +z] " Enter forward navigation mode (shows: NAV ] →:) +q " Execute ]q (next quickfix) +c " Execute ]c (next hunk) +c " Execute ]c (next hunk) +[ " Toggle to backward (shows: NAV [ ←:) +c " Execute [c (previous hunk) +d " Execute [d (previous diagnostic) + " Exit mode +``` ## Technical Implementation -### State Management -```lua -local NavMode = { - active = false, - prefix = '[', -- '[' or ']' -} -``` +### Approach: getchar() Loop (No Remapping) -### Key Remapping Strategy -1. Store original mappings for letters a-z, A-Z -2. On mode entry, create new mappings: `key → prefix .. key` -3. On mode exit, restore original mappings -4. On prefix toggle, update all mappings with new prefix +Instead of remapping keys, use a `while` loop with `vim.fn.getchar()` to read keypresses and manually execute the prefixed commands. -### Keys to Remap -- Lowercase: `a-z` (26 keys) -- Uppercase: `A-Z` (26 keys) -- Total: 52 keys dynamically remapped +**Why this approach:** +- ✅ Zero mapping conflicts (never touches existing keymaps) +- ✅ Simpler implementation (~50-80 lines vs ~150-250) +- ✅ Built-in visual feedback via `vim.api.nvim_echo()` +- ✅ Impossible to leak state (no cleanup needed if crashed) +- ✅ Predictable behavior (each keypress isolated) +- ✅ Naturally handles special keys + +**The "blocking" behavior is actually desired** - you're in a focused navigation mode, press `` to exit anytime. ### Common Bracket Navigation Pairs - `[c`/`]c` - Previous/next git hunk (gitsigns) @@ -66,74 +237,53 @@ Create `lua/navigation-mode.lua`: ```lua local M = {} -local state = { - active = false, - prefix = '[', - stored_mappings = {}, -} - -local LETTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' - +--- Enter navigation mode with getchar() loop +--- @param prefix string Either '[' or ']' function M.enter(prefix) - if state.active then return end + prefix = prefix or '[' - state.prefix = prefix or '[' - state.active = true - - -- Store original mappings and create prefixed ones - for i = 1, #LETTERS do - local key = LETTERS:sub(i, i) - -- Store original mapping (if exists) - -- Create new mapping: key → prefix .. key - vim.keymap.set('n', key, state.prefix .. key, { noremap = true, silent = true }) + while true do + -- Display prompt + local hl = prefix == '[' and 'DiagnosticInfo' or 'DiagnosticHint' + local arrow = prefix == '[' and '←' or '→' + vim.api.nvim_echo({{string.format('NAV %s %s: ', prefix, arrow), hl}}, false, {}) + + -- Get next keypress + local ok, char = pcall(vim.fn.getchar) + if not ok then break end -- Handle gracefully + + -- Convert to string + local key = type(char) == 'number' and vim.fn.nr2char(char) or char + + -- Handle special keys + if key == '\27' then -- ESC + break + elseif key == '[' then + prefix = '[' + elseif key == ']' then + prefix = ']' + else + -- Execute prefix + key as normal mode command + local cmd = prefix .. key + vim.cmd('normal! ' .. vim.api.nvim_replace_termcodes(cmd, true, false, true)) + end end - -- Special mappings for [ and ] to toggle prefix - vim.keymap.set('n', '[', function() M.set_prefix('[') end, { noremap = true, silent = true }) - vim.keymap.set('n', ']', function() M.set_prefix(']') end, { noremap = true, silent = true }) - - -- Exit mapping - vim.keymap.set('n', '', function() M.exit() end, { noremap = true, silent = true }) - - -- TODO: Set visual feedback (statusline, notification, etc.) -end - -function M.set_prefix(new_prefix) - if not state.active then return end - state.prefix = new_prefix - - -- Remap all letters with new prefix - for i = 1, #LETTERS do - local key = LETTERS:sub(i, i) - vim.keymap.set('n', key, state.prefix .. key, { noremap = true, silent = true }) - end - - -- TODO: Update visual feedback -end - -function M.exit() - if not state.active then return end - - -- Restore original mappings - for i = 1, #LETTERS do - local key = LETTERS:sub(i, i) - vim.keymap.del('n', key) - -- Restore stored mapping if it existed - end - - -- Clean up special mappings - vim.keymap.del('n', '[') - vim.keymap.del('n', ']') - vim.keymap.del('n', '') - - state.active = false - - -- TODO: Clear visual feedback + -- Clear prompt + vim.api.nvim_echo({{'', 'Normal'}}, false, {}) end return M ``` +**Key Points:** +- No state management needed (self-contained loop) +- `pcall(vim.fn.getchar)` handles `` interrupts gracefully +- `vim.api.nvim_replace_termcodes()` ensures special key sequences work +- Visual feedback built into the loop (shows `NAV [ ←:` or `NAV ] →:`) +- Press `[` or `]` to toggle prefix without exiting +- Press `` (or ``) to exit + ### Integration in keymaps.lua ```lua -- Navigation mode @@ -146,71 +296,224 @@ vim.keymap.set('n', 'z]', function() end, { desc = 'Enter navigation mode (forward)' }) ``` -## Visual Feedback Options (TODO) +## Visual Feedback -Need to decide on one or more: +Built into the getchar() loop: +- Shows `NAV [ ←:` (in blue) when in backward mode +- Shows `NAV ] →:` (in teal) when in forward mode +- Prompt updates immediately when toggling with `[` or `]` +- Clears when exiting with `` -1. **Statusline indicator**: Show `[NAV ←]` or `[NAV →]` in statusline -2. **Notification**: Brief message on mode entry/exit -3. **Command line**: `echo` message showing current prefix -4. **Cursor highlight**: Change cursor color/shape -5. **Virtual text**: Floating indicator in corner of window +No additional statusline integration needed - the command line prompt is clear and non-intrusive. -## Open Questions +## Design Decisions -1. **Mapping conflicts**: How to handle if a letter already has a mapping? - - Overwrite temporarily? - - Skip that letter? - - Warn user? +### Mapping Conflicts: Solved +No mapping conflicts possible - getchar() reads raw input without touching keymaps. -2. **Buffer-local mappings**: Should mode respect buffer-local mappings? - - Store and restore per-buffer? - - Global mode only? +### Buffer-local Mappings: Not Applicable +Loop executes `normal! [key` which uses whatever mappings exist naturally. -3. **Visual feedback**: Which approach is clearest without being intrusive? +### Visual Feedback: Built-in +Command line prompt is sufficient and non-intrusive. -4. **Number keys**: Should `0-9` also be prefixed? - - Useful for some navigation pairs - - But might conflict with counts +### Number Keys +Currently not prefixed - this allows using counts if a prefixed command accepts them. +Example: `3c` → executes `[3c` or `]3c` (may not be useful, but won't break anything) -5. **Operators**: Should `d`, `c`, `y` still work as operators or only as navigation? - - Current plan: They become navigation only while in mode - - Trade-off: Can't delete/change while navigating +Could add special handling if needed: +```lua +if key:match('^%d$') then + -- Handle numbers specially +end +``` -## Implementation Phases +### Operators (d, c, y, etc.) +In navigation mode, `d` executes `[d` (diagnostic navigation), not delete operator. +This is desired behavior - use `` to exit and edit normally. -1. **Core functionality** (essential) - - Mode enter/exit - - Key remapping for a-z - - Prefix toggle - - Basic state management +### Error Handling +If a command doesn't exist (e.g., `[z`), Vim will show an error but mode continues. +User can press `` to exit or try another key. -2. **Visual feedback** (important) - - Choose and implement feedback mechanism - - Ensure clarity when mode is active +### Pros & Cons -3. **Edge cases** (polish) - - Handle existing mappings gracefully - - Buffer-local mapping preservation - - Mode timeout/auto-exit? +**Pros:** +- ✅ Switch between different navigation types easily (`c` → `d` → `m`) +- ✅ Visual feedback (command line prompt) +- ✅ No mapping conflicts +- ✅ Clean state (nothing to cleanup if interrupted) +- ✅ Natural for rapid mixed navigation -4. **Documentation** (completion) - - Update README.md with keymaps - - Add examples of useful navigation pairs - - Consider adding to help docs +**Cons:** +- ⚠️ Requires mode switching (mental overhead) +- ⚠️ Blocking loop (though this is by design) +- ⚠️ Need to remember entry/exit keys -## Estimated Complexity -- **Core**: ~100-150 lines of Lua -- **Visual feedback**: +20-50 lines depending on approach -- **Edge case handling**: +30-50 lines -- **Total**: 150-250 lines +### Estimated Complexity +- **Total**: ~50-80 lines +- **Time**: 30-60 minutes -## Testing Checklist -- [ ] Enter mode with `z[` and `z]` -- [ ] Verify letter keys are prefixed correctly -- [ ] Toggle between `[` and `]` prefix -- [ ] Exit cleanly with `` -- [ ] No residual mappings after exit +--- + +## Comparison & Recommendation + +| Aspect | Repeat/Reverse | Navigation Mode | +|--------|---------------|----------------| +| **Best for** | Same-type scanning | Mixed navigation | +| **Complexity** | ~40 lines | ~50-80 lines | +| **Mental model** | Like `;`/`,` | New mode | +| **Typing (4× same nav)** | `]d ...` (4 keys) | `z] dddd ` (9 keys) | +| **Typing (mixed nav)** | `]d ]d ]c ]c` (8 keys) | `z] ddcc ` (9 keys) | +| **Mode switching** | No | Yes | +| **Visual feedback** | No (unless added) | Yes (built-in) | + +### Use Cases + +**Repeat/Reverse excels at:** +- Scanning diagnostics: `]d . . . .` +- Spell checking: `]s . . . .` +- Reviewing quickfix: `]q . . . .` +- Going back: `. . . , , ,` + +**Navigation Mode excels at:** +- Mixed workflow: `]q` → multiple `]c` hunks → check `]d` diagnostic +- When you want visual confirmation of direction +- Exploring unfamiliar code (trying different navigation types) + +### Implementation Strategy + +**Both can coexist!** They solve slightly different problems: + +1. **Start with Repeat/Reverse** (simpler, covers 80% of cases) + - Use for linear scanning (diagnostics, spelling, quickfix) + - Bind to `z.` and `z,` (or `;`/`,` if you don't use `f`/`t` repeat often) + +2. **Add Navigation Mode later** (optional, for mixed navigation) + - Use when you need to rapidly switch types + - Bind to `z[` and `z]` + +You'll naturally reach for whichever fits the situation better. + +--- + +## Common Bracket Pairs Reference + +- `[c`/`]c` - Previous/next git hunk (gitsigns) +- `[d`/`]d` - Previous/next diagnostic +- `[s`/`]s` - Previous/next misspelling +- `[q`/`]q` - Previous/next quickfix item +- `[l`/`]l` - Previous/next location list item +- `[m`/`]m` - Previous/next method (treesitter textobjects) +- `[f`/`]f` - Previous/next function (treesitter textobjects) +- `[p`/`]p` - Previous/next parameter (treesitter textobjects) +- `[t`/`]t` - Previous/next tag +- `[b`/`]b` - Previous/next buffer (if custom mapping exists) + +--- + +## Testing Checklists + +### Repeat/Reverse Testing +- [ ] Setup completes without errors +- [ ] `]d` followed by `.` repeats diagnostic navigation +- [ ] `]s` followed by `.` repeats spelling navigation +- [ ] `]q` followed by `.` repeats quickfix navigation +- [ ] `,` reverses last navigation direction +- [ ] Warning shown when no navigation to repeat/reverse - [ ] Works across different buffers -- [ ] Visual feedback is clear -- [ ] Common navigation pairs work (c, d, m, f, q) +- [ ] Multiple reverses work: `. . . , , ,` + +### Navigation Mode Testing +- [ ] Enter mode with `z[` and `z]` +- [ ] Verify visual prompt shows `NAV [ ←:` or `NAV ] →:` +- [ ] Press `c` → navigates to previous/next git hunk +- [ ] Press `d` → navigates to previous/next diagnostic +- [ ] Press `m` → navigates to previous/next method +- [ ] Press `f` → navigates to previous/next function +- [ ] Toggle with `[` → prompt updates to `NAV [ ←:` +- [ ] Toggle with `]` → prompt updates to `NAV ] →:` +- [ ] Exit with `` → prompt clears, back to normal mode +- [ ] Exit with `` → handles gracefully +- [ ] Works across different buffers +- [ ] Invalid keys (e.g., `z`) show error but don't crash +- [ ] Existing keymaps still work after exit + +--- + +## Implementation Priority + +**Recommended order:** + +1. **Phase 1: Repeat/Reverse** (start here) + - Simpler, faster to implement + - Covers most common use cases + - Get immediate value + +2. **Phase 2: Navigation Mode** (optional, evaluate need) + - Implement only if you find yourself wanting mixed navigation + - Can be added anytime without conflicts + - Both features work together + +**Time estimate:** +- Phase 1: 15-30 minutes +- Phase 2: 30-60 minutes +- Total: 45-90 minutes if both implemented + +--- + +## Implementation Steps + +### For Repeat/Reverse (Start Here) + +1. **Create `lua/bracket-repeat.lua`** (~40 lines) + - Implement tracking state + - Implement `setup()`, `repeat_last()`, `reverse_last()` + +2. **Add to `init.lua`** + ```lua + require('bracket-repeat').setup() + ``` + +3. **Add keymaps** (`lua/keymaps.lua`) + ```lua + vim.keymap.set('n', 'z.', function() require('bracket-repeat').repeat_last() end) + vim.keymap.set('n', 'z,', function() require('bracket-repeat').reverse_last() end) + ``` + +4. **Test** with diagnostics, spelling, quickfix + +5. **Document** in README.md + +### For Navigation Mode (Optional Later) + +1. **Create `lua/navigation-mode.lua`** (~50 lines) + - Implement `M.enter(prefix)` with getchar() loop + - Handle ESC, `[`, `]`, and general keys + - Add visual feedback via `nvim_echo` + +2. **Add keymaps** (`lua/keymaps.lua`) + ```lua + vim.keymap.set('n', 'z[', function() + require('navigation-mode').enter('[') + end, { desc = 'Navigation mode (backward)' }) + + vim.keymap.set('n', 'z]', function() + require('navigation-mode').enter(']') + end, { desc = 'Navigation mode (forward)' }) + ``` + +3. **Test basic functionality** + - Enter with `z[` / `z]` + - Navigate with `c`, `d`, `m`, `f`, `q` + - Toggle prefix with `[` / `]` + - Exit with `` + +4. **Polish** (optional) + - Add help text on first use + - Handle `` gracefully (already done with `pcall`) + - Consider adding common navigation cheatsheet + +5. **Document** + - Update `README.md` with new keymaps + - Add navigation pairs reference From 3b8be3598c5b30c98c2e9546e9385bf1567b3a01 Mon Sep 17 00:00:00 2001 From: ray Date: Sun, 18 Jan 2026 19:23:17 +0000 Subject: [PATCH 40/53] do not open floating window with [d and ]d --- lua/keymaps.lua | 12 +++++++++--- spell/en.utf-8.add | 1 + spell/en.utf-8.add.spl | Bin 9650 -> 9652 bytes 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lua/keymaps.lua b/lua/keymaps.lua index 2dbf084..7ff7d78 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -49,9 +49,15 @@ end, { desc = 'Diagnostics: Buffer errors (location list)', silent = true }) map('n', 'xE', function() vim.diagnostic.setqflist({ severity = vim.diagnostic.severity.ERROR }) end, { desc = 'Diagnostics: All errors (quickfix)', silent = true }) -map('n', '[d', vim.diagnostic.goto_prev, { desc = 'Diagnostics: Previous diagnostic', silent = true }) -map('n', ']d', vim.diagnostic.goto_next, { desc = 'Diagnostics: Next diagnostic', silent = true }) -map('n', 'xd', vim.diagnostic.open_float, { desc = 'Diagnostics: Show diagnostic under cursor', silent = true }) +map('n', '[d', function() + vim.diagnostic.goto_prev({ float = false }) +end, { desc = 'Diagnostics: Previous diagnostic', silent = true }) +map('n', ']d', function() + vim.diagnostic.goto_next({ float = false }) +end, { desc = 'Diagnostics: Next diagnostic', silent = true }) +map('n', 'xd', function() + pcall(vim.diagnostic.open_float) +end, { desc = 'Diagnostics: Show diagnostic under cursor', silent = true }) map('n', 'xt', function() vim.diagnostic.enable(not vim.diagnostic.is_enabled()) end, { desc = 'Diagnostics: Toggle display', silent = true }) diff --git a/spell/en.utf-8.add b/spell/en.utf-8.add index 4143d2a..e0c3f1a 100644 --- a/spell/en.utf-8.add +++ b/spell/en.utf-8.add @@ -774,3 +774,4 @@ Trengo Hostinger's hPanel reauthoring +dropdowns diff --git a/spell/en.utf-8.add.spl b/spell/en.utf-8.add.spl index f891df9ecc7eeb4a55c7d2f445c503826ea914a4..73205d8de0092466c4574dc63d7f9bd5beba213b 100644 GIT binary patch delta 1453 zcmXw3Uuaup6u;+7nxwfmY0@-l`ggSx+O=sb8%SxX?x9TT`iD}c7B;t=~_7v~;^9_a=v6)m_?M{!mVeVBdd1RteR@WG0R4|@=8L6~+>&-sEcH{ZSAIluEe zzwfrsS5I5$SO>)g zS!XhpDMN{(YIZPKI|uDs<=Ps6LwwtN3jV@--gkrxuvfhbv$&#mKs$b=cEK9{qV~fk z`n48$t;JvrU(>R19lz7g_&4){;NhD*49Z2+L^HJ&5?`>5Et~Ap=D)g}p$pwd)uQbP~)|;Can9 zK!=1dC~pkk4@996Hv;`c^ryg!k;_Fepuh+d8DSJDQo{EK`-t|%;OoQFxjLQn*hw1m zW+s=n3N|RpMzKWCfpW#2GIIrck#dg9U^wDby+l+qgyLGmzk_GEyPoBbj1#xjJF|!76*Nc1A~?ThOzn~knPKgclw#AsnW!?bD5G3A zYyz>E%CK3K^YVUgI_Pyz&sQkW(^!O*lwJ8zrxYEqrg6IO0)s$=9pb&hc?NaGr782b zoX|`EV}hd2;Bw|Y~K4A}Kzg$456)TKcfoYS;FbF2-StcU78>ex`NoAeB)BSG7 zkw|B$8X22pq?(6(md^M&8gpObm2mA~ps8cXB}pkWaffb{L~emtT2+*Z455>`tDD2+ zDzX*#qY??KcH`BiZg_&&^y*~AtRcX=dNPx>g;5}GmSkEt$$Zppof0LFwX`spdWt^7 zh(VOAehD_2yhY4WoyNiDlbu7B>`gn!@VI8kB{VcbrPA8HtX#IYnn%>Tw4zmkAL66t z3+_+Y5ZZNorsag@E>f`PgA4_)<9JI)=!nZI18K=d&LV_ME!|58)xGiVa;tUYmPk|B za9PSwvu%ldK^Do9%GIUU9wtOGO~gDu%*|Jc!ZmK2Ey!})^jq}}#mwq%;fzzmtBE<< zgRZQ_uB40m%!C}H4r6y~+o?}&A)FP`g#WjA(8>U>PLfW6Rl}L2#tx`+-^-Y5?e+Xq O5;~RI-uk#TSo+H6kb_U=-?`1d1;f)F%CYpMe_;&OdvFd5Oju5_Y z9D^v2#0G{Iib76y%VtiAEWdPITAp^&3RNQzA*7h>p@W1`CATGzI3l+2sPU^Y8y5La z>7o950%;LQ~ZX!zqchcY`ubD5{P6ZF)t^(MiuKraO zXl_^|Ub9!JU~E)FEmZ;6x1hTuz3c|b&9qG3#`Ku%k&fSK^b&-h=$N304kdoKInlal zy0ly#QT;!J-Wu(x0r+ptJ?aJOc<)9UV@fNXxJ+ni3oJKd%+2N$nH z!VD6%1(CpriP^D->oY@%t3r*2AIhk-{5&`J9)u_S#@@-XSp%O5?;SO5<{hRN5v46K zhhHH{7DQ{<(N(+GI)TKI<`T*JEBbn4GY1S&+`*4`#T2e8Yo*P)o}uQ0;`Ep zF}3n}Ud+^7%SrhT4yQn++~*HkUhDsaIz+jwx>9K0XE+VoG|v5dnQYE>gm~18K~D+8 z_&zi-%rYNqZI3-AKvG{$;1q?9@`cueb6fS93T%=H;d3_2q1j+X2;^C}nN<|&^Ef>9 zsaNi6ES*EhzMrSFbqX*~g6QJO^wu2wQj1c7eUoYfb0bn%BAEUIc?3xWd@F Date: Mon, 19 Jan 2026 14:20:41 +0000 Subject: [PATCH 41/53] Update color definitions and spelling options Refactor diagnostic colors for better visibility in editor and plugin highlight groups. Add new spelling options for enhanced functionality. --- lua/paper-tonic-modern/colors.lua | 13 ++++++++++--- lua/paper-tonic-modern/groups/editor.lua | 15 +++++++++------ lua/paper-tonic-modern/groups/plugins.lua | 6 +++--- lua/paper-tonic-modern/groups/syntax.lua | 4 ++-- lua/paper-tonic-modern/groups/treesitter.lua | 8 ++++---- lua/settings.lua | 1 + spell/en.utf-8.add | 3 +++ spell/en.utf-8.add.spl | Bin 9652 -> 9665 bytes 8 files changed, 32 insertions(+), 18 deletions(-) diff --git a/lua/paper-tonic-modern/colors.lua b/lua/paper-tonic-modern/colors.lua index 546180b..4bb99e5 100644 --- a/lua/paper-tonic-modern/colors.lua +++ b/lua/paper-tonic-modern/colors.lua @@ -92,26 +92,33 @@ M.qf_info = {'#5f8faf', 196, 'white'} -- Info (muted blue) M.qf_hint = {'#5fafaf', 196, 'white'} -- Hint (lighter muted blue) -- ============================================================================ --- Alert/Diagnostic Colors (Fluorescent/Neon - intentionally jarring) +-- Diagnostic/Alert Colors (Fluorescent/Neon - intentionally jarring) -- ============================================================================ -- These colors are designed to be NOTICED, not blend in with code -- Fluorescent/neon aesthetic similar to bg_hl_special_alt colors -- Think "highlighter marker" - bright, synthetic, stands out on white paper +-- Used for: LSP diagnostics, spelling errors, UI alerts, error messages, etc. --- LSP Diagnostics - Fluorescent colors for maximum visibility +-- Diagnostic foreground - Fluorescent colors for maximum visibility M.diag_error = {'#ff0066', 197, 'red'} -- Hot pink-red (screams "error!") M.diag_warn = {'#ff6600', 202, 'red'} -- Fluorescent orange (warnings) M.diag_info = {'#00ccff', 45, 'cyan'} -- Bright fluorescent cyan (info - more prominent) M.diag_hint = {'#66e0ff', 81, 'cyan'} -- Softer fluorescent cyan (hint - less prominent) M.diag_hint_dark = {'#0099cc', 38, 'cyan'} -- Darker cyan for UI elements (readable on white) --- LSP Diagnostic backgrounds - Light tinted versions for highlighting code +-- Additional severity level for lighter warnings (spelling, etc.) +M.diag_weak = {'#ff9933', 208, 'yellow'} -- Lighter orange (for SpellLocal, SpellRare, etc.) + +-- Diagnostic backgrounds - Light tinted versions for highlighting code M.bg_diag_error = {'#ffe6f0', 224, 'white'} -- Very light pink (for error backgrounds) M.bg_diag_warn = {'#fff0e6', 223, 'white'} -- Very light orange (for warning backgrounds) M.bg_diag_info = {'#e6f9ff', 195, 'white'} -- Very light cyan (for info backgrounds) M.bg_diag_hint = {'#f0fcff', 195, 'white'} -- Very light cyan (for hint backgrounds) +-- Question/prompt color - Fluorescent cyan for prompts +M.question = {'#00ccff', 45, 'cyan'} -- Bright cyan for questions/prompts (same as diag_info) + -- ============================================================================ -- Primary Accent Colors (brownish-red tones) -- ============================================================================ diff --git a/lua/paper-tonic-modern/groups/editor.lua b/lua/paper-tonic-modern/groups/editor.lua index 5c33094..0555ff5 100644 --- a/lua/paper-tonic-modern/groups/editor.lua +++ b/lua/paper-tonic-modern/groups/editor.lua @@ -94,17 +94,20 @@ return { -- Spelling -- ============================================================================ - SpellBad = { fg = c.alert_strong, bg = c.bg_error_weak }, - SpellCap = { fg = c.alert, bg = c.bg_error_weak }, - SpellLocal = { fg = c.alert_weak, bg = c.bg_error_weak }, - SpellRare = { fg = c.alert_weak, bg = c.bg_error_weak }, + -- Spelling errors: normal text color with bright colored underline + -- sp (special) sets the underline color, separate from text foreground + -- Both underline and undercurl for terminal compatibility + SpellBad = { sp = c.diag_error, underline = true, undercurl = true }, -- Serious error: hot pink-red underline + SpellCap = { sp = c.diag_warn, underline = true, undercurl = true }, -- Capitalization: orange underline + SpellLocal = { sp = c.diag_weak, underline = true, undercurl = true }, -- Wrong region: lighter orange underline + SpellRare = { sp = c.diag_weak, underline = true, undercurl = true }, -- Rare word: lighter orange underline -- ============================================================================ -- Messages & Prompts -- ============================================================================ - ErrorMsg = { fg = c.alert, bold = true }, - WarningMsg = { fg = c.alert, bold = true }, + ErrorMsg = { fg = c.diag_error, bold = true }, + WarningMsg = { fg = c.diag_warn, bold = true }, Question = { fg = c.question, bold = true }, ModeMsg = { fg = c.question }, MoreMsg = { fg = c.question }, diff --git a/lua/paper-tonic-modern/groups/plugins.lua b/lua/paper-tonic-modern/groups/plugins.lua index fd847fe..5cacba6 100644 --- a/lua/paper-tonic-modern/groups/plugins.lua +++ b/lua/paper-tonic-modern/groups/plugins.lua @@ -88,7 +88,7 @@ return { CmpItemKindText = { fg = c.fg }, CmpItemKindFile = { fg = c.fg }, CmpItemKindFolder = { fg = c.fg_strong }, - CmpItemKindColor = { fg = c.alert }, + CmpItemKindColor = { fg = c.diag_warn }, CmpItemKindUnit = { fg = c.fg_weak }, CmpItemKindValue = { fg = c.fg_strong }, CmpItemKindConstant = { fg = c.fg_strong }, @@ -109,7 +109,7 @@ return { OilSize = { fg = c.fg_weak }, OilPermissionNone = { fg = c.fg_weaker }, OilPermissionRead = { fg = c.success }, - OilPermissionWrite = { fg = c.alert }, + OilPermissionWrite = { fg = c.diag_warn }, OilPermissionExecute = { fg = c.modified }, OilCopy = { fg = c.success, bold = true }, OilMove = { fg = c.modified, bold = true }, @@ -117,7 +117,7 @@ return { OilDelete = { fg = c.fail, bold = true }, OilChange = { fg = c.modified, bold = true }, OilRestore = { fg = c.success }, - OilPurge = { fg = c.alert_strong, bold = true }, + OilPurge = { fg = c.diag_error, bold = true }, OilTrash = { fg = c.fail }, OilTrashSourcePath = { fg = c.fg_weak, italic = true }, diff --git a/lua/paper-tonic-modern/groups/syntax.lua b/lua/paper-tonic-modern/groups/syntax.lua index b216386..0e6334a 100644 --- a/lua/paper-tonic-modern/groups/syntax.lua +++ b/lua/paper-tonic-modern/groups/syntax.lua @@ -76,8 +76,8 @@ return { Underlined = { underline = true }, Ignore = { fg = c.fg_weak }, - Error = { fg = c.alert_strong, bold = true }, - Todo = { fg = c.alert_strong }, + Error = { fg = c.diag_error, bold = true }, + Todo = { fg = c.diag_error }, -- ============================================================================ -- Language-Specific: CSS diff --git a/lua/paper-tonic-modern/groups/treesitter.lua b/lua/paper-tonic-modern/groups/treesitter.lua index 9396ed7..1819327 100644 --- a/lua/paper-tonic-modern/groups/treesitter.lua +++ b/lua/paper-tonic-modern/groups/treesitter.lua @@ -101,10 +101,10 @@ return { ['@comment'] = { fg = c.fg_weaker, italic = true, bold = true }, ['@comment.documentation'] = { fg = c.fg_weak, italic = true }, - ['@comment.error'] = { fg = c.alert_strong, bold = true }, - ['@comment.warning'] = { fg = c.alert, bold = true }, - ['@comment.todo'] = { fg = c.alert_strong }, - ['@comment.note'] = { fg = c.alert_weak }, + ['@comment.error'] = { fg = c.diag_error, bold = true }, + ['@comment.warning'] = { fg = c.diag_warn, bold = true }, + ['@comment.todo'] = { fg = c.diag_error }, + ['@comment.note'] = { fg = c.diag_weak }, -- ============================================================================ -- Markup (Markdown, etc.) diff --git a/lua/settings.lua b/lua/settings.lua index 4d6af08..697dd78 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -48,6 +48,7 @@ vim.opt.pumblend = 0 -- No transparency -- Spelling vim.opt.spelllang = { "en_gb" } +vim.opt.spelloptions = { "camel,noplainbuffer" } -- Search behavior vim.opt.ignorecase = true -- Case-insensitive search by default diff --git a/spell/en.utf-8.add b/spell/en.utf-8.add index e0c3f1a..c779ae2 100644 --- a/spell/en.utf-8.add +++ b/spell/en.utf-8.add @@ -775,3 +775,6 @@ Hostinger's hPanel reauthoring dropdowns +contenteditable +#uer/! +suer/! diff --git a/spell/en.utf-8.add.spl b/spell/en.utf-8.add.spl index 73205d8de0092466c4574dc63d7f9bd5beba213b..5382f71c4a45042e016f3337a3b5e8731ef29b5e 100644 GIT binary patch delta 2049 zcmXw4U2GIp6rOu$cDw)G7Is_OVp~CeTH6BtgMv{62^4Jjk(h=sJ2Ty#barRx?83Hb z$qrn&CEJwR>E4~ zIz=eo=b43FgZLG3hmyfOuIk+~w+3;e9TBU;>sYH83iq(P;%0ay0L+DZYR86n-vLxa z6=Q$`uL(W!7@HM`BFovIVlwg?`%c`8yvQ`s5q+AS6`APr_>Vr26<8MWfnG5dZ9(F1 zqf6O@xD)MU7ez9b3LUjnov@0I*pqCFcq^7?$Ha};Q;CZ%kziM;W}sDs_sW(&|Y2M$uBCJ#Y@Sq?$Brv z3z{SH)oa;D;*09F@x?Y)@|A7F;z4z5I+;TZKW#Gyb#3kw&hUBBnXHSA>(Uu&DK&?8JKU;( z1Kt%i9T0X;&2#LkI9;=3Jw!a#A5^-HOvPskKo#h&@S4I5k}^dNS{_c@#j<)keYyET7Ve6Eb{@0T@$Z;NoM zec^2bPgE(k(Wn@`8}tn#9=;=bQf=&r7)~vQJ10_W*^Kx#wRF*YHk0QzYE$~I9Qoaa z?Xcy9>{jfIXiK-HHrlWnLU__7o*4urwh24k+_|Exnpx@_RY#8Bsecipz$~84qnWPM zO}j=pV|&Hd>E_3e`v~=@;2OIq`yIJyt9YEzT{^>|2Js-R$9L1gVQEq`VhF3Qne7rI zbuC-8jOF=iE>E4*aX^Tm@+uJsKeAlBAb;`G^g4b*M}D4^t;iyhQpWzsV0!f9E;udj z)={6U>X#k7lp#Bjn+AFI09K>B9Ed|ERo3haI{AX%%Uo(?hZ!o#CRmP@is`aa5x}4{ZNW#NaI5 zldZ%r_348Tb&xA^eh8SlXabVtvAuM$=0JJdYhofX9eE$pEfB6af1{6+#-rJpx8?myj8JWO{+~uoXACY6j2Z|jXB)(r5t_S zD^?miQvwa9)VxCN2HE=#Ii&$36n`@)ipALHc^Yhfh&}{jq@j7yQ9`V2qr*dPEddR6 z41n=QnqW#?Z`ir&Qz_svUCki8%l4~`KI9n5=K8(=fM8cVQbdu^DF;Q)i>-|}0JS*jkG_P9GB;n1M148GWu z!Oea!4BhI>RT#5Fq%M8*fPdDPA3l0UOs`(WH6Ul>1UuZ82WTP``ihXDWzn^wt!d%v z&&UJ#u%cK_!c)5Fkedieeqw2u&9VDXFy4(X2XFO}oFtPo@^Di_=BuDYi;!EhLHPV4|8uHoPJ-p>~=RuZI@XZE-2|G+h(_gq|Q0tHP`3l(55# zqd$9OQ=ZbCXGpvs?nL0}@FJQLv*Cx^FXUBHl)+M&3>66r z!=xghq;LeYJwmJAK%2$p>Ykp-9EWgyCN8~B5eAOx!RhZ3l~6DrAnj=`LLU;RtG5Kk zU4%F!{;Xbs`nzIv_+1@aT7NWyLQ*AZuTPX<@3%!Mww{iO%dz#*E*o!h_#$>$+>dop zRCLDc7o6quB8W05$(h0qd2DmwCEc*nnSxBaEB6`f)Ccl019e>& z-`8|>Y7fT9@+G}}5Q$9=oC8xSauLYFz3;dP)ppn5-X;?k4uIkGGqI_5vTdj+@hiO< z^fX84E?Cb=Sn&I`b_Lam#=6J)CW=yk>U*y0F$G|B2zaX(>1 zpW{iP1alYKE1bp-8We9fcB;Ol_@uFE%YRk@jp5+xy9_^i*pU3D(N6Y^{FOu_v$zXP z+h|`7C@YPon<-x|?}0Zdh5DxMx{FCGV><=6=#|wp^B*1*JDL*VWqDXhaxMtBseOFH z@+sDc9|fsqI>&lK_*<;1VI*adhVHVecClaFp1lvZ}@9 z@cN)9Evz70?zx&~n`e=6hX+bX1WJyI9WC?5*H&1G+;O<2*lpZcLF>|kZHzD59u`vA;1k^Q}zk`l7`SQ#h%F(T9vG)eq!!l3@oIbPmoK)~1pfz) C3cG0l From 5c7a63ab8d6c7c4f1d0df74bd803e7363b46ccf7 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 19 Jan 2026 15:03:16 +0000 Subject: [PATCH 42/53] enabled coloured underlines for spelling diagnostics --- lua/paper-tonic-modern/groups/treesitter.lua | 4 ++-- lua/settings.lua | 3 +++ spell/en.utf-8.add | 4 ++++ spell/en.utf-8.add.spl | Bin 9665 -> 9679 bytes 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lua/paper-tonic-modern/groups/treesitter.lua b/lua/paper-tonic-modern/groups/treesitter.lua index 1819327..8b2fa64 100644 --- a/lua/paper-tonic-modern/groups/treesitter.lua +++ b/lua/paper-tonic-modern/groups/treesitter.lua @@ -292,6 +292,6 @@ return { ['@none'] = {}, ['@conceal'] = { fg = c.fg_weaker }, - ['@spell'] = {}, - ['@nospell'] = {}, + -- @spell and @nospell should not define any styling + -- This allows vim.wo.spell to control spell highlighting via SpellBad/SpellCap/etc } diff --git a/lua/settings.lua b/lua/settings.lua index 697dd78..ed06b65 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -60,6 +60,9 @@ vim.opt.hlsearch = true -- Highlight all search matches vim.opt.iskeyword:append("$") vim.opt.iskeyword:append("-") +-- Terminal and color support +vim.opt.termguicolors = true -- Enable 24-bit RGB color (required for colored underlines) + -- Visuals vim.opt.showbreak = " ↳" vim.opt.listchars = { diff --git a/spell/en.utf-8.add b/spell/en.utf-8.add index c779ae2..47446ee 100644 --- a/spell/en.utf-8.add +++ b/spell/en.utf-8.add @@ -778,3 +778,7 @@ dropdowns contenteditable #uer/! suer/! +RDV +param +int +bool diff --git a/spell/en.utf-8.add.spl b/spell/en.utf-8.add.spl index 5382f71c4a45042e016f3337a3b5e8731ef29b5e..3890753e6db0a33f3b25122b2a97f364ea446d79 100644 GIT binary patch delta 2802 zcmXw5Yit}>8J&A)XWzSiG}-lQ<2WQSB@G+fi9<1w8WBXBDr-XIP=SavJ2T!L@667y zGwVmH)PbPJKSERKMlyz`buCbkBSF)E5TKS-*@Nd&%XO?{`4~^0^f3+(Je*k{`QUhfXcZd_Nm|KJ21;^7BY(~Oi{`K z!{^2{$!fIni{iD+PPR+@EHlj3#J$W%cf2VBo(dP3&hZyx(^#i2SrKb-F#7~M zA@uA3CG&;sXV}-oyV@O5!AKQLA@p^CcX(mB3r;H&Jku*2a2#{WZldk8}z*eVH;JR?@~2iU6keZCmK z;3M*7@m_vt?|NiI1g?Z+5N)$5QQ7bmB9+LIko!=_zHKXYC}xH-XmW{+8*@e2v3G2x z!Det=TjEBfj3uGQzDeGimi{6O*w~?5l9nD7zwOw)xW`5;H#+E?PRM!XEEb{?DCcFJ zvPiik0;z72iZ9#Dp;$a1y;9$y5G^dxQ9f9WWOVM#4bK6-I;)!2&-bj z`i$esNwyIn5YdgVk!=H+Wwleh*Ev=&BtOjYio>l)O$dCrYd>sfbsaAMgis*dZ67>o z8OjWgrzoEe;1ge_WKK6bbR6)G_)XW&L3JJws*t3OV;xM#*Gx2%h%H(|x_fXRASTb~ zu#rjgnuV1H)<3GNAGPF@JaI})c8{_9;zIYa(wSD&C(3n{ii-}RV}C>wasS>u%8H`+ z*vF17wPdH@D?Se>Nc?+&YPj@ES*J#mE=IJpyOA0!<^diBC&+TgrVx+$>@^YFc3|`k zQmKl0kIi8>`(%*NO{-3|XM62fvxV}dwg(}GNiA(sSlf1M_@$f@OWU5D{Imz8q@e6h z8+9_&6&sel*yiXZk?X092?Pns?|ZTVDeH3>V@hIc9?!Ef7x6{WxqaW$cMUud)a-FX zuH_Y|Zjj!ozj?B+h0v2uM!b8b{7v9zd6R(96qbHfT-d%lcX@Vh0VA@e$Hn)z@0=JX z&U7abI0})zVR-+1#gE!RYuj3%TJ(ES60Lx?7q<6UP1Hus1qv!@>cw=+WDW6FVQ|-F za<)%mPS~N;E&V&2%zs<==xRvzi~kh{SxM|D9?gB8=&}OUZQ2q}agaSHE){p+61!d; zSlqHaScy*H%Oqj$>*Q?S3Gyved?Z%(-&K&L3oL%i21*x9BFC~nnX20iqAT<20=3O~ zETbe-ve^+74QdAhk;LqXo6({D52{w(c09ioq!xIMtd)6AO!gEO4>b`=z! zG0CUMyS!o`OjLJCToj7cjzO2WQFE!rfbFl8xM}PnC#Bz~+M?$B=QHQSbcE!#y4??Qzo^$sESy1+6!c6SbSbaRR60(sF3sXAc*&gsB2^m@;^GjVDAv_ zzoWf9!=EKXp{+z+n;2UA4vZG_n1W`jEP?df`?5<4HgB9Z~)n^g2v zZ{#*~@dS?nIeUd*r=D;Dng}It5Hcv2c}g4}D1H2T0Ojybhr{YJhf3MQ6ZQYjbt{Iu lE&C9%=T72vfDZ)-PHIAktKy}BzT}paJAUit8v|Xb{{wYfSY!YI delta 2664 zcmYLLYit}>6`pfv-w(fHukH25j=@Q&li)bPw2*{HNhwjYO{-WDf*?9OGv1x<%+7Xa z{a7oa52;B7QKaS;V?}Oa^N1Al2q6-Js;UzFqsV{)5(-58A^j6Y5mE)Q1PTdQ2A%Af}RZK}Zv(-SV0Flta8BmHH0A$lNvr-e12R~O`+=%49?ycRo1 zi!vJbH#?p$N*)>557+yutt|hjQ zx0T^q;d-M9sJu~(`_!VEAT1^gJR)AiD!X$ujkV~zav-@mb#G4CEes>C$j6ggKYgen zYzD!*wQ7X|QJ=;T=EDudx@-fw_SCnUMOKQx*=anJwpGGKTAD9 zzmoqmond0Z~1x6rV>p59I?@^1R$gIAQpqs0)G*j zf}MWK)(xgZPGoit9(KZ%S_%?fwgBHy{wUWV`BLT?IxYW@*_*rKGl}#TCono}hJoFY z`RuN(^F|a)+EL4P>QSr4z8gGlBJ|1m(DIOUv*WZN&u53}l)RQ5`dG1vV@#^RUu53E zq0E79*}^cPn;M8^=3^xpZ~NR=5BZ`B`C&p_vV@JFE(Zf`l{FVYWwu(>3l=EUPkDTN zT{rPF<~HES6#?e|#mVvEojubsvz9n6zuUHZ@B*;=uzsyE&Ci)u)GRx!i#i~jk?*$+ zZ@U!A8%GzLEWGGOO~H4RF7yH`9oc%ceMk3V1%z8nBn~*q1rc%OO#3#7{mu5?fpe_T zDWy`@iwfklDvWV?qkW`2;$Wv2x;sp=#}Hd#;RY(Bdb@nTV{fjcKr_CgE9{UEaP+CpU36HM zJ0I#o|MPB_R?MX&8~xVT;Av!pe1>)YY*k*;HJk$Pc~Fz5Q}vY zuzBn!fo^*Irmb>cN%e$KqqpQsoBC-(p4(KX^D^DNd;G^7WeF_$Gz&}mLJ%|T3YUrF zPe*DE)E_rA($ZTw*yz*J>mHHc>3)p9D1YDG_sNsf0yQ7XiwleUCbtH0*H=}kte7|$riV&9O1Jv+X1%fyI5W}Y;attf&llW9uc_ErCBf(u{m5qB*0&JbrrtqE#) z38t^e*Ly}Pr)Oqcmn& zJL(5$Xr6KvDBX#>s0h3<()hRJ=3G8;HW26!ne+0qxzW;-C_dJ<=&;yX31)3a98qTl z^I22W6lOgPaKTcO$=!3hRb~)O0Z#oJQxc=12B z55@6SA{LnK>{+fi(E&US)MdzP=Iil;k%hvfzgW6gv_3MqmZHY2#Eq%;YorFVrA?A9udcc0#$7*n` zRB(m6xlf`C(_)g9EQ%)lGFi9g4>MiJ(cfGuk!Z*Yaw%8zL(#J{0|QF@A(01E_2$@OQ3$t3F77y_PtuJROE=p%$%SS)pmaUCd6+k77oQ<2giN+DKt)GxT8tB`9|HGPMmfDZh zMU^u=r<5OEs|%m|nh!yR-$@M46jl{lPWHk-(h{Rgj(Whz z_!E~I2x$2X8#W%MJp2sfMii Date: Mon, 19 Jan 2026 15:05:37 +0000 Subject: [PATCH 43/53] use straight uinderlines instead --- lua/paper-tonic-modern/groups/editor.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lua/paper-tonic-modern/groups/editor.lua b/lua/paper-tonic-modern/groups/editor.lua index 0555ff5..9afc2bc 100644 --- a/lua/paper-tonic-modern/groups/editor.lua +++ b/lua/paper-tonic-modern/groups/editor.lua @@ -94,13 +94,12 @@ return { -- Spelling -- ============================================================================ - -- Spelling errors: normal text color with bright colored underline + -- Spelling errors: normal text color with bright colored straight underline -- sp (special) sets the underline color, separate from text foreground - -- Both underline and undercurl for terminal compatibility - SpellBad = { sp = c.diag_error, underline = true, undercurl = true }, -- Serious error: hot pink-red underline - SpellCap = { sp = c.diag_warn, underline = true, undercurl = true }, -- Capitalization: orange underline - SpellLocal = { sp = c.diag_weak, underline = true, undercurl = true }, -- Wrong region: lighter orange underline - SpellRare = { sp = c.diag_weak, underline = true, undercurl = true }, -- Rare word: lighter orange underline + SpellBad = { sp = c.diag_error, underline = true }, -- Serious error: hot pink-red underline + SpellCap = { sp = c.diag_warn, underline = true }, -- Capitalization: orange underline + SpellLocal = { sp = c.diag_weak, underline = true }, -- Wrong region: lighter orange underline + SpellRare = { sp = c.diag_weak, underline = true }, -- Rare word: lighter orange underline -- ============================================================================ -- Messages & Prompts From 75b8b88a047dc43eaafab86d6cdcc7815841963a Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 19 Jan 2026 15:29:19 +0000 Subject: [PATCH 44/53] Enable spell checking in settings This change activates spell checking and sets the language to British English. --- lua/plugins/indent-blankline.lua | 6 +++--- lua/settings.lua | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lua/plugins/indent-blankline.lua b/lua/plugins/indent-blankline.lua index 6bfe898..89e9058 100644 --- a/lua/plugins/indent-blankline.lua +++ b/lua/plugins/indent-blankline.lua @@ -9,9 +9,9 @@ return { tab_char = '│', }, scope = { - enabled = true, -- Highlight current scope - show_start = true, -- Show underline at start of scope - show_end = false, -- Don't show underline at end (can be noisy) + enabled = true, -- Highlight current scope indent guide + show_start = true, -- Disable underline at start of scope + show_end = false, -- Don't show underline at end }, exclude = { filetypes = { diff --git a/lua/settings.lua b/lua/settings.lua index ed06b65..84cc889 100644 --- a/lua/settings.lua +++ b/lua/settings.lua @@ -47,6 +47,7 @@ vim.opt.pumblend = 0 -- No transparency -- The Pmenu* highlight groups control its appearance -- Spelling +vim.opt.spell = true vim.opt.spelllang = { "en_gb" } vim.opt.spelloptions = { "camel,noplainbuffer" } From b52a6d4065e955115b83356914704092181fb175 Mon Sep 17 00:00:00 2001 From: ray Date: Mon, 19 Jan 2026 17:27:05 +0000 Subject: [PATCH 45/53] add colorscheme dark mode --- colors/paper-tonic-modern.lua | 8 +- lua/paper-tonic-modern/colors.lua | 300 +++++++++++++++++++----------- lua/paper-tonic-modern/init.lua | 28 ++- spell/en.utf-8.add | 1 + 4 files changed, 219 insertions(+), 118 deletions(-) diff --git a/colors/paper-tonic-modern.lua b/colors/paper-tonic-modern.lua index 5865390..d6179e5 100644 --- a/colors/paper-tonic-modern.lua +++ b/colors/paper-tonic-modern.lua @@ -1,5 +1,5 @@ -- Paper Tonic Modern --- A light, paper-like colorscheme for Neovim +-- A paper-like colorscheme for Neovim supporting both light and dark modes -- Forked from the original Paper Tonic with modern Lua implementation -- Reset highlights and syntax @@ -11,8 +11,10 @@ end -- Set colorscheme name vim.g.colors_name = 'paper-tonic-modern' --- Set background to light -vim.o.background = 'light' +-- Set default background if not already set +if vim.o.background == '' then + vim.o.background = 'light' +end -- Load the colorscheme require('paper-tonic-modern').load() diff --git a/lua/paper-tonic-modern/colors.lua b/lua/paper-tonic-modern/colors.lua index 4bb99e5..e0196ed 100644 --- a/lua/paper-tonic-modern/colors.lua +++ b/lua/paper-tonic-modern/colors.lua @@ -1,6 +1,6 @@ -- Paper Tonic Modern - Color Palette -- Extracted from original Paper Tonic colorscheme --- Light, paper-like theme with subtle colors +-- Supports both light (paper-like) and dark modes local M = {} @@ -9,115 +9,177 @@ local M = {} -- 256: Integer 0-255 for 256-color terminals -- ansi: ANSI color name for basic 16-color terminals +-- Detect current background mode +local is_dark = vim.o.background == 'dark' + -- ============================================================================ -- Background Colors -- ============================================================================ --- Main background: pure white paper -M.bg = {'#ffffff', 255, 'white'} - --- Darkest background: used for very strong contrast elements -M.bg_darkest = {'#505050', 244, 'gray'} - --- UI background: slightly off-white for UI elements (statusline, etc.) -M.bg_ui = {'#efefef', 0, 'darkgray'} +if is_dark then + -- Dark mode: soft dark backgrounds + M.bg = {'#1a1a1a', 234, 'black'} + M.bg_darkest = {'#ffffff', 255, 'white'} -- Inverted: lightest for strong contrast + M.bg_ui = {'#2a2a2a', 236, 'darkgray'} +else + -- Light mode: pure white paper + M.bg = {'#ffffff', 255, 'white'} + M.bg_darkest = {'#505050', 244, 'gray'} + M.bg_ui = {'#efefef', 0, 'darkgray'} +end -- ============================================================================ -- Highlight Backgrounds (general) -- ============================================================================ --- Selection/highlight backgrounds (neutral gray tones) -M.bg_hl_strong = {"#dddddd", 17, "white"} -M.bg_hl = {"#eeeeee", 250, "white"} -M.bg_hl_weak = {"#f7f2f2", 250, "white"} - --- Special highlight backgrounds (cyan/blue tones - for LSP references, search, etc.) -M.bg_hl_special_strong = {"#a3e0ff", 17, "cyan"} -M.bg_hl_special = {"#d4f0ff", 250, "cyan"} -M.bg_hl_special_weak = {"#e0eaff", 250, "cyan"} - --- Alternative special highlight (green tones - for diff additions, etc.) -M.bg_hl_special_alt_strong = {"#74f283", 17, "cyan"} -M.bg_hl_special_alt = {"#bff2cd", 250, "cyan"} +if is_dark then + -- Dark mode: lighter backgrounds for highlights + M.bg_hl_strong = {"#3a3a3a", 237, "darkgray"} + M.bg_hl = {"#2a2a2a", 236, "darkgray"} + M.bg_hl_weak = {"#252525", 235, "darkgray"} + + -- Special highlight backgrounds (cyan/blue tones) + M.bg_hl_special_strong = {"#1a4a5a", 24, "darkblue"} + M.bg_hl_special = {"#0a3a4a", 23, "darkblue"} + M.bg_hl_special_weak = {"#1a3545", 23, "darkblue"} + + -- Alternative special highlight (green tones) + M.bg_hl_special_alt_strong = {"#1a5a2a", 28, "darkgreen"} + M.bg_hl_special_alt = {"#0a4a1a", 22, "darkgreen"} +else + -- Light mode: neutral gray tones + M.bg_hl_strong = {"#dddddd", 17, "white"} + M.bg_hl = {"#eeeeee", 250, "white"} + M.bg_hl_weak = {"#f7f2f2", 250, "white"} + + -- Special highlight backgrounds (cyan/blue tones) + M.bg_hl_special_strong = {"#a3e0ff", 17, "cyan"} + M.bg_hl_special = {"#d4f0ff", 250, "cyan"} + M.bg_hl_special_weak = {"#e0eaff", 250, "cyan"} + + -- Alternative special highlight (green tones) + M.bg_hl_special_alt_strong = {"#74f283", 17, "cyan"} + M.bg_hl_special_alt = {"#bff2cd", 250, "cyan"} +end -- ============================================================================ -- Status Backgrounds (error, success, modified, fail) -- ============================================================================ --- Error backgrounds (red/pink tones) -M.bg_error = {'#ffd7d7', 196, 'white'} -M.bg_error_weak = {'#ffefef', 196, 'white'} - --- Success background (green tone) -M.bg_success = {'#e0ece0', 196, 'white'} - --- Modified background (blue tone) -M.bg_modified = {'#e0e0ec', 196, 'white'} - --- Fail/warning background (red tone) -M.bg_fail = {'#ece0e0', 196, 'white'} +if is_dark then + -- Dark mode: darker tinted backgrounds + M.bg_error = {'#4a2020', 52, 'darkred'} + M.bg_error_weak = {'#3a1a1a', 52, 'darkred'} + M.bg_success = {'#1a3a1a', 22, 'darkgreen'} + M.bg_modified = {'#1a1a3a', 17, 'darkblue'} + M.bg_fail = {'#3a1a1a', 52, 'darkred'} +else + -- Light mode: light tinted backgrounds + M.bg_error = {'#ffd7d7', 196, 'white'} + M.bg_error_weak = {'#ffefef', 196, 'white'} + M.bg_success = {'#e0ece0', 196, 'white'} + M.bg_modified = {'#e0e0ec', 196, 'white'} + M.bg_fail = {'#ece0e0', 196, 'white'} +end -- ============================================================================ -- Foreground Colors (text) -- ============================================================================ --- Main text colors (gray scale, strongest to weakest) -M.fg_stronger = {'#444444', 236, 'darkgrey'} -- Darkest text -M.fg_strong = {'#666666', 236, 'darkgrey'} -- Dark text -M.fg = {'#8c8c8c', 244, 'gray'} -- Normal text (default) -M.fg_weak = {'#9d9d9d', 251, 'gray'} -- Light text (comments, less important) -M.fg_weaker = {'#bbbbbb', 251, 'gray'} -- Lightest text (very subtle) - --- Exception foreground (reddish-brown for errors/exceptions) -M.fg_exception = {'#7c4444', 251, 'gray'} +if is_dark then + -- Dark mode: light text (inverted from light mode) + M.fg_stronger = {'#e0e0e0', 253, 'white'} -- Lightest text + M.fg_strong = {'#c0c0c0', 250, 'white'} -- Light text + M.fg = {'#a0a0a0', 248, 'gray'} -- Normal text (default) + M.fg_weak = {'#808080', 244, 'gray'} -- Dimmer text (comments, less important) + M.fg_weaker = {'#606060', 241, 'darkgray'} -- Dimmest text (very subtle) + M.fg_exception = {'#d08080', 174, 'red'} -- Light reddish for errors/exceptions +else + -- Light mode: dark text on paper + M.fg_stronger = {'#444444', 236, 'darkgrey'} -- Darkest text + M.fg_strong = {'#666666', 236, 'darkgrey'} -- Dark text + M.fg = {'#8c8c8c', 244, 'gray'} -- Normal text (default) + M.fg_weak = {'#9d9d9d', 251, 'gray'} -- Light text (comments, less important) + M.fg_weaker = {'#bbbbbb', 251, 'gray'} -- Lightest text (very subtle) + M.fg_exception = {'#7c4444', 251, 'gray'} -- Reddish-brown for errors/exceptions +end -- ============================================================================ -- Status Foreground Colors (Git/Diff - muted natural tones) -- ============================================================================ --- Git/diff status indicators (used in statusline, signs, etc.) --- These are muted, natural colors for git changes -M.success = {'#89af89', 196, 'white'} -- Success/addition (muted green) -M.modified = {'#8989af', 196, 'white'} -- Modified/change (muted blue) -M.fail = {'#af8989', 196, 'white'} -- Fail/deletion (muted red) +if is_dark then + -- Dark mode: lighter muted tones + M.success = {'#a0d0a0', 114, 'green'} -- Success/addition (muted green) + M.modified = {'#a0a0d0', 110, 'blue'} -- Modified/change (muted blue) + M.fail = {'#d0a0a0', 174, 'red'} -- Fail/deletion (muted red) +else + -- Light mode: natural muted colors + M.success = {'#89af89', 196, 'white'} -- Success/addition (muted green) + M.modified = {'#8989af', 196, 'white'} -- Modified/change (muted blue) + M.fail = {'#af8989', 196, 'white'} -- Fail/deletion (muted red) +end -- ============================================================================ -- Quickfix/Location List Diagnostic Colors (Muted tones like git/diff) -- ============================================================================ --- These are for quickfix/location list only - similar muted tone to git/diff -M.qf_error = {'#af3f3f', 196, 'white'} -- Error (darker muted red for more contrast) -M.qf_warn = {'#af7f5f', 196, 'white'} -- Warning (muted orange) -M.qf_info = {'#5f8faf', 196, 'white'} -- Info (muted blue) -M.qf_hint = {'#5fafaf', 196, 'white'} -- Hint (lighter muted blue) +if is_dark then + -- Dark mode: lighter muted tones + M.qf_error = {'#d07070', 167, 'red'} -- Error (lighter muted red) + M.qf_warn = {'#d0a080', 173, 'yellow'} -- Warning (muted orange) + M.qf_info = {'#80b0d0', 110, 'blue'} -- Info (muted blue) + M.qf_hint = {'#80d0d0', 116, 'cyan'} -- Hint (lighter muted cyan) +else + -- Light mode: darker muted tones + M.qf_error = {'#af3f3f', 196, 'white'} -- Error (darker muted red for more contrast) + M.qf_warn = {'#af7f5f', 196, 'white'} -- Warning (muted orange) + M.qf_info = {'#5f8faf', 196, 'white'} -- Info (muted blue) + M.qf_hint = {'#5fafaf', 196, 'white'} -- Hint (lighter muted blue) +end -- ============================================================================ -- Diagnostic/Alert Colors (Fluorescent/Neon - intentionally jarring) -- ============================================================================ -- These colors are designed to be NOTICED, not blend in with code --- Fluorescent/neon aesthetic similar to bg_hl_special_alt colors --- Think "highlighter marker" - bright, synthetic, stands out on white paper --- Used for: LSP diagnostics, spelling errors, UI alerts, error messages, etc. +-- Fluorescent/neon aesthetic +-- In light mode: bright, synthetic, stands out on white paper ("highlighter marker") +-- In dark mode: slightly toned down but still prominent --- Diagnostic foreground - Fluorescent colors for maximum visibility -M.diag_error = {'#ff0066', 197, 'red'} -- Hot pink-red (screams "error!") -M.diag_warn = {'#ff6600', 202, 'red'} -- Fluorescent orange (warnings) -M.diag_info = {'#00ccff', 45, 'cyan'} -- Bright fluorescent cyan (info - more prominent) -M.diag_hint = {'#66e0ff', 81, 'cyan'} -- Softer fluorescent cyan (hint - less prominent) -M.diag_hint_dark = {'#0099cc', 38, 'cyan'} -- Darker cyan for UI elements (readable on white) - --- Additional severity level for lighter warnings (spelling, etc.) -M.diag_weak = {'#ff9933', 208, 'yellow'} -- Lighter orange (for SpellLocal, SpellRare, etc.) - --- Diagnostic backgrounds - Light tinted versions for highlighting code -M.bg_diag_error = {'#ffe6f0', 224, 'white'} -- Very light pink (for error backgrounds) -M.bg_diag_warn = {'#fff0e6', 223, 'white'} -- Very light orange (for warning backgrounds) -M.bg_diag_info = {'#e6f9ff', 195, 'white'} -- Very light cyan (for info backgrounds) -M.bg_diag_hint = {'#f0fcff', 195, 'white'} -- Very light cyan (for hint backgrounds) - --- Question/prompt color - Fluorescent cyan for prompts -M.question = {'#00ccff', 45, 'cyan'} -- Bright cyan for questions/prompts (same as diag_info) +if is_dark then + -- Dark mode: still bright but not as harsh + M.diag_error = {'#ff4488', 204, 'red'} -- Bright pink-red + M.diag_warn = {'#ff8833', 208, 'yellow'} -- Bright orange + M.diag_info = {'#44ccff', 81, 'cyan'} -- Bright cyan + M.diag_hint = {'#88ddff', 117, 'cyan'} -- Softer cyan + M.diag_hint_dark = {'#44aacc', 74, 'cyan'} -- Medium cyan + M.diag_weak = {'#ffaa66', 215, 'yellow'} -- Lighter orange + + -- Diagnostic backgrounds + M.bg_diag_error = {'#3a1a25', 52, 'darkred'} + M.bg_diag_warn = {'#3a2a1a', 58, 'brown'} + M.bg_diag_info = {'#1a2a3a', 17, 'darkblue'} + M.bg_diag_hint = {'#1a2f3a', 23, 'darkblue'} + + M.question = {'#44ccff', 81, 'cyan'} +else + -- Light mode: fluorescent colors for maximum visibility + M.diag_error = {'#ff0066', 197, 'red'} -- Hot pink-red (screams "error!") + M.diag_warn = {'#ff6600', 202, 'red'} -- Fluorescent orange (warnings) + M.diag_info = {'#00ccff', 45, 'cyan'} -- Bright fluorescent cyan (info) + M.diag_hint = {'#66e0ff', 81, 'cyan'} -- Softer fluorescent cyan (hint) + M.diag_hint_dark = {'#0099cc', 38, 'cyan'} -- Darker cyan for UI elements + M.diag_weak = {'#ff9933', 208, 'yellow'} -- Lighter orange (spelling, etc.) + + -- Diagnostic backgrounds + M.bg_diag_error = {'#ffe6f0', 224, 'white'} + M.bg_diag_warn = {'#fff0e6', 223, 'white'} + M.bg_diag_info = {'#e6f9ff', 195, 'white'} + M.bg_diag_hint = {'#f0fcff', 195, 'white'} + + M.question = {'#00ccff', 45, 'cyan'} +end -- ============================================================================ -- Primary Accent Colors (brownish-red tones) @@ -125,15 +187,19 @@ M.question = {'#00ccff', 45, 'cyan'} -- Bright cyan for questions/promp -- Primary accent: For "base" languages -- Used for: PHP, JavaScript, Python, etc. --- Languages using primary: --- - PHP: always primary (whether standalone or in tags within HTML) --- - JavaScript: always primary (standalone or embedded) --- - Python: always primary --- Note: Both PHP and JS use primary; acceptable since embedded JS in PHP is discouraged -M.primary_stronger = {"#7f4b4b", 236, "black"} -- Darkest accent -M.primary_strong = {"#5a4444", 236, "black"} -- Dark accent -M.primary = {"#6b5555", 244, "gray"} -- Normal accent -M.primary_weak = {"#7c6666", 248, "darkgray"} -- Light accent +if is_dark then + -- Dark mode: lighter brownish-red tones + M.primary_stronger = {"#d0a0a0", 181, "white"} + M.primary_strong = {"#c09090", 181, "white"} + M.primary = {"#b08080", 138, "gray"} + M.primary_weak = {"#a07070", 131, "darkgray"} +else + -- Light mode: darker brownish-red tones + M.primary_stronger = {"#7f4b4b", 236, "black"} + M.primary_strong = {"#5a4444", 236, "black"} + M.primary = {"#6b5555", 244, "gray"} + M.primary_weak = {"#7c6666", 248, "darkgray"} +end -- ============================================================================ -- Language-Specific Color Palettes (for mixed-language contexts) @@ -148,31 +214,49 @@ M.primary_weak = {"#7c6666", 248, "darkgray"} -- Light accent -- - HTML tags: c3 (blue) - everywhere, whether in .html or embedded in PHP -- - CSS rules: c2 (green) - everywhere, whether in .css or