Compare commits

..

No commits in common. "master" and "modern-restart" have entirely different histories.

41 changed files with 466 additions and 1804 deletions

View File

@ -43,7 +43,7 @@ This repository is being migrated to a modern, minimal Neovim setup driven by Lu
├── oil.lua ├── oil.lua
├── ufo.lua ├── ufo.lua
├── gitsigns.lua ├── gitsigns.lua
├── conform.lua ├── none-ls.lua
├── nvim-lint.lua ├── nvim-lint.lua
├── mason.lua ├── mason.lua
├── mason-lspconfig.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`. - UX/Editing: `Comment.nvim`, `nvim-surround`, `nvim-autopairs`, `indent-blankline.nvim`, `nvim-ufo`, `undotree`.
- Git: `gitsigns.nvim`. - Git: `gitsigns.nvim`.
- Copilot: `copilot.lua`, `copilot-cmp`. - Copilot: `copilot.lua`, `copilot-cmp`.
- Formatting/Linting: `conform.nvim`, `nvim-lint`. - Formatting/Linting: `none-ls.nvim`, `nvim-lint`.
- LSP Management: `mason.nvim`, `mason-lspconfig.nvim`, `mason-tool-installer.nvim`. - LSP Management: `mason.nvim`, `mason-lspconfig.nvim`, `mason-tool-installer.nvim`.
## Workflow Requirements to Preserve ## 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 6: UX/Editing (Comment, surround, autopairs, indent guides, UFO, undotree)
- ✅ Phase 7: Git integration (Gitsigns) - ✅ Phase 7: Git integration (Gitsigns)
- ✅ Phase 8: Copilot integration (copilot.lua + copilot-cmp) - ✅ Phase 8: Copilot integration (copilot.lua + copilot-cmp)
- ✅ Phase 9: Formatting & Linting (conform.nvim, nvim-lint, Mason tool installer) - ✅ Phase 9: Formatting & Linting (none-ls, nvim-lint, Mason tool installer)
- ✅ Phase 10: Migrate kept behaviors (abbreviations, templates, custom Treesitter queries) - ✅ Phase 10: Migrate kept behaviors (abbreviations, templates, custom Treesitter queries)
- ⏸️ Phase 11: Cleanup & validation (pending) - ⏸️ Phase 11: Cleanup & validation (pending)
@ -201,7 +201,6 @@ 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 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 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 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. - 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). - 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. - Record decisions and rationale in `LOG.md:1` as they're made.

View File

@ -1,72 +0,0 @@
# 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 (conform.nvim):**
- ✅ prettier (JS/TS/CSS/JSON/HTML/Markdown)
- ✅ stylua (Lua)
- ✅ phpcbf (PHP) with WordPress standard
**Linting (nvim-lint):**
- phpcs, eslint_d, markdownlint
**LSP (lspconfig):**
- Language features (autocomplete, goto-def, hover, etc.)
## Migration Checklist
### Phase 1: Setup conform.nvim
- [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 (`<leader>lf`, `<leader>lt`)
### Phase 2: Remove none-ls
- [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
- [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 (`<leader>lt`)
- [x] Test manual format (`<leader>lf`)
- [x] Test project-local formatter detection
- [x] Verify phpcs.xml is respected when present
### Phase 4: Documentation
- [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
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

15
LOG.md
View File

@ -9,9 +9,6 @@ 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. 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 `<leader>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 `<leader>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: PHP LSP = `intelephense` (good PHP ecosystem support; integrates includePaths).
- 2025-12-06: Enable Markdown LSP = `marksman` (lightweight, good MD features). - 2025-12-06: Enable Markdown LSP = `marksman` (lightweight, good MD features).
- 2025-12-06: Use legacy `listchars` and `showbreak` values (preserve muscle memory). - 2025-12-06: Use legacy `listchars` and `showbreak` values (preserve muscle memory).
@ -29,7 +26,6 @@ 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) - 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 - 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 - 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: - 2025-12-07: Treesitter Phase 5 decisions:
- Focus on core languages: PHP, HTML, JavaScript, TypeScript, CSS, Markdown, Lua, Bash, JSON - Focus on core languages: PHP, HTML, JavaScript, TypeScript, CSS, Markdown, Lua, Bash, JSON
- Enable syntax highlighting with performance safeguard (disable for files >100KB) - Enable syntax highlighting with performance safeguard (disable for files >100KB)
@ -73,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 - **WordPress**: phpcs/phpcbf already installed globally; use phpcs.xml or --standard=WordPress
- **Format-on-save**: Enabled by default, toggle with `<leader>lt` - **Format-on-save**: Enabled by default, toggle with `<leader>lt`
- **Manual format**: `<leader>lf` (buffer), `<leader>lf` (visual range) - **Manual format**: `<leader>lf` (buffer), `<leader>lf` (visual range)
- **Linting split**: conform.nvim for formatting, nvim-lint for diagnostics (replaced none-ls after discovering phpcbf preprocessing bug) - **Linting split**: none-ls for formatting only, nvim-lint for diagnostics (none-ls removed linters)
- **Python support**: pyright LSP, black formatter, ruff linter, treesitter parser - **Python support**: pyright LSP, black formatter, ruff linter, treesitter parser
- **Per-filetype indentation**: Explicit settings per filetype to match formatters - **Per-filetype indentation**: Explicit settings per filetype to match formatters
- PHP: tabs, 2-space display (WordPress standards) - PHP: tabs, 2-space display (WordPress standards)
@ -82,20 +78,11 @@ Record every decision here with a short rationale. Append new entries; do not re
- Markdown: 2 spaces (Prettier) - Markdown: 2 spaces (Prettier)
- Python: 4 spaces (Black/PEP 8) - Python: 4 spaces (Black/PEP 8)
- **Global defaults**: 4 spaces (reasonable baseline for other filetypes) - **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: - 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 - **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 - **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) - **Whitespace highlighting**: Already handled via `listchars` in Phase 3.2 (settings.lua)
- **Persistent folds**: Not needed; UFO handles folding without explicit persistence mechanism - **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: - 2025-12-11: Colorscheme Phase 11.2:
- **Color palette extraction**: Extracted all 45 color definitions from original Paper Tonic colorscheme - **Color palette extraction**: Extracted all 45 color definitions from original Paper Tonic colorscheme
- **Structure**: Created `lua/paper-tonic-modern/colors.lua` with comprehensive documentation - **Structure**: Created `lua/paper-tonic-modern/colors.lua` with comprehensive documentation

View File

@ -24,7 +24,6 @@ Source of truth for the step-by-step rebuild. Keep this concise and up to date.
## Phase 1.6 — Preserve selected directories ## Phase 1.6 — Preserve selected directories
- [x] Keep `undodir`, `spell`, `view`, `UltiSnips`, `templates` as-is for now (review later) - [x] Keep `undodir`, `spell`, `view`, `UltiSnips`, `templates` as-is for now (review later)
- [x] Re-enabled persistent undo in settings.lua (2025-01-06)
## Phase 2 — Bootstrap ## Phase 2 — Bootstrap
@ -275,13 +274,14 @@ 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] 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 - [x] Note: Phase 9.4 will align Neovim editor settings (tabstop, shiftwidth, expandtab) with formatter configurations
## Phase 9.2 — conform.nvim setup with project-aware executables ## Phase 9.2 — none-ls setup with project-aware executables
- [x] Add `stevearc/conform.nvim` (replaced none-ls due to phpcbf preprocessing bug) - [x] Add `nvimtools/none-ls.nvim`
- [x] Configure formatters (conform has built-in support for all): - [x] Create helper function to detect project-local executables (node_modules/.bin/, vendor/bin/, Mason bin)
- [x] prettier (auto-detects project-local, Mason, global) - [x] Configure formatters:
- [x] phpcbf (auto-detects project-local, global; customized for WordPress standard) - [x] prettier (project-local first, then Mason, then global)
- [x] stylua (auto-detects project-local, Mason, global) - [x] phpcbf (project-local first, then global system install - already available)
- [x] Add `mfussenegger/nvim-lint` for linting (separate from formatting) - [x] stylua (Mason installed)
- [x] Add `mfussenegger/nvim-lint` for linting (none-ls removed most linters from builtins)
- [x] Configure linters via nvim-lint: - [x] Configure linters via nvim-lint:
- [x] eslint_d (project-local first, then Mason, then global - daemon version for speed) - [x] eslint_d (project-local first, then Mason, then global - daemon version for speed)
- [x] phpcs (project-local first, then global system install - already available) - [x] phpcs (project-local first, then global system install - already available)
@ -289,9 +289,8 @@ 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 (`<leader>lt` to toggle) - [x] Add format-on-save autocommand with toggle capability (`<leader>lt` to toggle)
- [x] Add manual format keymaps: `<leader>lf` (buffer), `<leader>lf` (visual range) - [x] Add manual format keymaps: `<leader>lf` (buffer), `<leader>lf` (visual range)
- [x] Ensure WordPress coding standards work via phpcs.xml or --standard flag - [x] Ensure WordPress coding standards work via phpcs.xml or --standard flag
- [x] Search order handled by conform.nvim automatically: project → Mason → system PATH - [x] Search order: project node_modules/.bin/ → project vendor/bin/ → Mason bin → system PATH
- [x] Note: Linting runs on BufEnter, BufWritePost, InsertLeave events - [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 ## Phase 9.3 — Mason formatter/linter installation
- [x] Add `WhoIsSethDaniel/mason-tool-installer.nvim` for automated installation - [x] Add `WhoIsSethDaniel/mason-tool-installer.nvim` for automated installation
@ -336,12 +335,6 @@ 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] Persistent folds (via UFO) -- no need, this is no longer required.
- [x] Note: UFO handles folding; no explicit persistence mechanism needed - [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 — Colorscheme: Modern Paper Tonic
## Phase 11.1 — Confirm scope and priorities ## Phase 11.1 — Confirm scope and priorities
@ -480,7 +473,6 @@ Source of truth for the step-by-step rebuild. Keep this concise and up to date.
- [ ] Validate Telescope navigation + LSP jumps - [ ] Validate Telescope navigation + LSP jumps
- [ ] Validate netrw browsing and preview splits - [ ] Validate netrw browsing and preview splits
- [ ] Validate Oil.nvim file operations - [ ] Validate Oil.nvim file operations
- [x] Fix working directory behavior (disabled autochdir, locked Telescope to project root)
## Phase 12.5 — Language tooling validation ## Phase 12.5 — Language tooling validation
- [ ] Validate HTML/PHP/JS/Markdown tooling - [ ] Validate HTML/PHP/JS/Markdown tooling

View File

@ -1,519 +0,0 @@
# Navigation Enhancement Plans
**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', '<leader>.', function() require('bracket-repeat').repeat_last() end,
{ desc = 'Repeat bracket navigation' })
vim.keymap.set('n', '<leader>,', 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
### 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
- `<Esc>` - 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)
<Esc> " Exit mode
```
## Technical Implementation
### Approach: getchar() Loop (No Remapping)
Instead of remapping keys, use a `while` loop with `vim.fn.getchar()` to read keypresses and manually execute the prefixed commands.
**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 `<Esc>` to exit anytime.
### 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 = {}
--- Enter navigation mode with getchar() loop
--- @param prefix string Either '[' or ']'
function M.enter(prefix)
prefix = prefix or '['
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 <C-c> 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
-- 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 `<C-c>` 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 `<Esc>` (or `<C-c>`) to exit
### 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
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 `<Esc>`
No additional statusline integration needed - the command line prompt is clear and non-intrusive.
## Design Decisions
### Mapping Conflicts: Solved
No mapping conflicts possible - getchar() reads raw input without touching keymaps.
### Buffer-local Mappings: Not Applicable
Loop executes `normal! [key` which uses whatever mappings exist naturally.
### Visual Feedback: Built-in
Command line prompt is sufficient and non-intrusive.
### 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)
Could add special handling if needed:
```lua
if key:match('^%d$') then
-- Handle numbers specially
end
```
### Operators (d, c, y, etc.)
In navigation mode, `d` executes `[d` (diagnostic navigation), not delete operator.
This is desired behavior - use `<Esc>` to exit and edit normally.
### Error Handling
If a command doesn't exist (e.g., `[z`), Vim will show an error but mode continues.
User can press `<Esc>` to exit or try another key.
### Pros & Cons
**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
**Cons:**
- ⚠️ Requires mode switching (mental overhead)
- ⚠️ Blocking loop (though this is by design)
- ⚠️ Need to remember entry/exit keys
### Estimated Complexity
- **Total**: ~50-80 lines
- **Time**: 30-60 minutes
---
## 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 <Esc>` (9 keys) |
| **Typing (mixed nav)** | `]d ]d ]c ]c` (8 keys) | `z] ddcc <Esc>` (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
- [ ] 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 `<Esc>` → prompt clears, back to normal mode
- [ ] Exit with `<C-c>` → 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 `<Esc>`
4. **Polish** (optional)
- Add help text on first use
- Handle `<C-c>` gracefully (already done with `pcall`)
- Consider adding common navigation cheatsheet
5. **Document**
- Update `README.md` with new keymaps
- Add navigation pairs reference

207
README.md
View File

@ -40,10 +40,6 @@ Core keymaps available globally (not plugin-specific). These provide fallbacks f
| n | `gr` | LSP references placeholder | `<Nop>` so LSP buffers can override cleanly | | n | `gr` | LSP references placeholder | `<Nop>` so LSP buffers can override cleanly |
| n | `gI` | LSP implementation placeholder | `<Nop>` so LSP buffers can override cleanly | | n | `gI` | LSP implementation placeholder | `<Nop>` so LSP buffers can override cleanly |
| n | `K` | Keyword help fallback | Uses `keywordprg` (e.g., `man`) when LSP hover is unavailable | | n | `K` | Keyword help fallback | Uses `keywordprg` (e.g., `man`) when LSP hover is unavailable |
| n | `<leader>co` | Quickfix: Open | Opens quickfix window |
| n | `<leader>cc` | Quickfix: Close | Closes quickfix window |
| n | `<leader>lo` | Location list: Open | Opens location list window |
| n | `<leader>lc` | Location list: Close | Closes location list window |
| n | `<leader>xx` | Diagnostics → location list | Populates current buffer diagnostics | | n | `<leader>xx` | Diagnostics → location list | Populates current buffer diagnostics |
| n | `<leader>xX` | Diagnostics → quickfix | Populates project-wide diagnostics | | n | `<leader>xX` | Diagnostics → quickfix | Populates project-wide diagnostics |
| n | `<leader>xe` | Diagnostics → buffer errors | Location list filtered to errors | | n | `<leader>xe` | Diagnostics → buffer errors | Location list filtered to errors |
@ -52,74 +48,14 @@ Core keymaps available globally (not plugin-specific). These provide fallbacks f
| n | `]d` | Diagnostics: next item | Uses `vim.diagnostic.goto_next` | | n | `]d` | Diagnostics: next item | Uses `vim.diagnostic.goto_next` |
| n | `<leader>xd` | Diagnostic float | Opens hover window for cursor diagnostic | | n | `<leader>xd` | Diagnostic float | Opens hover window for cursor diagnostic |
| n | `<leader>xt` | Toggle diagnostics | Flips `vim.diagnostic.enable()` | | n | `<leader>xt` | Toggle diagnostics | Flips `vim.diagnostic.enable()` |
| n | `<leader>gg` | Git: Changed files → quickfix | Lists all modified/deleted/untracked files with status |
| n | `<leader>hi` | Highlight inspector | Shows highlight/capture stack under cursor | | n | `<leader>hi` | Highlight inspector | Shows highlight/capture stack under cursor |
| n | `<leader>bg` | Toggle background (light/dark) | Switches between light and dark colorscheme modes |
### `lua/netrw-config.lua` ### `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 | | Mode | Key | Description |
| --- | --- | --- | | --- | --- | --- |
| n | `<leader>nt` | Open netrw in new tab at current file's directory | | n | `<leader>te` | Open netrw in a new tab rooted at current file directory |
| n | `<leader>nT` | Open netrw in new tab at project root | | n | `<leader>tE` | Open netrw in a new tab rooted at project cwd |
| n | `<leader>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
##### Navigation & View
| Key | Description |
| --- | --- |
| `<CR>` | 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 ## Plugin Reference
@ -137,7 +73,7 @@ Buffer-local keymaps available when an LSP client attaches:
| n | `gI` | LSP: implementation | | n | `gI` | LSP: implementation |
| n | `K` | LSP hover | | n | `K` | LSP hover |
### Conform (Formatting) `lua/plugins/conform.lua` ### None-ls (Formatting) `lua/plugins/none-ls.lua`
#### Keymaps #### Keymaps
@ -178,7 +114,6 @@ Buffer-local keymaps available when inside a git repository:
| n | `<leader>hp` | Preview hunk | | n | `<leader>hp` | Preview hunk |
| n | `<leader>hd` | Diff against index | | n | `<leader>hd` | Diff against index |
| n | `<leader>hD` | Diff against previous commit (`~`) | | n | `<leader>hD` | Diff against previous commit (`~`) |
| n | `<leader>hq` | Send all hunks to quickfix list |
| o/x | `ih` | Text object: select git hunk | | o/x | `ih` | Text object: select git hunk |
### Telescope `lua/plugins/telescope.lua` ### Telescope `lua/plugins/telescope.lua`
@ -369,139 +304,3 @@ Buffer-local keymaps available when inside a git repository:
| Mode | Key | Description | | Mode | Key | Description |
| --- | --- | --- | | --- | --- | --- |
| insert | `<M-e>` | Fast wrap the previous text with a pair | | insert | `<M-e>` | Fast wrap the previous text with a pair |
## Configuration
### Formatting & Linting
This config uses **conform.nvim** 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)
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/conform.lua`):
- **JavaScript/TypeScript/CSS/JSON/HTML/Markdown**: `prettier`
- **PHP**: `phpcbf` (WordPress standard by default)
- **Lua**: `stylua`
- **Python**: handled by `ruff` LSP (not conform)
**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
<?xml version="1.0"?>
<ruleset name="Custom">
<description>Project-specific PHP rules</description>
<rule ref="PSR12"/>
<!-- or: <rule ref="WordPress-Core"/> -->
<!-- Customize rules -->
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="120"/>
</properties>
</rule>
</ruleset>
```
**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
<?xml version="1.0"?>
<ruleset name="PSR-12">
<rule ref="PSR12"/>
</ruleset>
```
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**: `<leader>lt` (see keymaps below)
- **Manual format**: `<leader>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`)
## 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 <path>` to manually change directory if needed (affects Telescope search scope)

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks
(comment) @fold

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks
(comment) @fold

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks
(comment) @fold

View File

@ -1,8 +0,0 @@
;; extends
;; Fold comment blocks (including consecutive single-line comments)
(comment) @fold
;; Fold consecutive single-line comments as a block
((comment) @fold
(#match? @fold "^//"))

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks
(comment) @fold

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks (including /** */ docblocks)
(comment) @fold

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks
(comment) @fold

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks
(comment) @fold

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks
(comment) @fold

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks
(comment) @fold

View File

@ -1,4 +0,0 @@
;; extends
;; Fold comment blocks
(comment) @fold

View File

@ -1,5 +1,5 @@
-- Paper Tonic Modern -- Paper Tonic Modern
-- A paper-like colorscheme for Neovim supporting both light and dark modes -- A light, paper-like colorscheme for Neovim
-- Forked from the original Paper Tonic with modern Lua implementation -- Forked from the original Paper Tonic with modern Lua implementation
-- Reset highlights and syntax -- Reset highlights and syntax
@ -11,10 +11,8 @@ end
-- Set colorscheme name -- Set colorscheme name
vim.g.colors_name = 'paper-tonic-modern' vim.g.colors_name = 'paper-tonic-modern'
-- Set default background if not already set -- Set background to light
if vim.o.background == '' then vim.o.background = 'light'
vim.o.background = 'light'
end
-- Load the colorscheme -- Load the colorscheme
require('paper-tonic-modern').load() require('paper-tonic-modern').load()

View File

@ -4,28 +4,28 @@
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" }, "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
"cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" }, "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
"cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" }, "cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
"conform.nvim": { "branch": "master", "commit": "238f542a118984a88124fc915d5b981680418707" },
"copilot-cmp": { "branch": "master", "commit": "15fc12af3d0109fa76b60b5cffa1373697e261d1" }, "copilot-cmp": { "branch": "master", "commit": "15fc12af3d0109fa76b60b5cffa1373697e261d1" },
"copilot.lua": { "branch": "master", "commit": "5ace9ecd0db9a7a6c14064e4ce4ede5b800325f3" }, "copilot.lua": { "branch": "master", "commit": "efe563802a550b7f1b7743b007987e97cba22718" },
"gitsigns.nvim": { "branch": "main", "commit": "42d6aed4e94e0f0bbced16bbdcc42f57673bd75e" }, "gitsigns.nvim": { "branch": "main", "commit": "5813e4878748805f1518cee7abb50fd7205a3a48" },
"indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" }, "indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" },
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" }, "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "fe661093f4b05136437b531e7f959af2a2ae66c8" }, "mason-lspconfig.nvim": { "branch": "main", "commit": "0b9bb925c000ae649ff7e7149c8cd00031f4b539" },
"mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" }, "mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" },
"mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" }, "mason.nvim": { "branch": "main", "commit": "57e5a8addb8c71fb063ee4acda466c7cf6ad2800" },
"nvim-autopairs": { "branch": "master", "commit": "c2a0dd0d931d0fb07665e1fedb1ea688da3b80b4" }, "none-ls.nvim": { "branch": "main", "commit": "5abf61927023ea83031753504adb19630ba80eef" },
"nvim-cmp": { "branch": "main", "commit": "85bbfad83f804f11688d1ab9486b459e699292d6" }, "nvim-autopairs": { "branch": "master", "commit": "7a2c97cccd60abc559344042fefb1d5a85b3e33b" },
"nvim-lint": { "branch": "master", "commit": "ca6ea12daf0a4d92dc24c5c9ae22a1f0418ade37" }, "nvim-cmp": { "branch": "main", "commit": "d97d85e01339f01b842e6ec1502f639b080cb0fc" },
"nvim-lspconfig": { "branch": "master", "commit": "92ee7d42320edfbb81f3cad851314ab197fa324a" }, "nvim-lint": { "branch": "master", "commit": "ebe535956106c60405b02220246e135910f6853d" },
"nvim-surround": { "branch": "main", "commit": "1098d7b3c34adcfa7feb3289ee434529abd4afd1" }, "nvim-lspconfig": { "branch": "master", "commit": "9c923997123ff9071198ea3b594d4c1931fab169" },
"nvim-surround": { "branch": "main", "commit": "fcfa7e02323d57bfacc3a141f8a74498e1522064" },
"nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
"nvim-treesitter-textobjects": { "branch": "master", "commit": "5ca4aaa6efdcc59be46b95a3e876300cfead05ef" }, "nvim-treesitter-textobjects": { "branch": "master", "commit": "5ca4aaa6efdcc59be46b95a3e876300cfead05ef" },
"nvim-ts-autotag": { "branch": "main", "commit": "c4ca798ab95b316a768d51eaaaee48f64a4a46bc" }, "nvim-ts-autotag": { "branch": "main", "commit": "c4ca798ab95b316a768d51eaaaee48f64a4a46bc" },
"nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" }, "nvim-ufo": { "branch": "main", "commit": "72d54c31079d38d8dfc5456131b1d0fb5c0264b0" },
"oil.nvim": { "branch": "master", "commit": "d278dc40f9de9980868a0a55fa666fba5e6aeacb" }, "oil.nvim": { "branch": "master", "commit": "cbcb3f997f6f261c577b943ec94e4ef55108dd95" },
"plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
"promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" }, "promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" },
"telescope-fzf-native.nvim": { "branch": "main", "commit": "6fea601bd2b694c6f2ae08a6c6fab14930c60e2c" }, "telescope-fzf-native.nvim": { "branch": "main", "commit": "6fea601bd2b694c6f2ae08a6c6fab14930c60e2c" },
"telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" }, "telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" },
"undotree": { "branch": "master", "commit": "178d19e00a643f825ea11d581b1684745d0c4eda" } "undotree": { "branch": "master", "commit": "0f1c9816975b5d7f87d5003a19c53c6fd2ff6f7f" }
} }

View File

@ -82,9 +82,6 @@ vim.api.nvim_create_autocmd("FileType", {
group = aug, group = aug,
pattern = "qf", -- Applies to both quickfix and location list pattern = "qf", -- Applies to both quickfix and location list
callback = function() callback = function()
-- Disable spell checking in quickfix/location list windows
vim.opt_local.spell = false
-- Use matchadd() for higher priority highlighting -- Use matchadd() for higher priority highlighting
-- This will override the default qf syntax -- This will override the default qf syntax
vim.fn.matchadd("qfError", "\\<error\\>", 10) vim.fn.matchadd("qfError", "\\<error\\>", 10)
@ -109,34 +106,18 @@ local session_aug = vim.api.nvim_create_augroup("SessionManagement", { clear = t
vim.api.nvim_create_autocmd("VimEnter", { vim.api.nvim_create_autocmd("VimEnter", {
group = session_aug, group = session_aug,
pattern = "*", pattern = "*",
once = true, nested = true, -- Allow other autocmds to fire after loading session
callback = function() callback = function()
-- Only auto-load if: -- Only auto-load if:
-- 1. Session.vim exists in cwd -- 1. Session.vim exists in cwd
-- 2. No files were specified on command line (vim.fn.argc() == 0) -- 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) -- 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 if vim.fn.argc() == 0 and vim.fn.filereadable("Session.vim") == 1 and vim.v.this_session == "" then
vim.cmd("silent! source Session.vim") vim.cmd("source Session.vim")
end end
end, end,
}) })
-- Ensure filetype is detected for current buffer after session load
vim.api.nvim_create_autocmd("SessionLoadPost", {
group = session_aug,
pattern = "*",
callback = function()
-- 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,
})
-- Auto-save session on exit, but ONLY if Session.vim already exists -- Auto-save session on exit, but ONLY if Session.vim already exists
vim.api.nvim_create_autocmd("VimLeavePre", { vim.api.nvim_create_autocmd("VimLeavePre", {
group = session_aug, group = session_aug,

View File

@ -34,12 +34,6 @@ map('n', 'K', function()
end end
end, { desc = 'Vim: Hover/Help (keywordprg fallback)', silent = true }) end, { desc = 'Vim: Hover/Help (keywordprg fallback)', silent = true })
-- Quickfix and Location list keymaps
map('n', '<leader>co', '<cmd>copen<cr>', { desc = 'Quickfix: Open', silent = true })
map('n', '<leader>cc', '<cmd>cclose<cr>', { desc = 'Quickfix: Close', silent = true })
map('n', '<leader>lo', '<cmd>lopen<cr>', { desc = 'Location list: Open', silent = true })
map('n', '<leader>lc', '<cmd>lclose<cr>', { desc = 'Location list: Close', silent = true })
-- Diagnostic keymaps -- Diagnostic keymaps
map('n', '<leader>xx', vim.diagnostic.setloclist, { desc = 'Diagnostics: Buffer diagnostics (location list)', silent = true }) map('n', '<leader>xx', vim.diagnostic.setloclist, { desc = 'Diagnostics: Buffer diagnostics (location list)', silent = true })
map('n', '<leader>xX', vim.diagnostic.setqflist, { desc = 'Diagnostics: All diagnostics (quickfix)', silent = true }) map('n', '<leader>xX', vim.diagnostic.setqflist, { desc = 'Diagnostics: All diagnostics (quickfix)', silent = true })
@ -49,39 +43,144 @@ end, { desc = 'Diagnostics: Buffer errors (location list)', silent = true })
map('n', '<leader>xE', function() map('n', '<leader>xE', function()
vim.diagnostic.setqflist({ severity = vim.diagnostic.severity.ERROR }) vim.diagnostic.setqflist({ severity = vim.diagnostic.severity.ERROR })
end, { desc = 'Diagnostics: All errors (quickfix)', silent = true }) end, { desc = 'Diagnostics: All errors (quickfix)', silent = true })
map('n', '[d', function() map('n', '[d', vim.diagnostic.goto_prev, { desc = 'Diagnostics: Previous diagnostic', silent = true })
vim.diagnostic.goto_prev({ float = false }) map('n', ']d', vim.diagnostic.goto_next, { desc = 'Diagnostics: Next diagnostic', silent = true })
end, { desc = 'Diagnostics: Previous diagnostic', silent = true }) map('n', '<leader>xd', vim.diagnostic.open_float, { desc = 'Diagnostics: Show diagnostic under cursor', silent = true })
map('n', ']d', function()
vim.diagnostic.goto_next({ float = false })
end, { desc = 'Diagnostics: Next diagnostic', silent = true })
map('n', '<leader>xd', function()
pcall(vim.diagnostic.open_float)
end, { desc = 'Diagnostics: Show diagnostic under cursor', silent = true })
map('n', '<leader>xt', function() map('n', '<leader>xt', function()
vim.diagnostic.enable(not vim.diagnostic.is_enabled()) vim.diagnostic.enable(not vim.diagnostic.is_enabled())
end, { desc = 'Diagnostics: Toggle display', silent = true }) end, { desc = 'Diagnostics: Toggle display', silent = true })
-- Git: Modified, deleted, and untracked files to quickfix
map('n', '<leader>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
end, { desc = 'Git: Changed files to quickfix (with status)', silent = true })
-- Debug: Show highlight group and color under cursor -- Debug: Show highlight group and color under cursor
map('n', '<leader>hi', function() map('n', '<leader>hi', function()
require('utils').show_highlight_info() local cursor_pos = vim.api.nvim_win_get_cursor(0)
end, { desc = 'Debug: Show highlight group and color under cursor', silent = true }) local row, col = cursor_pos[1] - 1, cursor_pos[2]
-- UI: Toggle background (light/dark) -- Get all highlight groups at cursor position
map('n', '<leader>bg', function() local ts_hl = vim.treesitter.get_captures_at_pos(0, row, col)
if vim.o.background == 'dark' then local synID = vim.fn.synID(row + 1, col + 1, 1)
vim.o.background = 'light' local synName = vim.fn.synIDattr(synID, 'name')
else local synTrans = vim.fn.synIDattr(vim.fn.synIDtrans(synID), 'name')
vim.o.background = 'dark'
-- 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 end
end, { desc = 'UI: Toggle background (light/dark)', silent = true }) 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, { desc = 'Debug: Show highlight group and color under cursor', silent = true })

View File

@ -20,9 +20,9 @@ vim.g.netrw_alto = 0
-- 50% split when pressing 'p' -- 50% split when pressing 'p'
vim.g.netrw_winsize = 50 vim.g.netrw_winsize = 50
-- Keep working directory unchanged when browsing (1 = don't change directory) -- Keep the current directory and browsing directory synced
-- Setting to 1 prevents netrw from changing vim's working directory -- This makes netrw respect your current working directory
vim.g.netrw_keepdir = 1 vim.g.netrw_keepdir = 0
-- Open files in the same window (replace netrw buffer) -- 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 -- Options: 0=same window, 1=horizontal split, 2=vertical split, 3=new tab, 4=previous window
@ -40,22 +40,17 @@ vim.g.netrw_sizestyle = 'H'
-- Netrw file explorer keymaps -- Netrw file explorer keymaps
-- Open netrw in new tab at current file's directory -- Open netrw in new tab at current file's directory
map('n', '<leader>nt', function() map('n', '<leader>te', function()
local current_file_dir = vim.fn.expand('%:p:h') local current_file_dir = vim.fn.expand('%:p:h')
vim.cmd('tabnew') vim.cmd('tabnew')
vim.cmd('Explore ' .. vim.fn.fnameescape(current_file_dir)) vim.cmd('Explore ' .. vim.fn.fnameescape(current_file_dir))
end, { desc = 'Netrw: Tab at current file dir', silent = true }) end, { desc = 'Netrw: Tab explore (current file dir)', silent = true })
-- Open netrw in new tab at project root (stored at startup) -- Open netrw in new tab at project root (cwd)
map('n', '<leader>nT', function() map('n', '<leader>tE', function()
vim.cmd('tabnew') vim.cmd('tabnew')
vim.cmd('Explore ' .. vim.fn.fnameescape(vim.g.project_root)) vim.cmd('Explore ' .. vim.fn.fnameescape(vim.fn.getcwd()))
end, { desc = 'Netrw: Tab at project root', silent = true }) end, { desc = 'Netrw: Tab explore (project root)', silent = true })
-- Open netrw in current window at project root (stored at startup)
map('n', '<leader>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 -- Enable line numbers in netrw buffers
vim.api.nvim_create_autocmd('FileType', { vim.api.nvim_create_autocmd('FileType', {
pattern = 'netrw', pattern = 'netrw',

View File

@ -1,6 +1,6 @@
-- Paper Tonic Modern - Color Palette -- Paper Tonic Modern - Color Palette
-- Extracted from original Paper Tonic colorscheme -- Extracted from original Paper Tonic colorscheme
-- Supports both light (paper-like) and dark modes -- Light, paper-like theme with subtle colors
local M = {} local M = {}
@ -9,177 +9,107 @@ local M = {}
-- 256: Integer 0-255 for 256-color terminals -- 256: Integer 0-255 for 256-color terminals
-- ansi: ANSI color name for basic 16-color terminals -- ansi: ANSI color name for basic 16-color terminals
-- Detect current background mode
local is_dark = vim.o.background == 'dark'
-- ============================================================================ -- ============================================================================
-- Background Colors -- Background Colors
-- ============================================================================ -- ============================================================================
if is_dark then -- Main background: pure white paper
-- Dark mode: soft dark backgrounds M.bg = {'#ffffff', 255, 'white'}
M.bg = {'#27292c', 234, 'black'}
M.bg_darkest = {'#ffffff', 255, 'white'} -- Inverted: lightest for strong contrast -- Darkest background: used for very strong contrast elements
M.bg_ui = {'#353535', 236, 'darkgray'} M.bg_darkest = {'#505050', 244, 'gray'}
else
-- Light mode: pure white paper -- UI background: slightly off-white for UI elements (statusline, etc.)
M.bg = {'#ffffff', 255, 'white'} M.bg_ui = {'#efefef', 0, 'darkgray'}
M.bg_darkest = {'#505050', 244, 'gray'}
M.bg_ui = {'#efefef', 0, 'darkgray'}
end
-- ============================================================================ -- ============================================================================
-- Highlight Backgrounds (general) -- Highlight Backgrounds (general)
-- ============================================================================ -- ============================================================================
if is_dark then -- Selection/highlight backgrounds (neutral gray tones)
-- Dark mode: lighter backgrounds for highlights M.bg_hl_strong = {"#dddddd", 17, "white"}
M.bg_hl_strong = {"#3a3a3a", 237, "darkgray"} M.bg_hl = {"#eeeeee", 250, "white"}
M.bg_hl = {"#2a2a2a", 236, "darkgray"} M.bg_hl_weak = {"#f7f2f2", 250, "white"}
M.bg_hl_weak = {"#252525", 235, "darkgray"}
-- Special highlight backgrounds (cyan/blue tones) -- Special highlight backgrounds (cyan/blue tones - for LSP references, search, etc.)
M.bg_hl_special_strong = {"#1a4a5a", 24, "darkblue"} M.bg_hl_special_strong = {"#a3e0ff", 17, "cyan"}
M.bg_hl_special = {"#0a3a4a", 23, "darkblue"} M.bg_hl_special = {"#d4f0ff", 250, "cyan"}
M.bg_hl_special_weak = {"#1a3545", 23, "darkblue"} M.bg_hl_special_weak = {"#e0eaff", 250, "cyan"}
-- Alternative special highlight (green tones) -- Alternative special highlight (green tones - for diff additions, etc.)
M.bg_hl_special_alt_strong = {"#1a5a2a", 28, "darkgreen"} M.bg_hl_special_alt_strong = {"#74f283", 17, "cyan"}
M.bg_hl_special_alt = {"#0a4a1a", 22, "darkgreen"} M.bg_hl_special_alt = {"#bff2cd", 250, "cyan"}
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) -- Status Backgrounds (error, success, modified, fail)
-- ============================================================================ -- ============================================================================
if is_dark then -- Error backgrounds (red/pink tones)
-- Dark mode: darker tinted backgrounds M.bg_error = {'#ffd7d7', 196, 'white'}
M.bg_error = {'#4a2020', 52, 'darkred'} M.bg_error_weak = {'#ffefef', 196, 'white'}
M.bg_error_weak = {'#3a1a1a', 52, 'darkred'}
M.bg_success = {'#1a3a1a', 22, 'darkgreen'} -- Success background (green tone)
M.bg_modified = {'#1a1a3a', 17, 'darkblue'} M.bg_success = {'#e0ece0', 196, 'white'}
M.bg_fail = {'#3a1a1a', 52, 'darkred'}
else -- Modified background (blue tone)
-- Light mode: light tinted backgrounds M.bg_modified = {'#e0e0ec', 196, 'white'}
M.bg_error = {'#ffd7d7', 196, 'white'}
M.bg_error_weak = {'#ffefef', 196, 'white'} -- Fail/warning background (red tone)
M.bg_success = {'#e0ece0', 196, 'white'} M.bg_fail = {'#ece0e0', 196, 'white'}
M.bg_modified = {'#e0e0ec', 196, 'white'}
M.bg_fail = {'#ece0e0', 196, 'white'}
end
-- ============================================================================ -- ============================================================================
-- Foreground Colors (text) -- Foreground Colors (text)
-- ============================================================================ -- ============================================================================
if is_dark then -- Main text colors (gray scale, strongest to weakest)
-- Dark mode: light text (inverted from light mode) M.fg_stronger = {'#444444', 236, 'darkgrey'} -- Darkest text
M.fg_stronger = {'#e0e0e0', 253, 'white'} -- Lightest text M.fg_strong = {'#666666', 236, 'darkgrey'} -- Dark text
M.fg_strong = {'#c0c0c0', 250, 'white'} -- Light text M.fg = {'#8c8c8c', 244, 'gray'} -- Normal text (default)
M.fg = {'#a0a0a0', 248, 'gray'} -- Normal text (default) M.fg_weak = {'#9d9d9d', 251, 'gray'} -- Light text (comments, less important)
M.fg_weak = {'#808080', 244, 'gray'} -- Dimmer text (comments, less important) M.fg_weaker = {'#bbbbbb', 251, 'gray'} -- Lightest text (very subtle)
M.fg_weaker = {'#606060', 241, 'darkgray'} -- Dimmest text (very subtle)
M.fg_exception = {'#d08080', 174, 'red'} -- Light reddish for errors/exceptions -- Exception foreground (reddish-brown for errors/exceptions)
else M.fg_exception = {'#7c4444', 251, 'gray'}
-- 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) -- Status Foreground Colors (Git/Diff - muted natural tones)
-- ============================================================================ -- ============================================================================
if is_dark then -- Git/diff status indicators (used in statusline, signs, etc.)
-- Dark mode: lighter muted tones -- These are muted, natural colors for git changes
M.success = {'#a0d0a0', 114, 'green'} -- Success/addition (muted green) M.success = {'#89af89', 196, 'white'} -- Success/addition (muted green)
M.modified = {'#a0a0d0', 110, 'blue'} -- Modified/change (muted blue) M.modified = {'#8989af', 196, 'white'} -- Modified/change (muted blue)
M.fail = {'#d0a0a0', 174, 'red'} -- Fail/deletion (muted red) M.fail = {'#af8989', 196, 'white'} -- 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) -- Quickfix/Location List Diagnostic Colors (Muted tones like git/diff)
-- ============================================================================ -- ============================================================================
if is_dark then -- These are for quickfix/location list only - similar muted tone to git/diff
-- Dark mode: lighter muted tones M.qf_error = {'#af3f3f', 196, 'white'} -- Error (darker muted red for more contrast)
M.qf_error = {'#d07070', 167, 'red'} -- Error (lighter muted red) M.qf_warn = {'#af7f5f', 196, 'white'} -- Warning (muted orange)
M.qf_warn = {'#d0a080', 173, 'yellow'} -- Warning (muted orange) M.qf_info = {'#5f8faf', 196, 'white'} -- Info (muted blue)
M.qf_info = {'#80b0d0', 110, 'blue'} -- Info (muted blue) M.qf_hint = {'#5fafaf', 196, 'white'} -- Hint (lighter 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) -- Alert/Diagnostic Colors (Fluorescent/Neon - intentionally jarring)
-- ============================================================================ -- ============================================================================
-- These colors are designed to be NOTICED, not blend in with code -- These colors are designed to be NOTICED, not blend in with code
-- Fluorescent/neon aesthetic -- Fluorescent/neon aesthetic similar to bg_hl_special_alt colors
-- In light mode: bright, synthetic, stands out on white paper ("highlighter marker") -- Think "highlighter marker" - bright, synthetic, stands out on white paper
-- In dark mode: slightly toned down but still prominent
if is_dark then -- LSP Diagnostics - Fluorescent colors for maximum visibility
-- Dark mode: still bright but not as harsh M.diag_error = {'#ff0066', 197, 'red'} -- Hot pink-red (screams "error!")
M.diag_error = {'#ff4488', 204, 'red'} -- Bright pink-red M.diag_warn = {'#ff6600', 202, 'red'} -- Fluorescent orange (warnings)
M.diag_warn = {'#ff8833', 208, 'yellow'} -- Bright orange M.diag_info = {'#00ccff', 45, 'cyan'} -- Bright fluorescent cyan (info - more prominent)
M.diag_info = {'#44ccff', 81, 'cyan'} -- Bright cyan M.diag_hint = {'#66e0ff', 81, 'cyan'} -- Softer fluorescent cyan (hint - less prominent)
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 -- LSP Diagnostic backgrounds - Light tinted versions for highlighting code
M.bg_diag_error = {'#3a1a25', 52, 'darkred'} M.bg_diag_error = {'#ffe6f0', 224, 'white'} -- Very light pink (for error backgrounds)
M.bg_diag_warn = {'#3a2a1a', 58, 'brown'} M.bg_diag_warn = {'#fff0e6', 223, 'white'} -- Very light orange (for warning backgrounds)
M.bg_diag_info = {'#1a2a3a', 17, 'darkblue'} M.bg_diag_info = {'#e6f9ff', 195, 'white'} -- Very light cyan (for info backgrounds)
M.bg_diag_hint = {'#1a2f3a', 23, 'darkblue'} M.bg_diag_hint = {'#f0fcff', 195, 'white'} -- Very light cyan (for hint backgrounds)
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) -- Primary Accent Colors (brownish-red tones)
@ -187,19 +117,15 @@ end
-- Primary accent: For "base" languages -- Primary accent: For "base" languages
-- Used for: PHP, JavaScript, Python, etc. -- Used for: PHP, JavaScript, Python, etc.
if is_dark then -- Languages using primary:
-- Dark mode: lighter brownish-red tones -- - PHP: always primary (whether standalone or in <?php ?> tags within HTML)
M.primary_stronger = {"#d0a0a0", 181, "white"} -- - JavaScript: always primary (standalone or embedded)
M.primary_strong = {"#c09090", 181, "white"} -- - Python: always primary
M.primary = {"#b08080", 138, "gray"} -- Note: Both PHP and JS use primary; acceptable since embedded JS in PHP is discouraged
M.primary_weak = {"#a07070", 131, "darkgray"} M.primary_stronger = {"#7f4b4b", 236, "black"} -- Darkest accent
else M.primary_strong = {"#5a4444", 236, "black"} -- Dark accent
-- Light mode: darker brownish-red tones M.primary = {"#6b5555", 244, "gray"} -- Normal accent
M.primary_stronger = {"#7f4b4b", 236, "black"} M.primary_weak = {"#7c6666", 248, "darkgray"} -- Light accent
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) -- Language-Specific Color Palettes (for mixed-language contexts)
@ -214,49 +140,31 @@ end
-- - HTML tags: c3 (blue) - everywhere, whether in .html or embedded in PHP -- - HTML tags: c3 (blue) - everywhere, whether in .html or embedded in PHP
-- - CSS rules: c2 (green) - everywhere, whether in .css or <style> tags -- - CSS rules: c2 (green) - everywhere, whether in .css or <style> tags
if is_dark then -- Color 2: Green tones - CSS syntax
-- Dark mode: lighter tones -- CSS maintains green coloring in <style> tags, CSS files, CSS-in-JS, etc.
-- Color 2: Green tones - CSS syntax M.c2_weak = {"#5e955e", 28, "darkgreen"}
M.c2_weak = {"#90d090", 114, "green"} M.c2 = {"#008700", 22, "darkgreen"}
M.c2 = {"#70c070", 77, "green"} M.c2_strong = {"#005a00", 22, "darkgreen"}
M.c2_strong = {"#50a050", 71, "darkgreen"}
-- Color 3: Blue tones - HTML syntax -- Color 3: Blue tones - HTML syntax
M.c3_weak = {"#80b0e0", 110, "blue"} -- HTML maintains blue coloring in .html files, within PHP, within templates, etc.
M.c3 = {"#60a0d0", 74, "blue"} M.c3_weak = {"#2d78b7", 20, "blue"}
M.c3_strong = {"#4080b0", 67, "darkblue"} M.c3 = {"#005faf", 19, "blue"}
M.c3_strong = {"#004f92", 17, "darkblue"}
-- Color 4: Cyan tones - Reserved/Future use -- Color 4: Cyan tones - Reserved/Future use
M.c4_weak = {"#a0d0e0", 152, "cyan"} -- Available for additional language if needed (TypeScript? SQL?)
M.c4 = {"#80c0d0", 116, "cyan"} M.c4_weak = {"#78b7d5", 20, "blue"}
M.c4_strong = {"#60a0c0", 74, "darkcyan"} M.c4 = {"#56acd7", 19, "blue"}
M.c4_strong = {"#1596d7", 17, "darkblue"}
-- Color 5: Magenta/Pink tones - Template syntax -- Color 5: Magenta/Pink tones - Template syntax
M.c5_weak = {"#e080c0", 213, "magenta"} -- Used for: Django templates, Twig, Handlebars, Jinja
M.c5 = {"#d060a0", 170, "magenta"} -- The base language keeps its color while template syntax uses c5
M.c5_strong = {"#b04080", 133, "darkmagenta"} -- Example in Twig: HTML stays blue (c3), but {{ }}, {% %}, {# #} are magenta (c5)
else M.c5_weak = {"#e846ac", 164, "magenta"}
-- Light mode: darker tones M.c5 = {"#d70087", 164, "magenta"}
-- Color 2: Green tones - CSS syntax M.c5_strong = {"#ad006d", 164, "magenta"}
M.c2_weak = {"#5e955e", 28, "darkgreen"}
M.c2 = {"#008700", 22, "darkgreen"}
M.c2_strong = {"#005a00", 22, "darkgreen"}
-- Color 3: Blue tones - HTML syntax
M.c3_weak = {"#2d78b7", 20, "blue"}
M.c3 = {"#005faf", 19, "blue"}
M.c3_strong = {"#004f92", 17, "darkblue"}
-- Color 4: Cyan tones - Reserved/Future use
M.c4_weak = {"#78b7d5", 20, "blue"}
M.c4 = {"#56acd7", 19, "blue"}
M.c4_strong = {"#1596d7", 17, "darkblue"}
-- Color 5: Magenta/Pink tones - Template syntax
M.c5_weak = {"#e846ac", 164, "magenta"}
M.c5 = {"#d70087", 164, "magenta"}
M.c5_strong = {"#ad006d", 164, "magenta"}
end
-- ============================================================================ -- ============================================================================
-- Helper Constants -- Helper Constants

View File

@ -9,8 +9,8 @@ return {
-- ============================================================================ -- ============================================================================
Normal = { fg = c.fg, bg = c.bg }, Normal = { fg = c.fg, bg = c.bg },
NormalFloat = { fg = c.fg, bg = c.bg }, NormalFloat = { fg = c.fg, bg = c.NONE },
FloatBorder = { fg = c.diag_hint_dark, bg = c.bg }, FloatBorder = { fg = c.fg_stronger, bg = c.NONE },
-- ============================================================================ -- ============================================================================
-- Cursor & Line Highlighting -- Cursor & Line Highlighting
@ -66,19 +66,17 @@ return {
-- Popup Menu (Completion) -- Popup Menu (Completion)
-- ============================================================================ -- ============================================================================
Pmenu = { fg = c.diag_hint_dark, bg = c.bg }, Pmenu = { fg = c.fg, bg = c.bg_ui },
PmenuSel = { fg = c.fg_strong, bg = c.bg_hl, bold = true }, PmenuSel = { fg = c.fg_strong, bg = c.bg_ui, bold = true },
PmenuSbar = { fg = c.NONE, bg = c.bg_hl }, PmenuSbar = 'Pmenu',
PmenuThumb = { fg = c.NONE, bg = c.fg_weaker }, PmenuThumb = 'Pmenu',
PmenuBorder = { fg = c.diag_hint_dark, bg = c.bg }, WildMenu = { fg = c.fg_strong, bg = c.bg_ui, bold = true },
WildMenu = { fg = c.fg_strong, bg = c.bg_hl, bold = true },
-- ============================================================================ -- ============================================================================
-- Folds -- Folds
-- ============================================================================ -- ============================================================================
Folded = { fg = c.diag_hint_dark, bg = c.NONE, bold = true }, Folded = { fg = c.fg_strong, bold = true },
CursorLineFold = { fg = c.diag_hint_dark, bg = c.bg_hl, bold = true },
FoldColumn = { fg = c.fg_weak }, FoldColumn = { fg = c.fg_weak },
-- ============================================================================ -- ============================================================================
@ -94,19 +92,17 @@ return {
-- Spelling -- Spelling
-- ============================================================================ -- ============================================================================
-- Spelling errors: normal text color with bright colored straight underline SpellBad = { fg = c.alert_strong, bg = c.bg_error_weak },
-- sp (special) sets the underline color, separate from text foreground SpellCap = { fg = c.alert, bg = c.bg_error_weak },
SpellBad = { sp = c.diag_error, underline = true }, -- Serious error: hot pink-red underline SpellLocal = { fg = c.alert_weak, bg = c.bg_error_weak },
SpellCap = { sp = c.diag_warn, underline = true }, -- Capitalization: orange underline SpellRare = { fg = c.alert_weak, bg = c.bg_error_weak },
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 -- Messages & Prompts
-- ============================================================================ -- ============================================================================
ErrorMsg = { fg = c.diag_error, bold = true }, ErrorMsg = { fg = c.alert, bold = true },
WarningMsg = { fg = c.diag_warn, bold = true }, WarningMsg = { fg = c.alert, bold = true },
Question = { fg = c.question, bold = true }, Question = { fg = c.question, bold = true },
ModeMsg = { fg = c.question }, ModeMsg = { fg = c.question },
MoreMsg = { fg = c.question }, MoreMsg = { fg = c.question },

View File

@ -63,11 +63,10 @@ return {
-- ============================================================================ -- ============================================================================
-- Menu -- Menu
CmpItemAbbr = { fg = c.fg }, CmpItemMenu = { fg = c.fg_weak, italic = true },
CmpItemAbbrMatch = { fg = c.primary, bold = true }, CmpItemAbbrMatch = { fg = c.primary, bold = true },
CmpItemAbbrMatchFuzzy = { fg = c.primary_weak }, CmpItemAbbrMatchFuzzy = { fg = c.primary_weak },
CmpItemAbbrDeprecated = { fg = c.fg_weak, strikethrough = true }, CmpItemAbbrDeprecated = { fg = c.fg_weak, strikethrough = true },
CmpItemMenu = { fg = c.fg_weak, italic = true },
-- Kind icons/labels -- Kind icons/labels
CmpItemKindDefault = { fg = c.fg }, CmpItemKindDefault = { fg = c.fg },
@ -88,7 +87,7 @@ return {
CmpItemKindText = { fg = c.fg }, CmpItemKindText = { fg = c.fg },
CmpItemKindFile = { fg = c.fg }, CmpItemKindFile = { fg = c.fg },
CmpItemKindFolder = { fg = c.fg_strong }, CmpItemKindFolder = { fg = c.fg_strong },
CmpItemKindColor = { fg = c.diag_warn }, CmpItemKindColor = { fg = c.alert },
CmpItemKindUnit = { fg = c.fg_weak }, CmpItemKindUnit = { fg = c.fg_weak },
CmpItemKindValue = { fg = c.fg_strong }, CmpItemKindValue = { fg = c.fg_strong },
CmpItemKindConstant = { fg = c.fg_strong }, CmpItemKindConstant = { fg = c.fg_strong },
@ -109,7 +108,7 @@ return {
OilSize = { fg = c.fg_weak }, OilSize = { fg = c.fg_weak },
OilPermissionNone = { fg = c.fg_weaker }, OilPermissionNone = { fg = c.fg_weaker },
OilPermissionRead = { fg = c.success }, OilPermissionRead = { fg = c.success },
OilPermissionWrite = { fg = c.diag_warn }, OilPermissionWrite = { fg = c.alert },
OilPermissionExecute = { fg = c.modified }, OilPermissionExecute = { fg = c.modified },
OilCopy = { fg = c.success, bold = true }, OilCopy = { fg = c.success, bold = true },
OilMove = { fg = c.modified, bold = true }, OilMove = { fg = c.modified, bold = true },
@ -117,7 +116,7 @@ return {
OilDelete = { fg = c.fail, bold = true }, OilDelete = { fg = c.fail, bold = true },
OilChange = { fg = c.modified, bold = true }, OilChange = { fg = c.modified, bold = true },
OilRestore = { fg = c.success }, OilRestore = { fg = c.success },
OilPurge = { fg = c.diag_error, bold = true }, OilPurge = { fg = c.alert_strong, bold = true },
OilTrash = { fg = c.fail }, OilTrash = { fg = c.fail },
OilTrashSourcePath = { fg = c.fg_weak, italic = true }, OilTrashSourcePath = { fg = c.fg_weak, italic = true },
@ -151,8 +150,8 @@ return {
-- ============================================================================ -- ============================================================================
UfoFoldedBg = { bg = c.bg_hl_weak }, UfoFoldedBg = { bg = c.bg_hl_weak },
UfoFoldedFg = { fg = c.diag_hint_dark }, UfoFoldedFg = { fg = c.fg_weak },
UfoCursorFoldedLine = { bg = c.bg_hl, fg = c.diag_hint_dark }, UfoCursorFoldedLine = { bg = c.bg_hl, fg = c.fg },
UfoPreviewBorder = { fg = c.fg_weak }, UfoPreviewBorder = { fg = c.fg_weak },
UfoPreviewNormal = { bg = c.bg_ui }, UfoPreviewNormal = { bg = c.bg_ui },
UfoPreviewCursorLine = { bg = c.bg_hl }, UfoPreviewCursorLine = { bg = c.bg_hl },

View File

@ -76,8 +76,8 @@ return {
Underlined = { underline = true }, Underlined = { underline = true },
Ignore = { fg = c.fg_weak }, Ignore = { fg = c.fg_weak },
Error = { fg = c.diag_error, bold = true }, Error = { fg = c.alert_strong, bold = true },
Todo = { fg = c.diag_error }, Todo = { fg = c.alert_strong },
-- ============================================================================ -- ============================================================================
-- Language-Specific: CSS -- Language-Specific: CSS

View File

@ -101,10 +101,10 @@ return {
['@comment'] = { fg = c.fg_weaker, italic = true, bold = true }, ['@comment'] = { fg = c.fg_weaker, italic = true, bold = true },
['@comment.documentation'] = { fg = c.fg_weak, italic = true }, ['@comment.documentation'] = { fg = c.fg_weak, italic = true },
['@comment.error'] = { fg = c.diag_error, bold = true }, ['@comment.error'] = { fg = c.alert_strong, bold = true },
['@comment.warning'] = { fg = c.diag_warn, bold = true }, ['@comment.warning'] = { fg = c.alert, bold = true },
['@comment.todo'] = { fg = c.diag_error }, ['@comment.todo'] = { fg = c.alert_strong },
['@comment.note'] = { fg = c.diag_weak }, ['@comment.note'] = { fg = c.alert_weak },
-- ============================================================================ -- ============================================================================
-- Markup (Markdown, etc.) -- Markup (Markdown, etc.)
@ -292,6 +292,6 @@ return {
['@none'] = {}, ['@none'] = {},
['@conceal'] = { fg = c.fg_weaker }, ['@conceal'] = { fg = c.fg_weaker },
-- @spell and @nospell should not define any styling ['@spell'] = {},
-- This allows vim.wo.spell to control spell highlighting via SpellBad/SpellCap/etc ['@nospell'] = {},
} }

View File

@ -3,17 +3,13 @@
local M = {} local M = {}
-- Import color palette
local colors = require('paper-tonic-modern.colors')
-- ============================================================================ -- ============================================================================
-- Helper Functions -- Helper Functions
-- ============================================================================ -- ============================================================================
-- Get fresh color palette (reload to respect background changes)
local function get_colors()
-- Clear cached module to force reload
package.loaded['paper-tonic-modern.colors'] = nil
return require('paper-tonic-modern.colors')
end
-- Convert color table {hex, 256, ansi} to usable formats -- Convert color table {hex, 256, ansi} to usable formats
local function get_color(color_def, mode) local function get_color(color_def, mode)
if type(color_def) == 'string' then if type(color_def) == 'string' then
@ -96,17 +92,6 @@ end
-- ============================================================================ -- ============================================================================
function M.load() function M.load()
-- Get fresh colors for current background mode
local colors = get_colors()
-- Clear all cached group modules to force reload with new colors
package.loaded['paper-tonic-modern.groups.editor'] = nil
package.loaded['paper-tonic-modern.groups.syntax'] = nil
package.loaded['paper-tonic-modern.groups.treesitter'] = nil
package.loaded['paper-tonic-modern.groups.semantic'] = nil
package.loaded['paper-tonic-modern.groups.lsp'] = nil
package.loaded['paper-tonic-modern.groups.plugins'] = nil
-- Load highlight groups in order -- Load highlight groups in order
-- Each module returns a table of { group_name = spec } -- Each module returns a table of { group_name = spec }
@ -133,8 +118,9 @@ function M.load()
-- Plugin highlights (Telescope, Gitsigns, cmp, etc.) -- Plugin highlights (Telescope, Gitsigns, cmp, etc.)
local plugins = require('paper-tonic-modern.groups.plugins') local plugins = require('paper-tonic-modern.groups.plugins')
M.highlight_all(plugins) M.highlight_all(plugins)
-- Export colors for external use
M.colors = colors
end end
-- Export colors for use in other modules
M.colors = colors
return M return M

View File

@ -34,32 +34,8 @@ return {
preselect = cmp.PreselectMode.None, preselect = cmp.PreselectMode.None,
completion = { completeopt = "menu,menuone,noinsert,noselect" }, completion = { completeopt = "menu,menuone,noinsert,noselect" },
window = { window = {
completion = cmp.config.window.bordered({ completion = cmp.config.window.bordered(),
border = { documentation = cmp.config.window.bordered(),
{ "🭽", "FloatBorder" },
{ "", "FloatBorder" },
{ "🭾", "FloatBorder" },
{ "", "FloatBorder" },
{ "🭿", "FloatBorder" },
{ "", "FloatBorder" },
{ "🭼", "FloatBorder" },
{ "", "FloatBorder" },
},
winhighlight = "Normal:Normal,FloatBorder:FloatBorder,CursorLine:PmenuSel",
}),
documentation = cmp.config.window.bordered({
border = {
{ "🭽", "FloatBorder" },
{ "", "FloatBorder" },
{ "🭾", "FloatBorder" },
{ "", "FloatBorder" },
{ "🭿", "FloatBorder" },
{ "", "FloatBorder" },
{ "🭼", "FloatBorder" },
{ "", "FloatBorder" },
},
winhighlight = "Normal:Normal,FloatBorder:FloatBorder",
}),
}, },
} }
end, end,

View File

@ -1,74 +0,0 @@
-- 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")
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" },
},
-- Formatter customization
formatters = {
-- Add WordPress coding standard to phpcbf
phpcbf = {
prepend_args = { "--standard=WordPress" },
},
},
-- 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", "<leader>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", "<leader>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", "<leader>lf", function()
conform.format({ async = false, lsp_fallback = false })
end, { desc = "Formatting: Format selection", silent = true, noremap = true })
end,
}

View File

@ -25,27 +25,16 @@ return {
vim.keymap.set(mode, l, r, opts) vim.keymap.set(mode, l, r, opts)
end end
-- Hunk navigation (includes zt to center after movement) -- Hunk navigation
-- Note: gs.next_hunk/prev_hunk are async, so zt needs a delay to center after cursor moves
map('n', ']h', function() map('n', ']h', function()
if vim.wo.diff then return ']czt' end if vim.wo.diff then return ']c' end
vim.schedule(function() vim.schedule(function() gs.next_hunk() end)
gs.next_hunk()
end)
vim.defer_fn(function()
vim.cmd('normal! zt')
end, 50)
return '<Ignore>' return '<Ignore>'
end, { expr = true, desc = 'Gitsigns: Next hunk' }) end, { expr = true, desc = 'Gitsigns: Next hunk' })
map('n', '[h', function() map('n', '[h', function()
if vim.wo.diff then return '[czt' end if vim.wo.diff then return '[c' end
vim.schedule(function() vim.schedule(function() gs.prev_hunk() end)
gs.prev_hunk()
end)
vim.defer_fn(function()
vim.cmd('normal! zt')
end, 50)
return '<Ignore>' return '<Ignore>'
end, { expr = true, desc = 'Gitsigns: Previous hunk' }) end, { expr = true, desc = 'Gitsigns: Previous hunk' })
@ -61,12 +50,6 @@ return {
map('n', '<leader>hd', gs.diffthis, { desc = 'Gitsigns: Diff this' }) map('n', '<leader>hd', gs.diffthis, { desc = 'Gitsigns: Diff this' })
map('n', '<leader>hD', function() gs.diffthis('~') end, { desc = 'Gitsigns: Diff this ~' }) map('n', '<leader>hD', function() gs.diffthis('~') end, { desc = 'Gitsigns: Diff this ~' })
-- Quickfix/Location list
map('n', '<leader>hq', function()
gs.setqflist('all')
vim.cmd('copen')
end, { desc = 'Gitsigns: All hunks to quickfix' })
-- Text object -- Text object
map({ 'o', 'x' }, 'ih', ':<C-U>Gitsigns select_hunk<CR>', { desc = 'Gitsigns: Select hunk' }) map({ 'o', 'x' }, 'ih', ':<C-U>Gitsigns select_hunk<CR>', { desc = 'Gitsigns: Select hunk' })
end, end,

View File

@ -9,9 +9,9 @@ return {
tab_char = '', tab_char = '',
}, },
scope = { scope = {
enabled = true, -- Highlight current scope indent guide enabled = true, -- Highlight current scope
show_start = true, -- Disable underline at start of scope show_start = true, -- Show underline at start of scope
show_end = false, -- Don't show underline at end show_end = false, -- Don't show underline at end (can be noisy)
}, },
exclude = { exclude = {
filetypes = { filetypes = {

123
lua/plugins/none-ls.lua Normal file
View File

@ -0,0 +1,123 @@
-- 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 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()
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 {}
end,
}),
-- 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", "<leader>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", "<leader>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", "<leader>lf", function()
vim.lsp.buf.format({ async = false })
end, { desc = "Formatting: Format selection", silent = true, noremap = true })
end,
}

View File

@ -47,21 +47,21 @@ return {
-- Configure phpcs for WordPress standards -- Configure phpcs for WordPress standards
lint.linters.phpcs.cmd = find_executable({ "phpcs" }) or "phpcs" lint.linters.phpcs.cmd = find_executable({ "phpcs" }) or "phpcs"
lint.linters.phpcs.args = {
-- Build args dynamically based on project ruleset presence "-q",
-- Note: This runs once at config load, checks cwd for phpcs.xml "--report=json",
local cwd = vim.fn.getcwd() function()
local has_project_ruleset = local phpcs_xml = vim.fn.findfile("phpcs.xml", ".;")
vim.loop.fs_stat(cwd .. "/phpcs.xml") if phpcs_xml == "" then
or vim.loop.fs_stat(cwd .. "/phpcs.xml.dist") phpcs_xml = vim.fn.findfile("phpcs.xml.dist", ".;")
local phpcs_args = { "-q", "--report=json" }
if not has_project_ruleset then
table.insert(phpcs_args, "--standard=WordPress")
end end
table.insert(phpcs_args, "-") -- stdin if phpcs_xml == "" then
return "--standard=WordPress"
lint.linters.phpcs.args = phpcs_args end
return nil
end,
"-", -- stdin
}
-- Configure eslint_d to use project-local first -- Configure eslint_d to use project-local first
lint.linters.eslint_d.cmd = find_executable({ "eslint_d", "eslint" }) or "eslint_d" lint.linters.eslint_d.cmd = find_executable({ "eslint_d", "eslint" }) or "eslint_d"

View File

@ -19,9 +19,6 @@ return {
telescope.setup({ telescope.setup({
defaults = { defaults = {
-- Always search from project root (where Neovim was opened)
cwd = vim.g.project_root,
-- Minimal UI, keep it clean -- Minimal UI, keep it clean
prompt_prefix = '> ', prompt_prefix = '> ',
selection_caret = '> ', selection_caret = '> ',

View File

@ -58,12 +58,9 @@ return {
}, },
}, },
-- Indentation: Enable for languages with good indent queries -- Indentation (experimental, disable if issues)
-- PHP requires this because GetPhpIndent() depends on Vim syntax (disabled by Treesitter)
indent = { indent = {
enable = true, enable = false, -- Start disabled; can enable per-filetype if stable
-- Disable for languages with poor/incomplete indent queries if needed
disable = {},
}, },
-- Textobjects -- Textobjects

View File

@ -26,7 +26,7 @@ return {
-- Open folds when searching -- Open folds when searching
open_fold_hl_timeout = 150, open_fold_hl_timeout = 150,
close_fold_kinds_for_ft = { close_fold_kinds_for_ft = {
default = { 'imports' }, default = { 'imports', 'comment' },
}, },
preview = { preview = {
win_config = { win_config = {

View File

@ -5,93 +5,21 @@ vim.g.loaded_ruby_provider = 0
vim.g.loaded_perl_provider = 0 vim.g.loaded_perl_provider = 0
vim.g.loaded_node_provider = 0 vim.g.loaded_node_provider = 0
-- Auto-detect system theme (Linux/GNOME)
local function set_background_from_system()
local handle = io.popen('gsettings get org.gnome.desktop.interface color-scheme 2>/dev/null')
if handle then
local result = handle:read("*a")
handle:close()
if result:match("dark") then
vim.o.background = 'dark'
else
vim.o.background = 'light'
end
end
end
-- Set background based on system theme on startup
set_background_from_system()
-- Handle SIGUSR1 signal to update theme when system theme changes
-- External watcher script (scripts/theme-watcher.sh) sends this signal
vim.api.nvim_create_autocmd('Signal', {
pattern = 'SIGUSR1',
callback = function()
set_background_from_system()
end,
desc = 'Update colorscheme when system theme changes',
})
-- 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()
-- Prevent automatic directory changes when switching files
vim.opt.autochdir = false -- Keep working directory at project root
-- Enable project-local configuration files -- Enable project-local configuration files
vim.opt.exrc = true -- Load .nvim.lua from project root vim.opt.exrc = true -- Load .nvim.lua from project root
vim.opt.secure = true -- Prompt before loading untrusted files 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) -- Phase 3.2: non-plugin settings (legacy values where specified)
-- Completion UI for nvim-cmp -- Completion UI for nvim-cmp
vim.opt.completeopt = { "menu", "menuone", "noselect" } 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 -- Spelling
vim.opt.spell = true
vim.opt.spelllang = { "en_gb" } vim.opt.spelllang = { "en_gb" }
vim.opt.spelloptions = { "camel,noplainbuffer" }
-- 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) -- Keyword characters (add $ for PHP/shell variables, - for CSS/HTML/config files)
vim.opt.iskeyword:append("$") vim.opt.iskeyword:append("$")
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 -- Visuals
vim.opt.showbreak = "" vim.opt.showbreak = ""
vim.opt.listchars = { vim.opt.listchars = {
@ -120,12 +48,6 @@ vim.opt.tabstop = 4 -- Display tabs as 4 spaces
vim.opt.shiftwidth = 4 -- Indent by 4 vim.opt.shiftwidth = 4 -- Indent by 4
vim.opt.softtabstop = 4 -- Backspace removes 4 spaces vim.opt.softtabstop = 4 -- Backspace removes 4 spaces
vim.opt.expandtab = true -- Use spaces by default (overridden per-filetype) 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
vim.opt.undodir = vim.fn.stdpath("config") .. "/undodir" -- Store undo files in config directory
-- LSP diagnostics configuration -- LSP diagnostics configuration
vim.diagnostic.config({ vim.diagnostic.config({
@ -142,55 +64,9 @@ vim.diagnostic.config({
update_in_insert = false, -- Don't update diagnostics while typing update_in_insert = false, -- Don't update diagnostics while typing
severity_sort = true, -- Sort by severity (errors first) severity_sort = true, -- Sort by severity (errors first)
float = { float = {
border = "single", border = "rounded",
source = "always", -- Show diagnostic source source = "always", -- Show diagnostic source
header = "", header = "",
prefix = "", 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,
})

View File

@ -6,281 +6,5 @@ function M.safe_require(name)
return nil return nil
end 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
-- 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
-- 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()
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 return M

View File

@ -773,43 +773,3 @@ Tengo/!
Trengo Trengo
Hostinger's Hostinger's
hPanel hPanel
reauthoring
dropdowns
contenteditable
#uer/!
suer/!
RDV
param
int
bool
#/!
bspwm
composable
XDG
configs
Alacritty
overridable
www
Args
autostart
startup
deckboard
bwrap
bubblewrap
hardcodes
connectbot
pcmanfm
sxhkd
dmenu
Gippity
Keepass
bspc
POSIX
shebang
GI
Wyvern
journalwp
Jax
APA
RIS
mathjax

Binary file not shown.