Compare commits
No commits in common. "master" and "modern-restart" have entirely different histories.
master
...
modern-res
|
|
@ -43,7 +43,7 @@ This repository is being migrated to a modern, minimal Neovim setup driven by Lu
|
|||
├── oil.lua
|
||||
├── ufo.lua
|
||||
├── gitsigns.lua
|
||||
├── conform.lua
|
||||
├── none-ls.lua
|
||||
├── nvim-lint.lua
|
||||
├── mason.lua
|
||||
├── mason-lspconfig.lua
|
||||
|
|
@ -87,7 +87,7 @@ This repository is being migrated to a modern, minimal Neovim setup driven by Lu
|
|||
- UX/Editing: `Comment.nvim`, `nvim-surround`, `nvim-autopairs`, `indent-blankline.nvim`, `nvim-ufo`, `undotree`.
|
||||
- Git: `gitsigns.nvim`.
|
||||
- Copilot: `copilot.lua`, `copilot-cmp`.
|
||||
- Formatting/Linting: `conform.nvim`, `nvim-lint`.
|
||||
- Formatting/Linting: `none-ls.nvim`, `nvim-lint`.
|
||||
- LSP Management: `mason.nvim`, `mason-lspconfig.nvim`, `mason-tool-installer.nvim`.
|
||||
|
||||
## Workflow Requirements to Preserve
|
||||
|
|
@ -115,7 +115,7 @@ This repository is being migrated to a modern, minimal Neovim setup driven by Lu
|
|||
- ✅ Phase 6: UX/Editing (Comment, surround, autopairs, indent guides, UFO, undotree)
|
||||
- ✅ Phase 7: Git integration (Gitsigns)
|
||||
- ✅ Phase 8: Copilot integration (copilot.lua + copilot-cmp)
|
||||
- ✅ Phase 9: Formatting & Linting (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 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 change or decision, also update `LOG.md:1` (decisions log and design docs).
|
||||
- **After adding/removing/editing any keymaps**, immediately update `README.md` with the change (add new entries, remove deleted mappings, or update descriptions).
|
||||
- **After changing formatter/linter configuration, standards, or tool resolution**, immediately update the "Configuration" section in `README.md` (update tool lists, override instructions, or behavior descriptions).
|
||||
- At the start of each phase, confirm scope and priorities for that phase.
|
||||
- Execute phases via subphases (`N.x`), where each bullet under a phase is its own implement-and-test step (e.g., Phase 3.1, 3.2, 3.3, 3.4).
|
||||
- Record decisions and rationale in `LOG.md:1` as they're made.
|
||||
|
|
|
|||
|
|
@ -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
15
LOG.md
|
|
@ -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.
|
||||
|
||||
- 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: Enable Markdown LSP = `marksman` (lightweight, good MD features).
|
||||
- 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)
|
||||
- netrw for tree view and preview splits; Oil for operations that would break buffer names
|
||||
- PHP `gf` enhancement via `includeexpr` for WordPress/PHP path resolution
|
||||
- **Working directory locked to project root**: Disabled `autochdir`, set `netrw_keepdir = 1`, captured initial cwd in `vim.g.project_root`, configured Telescope to always search from project root regardless of current buffer
|
||||
- 2025-12-07: Treesitter Phase 5 decisions:
|
||||
- Focus on core languages: PHP, HTML, JavaScript, TypeScript, CSS, Markdown, Lua, Bash, JSON
|
||||
- Enable syntax highlighting with performance safeguard (disable for files >100KB)
|
||||
|
|
@ -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
|
||||
- **Format-on-save**: Enabled by default, toggle with `<leader>lt`
|
||||
- **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
|
||||
- **Per-filetype indentation**: Explicit settings per filetype to match formatters
|
||||
- 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)
|
||||
- Python: 4 spaces (Black/PEP 8)
|
||||
- **Global defaults**: 4 spaces (reasonable baseline for other filetypes)
|
||||
- 2026-01-12: **conform.nvim migration**:
|
||||
- **Trigger**: none-ls phpcbf builtin had preprocessing bug that added blank lines before formatting
|
||||
- **Root cause**: none-ls uses LSP protocol for formatters, adds preprocessing step that corrupted input
|
||||
- **Investigation**: Created debug wrappers, confirmed phpcbf CLI works correctly, traced issue to none-ls preprocessing
|
||||
- **Solution**: Migrated to conform.nvim (modern, actively maintained, no LSP overhead)
|
||||
- **Configuration**: Simplified config using conform's built-in formatters, only customized phpcbf for WordPress standard
|
||||
- **Benefits**: Simpler code, no custom executable resolution needed, proper stdin/tmpfile handling per formatter
|
||||
- **Removed**: none-ls.lua deleted (was renamed to .disabled during migration)
|
||||
- 2025-12-07: Kept Behaviors Phase 10:
|
||||
- **Abbreviations**: Common typo corrections (`adn→and`, `waht→what`, `tehn→then`, `functin→function`, `positin→position`) in dedicated `lua/abbreviations.lua` file for modularity
|
||||
- **Templates**: Shell script template (template.sh) auto-loaded via BufNewFile autocmd for `*.sh` files
|
||||
- **Whitespace highlighting**: Already handled via `listchars` in Phase 3.2 (settings.lua)
|
||||
- **Persistent folds**: Not needed; UFO handles folding without explicit persistence mechanism
|
||||
- **Persistent undo**: Re-enabled 2025-01-06 via `undofile=true` and `undodir` in settings.lua (preserves undo history across sessions)
|
||||
- 2025-12-11: Colorscheme Phase 11.2:
|
||||
- **Color palette extraction**: Extracted all 45 color definitions from original Paper Tonic colorscheme
|
||||
- **Structure**: Created `lua/paper-tonic-modern/colors.lua` with comprehensive documentation
|
||||
|
|
|
|||
|
|
@ -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
|
||||
- [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
|
||||
|
||||
|
|
@ -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] 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
|
||||
- [x] Add `stevearc/conform.nvim` (replaced none-ls due to phpcbf preprocessing bug)
|
||||
- [x] Configure formatters (conform has built-in support for all):
|
||||
- [x] prettier (auto-detects project-local, Mason, global)
|
||||
- [x] phpcbf (auto-detects project-local, global; customized for WordPress standard)
|
||||
- [x] stylua (auto-detects project-local, Mason, global)
|
||||
- [x] Add `mfussenegger/nvim-lint` for linting (separate from formatting)
|
||||
## Phase 9.2 — none-ls setup with project-aware executables
|
||||
- [x] Add `nvimtools/none-ls.nvim`
|
||||
- [x] Create helper function to detect project-local executables (node_modules/.bin/, vendor/bin/, Mason bin)
|
||||
- [x] Configure formatters:
|
||||
- [x] prettier (project-local first, then Mason, then global)
|
||||
- [x] phpcbf (project-local first, then global system install - already available)
|
||||
- [x] stylua (Mason installed)
|
||||
- [x] Add `mfussenegger/nvim-lint` for linting (none-ls removed most linters from builtins)
|
||||
- [x] Configure linters via nvim-lint:
|
||||
- [x] eslint_d (project-local first, then Mason, then global - daemon version for speed)
|
||||
- [x] phpcs (project-local first, then global system install - already available)
|
||||
|
|
@ -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 manual format keymaps: `<leader>lf` (buffer), `<leader>lf` (visual range)
|
||||
- [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] Removed none-ls.lua (bug caused blank lines in PHP formatting)
|
||||
|
||||
## Phase 9.3 — Mason formatter/linter installation
|
||||
- [x] Add `WhoIsSethDaniel/mason-tool-installer.nvim` for automated installation
|
||||
|
|
@ -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] Note: UFO handles folding; no explicit persistence mechanism needed
|
||||
|
||||
## Phase 10.6 — Custom tabline function
|
||||
- [x] Implement custom tabline with configurable path shortening (`lua/utils.lua`)
|
||||
- [x] Configure tabline in `lua/settings.lua` with `utils.tabline_full_parents`
|
||||
- [x] Document tabline configuration in `README.md`
|
||||
- [x] Show N parent directories in full, shorten earlier paths to first letter
|
||||
|
||||
## Phase 11 — Colorscheme: Modern Paper Tonic
|
||||
|
||||
## Phase 11.1 — Confirm scope and priorities
|
||||
|
|
@ -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 netrw browsing and preview splits
|
||||
- [ ] Validate Oil.nvim file operations
|
||||
- [x] Fix working directory behavior (disabled autochdir, locked Telescope to project root)
|
||||
|
||||
## Phase 12.5 — Language tooling validation
|
||||
- [ ] Validate HTML/PHP/JS/Markdown tooling
|
||||
|
|
|
|||
|
|
@ -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
207
README.md
|
|
@ -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 | `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 | `<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 → quickfix | Populates project-wide diagnostics |
|
||||
| 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 | `<leader>xd` | Diagnostic float | Opens hover window for cursor diagnostic |
|
||||
| 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>bg` | Toggle background (light/dark) | Switches between light and dark colorscheme modes |
|
||||
|
||||
### `lua/netrw-config.lua`
|
||||
|
||||
#### `:Ex` Command Variants
|
||||
|
||||
| Command | Opens netrw at |
|
||||
| --- | --- |
|
||||
| `:Ex` | Current working directory (`:pwd`) |
|
||||
| `:Ex .` | Current file's directory |
|
||||
| `:Ex %:h` | Current file's directory (explicit) |
|
||||
| `:Ex /path/to/dir` | Specific path |
|
||||
|
||||
#### Custom Keymaps
|
||||
|
||||
| Mode | Key | Description |
|
||||
| --- | --- | --- |
|
||||
| n | `<leader>nt` | Open netrw in new tab at current file's directory |
|
||||
| n | `<leader>nT` | Open netrw in new tab at project root |
|
||||
| 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 |
|
||||
| n | `<leader>te` | Open netrw in a new tab rooted at current file directory |
|
||||
| n | `<leader>tE` | Open netrw in a new tab rooted at project cwd |
|
||||
|
||||
## Plugin Reference
|
||||
|
||||
|
|
@ -137,7 +73,7 @@ Buffer-local keymaps available when an LSP client attaches:
|
|||
| n | `gI` | LSP: implementation |
|
||||
| n | `K` | LSP hover |
|
||||
|
||||
### Conform (Formatting) `lua/plugins/conform.lua`
|
||||
### None-ls (Formatting) `lua/plugins/none-ls.lua`
|
||||
|
||||
#### Keymaps
|
||||
|
||||
|
|
@ -178,7 +114,6 @@ Buffer-local keymaps available when inside a git repository:
|
|||
| n | `<leader>hp` | Preview hunk |
|
||||
| n | `<leader>hd` | Diff against index |
|
||||
| n | `<leader>hD` | Diff against previous commit (`~`) |
|
||||
| n | `<leader>hq` | Send all hunks to quickfix list |
|
||||
| o/x | `ih` | Text object: select git hunk |
|
||||
|
||||
### Telescope `lua/plugins/telescope.lua`
|
||||
|
|
@ -369,139 +304,3 @@ Buffer-local keymaps available when inside a git repository:
|
|||
| Mode | Key | Description |
|
||||
| --- | --- | --- |
|
||||
| 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)
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks
|
||||
(comment) @fold
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks
|
||||
(comment) @fold
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks
|
||||
(comment) @fold
|
||||
|
|
@ -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 "^//"))
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks
|
||||
(comment) @fold
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks (including /** */ docblocks)
|
||||
(comment) @fold
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks
|
||||
(comment) @fold
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks
|
||||
(comment) @fold
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks
|
||||
(comment) @fold
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks
|
||||
(comment) @fold
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
;; extends
|
||||
|
||||
;; Fold comment blocks
|
||||
(comment) @fold
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
-- 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
|
||||
|
||||
-- Reset highlights and syntax
|
||||
|
|
@ -11,10 +11,8 @@ end
|
|||
-- Set colorscheme name
|
||||
vim.g.colors_name = 'paper-tonic-modern'
|
||||
|
||||
-- Set default background if not already set
|
||||
if vim.o.background == '' then
|
||||
vim.o.background = 'light'
|
||||
end
|
||||
-- Set background to light
|
||||
vim.o.background = 'light'
|
||||
|
||||
-- Load the colorscheme
|
||||
require('paper-tonic-modern').load()
|
||||
|
|
|
|||
|
|
@ -4,28 +4,28 @@
|
|||
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
|
||||
"cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
|
||||
"cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
|
||||
"conform.nvim": { "branch": "master", "commit": "238f542a118984a88124fc915d5b981680418707" },
|
||||
"copilot-cmp": { "branch": "master", "commit": "15fc12af3d0109fa76b60b5cffa1373697e261d1" },
|
||||
"copilot.lua": { "branch": "master", "commit": "5ace9ecd0db9a7a6c14064e4ce4ede5b800325f3" },
|
||||
"gitsigns.nvim": { "branch": "main", "commit": "42d6aed4e94e0f0bbced16bbdcc42f57673bd75e" },
|
||||
"copilot.lua": { "branch": "master", "commit": "efe563802a550b7f1b7743b007987e97cba22718" },
|
||||
"gitsigns.nvim": { "branch": "main", "commit": "5813e4878748805f1518cee7abb50fd7205a3a48" },
|
||||
"indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" },
|
||||
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
|
||||
"mason-lspconfig.nvim": { "branch": "main", "commit": "fe661093f4b05136437b531e7f959af2a2ae66c8" },
|
||||
"lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" },
|
||||
"mason-lspconfig.nvim": { "branch": "main", "commit": "0b9bb925c000ae649ff7e7149c8cd00031f4b539" },
|
||||
"mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" },
|
||||
"mason.nvim": { "branch": "main", "commit": "44d1e90e1f66e077268191e3ee9d2ac97cc18e65" },
|
||||
"nvim-autopairs": { "branch": "master", "commit": "c2a0dd0d931d0fb07665e1fedb1ea688da3b80b4" },
|
||||
"nvim-cmp": { "branch": "main", "commit": "85bbfad83f804f11688d1ab9486b459e699292d6" },
|
||||
"nvim-lint": { "branch": "master", "commit": "ca6ea12daf0a4d92dc24c5c9ae22a1f0418ade37" },
|
||||
"nvim-lspconfig": { "branch": "master", "commit": "92ee7d42320edfbb81f3cad851314ab197fa324a" },
|
||||
"nvim-surround": { "branch": "main", "commit": "1098d7b3c34adcfa7feb3289ee434529abd4afd1" },
|
||||
"mason.nvim": { "branch": "main", "commit": "57e5a8addb8c71fb063ee4acda466c7cf6ad2800" },
|
||||
"none-ls.nvim": { "branch": "main", "commit": "5abf61927023ea83031753504adb19630ba80eef" },
|
||||
"nvim-autopairs": { "branch": "master", "commit": "7a2c97cccd60abc559344042fefb1d5a85b3e33b" },
|
||||
"nvim-cmp": { "branch": "main", "commit": "d97d85e01339f01b842e6ec1502f639b080cb0fc" },
|
||||
"nvim-lint": { "branch": "master", "commit": "ebe535956106c60405b02220246e135910f6853d" },
|
||||
"nvim-lspconfig": { "branch": "master", "commit": "9c923997123ff9071198ea3b594d4c1931fab169" },
|
||||
"nvim-surround": { "branch": "main", "commit": "fcfa7e02323d57bfacc3a141f8a74498e1522064" },
|
||||
"nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
|
||||
"nvim-treesitter-textobjects": { "branch": "master", "commit": "5ca4aaa6efdcc59be46b95a3e876300cfead05ef" },
|
||||
"nvim-ts-autotag": { "branch": "main", "commit": "c4ca798ab95b316a768d51eaaaee48f64a4a46bc" },
|
||||
"nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" },
|
||||
"oil.nvim": { "branch": "master", "commit": "d278dc40f9de9980868a0a55fa666fba5e6aeacb" },
|
||||
"nvim-ufo": { "branch": "main", "commit": "72d54c31079d38d8dfc5456131b1d0fb5c0264b0" },
|
||||
"oil.nvim": { "branch": "master", "commit": "cbcb3f997f6f261c577b943ec94e4ef55108dd95" },
|
||||
"plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
|
||||
"promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" },
|
||||
"telescope-fzf-native.nvim": { "branch": "main", "commit": "6fea601bd2b694c6f2ae08a6c6fab14930c60e2c" },
|
||||
"telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" },
|
||||
"undotree": { "branch": "master", "commit": "178d19e00a643f825ea11d581b1684745d0c4eda" }
|
||||
"undotree": { "branch": "master", "commit": "0f1c9816975b5d7f87d5003a19c53c6fd2ff6f7f" }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,9 +82,6 @@ vim.api.nvim_create_autocmd("FileType", {
|
|||
group = aug,
|
||||
pattern = "qf", -- Applies to both quickfix and location list
|
||||
callback = function()
|
||||
-- Disable spell checking in quickfix/location list windows
|
||||
vim.opt_local.spell = false
|
||||
|
||||
-- Use matchadd() for higher priority highlighting
|
||||
-- This will override the default qf syntax
|
||||
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", {
|
||||
group = session_aug,
|
||||
pattern = "*",
|
||||
once = true,
|
||||
nested = true, -- Allow other autocmds to fire after loading session
|
||||
callback = function()
|
||||
-- Only auto-load if:
|
||||
-- 1. Session.vim exists in cwd
|
||||
-- 2. No files were specified on command line (vim.fn.argc() == 0)
|
||||
-- 3. Not started with nvim -S (check if we already loaded a session)
|
||||
if vim.fn.argc() == 0 and vim.fn.filereadable("Session.vim") == 1 and vim.v.this_session == "" then
|
||||
vim.cmd("silent! source Session.vim")
|
||||
vim.cmd("source Session.vim")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Ensure filetype is detected for current buffer after session load
|
||||
vim.api.nvim_create_autocmd("SessionLoadPost", {
|
||||
group = session_aug,
|
||||
pattern = "*",
|
||||
callback = function()
|
||||
-- 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
|
||||
vim.api.nvim_create_autocmd("VimLeavePre", {
|
||||
group = session_aug,
|
||||
|
|
|
|||
169
lua/keymaps.lua
169
lua/keymaps.lua
|
|
@ -34,12 +34,6 @@ map('n', 'K', function()
|
|||
end
|
||||
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
|
||||
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 })
|
||||
|
|
@ -49,39 +43,144 @@ end, { desc = 'Diagnostics: Buffer errors (location list)', silent = true })
|
|||
map('n', '<leader>xE', function()
|
||||
vim.diagnostic.setqflist({ severity = vim.diagnostic.severity.ERROR })
|
||||
end, { desc = 'Diagnostics: All errors (quickfix)', silent = true })
|
||||
map('n', '[d', function()
|
||||
vim.diagnostic.goto_prev({ float = false })
|
||||
end, { desc = 'Diagnostics: Previous diagnostic', silent = true })
|
||||
map('n', ']d', function()
|
||||
vim.diagnostic.goto_next({ float = false })
|
||||
end, { desc = 'Diagnostics: Next diagnostic', silent = true })
|
||||
map('n', '<leader>xd', function()
|
||||
pcall(vim.diagnostic.open_float)
|
||||
end, { desc = 'Diagnostics: Show diagnostic under cursor', silent = true })
|
||||
map('n', '[d', vim.diagnostic.goto_prev, { desc = 'Diagnostics: Previous diagnostic', silent = true })
|
||||
map('n', ']d', vim.diagnostic.goto_next, { desc = 'Diagnostics: Next diagnostic', silent = true })
|
||||
map('n', '<leader>xd', vim.diagnostic.open_float, { desc = 'Diagnostics: Show diagnostic under cursor', silent = true })
|
||||
map('n', '<leader>xt', function()
|
||||
vim.diagnostic.enable(not vim.diagnostic.is_enabled())
|
||||
end, { desc = 'Diagnostics: Toggle display', silent = true })
|
||||
|
||||
-- Git: Modified, deleted, and untracked files to quickfix
|
||||
map('n', '<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
|
||||
map('n', '<leader>hi', function()
|
||||
require('utils').show_highlight_info()
|
||||
end, { desc = 'Debug: Show highlight group and color under cursor', silent = true })
|
||||
|
||||
-- UI: Toggle background (light/dark)
|
||||
map('n', '<leader>bg', function()
|
||||
if vim.o.background == 'dark' then
|
||||
vim.o.background = 'light'
|
||||
else
|
||||
vim.o.background = 'dark'
|
||||
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
|
||||
end, { desc = 'UI: Toggle background (light/dark)', silent = true })
|
||||
|
||||
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 })
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ vim.g.netrw_alto = 0
|
|||
-- 50% split when pressing 'p'
|
||||
vim.g.netrw_winsize = 50
|
||||
|
||||
-- Keep working directory unchanged when browsing (1 = don't change directory)
|
||||
-- Setting to 1 prevents netrw from changing vim's working directory
|
||||
vim.g.netrw_keepdir = 1
|
||||
-- Keep the current directory and browsing directory synced
|
||||
-- This makes netrw respect your current working directory
|
||||
vim.g.netrw_keepdir = 0
|
||||
|
||||
-- Open files in the same window (replace netrw buffer)
|
||||
-- Options: 0=same window, 1=horizontal split, 2=vertical split, 3=new tab, 4=previous window
|
||||
|
|
@ -40,22 +40,17 @@ vim.g.netrw_sizestyle = 'H'
|
|||
|
||||
-- Netrw file explorer keymaps
|
||||
-- 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')
|
||||
vim.cmd('tabnew')
|
||||
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)
|
||||
map('n', '<leader>nT', function()
|
||||
-- Open netrw in new tab at project root (cwd)
|
||||
map('n', '<leader>tE', function()
|
||||
vim.cmd('tabnew')
|
||||
vim.cmd('Explore ' .. vim.fn.fnameescape(vim.g.project_root))
|
||||
end, { desc = 'Netrw: Tab at project root', silent = true })
|
||||
|
||||
-- Open netrw in current window at project root (stored at startup)
|
||||
map('n', '<leader>nr', function()
|
||||
vim.cmd('Explore ' .. vim.fn.fnameescape(vim.g.project_root))
|
||||
end, { desc = 'Netrw: Open at project root', silent = true })
|
||||
vim.cmd('Explore ' .. vim.fn.fnameescape(vim.fn.getcwd()))
|
||||
end, { desc = 'Netrw: Tab explore (project root)', silent = true })
|
||||
-- Enable line numbers in netrw buffers
|
||||
vim.api.nvim_create_autocmd('FileType', {
|
||||
pattern = 'netrw',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
-- Paper Tonic Modern - Color Palette
|
||||
-- Extracted from original Paper Tonic colorscheme
|
||||
-- Supports both light (paper-like) and dark modes
|
||||
-- Light, paper-like theme with subtle colors
|
||||
|
||||
local M = {}
|
||||
|
||||
|
|
@ -9,177 +9,107 @@ local M = {}
|
|||
-- 256: Integer 0-255 for 256-color terminals
|
||||
-- ansi: ANSI color name for basic 16-color terminals
|
||||
|
||||
-- Detect current background mode
|
||||
local is_dark = vim.o.background == 'dark'
|
||||
|
||||
-- ============================================================================
|
||||
-- Background Colors
|
||||
-- ============================================================================
|
||||
|
||||
if is_dark then
|
||||
-- Dark mode: soft dark backgrounds
|
||||
M.bg = {'#1a1a1a', 234, 'black'}
|
||||
M.bg_darkest = {'#ffffff', 255, 'white'} -- Inverted: lightest for strong contrast
|
||||
M.bg_ui = {'#2a2a2a', 236, 'darkgray'}
|
||||
else
|
||||
-- Light mode: pure white paper
|
||||
M.bg = {'#ffffff', 255, 'white'}
|
||||
M.bg_darkest = {'#505050', 244, 'gray'}
|
||||
M.bg_ui = {'#efefef', 0, 'darkgray'}
|
||||
end
|
||||
-- Main background: pure white paper
|
||||
M.bg = {'#ffffff', 255, 'white'}
|
||||
|
||||
-- Darkest background: used for very strong contrast elements
|
||||
M.bg_darkest = {'#505050', 244, 'gray'}
|
||||
|
||||
-- UI background: slightly off-white for UI elements (statusline, etc.)
|
||||
M.bg_ui = {'#efefef', 0, 'darkgray'}
|
||||
|
||||
-- ============================================================================
|
||||
-- Highlight Backgrounds (general)
|
||||
-- ============================================================================
|
||||
|
||||
if is_dark then
|
||||
-- Dark mode: lighter backgrounds for highlights
|
||||
M.bg_hl_strong = {"#3a3a3a", 237, "darkgray"}
|
||||
M.bg_hl = {"#2a2a2a", 236, "darkgray"}
|
||||
M.bg_hl_weak = {"#252525", 235, "darkgray"}
|
||||
|
||||
-- Special highlight backgrounds (cyan/blue tones)
|
||||
M.bg_hl_special_strong = {"#1a4a5a", 24, "darkblue"}
|
||||
M.bg_hl_special = {"#0a3a4a", 23, "darkblue"}
|
||||
M.bg_hl_special_weak = {"#1a3545", 23, "darkblue"}
|
||||
|
||||
-- Alternative special highlight (green tones)
|
||||
M.bg_hl_special_alt_strong = {"#1a5a2a", 28, "darkgreen"}
|
||||
M.bg_hl_special_alt = {"#0a4a1a", 22, "darkgreen"}
|
||||
else
|
||||
-- Light mode: neutral gray tones
|
||||
M.bg_hl_strong = {"#dddddd", 17, "white"}
|
||||
M.bg_hl = {"#eeeeee", 250, "white"}
|
||||
M.bg_hl_weak = {"#f7f2f2", 250, "white"}
|
||||
|
||||
-- Special highlight backgrounds (cyan/blue tones)
|
||||
M.bg_hl_special_strong = {"#a3e0ff", 17, "cyan"}
|
||||
M.bg_hl_special = {"#d4f0ff", 250, "cyan"}
|
||||
M.bg_hl_special_weak = {"#e0eaff", 250, "cyan"}
|
||||
|
||||
-- Alternative special highlight (green tones)
|
||||
M.bg_hl_special_alt_strong = {"#74f283", 17, "cyan"}
|
||||
M.bg_hl_special_alt = {"#bff2cd", 250, "cyan"}
|
||||
end
|
||||
-- Selection/highlight backgrounds (neutral gray tones)
|
||||
M.bg_hl_strong = {"#dddddd", 17, "white"}
|
||||
M.bg_hl = {"#eeeeee", 250, "white"}
|
||||
M.bg_hl_weak = {"#f7f2f2", 250, "white"}
|
||||
|
||||
-- Special highlight backgrounds (cyan/blue tones - for LSP references, search, etc.)
|
||||
M.bg_hl_special_strong = {"#a3e0ff", 17, "cyan"}
|
||||
M.bg_hl_special = {"#d4f0ff", 250, "cyan"}
|
||||
M.bg_hl_special_weak = {"#e0eaff", 250, "cyan"}
|
||||
|
||||
-- Alternative special highlight (green tones - for diff additions, etc.)
|
||||
M.bg_hl_special_alt_strong = {"#74f283", 17, "cyan"}
|
||||
M.bg_hl_special_alt = {"#bff2cd", 250, "cyan"}
|
||||
|
||||
-- ============================================================================
|
||||
-- Status Backgrounds (error, success, modified, fail)
|
||||
-- ============================================================================
|
||||
|
||||
if is_dark then
|
||||
-- Dark mode: darker tinted backgrounds
|
||||
M.bg_error = {'#4a2020', 52, 'darkred'}
|
||||
M.bg_error_weak = {'#3a1a1a', 52, 'darkred'}
|
||||
M.bg_success = {'#1a3a1a', 22, 'darkgreen'}
|
||||
M.bg_modified = {'#1a1a3a', 17, 'darkblue'}
|
||||
M.bg_fail = {'#3a1a1a', 52, 'darkred'}
|
||||
else
|
||||
-- Light mode: light tinted backgrounds
|
||||
M.bg_error = {'#ffd7d7', 196, 'white'}
|
||||
M.bg_error_weak = {'#ffefef', 196, 'white'}
|
||||
M.bg_success = {'#e0ece0', 196, 'white'}
|
||||
M.bg_modified = {'#e0e0ec', 196, 'white'}
|
||||
M.bg_fail = {'#ece0e0', 196, 'white'}
|
||||
end
|
||||
-- Error backgrounds (red/pink tones)
|
||||
M.bg_error = {'#ffd7d7', 196, 'white'}
|
||||
M.bg_error_weak = {'#ffefef', 196, 'white'}
|
||||
|
||||
-- Success background (green tone)
|
||||
M.bg_success = {'#e0ece0', 196, 'white'}
|
||||
|
||||
-- Modified background (blue tone)
|
||||
M.bg_modified = {'#e0e0ec', 196, 'white'}
|
||||
|
||||
-- Fail/warning background (red tone)
|
||||
M.bg_fail = {'#ece0e0', 196, 'white'}
|
||||
|
||||
-- ============================================================================
|
||||
-- Foreground Colors (text)
|
||||
-- ============================================================================
|
||||
|
||||
if is_dark then
|
||||
-- Dark mode: light text (inverted from light mode)
|
||||
M.fg_stronger = {'#e0e0e0', 253, 'white'} -- Lightest text
|
||||
M.fg_strong = {'#c0c0c0', 250, 'white'} -- Light text
|
||||
M.fg = {'#a0a0a0', 248, 'gray'} -- Normal text (default)
|
||||
M.fg_weak = {'#808080', 244, 'gray'} -- Dimmer text (comments, less important)
|
||||
M.fg_weaker = {'#606060', 241, 'darkgray'} -- Dimmest text (very subtle)
|
||||
M.fg_exception = {'#d08080', 174, 'red'} -- Light reddish for errors/exceptions
|
||||
else
|
||||
-- Light mode: dark text on paper
|
||||
M.fg_stronger = {'#444444', 236, 'darkgrey'} -- Darkest text
|
||||
M.fg_strong = {'#666666', 236, 'darkgrey'} -- Dark text
|
||||
M.fg = {'#8c8c8c', 244, 'gray'} -- Normal text (default)
|
||||
M.fg_weak = {'#9d9d9d', 251, 'gray'} -- Light text (comments, less important)
|
||||
M.fg_weaker = {'#bbbbbb', 251, 'gray'} -- Lightest text (very subtle)
|
||||
M.fg_exception = {'#7c4444', 251, 'gray'} -- Reddish-brown for errors/exceptions
|
||||
end
|
||||
-- Main text colors (gray scale, strongest to weakest)
|
||||
M.fg_stronger = {'#444444', 236, 'darkgrey'} -- Darkest text
|
||||
M.fg_strong = {'#666666', 236, 'darkgrey'} -- Dark text
|
||||
M.fg = {'#8c8c8c', 244, 'gray'} -- Normal text (default)
|
||||
M.fg_weak = {'#9d9d9d', 251, 'gray'} -- Light text (comments, less important)
|
||||
M.fg_weaker = {'#bbbbbb', 251, 'gray'} -- Lightest text (very subtle)
|
||||
|
||||
-- Exception foreground (reddish-brown for errors/exceptions)
|
||||
M.fg_exception = {'#7c4444', 251, 'gray'}
|
||||
|
||||
-- ============================================================================
|
||||
-- Status Foreground Colors (Git/Diff - muted natural tones)
|
||||
-- ============================================================================
|
||||
|
||||
if is_dark then
|
||||
-- Dark mode: lighter muted tones
|
||||
M.success = {'#a0d0a0', 114, 'green'} -- Success/addition (muted green)
|
||||
M.modified = {'#a0a0d0', 110, 'blue'} -- Modified/change (muted blue)
|
||||
M.fail = {'#d0a0a0', 174, 'red'} -- Fail/deletion (muted red)
|
||||
else
|
||||
-- Light mode: natural muted colors
|
||||
M.success = {'#89af89', 196, 'white'} -- Success/addition (muted green)
|
||||
M.modified = {'#8989af', 196, 'white'} -- Modified/change (muted blue)
|
||||
M.fail = {'#af8989', 196, 'white'} -- Fail/deletion (muted red)
|
||||
end
|
||||
-- Git/diff status indicators (used in statusline, signs, etc.)
|
||||
-- These are muted, natural colors for git changes
|
||||
M.success = {'#89af89', 196, 'white'} -- Success/addition (muted green)
|
||||
M.modified = {'#8989af', 196, 'white'} -- Modified/change (muted blue)
|
||||
M.fail = {'#af8989', 196, 'white'} -- Fail/deletion (muted red)
|
||||
|
||||
-- ============================================================================
|
||||
-- Quickfix/Location List Diagnostic Colors (Muted tones like git/diff)
|
||||
-- ============================================================================
|
||||
|
||||
if is_dark then
|
||||
-- Dark mode: lighter muted tones
|
||||
M.qf_error = {'#d07070', 167, 'red'} -- Error (lighter muted red)
|
||||
M.qf_warn = {'#d0a080', 173, 'yellow'} -- Warning (muted orange)
|
||||
M.qf_info = {'#80b0d0', 110, 'blue'} -- Info (muted blue)
|
||||
M.qf_hint = {'#80d0d0', 116, 'cyan'} -- Hint (lighter muted cyan)
|
||||
else
|
||||
-- Light mode: darker muted tones
|
||||
M.qf_error = {'#af3f3f', 196, 'white'} -- Error (darker muted red for more contrast)
|
||||
M.qf_warn = {'#af7f5f', 196, 'white'} -- Warning (muted orange)
|
||||
M.qf_info = {'#5f8faf', 196, 'white'} -- Info (muted blue)
|
||||
M.qf_hint = {'#5fafaf', 196, 'white'} -- Hint (lighter muted blue)
|
||||
end
|
||||
-- These are for quickfix/location list only - similar muted tone to git/diff
|
||||
M.qf_error = {'#af3f3f', 196, 'white'} -- Error (darker muted red for more contrast)
|
||||
M.qf_warn = {'#af7f5f', 196, 'white'} -- Warning (muted orange)
|
||||
M.qf_info = {'#5f8faf', 196, 'white'} -- Info (muted blue)
|
||||
M.qf_hint = {'#5fafaf', 196, 'white'} -- Hint (lighter muted blue)
|
||||
|
||||
-- ============================================================================
|
||||
-- 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
|
||||
-- Fluorescent/neon aesthetic
|
||||
-- In light mode: bright, synthetic, stands out on white paper ("highlighter marker")
|
||||
-- In dark mode: slightly toned down but still prominent
|
||||
-- Fluorescent/neon aesthetic similar to bg_hl_special_alt colors
|
||||
-- Think "highlighter marker" - bright, synthetic, stands out on white paper
|
||||
|
||||
if is_dark then
|
||||
-- Dark mode: still bright but not as harsh
|
||||
M.diag_error = {'#ff4488', 204, 'red'} -- Bright pink-red
|
||||
M.diag_warn = {'#ff8833', 208, 'yellow'} -- Bright orange
|
||||
M.diag_info = {'#44ccff', 81, 'cyan'} -- Bright cyan
|
||||
M.diag_hint = {'#88ddff', 117, 'cyan'} -- Softer cyan
|
||||
M.diag_hint_dark = {'#44aacc', 74, 'cyan'} -- Medium cyan
|
||||
M.diag_weak = {'#ffaa66', 215, 'yellow'} -- Lighter orange
|
||||
|
||||
-- Diagnostic backgrounds
|
||||
M.bg_diag_error = {'#3a1a25', 52, 'darkred'}
|
||||
M.bg_diag_warn = {'#3a2a1a', 58, 'brown'}
|
||||
M.bg_diag_info = {'#1a2a3a', 17, 'darkblue'}
|
||||
M.bg_diag_hint = {'#1a2f3a', 23, 'darkblue'}
|
||||
|
||||
M.question = {'#44ccff', 81, 'cyan'}
|
||||
else
|
||||
-- Light mode: fluorescent colors for maximum visibility
|
||||
M.diag_error = {'#ff0066', 197, 'red'} -- Hot pink-red (screams "error!")
|
||||
M.diag_warn = {'#ff6600', 202, 'red'} -- Fluorescent orange (warnings)
|
||||
M.diag_info = {'#00ccff', 45, 'cyan'} -- Bright fluorescent cyan (info)
|
||||
M.diag_hint = {'#66e0ff', 81, 'cyan'} -- Softer fluorescent cyan (hint)
|
||||
M.diag_hint_dark = {'#0099cc', 38, 'cyan'} -- Darker cyan for UI elements
|
||||
M.diag_weak = {'#ff9933', 208, 'yellow'} -- Lighter orange (spelling, etc.)
|
||||
|
||||
-- Diagnostic backgrounds
|
||||
M.bg_diag_error = {'#ffe6f0', 224, 'white'}
|
||||
M.bg_diag_warn = {'#fff0e6', 223, 'white'}
|
||||
M.bg_diag_info = {'#e6f9ff', 195, 'white'}
|
||||
M.bg_diag_hint = {'#f0fcff', 195, 'white'}
|
||||
|
||||
M.question = {'#00ccff', 45, 'cyan'}
|
||||
end
|
||||
-- LSP Diagnostics - Fluorescent colors for maximum visibility
|
||||
M.diag_error = {'#ff0066', 197, 'red'} -- Hot pink-red (screams "error!")
|
||||
M.diag_warn = {'#ff6600', 202, 'red'} -- Fluorescent orange (warnings)
|
||||
M.diag_info = {'#00ccff', 45, 'cyan'} -- Bright fluorescent cyan (info - more prominent)
|
||||
M.diag_hint = {'#66e0ff', 81, 'cyan'} -- Softer fluorescent cyan (hint - less prominent)
|
||||
|
||||
-- LSP Diagnostic backgrounds - Light tinted versions for highlighting code
|
||||
M.bg_diag_error = {'#ffe6f0', 224, 'white'} -- Very light pink (for error backgrounds)
|
||||
M.bg_diag_warn = {'#fff0e6', 223, 'white'} -- Very light orange (for warning backgrounds)
|
||||
M.bg_diag_info = {'#e6f9ff', 195, 'white'} -- Very light cyan (for info backgrounds)
|
||||
M.bg_diag_hint = {'#f0fcff', 195, 'white'} -- Very light cyan (for hint backgrounds)
|
||||
|
||||
-- ============================================================================
|
||||
-- Primary Accent Colors (brownish-red tones)
|
||||
|
|
@ -187,19 +117,15 @@ end
|
|||
|
||||
-- Primary accent: For "base" languages
|
||||
-- Used for: PHP, JavaScript, Python, etc.
|
||||
if is_dark then
|
||||
-- Dark mode: lighter brownish-red tones
|
||||
M.primary_stronger = {"#d0a0a0", 181, "white"}
|
||||
M.primary_strong = {"#c09090", 181, "white"}
|
||||
M.primary = {"#b08080", 138, "gray"}
|
||||
M.primary_weak = {"#a07070", 131, "darkgray"}
|
||||
else
|
||||
-- Light mode: darker brownish-red tones
|
||||
M.primary_stronger = {"#7f4b4b", 236, "black"}
|
||||
M.primary_strong = {"#5a4444", 236, "black"}
|
||||
M.primary = {"#6b5555", 244, "gray"}
|
||||
M.primary_weak = {"#7c6666", 248, "darkgray"}
|
||||
end
|
||||
-- Languages using primary:
|
||||
-- - PHP: always primary (whether standalone or in <?php ?> tags within HTML)
|
||||
-- - JavaScript: always primary (standalone or embedded)
|
||||
-- - Python: always primary
|
||||
-- Note: Both PHP and JS use primary; acceptable since embedded JS in PHP is discouraged
|
||||
M.primary_stronger = {"#7f4b4b", 236, "black"} -- Darkest accent
|
||||
M.primary_strong = {"#5a4444", 236, "black"} -- Dark accent
|
||||
M.primary = {"#6b5555", 244, "gray"} -- Normal accent
|
||||
M.primary_weak = {"#7c6666", 248, "darkgray"} -- Light accent
|
||||
|
||||
-- ============================================================================
|
||||
-- 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
|
||||
-- - CSS rules: c2 (green) - everywhere, whether in .css or <style> tags
|
||||
|
||||
if is_dark then
|
||||
-- Dark mode: lighter tones
|
||||
-- Color 2: Green tones - CSS syntax
|
||||
M.c2_weak = {"#90d090", 114, "green"}
|
||||
M.c2 = {"#70c070", 77, "green"}
|
||||
M.c2_strong = {"#50a050", 71, "darkgreen"}
|
||||
|
||||
-- Color 3: Blue tones - HTML syntax
|
||||
M.c3_weak = {"#80b0e0", 110, "blue"}
|
||||
M.c3 = {"#60a0d0", 74, "blue"}
|
||||
M.c3_strong = {"#4080b0", 67, "darkblue"}
|
||||
|
||||
-- Color 4: Cyan tones - Reserved/Future use
|
||||
M.c4_weak = {"#a0d0e0", 152, "cyan"}
|
||||
M.c4 = {"#80c0d0", 116, "cyan"}
|
||||
M.c4_strong = {"#60a0c0", 74, "darkcyan"}
|
||||
|
||||
-- Color 5: Magenta/Pink tones - Template syntax
|
||||
M.c5_weak = {"#e080c0", 213, "magenta"}
|
||||
M.c5 = {"#d060a0", 170, "magenta"}
|
||||
M.c5_strong = {"#b04080", 133, "darkmagenta"}
|
||||
else
|
||||
-- Light mode: darker tones
|
||||
-- Color 2: Green tones - CSS syntax
|
||||
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
|
||||
-- Color 2: Green tones - CSS syntax
|
||||
-- CSS maintains green coloring in <style> tags, CSS files, CSS-in-JS, etc.
|
||||
M.c2_weak = {"#5e955e", 28, "darkgreen"}
|
||||
M.c2 = {"#008700", 22, "darkgreen"}
|
||||
M.c2_strong = {"#005a00", 22, "darkgreen"}
|
||||
|
||||
-- Color 3: Blue tones - HTML syntax
|
||||
-- HTML maintains blue coloring in .html files, within PHP, within templates, etc.
|
||||
M.c3_weak = {"#2d78b7", 20, "blue"}
|
||||
M.c3 = {"#005faf", 19, "blue"}
|
||||
M.c3_strong = {"#004f92", 17, "darkblue"}
|
||||
|
||||
-- Color 4: Cyan tones - Reserved/Future use
|
||||
-- Available for additional language if needed (TypeScript? SQL?)
|
||||
M.c4_weak = {"#78b7d5", 20, "blue"}
|
||||
M.c4 = {"#56acd7", 19, "blue"}
|
||||
M.c4_strong = {"#1596d7", 17, "darkblue"}
|
||||
|
||||
-- Color 5: Magenta/Pink tones - Template syntax
|
||||
-- Used for: Django templates, Twig, Handlebars, Jinja
|
||||
-- The base language keeps its color while template syntax uses c5
|
||||
-- Example in Twig: HTML stays blue (c3), but {{ }}, {% %}, {# #} are magenta (c5)
|
||||
M.c5_weak = {"#e846ac", 164, "magenta"}
|
||||
M.c5 = {"#d70087", 164, "magenta"}
|
||||
M.c5_strong = {"#ad006d", 164, "magenta"}
|
||||
|
||||
-- ============================================================================
|
||||
-- Helper Constants
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ return {
|
|||
-- ============================================================================
|
||||
|
||||
Normal = { fg = c.fg, bg = c.bg },
|
||||
NormalFloat = { fg = c.fg, bg = c.bg },
|
||||
FloatBorder = { fg = c.diag_hint_dark, bg = c.bg },
|
||||
NormalFloat = { fg = c.fg, bg = c.NONE },
|
||||
FloatBorder = { fg = c.fg_stronger, bg = c.NONE },
|
||||
|
||||
-- ============================================================================
|
||||
-- Cursor & Line Highlighting
|
||||
|
|
@ -66,19 +66,17 @@ return {
|
|||
-- Popup Menu (Completion)
|
||||
-- ============================================================================
|
||||
|
||||
Pmenu = { fg = c.diag_hint_dark, bg = c.bg },
|
||||
PmenuSel = { fg = c.fg_strong, bg = c.bg_hl, bold = true },
|
||||
PmenuSbar = { fg = c.NONE, bg = c.bg_hl },
|
||||
PmenuThumb = { fg = c.NONE, bg = c.fg_weaker },
|
||||
PmenuBorder = { fg = c.diag_hint_dark, bg = c.bg },
|
||||
WildMenu = { fg = c.fg_strong, bg = c.bg_hl, bold = true },
|
||||
Pmenu = { fg = c.fg, bg = c.bg_ui },
|
||||
PmenuSel = { fg = c.fg_strong, bg = c.bg_ui, bold = true },
|
||||
PmenuSbar = 'Pmenu',
|
||||
PmenuThumb = 'Pmenu',
|
||||
WildMenu = { fg = c.fg_strong, bg = c.bg_ui, bold = true },
|
||||
|
||||
-- ============================================================================
|
||||
-- Folds
|
||||
-- ============================================================================
|
||||
|
||||
Folded = { fg = c.diag_hint_dark, bg = c.NONE, bold = true },
|
||||
CursorLineFold = { fg = c.diag_hint_dark, bg = c.bg_hl, bold = true },
|
||||
Folded = { fg = c.fg_strong, bold = true },
|
||||
FoldColumn = { fg = c.fg_weak },
|
||||
|
||||
-- ============================================================================
|
||||
|
|
@ -94,19 +92,17 @@ return {
|
|||
-- Spelling
|
||||
-- ============================================================================
|
||||
|
||||
-- Spelling errors: normal text color with bright colored straight underline
|
||||
-- sp (special) sets the underline color, separate from text foreground
|
||||
SpellBad = { sp = c.diag_error, underline = true }, -- Serious error: hot pink-red underline
|
||||
SpellCap = { sp = c.diag_warn, underline = true }, -- Capitalization: orange underline
|
||||
SpellLocal = { sp = c.diag_weak, underline = true }, -- Wrong region: lighter orange underline
|
||||
SpellRare = { sp = c.diag_weak, underline = true }, -- Rare word: lighter orange underline
|
||||
SpellBad = { fg = c.alert_strong, bg = c.bg_error_weak },
|
||||
SpellCap = { fg = c.alert, bg = c.bg_error_weak },
|
||||
SpellLocal = { fg = c.alert_weak, bg = c.bg_error_weak },
|
||||
SpellRare = { fg = c.alert_weak, bg = c.bg_error_weak },
|
||||
|
||||
-- ============================================================================
|
||||
-- Messages & Prompts
|
||||
-- ============================================================================
|
||||
|
||||
ErrorMsg = { fg = c.diag_error, bold = true },
|
||||
WarningMsg = { fg = c.diag_warn, bold = true },
|
||||
ErrorMsg = { fg = c.alert, bold = true },
|
||||
WarningMsg = { fg = c.alert, bold = true },
|
||||
Question = { fg = c.question, bold = true },
|
||||
ModeMsg = { fg = c.question },
|
||||
MoreMsg = { fg = c.question },
|
||||
|
|
|
|||
|
|
@ -63,11 +63,10 @@ return {
|
|||
-- ============================================================================
|
||||
|
||||
-- Menu
|
||||
CmpItemAbbr = { fg = c.fg },
|
||||
CmpItemMenu = { fg = c.fg_weak, italic = true },
|
||||
CmpItemAbbrMatch = { fg = c.primary, bold = true },
|
||||
CmpItemAbbrMatchFuzzy = { fg = c.primary_weak },
|
||||
CmpItemAbbrDeprecated = { fg = c.fg_weak, strikethrough = true },
|
||||
CmpItemMenu = { fg = c.fg_weak, italic = true },
|
||||
|
||||
-- Kind icons/labels
|
||||
CmpItemKindDefault = { fg = c.fg },
|
||||
|
|
@ -88,7 +87,7 @@ return {
|
|||
CmpItemKindText = { fg = c.fg },
|
||||
CmpItemKindFile = { fg = c.fg },
|
||||
CmpItemKindFolder = { fg = c.fg_strong },
|
||||
CmpItemKindColor = { fg = c.diag_warn },
|
||||
CmpItemKindColor = { fg = c.alert },
|
||||
CmpItemKindUnit = { fg = c.fg_weak },
|
||||
CmpItemKindValue = { fg = c.fg_strong },
|
||||
CmpItemKindConstant = { fg = c.fg_strong },
|
||||
|
|
@ -109,7 +108,7 @@ return {
|
|||
OilSize = { fg = c.fg_weak },
|
||||
OilPermissionNone = { fg = c.fg_weaker },
|
||||
OilPermissionRead = { fg = c.success },
|
||||
OilPermissionWrite = { fg = c.diag_warn },
|
||||
OilPermissionWrite = { fg = c.alert },
|
||||
OilPermissionExecute = { fg = c.modified },
|
||||
OilCopy = { fg = c.success, bold = true },
|
||||
OilMove = { fg = c.modified, bold = true },
|
||||
|
|
@ -117,7 +116,7 @@ return {
|
|||
OilDelete = { fg = c.fail, bold = true },
|
||||
OilChange = { fg = c.modified, bold = true },
|
||||
OilRestore = { fg = c.success },
|
||||
OilPurge = { fg = c.diag_error, bold = true },
|
||||
OilPurge = { fg = c.alert_strong, bold = true },
|
||||
OilTrash = { fg = c.fail },
|
||||
OilTrashSourcePath = { fg = c.fg_weak, italic = true },
|
||||
|
||||
|
|
@ -151,8 +150,8 @@ return {
|
|||
-- ============================================================================
|
||||
|
||||
UfoFoldedBg = { bg = c.bg_hl_weak },
|
||||
UfoFoldedFg = { fg = c.diag_hint_dark },
|
||||
UfoCursorFoldedLine = { bg = c.bg_hl, fg = c.diag_hint_dark },
|
||||
UfoFoldedFg = { fg = c.fg_weak },
|
||||
UfoCursorFoldedLine = { bg = c.bg_hl, fg = c.fg },
|
||||
UfoPreviewBorder = { fg = c.fg_weak },
|
||||
UfoPreviewNormal = { bg = c.bg_ui },
|
||||
UfoPreviewCursorLine = { bg = c.bg_hl },
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@ return {
|
|||
|
||||
Underlined = { underline = true },
|
||||
Ignore = { fg = c.fg_weak },
|
||||
Error = { fg = c.diag_error, bold = true },
|
||||
Todo = { fg = c.diag_error },
|
||||
Error = { fg = c.alert_strong, bold = true },
|
||||
Todo = { fg = c.alert_strong },
|
||||
|
||||
-- ============================================================================
|
||||
-- Language-Specific: CSS
|
||||
|
|
|
|||
|
|
@ -101,10 +101,10 @@ return {
|
|||
|
||||
['@comment'] = { fg = c.fg_weaker, italic = true, bold = true },
|
||||
['@comment.documentation'] = { fg = c.fg_weak, italic = true },
|
||||
['@comment.error'] = { fg = c.diag_error, bold = true },
|
||||
['@comment.warning'] = { fg = c.diag_warn, bold = true },
|
||||
['@comment.todo'] = { fg = c.diag_error },
|
||||
['@comment.note'] = { fg = c.diag_weak },
|
||||
['@comment.error'] = { fg = c.alert_strong, bold = true },
|
||||
['@comment.warning'] = { fg = c.alert, bold = true },
|
||||
['@comment.todo'] = { fg = c.alert_strong },
|
||||
['@comment.note'] = { fg = c.alert_weak },
|
||||
|
||||
-- ============================================================================
|
||||
-- Markup (Markdown, etc.)
|
||||
|
|
@ -292,6 +292,6 @@ return {
|
|||
['@none'] = {},
|
||||
['@conceal'] = { fg = c.fg_weaker },
|
||||
|
||||
-- @spell and @nospell should not define any styling
|
||||
-- This allows vim.wo.spell to control spell highlighting via SpellBad/SpellCap/etc
|
||||
['@spell'] = {},
|
||||
['@nospell'] = {},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,13 @@
|
|||
|
||||
local M = {}
|
||||
|
||||
-- Import color palette
|
||||
local colors = require('paper-tonic-modern.colors')
|
||||
|
||||
-- ============================================================================
|
||||
-- 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
|
||||
local function get_color(color_def, mode)
|
||||
if type(color_def) == 'string' then
|
||||
|
|
@ -96,17 +92,6 @@ end
|
|||
-- ============================================================================
|
||||
|
||||
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
|
||||
-- Each module returns a table of { group_name = spec }
|
||||
|
||||
|
|
@ -133,8 +118,9 @@ function M.load()
|
|||
-- Plugin highlights (Telescope, Gitsigns, cmp, etc.)
|
||||
local plugins = require('paper-tonic-modern.groups.plugins')
|
||||
M.highlight_all(plugins)
|
||||
|
||||
-- Export colors for external use
|
||||
M.colors = colors
|
||||
end
|
||||
|
||||
-- Export colors for use in other modules
|
||||
M.colors = colors
|
||||
|
||||
return M
|
||||
|
|
|
|||
|
|
@ -34,32 +34,8 @@ return {
|
|||
preselect = cmp.PreselectMode.None,
|
||||
completion = { completeopt = "menu,menuone,noinsert,noselect" },
|
||||
window = {
|
||||
completion = cmp.config.window.bordered({
|
||||
border = {
|
||||
{ "🭽", "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",
|
||||
}),
|
||||
completion = cmp.config.window.bordered(),
|
||||
documentation = cmp.config.window.bordered(),
|
||||
},
|
||||
}
|
||||
end,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -25,27 +25,16 @@ return {
|
|||
vim.keymap.set(mode, l, r, opts)
|
||||
end
|
||||
|
||||
-- Hunk navigation (includes zt to center after movement)
|
||||
-- Note: gs.next_hunk/prev_hunk are async, so zt needs a delay to center after cursor moves
|
||||
-- Hunk navigation
|
||||
map('n', ']h', function()
|
||||
if vim.wo.diff then return ']czt' end
|
||||
vim.schedule(function()
|
||||
gs.next_hunk()
|
||||
end)
|
||||
vim.defer_fn(function()
|
||||
vim.cmd('normal! zt')
|
||||
end, 50)
|
||||
if vim.wo.diff then return ']c' end
|
||||
vim.schedule(function() gs.next_hunk() end)
|
||||
return '<Ignore>'
|
||||
end, { expr = true, desc = 'Gitsigns: Next hunk' })
|
||||
|
||||
map('n', '[h', function()
|
||||
if vim.wo.diff then return '[czt' end
|
||||
vim.schedule(function()
|
||||
gs.prev_hunk()
|
||||
end)
|
||||
vim.defer_fn(function()
|
||||
vim.cmd('normal! zt')
|
||||
end, 50)
|
||||
if vim.wo.diff then return '[c' end
|
||||
vim.schedule(function() gs.prev_hunk() end)
|
||||
return '<Ignore>'
|
||||
end, { expr = true, desc = 'Gitsigns: Previous hunk' })
|
||||
|
||||
|
|
@ -60,12 +49,6 @@ return {
|
|||
map('n', '<leader>hp', gs.preview_hunk, { desc = 'Gitsigns: Preview hunk' })
|
||||
map('n', '<leader>hd', gs.diffthis, { 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
|
||||
map({ 'o', 'x' }, 'ih', ':<C-U>Gitsigns select_hunk<CR>', { desc = 'Gitsigns: Select hunk' })
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ return {
|
|||
tab_char = '│',
|
||||
},
|
||||
scope = {
|
||||
enabled = true, -- Highlight current scope indent guide
|
||||
show_start = true, -- Disable underline at start of scope
|
||||
show_end = false, -- Don't show underline at end
|
||||
enabled = true, -- Highlight current scope
|
||||
show_start = true, -- Show underline at start of scope
|
||||
show_end = false, -- Don't show underline at end (can be noisy)
|
||||
},
|
||||
exclude = {
|
||||
filetypes = {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -47,21 +47,21 @@ return {
|
|||
|
||||
-- Configure phpcs for WordPress standards
|
||||
lint.linters.phpcs.cmd = find_executable({ "phpcs" }) or "phpcs"
|
||||
|
||||
-- Build args dynamically based on project ruleset presence
|
||||
-- Note: This runs once at config load, checks cwd for phpcs.xml
|
||||
local cwd = vim.fn.getcwd()
|
||||
local has_project_ruleset =
|
||||
vim.loop.fs_stat(cwd .. "/phpcs.xml")
|
||||
or vim.loop.fs_stat(cwd .. "/phpcs.xml.dist")
|
||||
|
||||
local phpcs_args = { "-q", "--report=json" }
|
||||
if not has_project_ruleset then
|
||||
table.insert(phpcs_args, "--standard=WordPress")
|
||||
end
|
||||
table.insert(phpcs_args, "-") -- stdin
|
||||
|
||||
lint.linters.phpcs.args = phpcs_args
|
||||
lint.linters.phpcs.args = {
|
||||
"-q",
|
||||
"--report=json",
|
||||
function()
|
||||
local phpcs_xml = vim.fn.findfile("phpcs.xml", ".;")
|
||||
if phpcs_xml == "" then
|
||||
phpcs_xml = vim.fn.findfile("phpcs.xml.dist", ".;")
|
||||
end
|
||||
if phpcs_xml == "" then
|
||||
return "--standard=WordPress"
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
"-", -- stdin
|
||||
}
|
||||
|
||||
-- Configure eslint_d to use project-local first
|
||||
lint.linters.eslint_d.cmd = find_executable({ "eslint_d", "eslint" }) or "eslint_d"
|
||||
|
|
|
|||
|
|
@ -19,9 +19,6 @@ return {
|
|||
|
||||
telescope.setup({
|
||||
defaults = {
|
||||
-- Always search from project root (where Neovim was opened)
|
||||
cwd = vim.g.project_root,
|
||||
|
||||
-- Minimal UI, keep it clean
|
||||
prompt_prefix = '> ',
|
||||
selection_caret = '> ',
|
||||
|
|
|
|||
|
|
@ -58,12 +58,9 @@ return {
|
|||
},
|
||||
},
|
||||
|
||||
-- Indentation: Enable for languages with good indent queries
|
||||
-- PHP requires this because GetPhpIndent() depends on Vim syntax (disabled by Treesitter)
|
||||
-- Indentation (experimental, disable if issues)
|
||||
indent = {
|
||||
enable = true,
|
||||
-- Disable for languages with poor/incomplete indent queries if needed
|
||||
disable = {},
|
||||
enable = false, -- Start disabled; can enable per-filetype if stable
|
||||
},
|
||||
|
||||
-- Textobjects
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ return {
|
|||
-- Open folds when searching
|
||||
open_fold_hl_timeout = 150,
|
||||
close_fold_kinds_for_ft = {
|
||||
default = { 'imports' },
|
||||
default = { 'imports', 'comment' },
|
||||
},
|
||||
preview = {
|
||||
win_config = {
|
||||
|
|
|
|||
126
lua/settings.lua
126
lua/settings.lua
|
|
@ -5,93 +5,21 @@ vim.g.loaded_ruby_provider = 0
|
|||
vim.g.loaded_perl_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
|
||||
vim.opt.exrc = true -- Load .nvim.lua from project root
|
||||
vim.opt.secure = true -- Prompt before loading untrusted files
|
||||
|
||||
-- Custom tabline configuration
|
||||
-- Load utils module for custom tabline function
|
||||
local utils = require('utils')
|
||||
|
||||
-- Configure number of parent directories to show in full (default: 1)
|
||||
-- Examples:
|
||||
-- 1: /path/to/my/project/src/file.txt → pat/to/my/project/src/file.txt
|
||||
-- 2: /path/to/my/project/src/file.txt → pat/to/my/project/src/file.txt
|
||||
utils.tabline_full_parents = 1
|
||||
|
||||
-- Configure number of characters to show for shortened directories (default: 3)
|
||||
-- Examples:
|
||||
-- 3: /path/to/my → pat/to/my
|
||||
-- 1: /path/to/my → p/t/m
|
||||
utils.tabline_shorten_length = 4
|
||||
|
||||
-- Use custom tabline function
|
||||
vim.opt.tabline = '%!v:lua.require("utils").custom_tabline()'
|
||||
vim.opt.showtabline = 1 -- Show tabline only when there are 2+ tabs
|
||||
|
||||
-- Phase 3.2: non-plugin settings (legacy values where specified)
|
||||
-- Completion UI for nvim-cmp
|
||||
vim.opt.completeopt = { "menu", "menuone", "noselect" }
|
||||
|
||||
-- Built-in completion popup style (for Ctrl-X Ctrl-N/F completions)
|
||||
-- Use floating window with border for native completion
|
||||
vim.opt.pumblend = 0 -- No transparency
|
||||
-- Note: Native completion (pumvisible) doesn't support custom borders like LSP floats
|
||||
-- The Pmenu* highlight groups control its appearance
|
||||
|
||||
-- Spelling
|
||||
vim.opt.spell = true
|
||||
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)
|
||||
vim.opt.iskeyword:append("$")
|
||||
vim.opt.iskeyword:append("-")
|
||||
|
||||
-- Terminal and color support
|
||||
vim.opt.termguicolors = true -- Enable 24-bit RGB color (required for colored underlines)
|
||||
|
||||
-- Visuals
|
||||
vim.opt.showbreak = " ↳"
|
||||
vim.opt.listchars = {
|
||||
|
|
@ -120,12 +48,6 @@ vim.opt.tabstop = 4 -- Display tabs as 4 spaces
|
|||
vim.opt.shiftwidth = 4 -- Indent by 4
|
||||
vim.opt.softtabstop = 4 -- Backspace removes 4 spaces
|
||||
vim.opt.expandtab = true -- Use spaces by default (overridden per-filetype)
|
||||
vim.opt.autoindent = true -- Copy indent from current line when starting new line
|
||||
vim.opt.smartindent = true -- Smart autoindenting (C-like programs, PHP, etc.)
|
||||
|
||||
-- Persistent undo
|
||||
vim.opt.undofile = true -- Enable persistent undo
|
||||
vim.opt.undodir = vim.fn.stdpath("config") .. "/undodir" -- Store undo files in config directory
|
||||
|
||||
-- LSP diagnostics configuration
|
||||
vim.diagnostic.config({
|
||||
|
|
@ -142,55 +64,9 @@ vim.diagnostic.config({
|
|||
update_in_insert = false, -- Don't update diagnostics while typing
|
||||
severity_sort = true, -- Sort by severity (errors first)
|
||||
float = {
|
||||
border = "single",
|
||||
border = "rounded",
|
||||
source = "always", -- Show diagnostic source
|
||||
header = "",
|
||||
prefix = "",
|
||||
},
|
||||
})
|
||||
|
||||
-- Configure LSP floating windows with borders and padding
|
||||
-- Custom border with box-drawing characters flush to window edges
|
||||
-- Using heavy/light box-drawing characters positioned at cell edges
|
||||
local border = {
|
||||
{ "🭽", "FloatBorder" }, -- top-left corner
|
||||
{ "▔", "FloatBorder" }, -- top horizontal line (at top edge)
|
||||
{ "🭾", "FloatBorder" }, -- top-right corner
|
||||
{ "▕", "FloatBorder" }, -- right vertical line (at right edge)
|
||||
{ "🭿", "FloatBorder" }, -- bottom-right corner
|
||||
{ "▁", "FloatBorder" }, -- bottom horizontal line (at bottom edge)
|
||||
{ "🭼", "FloatBorder" }, -- bottom-left corner
|
||||
{ "▏", "FloatBorder" }, -- left vertical line (at left edge)
|
||||
}
|
||||
|
||||
-- Override the default open_floating_preview to add padding by modifying content
|
||||
local orig_util_open_floating_preview = vim.lsp.util.open_floating_preview
|
||||
function vim.lsp.util.open_floating_preview(contents, syntax, opts, ...)
|
||||
opts = opts or {}
|
||||
opts.border = opts.border or border
|
||||
|
||||
-- Add padding by wrapping content with empty lines and spacing
|
||||
if type(contents) == "table" and #contents > 0 then
|
||||
-- Add empty lines at top and bottom
|
||||
table.insert(contents, 1, "")
|
||||
table.insert(contents, 1, "")
|
||||
table.insert(contents, "")
|
||||
table.insert(contents, "")
|
||||
|
||||
-- Add horizontal padding (spaces) to each content line
|
||||
for i = 3, #contents - 2 do
|
||||
if type(contents[i]) == "string" then
|
||||
contents[i] = " " .. contents[i] .. " "
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return orig_util_open_floating_preview(contents, syntax, opts, ...)
|
||||
end
|
||||
|
||||
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {
|
||||
border = border,
|
||||
})
|
||||
vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, {
|
||||
border = border,
|
||||
})
|
||||
|
|
|
|||
276
lua/utils.lua
276
lua/utils.lua
|
|
@ -6,281 +6,5 @@ function M.safe_require(name)
|
|||
return nil
|
||||
end
|
||||
|
||||
-- Number of parent directories to show in full in the tabline
|
||||
-- The rest will be shortened according to tabline_shorten_length
|
||||
-- Example: with full_parents = 1, /path/to/my/project/src/file.txt becomes pat/to/my/project/src/file.txt
|
||||
-- Example: with full_parents = 2, it becomes pat/to/my/project/src/file.txt
|
||||
M.tabline_full_parents = 1
|
||||
|
||||
-- Number of characters to show for shortened directory names in the tabline
|
||||
-- Example: with shorten_length = 3, /path/to/my becomes pat/to/my
|
||||
-- Example: with shorten_length = 1, /path/to/my becomes p/t/m
|
||||
M.tabline_shorten_length = 3
|
||||
|
||||
-- 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
|
||||
|
||||
|
|
|
|||
|
|
@ -773,20 +773,3 @@ Tengo/!
|
|||
Trengo
|
||||
Hostinger's
|
||||
hPanel
|
||||
reauthoring
|
||||
dropdowns
|
||||
contenteditable
|
||||
#uer/!
|
||||
suer/!
|
||||
RDV
|
||||
param
|
||||
int
|
||||
bool
|
||||
#/!
|
||||
bspwm
|
||||
composable
|
||||
XDG
|
||||
configs
|
||||
Alacritty
|
||||
overridable
|
||||
www
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue