Compare commits

..

No commits in common. "master" and "no-tree" have entirely different histories.

122 changed files with 2896 additions and 5375 deletions

View file

@ -1,25 +0,0 @@
# Neovim Config - AI Agent Instructions
**All agent instructions are in `AGENTS.md` at the repository root.**
This file exists to satisfy GitHub Copilot's convention of looking for instructions in `.github/copilot-instructions.md`.
## Quick Reference
- **Primary instructions**: See `AGENTS.md`
- **Decision log**: See `LOG.md`
- **Keymap reference**: See `README.md`
## Key Rules (Summary)
1. **Read `AGENTS.md` first** - it contains all coding rules, architecture decisions, and what NOT to reintroduce
2. **Update docs after changes**:
- Decisions → update `LOG.md`
- User-facing behavior and keymaps → update `README.md`
3. **Modern Lua-first**: Use `vim.opt`, `vim.keymap.set`, `vim.api` - avoid Vimscript
4. **LSP (Neovim 0.11+)**: Use `vim.lsp.config()` + `vim.lsp.enable()`, NOT lspconfig `.setup()`
5. **Project config**: Native `exrc` + `secure`, no neoconf plugin
6. **No new plugins** without explicit user approval
For complete details, workflow requirements, architecture decisions, and troubleshooting, see `AGENTS.md`.

9
.gitignore vendored
View file

@ -1,3 +1,6 @@
.nvimlog bundle/
Session.vim autoload/
nvim.log undodir/*
.vimtmp/*
view/*
tmpdir

View file

@ -1,70 +0,0 @@
# AGENTS: Neovim Configuration
Scope: the entire `~/.config/nvim` repository.
## Current setup
This is the active, daily-use Neovim 0.12 configuration. It is Lua-first,
managed with lazy.nvim, and intended to remain small, fast, and maintainable.
The main workflows are:
- netrw for tree browsing, previews, and file operations
- Telescope for fuzzy files, grep, buffers, and symbols
- native `vim.lsp.config()` / `vim.lsp.enable()` configuration
- native insert-mode completion and snippets
- Copilot inline suggestions, separate from the completion menu
- Tree-sitter, Conform, nvim-lint, Gitsigns, and nvim-ufo
- native `nvim.undotree`
- project-local `.nvim.lua` files through native `exrc` + `secure`
`README.md` documents the user-facing workflows and keymaps. `LOG.md` records
architectural decisions.
## Maintenance rules
- Prefer Lua APIs such as `vim.opt`, `vim.keymap.set`, and
`vim.api.nvim_create_autocmd`; use Vimscript only when a native integration
requires it.
- Keep non-plugin behavior in focused modules under `lua/`.
- Keep each plugin specification self-contained under `lua/plugins/`.
- Continue using lazy.nvim; do not mix plugin managers without an explicit
decision to migrate.
- Do not add a plugin without explicit user confirmation.
- Keep netrw as the sole file explorer and Telescope as the fuzzy navigator.
- Use Neovim's native completion rather than reintroducing nvim-cmp or LuaSnip.
- Keep Copilot as inline suggestions rather than a completion-menu source.
- Use the modern LSP API; do not restore legacy `lspconfig.setup()` calls or
neoconf.
- Preserve native project-local configuration through `exrc` + `secure`.
- Keep providers for Ruby, Perl, and Node disabled unless an active plugin
requires one.
- Preserve the existing Lua tabline, but do not restore the old Vimscript
tabline/statusline, foldtext, cursorline/column window hacks, CoC, Neo-tree,
motion plugins, or Markdown rendering plugins unless the user explicitly
revisits those decisions.
- Preserve unrelated user changes and keep patches narrowly scoped.
## Documentation and validation
- Update `README.md` whenever user-facing behavior or keymaps change.
- Update its configuration section when formatter, linter, or tool resolution
changes.
- Record significant architectural decisions in `LOG.md`.
- After configuration or plugin changes, run at least:
```bash
nvim --headless -c 'lua print("Config loads")' -c 'quitall'
```
- For relevant changes, also exercise the affected workflow interactively.
## History
This configuration began as a large Vimscript setup with bundled plugins and
was incrementally rebuilt as a modern Lua configuration. The migration covered
lazy.nvim, native LSP and project configuration, native completion and
Undotree, Telescope, a netrw-centered explorer workflow, Tree-sitter, Copilot,
formatting, linting, and the Paper Tonic colorscheme. After sustained daily use
on Neovim 0.12.6, the migration was declared complete on 2026-10-28. The
decision history remains in `LOG.md` and Git history.

277
LOG.md
View file

@ -1,277 +0,0 @@
# Neovim Rebuild — Decisions & Reference
Historical migration record and ongoing architectural decision log.
- Maintenance instructions: `AGENTS.md`
- Active workflows and keymap reference: `README.md` and `:KeymapsGuide`
## Decisions Log
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).
- 2025-12-06: No direnv fallback for local config (keep setup simple and repo-driven).
- 2025-12-06: Project-local config = layered JSON `.nvim.json` + `.nvim.local.json` with `.nvimroot` as workspace boundary marker (multi-repo friendly).
- 2025-12-06: Switch to `folke/neoconf.nvim` for project-local configuration (supersedes custom layered JSON loader); use `.neoconf.json` at workspace root.
- 2025-12-06: Use `mason.nvim` + `mason-lspconfig.nvim` to install/manage LSP servers; keep custom lspconfig setup; include `.neoconf.json` in root detection.
- For PHP validation inside this repo: we require `.neoconf.json` to attach `intelephense` to avoid the repo’s `.git` being chosen as the LSP root.
- 2025-12-06: Plugin approval policy — other plugins are allowed, but do not install any plugin not listed without explicit confirmation.
- 2025-12-07: **CRITICAL REGRESSION FIX**: Neovim 0.11+ does NOT support `on_new_config` callback in `vim.lsp.config()`. Project-local settings (`.nvim.lua`) must be loaded via `LspAttach` autocmd instead. The `on_new_config` hook was silently ignored, causing empty settings and breaking goto-definition for external includes. Solution: Use `LspAttach` to load project settings, merge into `client.settings`, and send `workspace/didChangeConfiguration` notification.
- 2025-12-07: Native `exrc` + `.nvim.lua` finalized as project-local config approach (replaces neoconf, which is incompatible with Neovim 0.11+ native LSP API). Security via `vim.opt.secure = true`.
- 2025-12-07: Navigation Phase 4 decisions:
- Skip Neo-tree in favor of netrw for project visualization
- Use Telescope as primary "find file" tool with fuzzy finding
- 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
- 2026-07-06: **Alternate-buffer netrw skip**: Added a lightweight real-file buffer tracker and mapped `<C-6>`/`<C-^>` so alternate-buffer jumps skip netrw explorer buffers when `#` points at netrw. Netrw buffers are also marked unlisted on FileType to reduce their impact on buffer navigation while preserving netrw browsing behavior. Fallback uses explicit `:buffer #` instead of `:normal! <C-^>` so regular alternate-file switching still works from the Lua mapping.
- 2026-07-07: **Netrw alternate detection fix**: Real netrw alternate buffers can lose `filetype=netrw` and remain as an unlisted `NetrwTreeListing` buffer. The alternate-buffer skip now detects that buffer name as netrw so `<C-6>` returns to the previous real file instead of reopening an empty explorer 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)
- Incremental selection: CR to expand, BS to shrink, S-CR for scope expansion
- Textobjects for functions (`af`/`if`), classes (`ac`/`ic`), parameters (`aa`/`ia`), conditionals, loops, comments
- Movement keymaps: `]f`/`[f` for next/prev function, `]c`/`[c` for classes, `]a`/`[a` for parameters
- Parameter swapping: `<leader>a`/`<leader>A` to swap with next/prev parameter
- Auto-tag for HTML/PHP/JS/React/TS/Vue files (auto-close, auto-rename, close-on-slash)
- Indent disabled initially (experimental); can enable per-filetype if stable
- 2025-12-07: UX/Editing Phase 6 decisions:
- Enable signcolumn=yes (always show for LSP diagnostics, git signs)
- Enable cursorline (highlight current line)
- Enable colorcolumn=80,120 (visual guides at 80 and 120 chars)
- NO cursorcolumn (as per AGENTS.md)
- NO auto-window toggling behavior (keep settings static, no per-window autocmds)
- NO motion plugin (skip leap/flash for simplicity)
- Undotree moved to separate phase (6.8)
- Comment.nvim for commenting (gcc/gc/gbc/gb keymaps)
- nvim-surround for text objects (ys/ds/cs operators)
- nvim-autopairs for auto-closing brackets/quotes with Treesitter and cmp integration
- indent-blankline for visual indent guides with scope highlighting
- nvim-ufo for enhanced folding with Treesitter/LSP providers (zR/zM/K for peek)
- undotree for visual undo history (<leader>u to toggle)
- 2025-12-07: Git integration Phase 7:
- Gitsigns with minimal config for signs in gutter (add, change, delete markers)
- Hunk navigation: `]h`/`[h` for next/prev hunk
- Hunk actions: stage, reset, preview
- NO inline blame or advanced features (keep minimal)
- 2025-12-07: Copilot Phase 8:
- Integrated via copilot.lua + copilot-cmp (completion source)
- Auto-trigger suggestions as you type
- Copilot suggestions appear before LSP in completion menu (higher priority)
- Enabled for all filetypes
- No specific Copilot keymaps (use existing cmp keymaps)
- Node.js v22.21.1 confirmed working
- 2025-12-07: Formatting & Linting Phase 9:
- **Philosophy**: Formatters are authoritative; Neovim settings match formatter output
- **Formatters**: prettier (JS/TS/CSS/JSON/MD/HTML), phpcbf (PHP/WordPress), stylua (Lua), black (Python)
- **Linters**: eslint_d (JS/TS), phpcs (PHP/WordPress), markdownlint (Markdown), ruff (Python)
- **Strategy**: Project-local executables first (node_modules/.bin/, vendor/bin/), then Mason, then system PATH
- **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)
- **Python support**: pyright LSP, ruff formatter/linter, treesitter parser (the original black plan was superseded)
- **Per-filetype indentation**: Explicit settings per filetype to match formatters
- PHP: tabs, 2-space display (WordPress standards)
- JS/TS/CSS/JSON/HTML: 2 spaces (Prettier)
- Lua: 2 spaces (common convention)
- 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
- **Categories**: Background colors (main, UI, highlights, status), foreground colors (text grays, exceptions), alerts/diagnostics, primary accents (brownish-red), and secondary accents (green/blue/cyan/magenta palettes)
- **Format**: Each color defined as `{hex, 256-color, ansi}` tuple for broad terminal compatibility
- **Philosophy**: Light, paper-like theme with subtle colors for long reading sessions
- **Next steps**: Phase 11.3 will create colorscheme structure and entry point
- 2025-12-11: Colorscheme Phase 11.3:
- **Colorscheme structure**: Created complete modular colorscheme architecture
- **Entry point**: `colors/paper-tonic-modern.lua` properly resets and loads colorscheme
- **Main module**: `lua/paper-tonic-modern/init.lua` with helper functions for setting highlights
- **Helper functions**: `M.highlight()` for single groups, `M.highlight_all()` for batch operations
- **Color conversion**: Smart conversion between gui/cterm/ansi formats
- **Group modules**: Created stub files for editor, syntax, treesitter, semantic, lsp, and plugins
- **Loading order**: Structured to load groups in logical sequence (editor → syntax → treesitter → semantic → lsp → plugins)
- **Validation**: Colorscheme loads successfully, highlights applied correctly
- **Language color consistency**: All documentation updated to reflect that each language maintains its color identity everywhere (PHP=primary, HTML=blue, CSS=green, templates=magenta)
- 2025-12-11: Colorscheme Phase 11.4:
- **Editor highlights**: Comprehensive UI element highlighting (Normal, StatusLine, CursorLine, Visual, Search, Pmenu, Folds, Diffs, Spelling, Messages)
- **Syntax highlights**: Traditional Vim syntax groups (Comment, String, Function, Keyword, Type, etc.) with language-specific overrides
- **Language support**: Added specific highlighting for CSS (green/c2), HTML (blue/c3), Lua, Markdown, JSON, JavaScript/TypeScript, PHP
- **Color verification**: All key groups tested and verified (Normal=#8c8c8c on #ffffff, htmlTag=#005faf, cssClassName=#008700)
- **CSS classes**: Green tones (c2) for CSS syntax elements
- **HTML tags**: Blue tones (c3) for HTML structure
- **PHP syntax**: Primary brownish-red tones for PHP keywords/functions
- **Test file**: Created test-colorscheme.lua for visual verification
- 2025-12-11: Colorscheme Phase 11.5:
- **TreeSitter highlights**: Comprehensive modern `@*` highlight groups implemented
- **Core groups**: @variable, @function, @keyword, @string, @number, @boolean, @comment, @type, @property, @operator, @punctuation
- **Language constructs**: @keyword.function, @keyword.return, @keyword.conditional, @keyword.repeat, @keyword.exception
- **Markup**: @markup.strong, @markup.italic, @markup.heading (1-6), @markup.link, @markup.raw, @markup.list
- **Template languages**: @punctuation.special.twig, @keyword.jinja (magenta c5 for template syntax)
- **Diagnostics**: @diff.plus, @diff.minus, @diff.delta
- **Debug tool**: Added `<leader>hi` keymap to show highlight group and resolved colors under cursor
- **Validation**: All TreeSitter groups tested and verified with correct palette colors
- 2025-12-11: Colorscheme Phase 11.6:
- **Language-specific TreeSitter groups**: Enhanced with comprehensive per-language overrides
- **CSS/SCSS**: Properties, variables ($var, --custom), functions, tag selectors (green c2 tones)
- **HTML**: Tags, attributes, delimiters (blue c3 throughout)
- **PHP**: Variables ($var), functions, methods, built-ins ($this, $GLOBALS), properties (primary brownish-red)
- **JavaScript/TypeScript**: Variables, functions, properties, built-ins (this, arguments), JSX/TSX tags (primary + blue for JSX)
- **Lua**: Variables, functions, built-ins (_G, _VERSION), properties (primary)
- **Markdown**: Headings (1-6), links, code blocks, inline code (primary for structure)
- **JSON**: Properties, strings, numbers, booleans (neutral colors)
- **Bash/Shell**: Variables, functions, built-ins, strings (primary)
- **Color consistency verified**: HTML=#005faf (blue), CSS properties=#7c6666 (primary_weak), PHP variables=#7c6666
- **Test files**: Created test-html.html, test-css.css, test-colors.php for validation
- 2025-12-11: Colorscheme Phase 11.7:
- **Semantic highlighting**: Custom TreeSitter captures for cross-language consistency
- **CSS semantic captures**: @CssClassName (green c2), @CssIdentifier (strong green c2_strong, bold), @cssPseudoClass (bold brownish-red primary_strong - distinguishes from green selectors), @cssPseudoElement (bold brownish-red), @cssNestingSelector (strong green), @CssUniversalSelector
- **CSS properties**: @CssProp (primary_weak), @CssPropertyValue (bold fg), @CssUnit (delimiter)
- **CSS media queries**: @cssMediaFeatureName, @cssMediaQuery, @cssMediaQueryValue
- **HTML attributes**: @ClassNameAttribute, @IdAttribute, @DataAttribute (all fg_weak for attribute names)
- **HTML data attributes**: @DataAttributeValue (Function color)
- **Cross-language consistency**: Critical feature - class="my-class" in HTML uses same color (@CssClassName, green c2) as .my-class in CSS; id="my-id" in HTML uses same color (@CssIdentifier, green c2_strong) as #my-id in CSS
- **Visual benefits**: Easy to visually match selectors between HTML and CSS files; consistent color coding across languages
- **Custom queries**: Leverages existing after/queries/css/highlights.scm and after/queries/html/highlights.scm
- **Priority fix**: Added `(#set! priority 200)` to all custom captures to override built-in captures (@string priority 99, @constant default priority)
- **Technical note**: Custom queries require `;extends` directive and higher priority than built-in captures to work correctly
- **Pseudo-class design**: Uses bold brownish-red (primary_strong) instead of green to distinguish behavioral modifiers (:hover, :focus) from structural selectors (.class, #id)
- 2025-12-11: Colorscheme Phase 11.8:
- **LSP highlights**: Comprehensive diagnostic and UI highlights
- **LSP diagnostics**: Error (red #d70000), Warn (orange #d75f00), Info (blue #8989af), Hint (green #89af89)
- **LSP diagnostic UI**: Underlines (undercurl for errors/warnings), signs, floating windows, virtual text with tinted backgrounds
- **LSP references**: Cyan background gradients - Text (#e0eaff subtle), Read (#d4f0ff medium), Write (#a3e0ff strong)
- **LSP semantic tokens**: Fallback groups linking to TreeSitter equivalents for consistency
- **Telescope highlights**: Neutral base with primary accent for matching, selection, and titles
- **Gitsigns highlights**: Status colors - Add (green), Change (blue), Delete (red); inline diff backgrounds
- **nvim-cmp highlights**: Primary tones for item kinds, bold for match highlighting, italic for menu
- **Oil.nvim highlights**: Bold directories, status colors for permissions (read=green, write=orange, execute=blue), operation colors
- **indent-blankline**: Subtle grays for guides and scope
- **nvim-ufo**: Subtle backgrounds for folds with preview support
- 2025-12-11: Colorscheme Phase 11.9:
- **Plugin configuration updated**: `lua/plugins/colorscheme.lua` loads `paper-tonic-modern`
- **Cleanup**: Moved old `paper-tonic/` directory to `legacy/paper-tonic-original/`
- **Validation**: Full config reload tested - all highlight groups load correctly
- **Colorscheme integration**: Native Neovim colorscheme at `colors/paper-tonic-modern.lua`
- **No overrides needed**: Modern implementation doesn't require separate override file
- 2025-12-12: Added keymap reference documentation (now `README.md`) + `:KeymapsGuide` floating viewer to document every configured keymap (global + plugin-specific) grouped by source file.
- 2025-12-12: nvim-cmp updated to disable auto-selection; confirm requires explicit item selection.
- 2026-10-27: Phase 12 cleanup retired 607 tracked `.vimtmp` backup artifacts and a broken legacy Paper Tonic symlink while preserving the meaningful Vimscript archive. Added an ignore rule for the retired backup directory and corrected the legacy archive index.
- 2026-10-27: Project `.nvim.lua` settings are now read with `vim.secure.read()` before evaluation. This closes the trust bypass caused by manually calling `dofile()` from the LSP attachment callback.
- 2026-10-27: Validation confirmed a clean Neovim 0.11.5 headless launch in approximately 45 ms with 29 configured plugins. Formatter and language-server executables managed by Mason are present in Mason's bin directory; PHP tools and `lua-language-server` are available system-wide.
- 2026-10-27: Repaired `test-diagnostics.lua` so its intentional unreachable-code example remains valid Lua and can be parsed by `lua_ls`.
- 2026-10-27: Live validation confirmed Intelephense attaches in `WORKSPACE_TEST` and receives the trusted external `includePaths`. Conform reports prettier, phpcbf, and stylua available; nvim-lint resolves eslint_d, phpcs, and markdownlint.
- 2026-10-27: Updated `AGENTS.md` status and LSP architecture notes to reflect completed colorscheme work and the current Phase 12 implementation.
- 2026-10-27: Added Conform's built-in `ruff_format` for Python. Ruff was already Mason-managed for diagnostics; this closes a gap where documentation claimed Python formatting but format-on-save had no Python formatter and disabled LSP fallback.
- 2026-10-27: Fixed `<C-6>` in netrw when no previous real-file buffer has been recorded. Buffer classification now rejects `nil`, non-numeric, and non-positive buffer identifiers before calling Neovim's buffer API, allowing the mapping to fall back safely.
- 2026-10-27: Removed Oil.nvim after workflow evaluation. Netrw is now the sole tree browser and file explorer, preserving the same navigation model on remote systems; Telescope remains dedicated to fuzzy finding. Removed Oil's plugin spec, lockfile entry, keymaps, colorscheme groups, and active documentation.
- 2026-10-27: Planned the next work in two ordered phases. Phase 13 upgrades the standalone Neovim installation from 0.11.5 to stable 0.12.6 using versioned installations, isolated plugin testing, an unchanged initial lockfile, and an immediate rollback path; plugin updates follow only after core compatibility, and Mason tools remain a later step. Phase 14 then adds netrw-only live previews and read-only directory summaries against the validated 0.12 runtime.
- 2026-10-27: Recorded the pre-upgrade rollback baseline: Neovim 0.11.5, 28 active lazy.nvim plugin specs after Oil removal, 32 compiled Treesitter parser binaries, clean headless startup, all active Lua files parsing, and `git diff --check` passing. The standalone 0.11.5 installation remains untouched under `/opt/neovim/nvim-linux-x86_64`.
- 2026-10-28: Installed the verified official Neovim 0.12.6 archive alongside the untouched 0.11.5 installation at `/opt/neovim/nvim-0.12.6`. Initial interactive startup with the existing configuration succeeded. Removed redundant `vim.lsp.with()` wrappers for hover and signature-help borders after 0.12 reported their deprecation; the shared `vim.lsp.util.open_floating_preview` override already applies the same border on both 0.11 and 0.12. Clean headless startup then passed with the existing locked plugins under both binaries.
- 2026-10-28: The 0.12 health review exposed overlapping LSP activation: mason-lspconfig v2 automatically enabled every installed server while `lsp.lua` explicitly configured and enabled the selected set. Disabled mason-lspconfig automatic activation, removed its obsolete `automatic_installation` option, and aligned it with nvim-lspconfig's buffer events. This prevents unintended activation of installed tools such as StyLua's LSP mode and leaves `lsp.lua` as the single activation authority.
- 2026-10-28: Live Intelephense attachment passed under both Neovim 0.11.5 and 0.12.6 after the LSP activation cleanup. The test also exposed the deprecated function-style `client.notify(...)` call in the project-settings callback; changed it to `client:notify(...)`, which works on both supported versions.
- 2026-10-28: Replaced the remaining `vim.loop.fs_stat` calls with `vim.uv.fs_stat` and migrated `[d`/`]d` from deprecated diagnostic navigation functions to `vim.diagnostic.jump({ count = -1/1 })`. Both APIs remain compatible with the rollback runtime, Neovim 0.11.5.
- 2026-10-28: Validated Neovim 0.12 project configuration using a disposable nested workspace and isolated trust database. With `exrc` and `secure` enabled through a normal XDG config, a trusted parent `.nvim.lua` executed exactly once and `vim.secure.read()` returned its contents. The live Intelephense test separately confirmed that the repository's project-local settings loader still receives trusted settings from the client's root.
- 2026-10-28: Validated the netrw workflow on Neovim 0.12.6. The default runtime wrapper automatically loads bundled netrw v184 from its optional package, so adding a configuration-level `packadd netrw` would be redundant and would complicate 0.11 rollback compatibility. `:Explore` opened the configured tree view, `p` created a real preview window, and `<C-6>` returned from the explorer to the source file.
- 2026-10-28: Kept `mbbill/undotree` after checking Neovim 0.12's built-in alternative. The native `nvim.undotree` package is optional and remains unloaded; it exposes `:Undotree`, whereas the configured plugin's `:UndotreeToggle` command, focused panel, and diff layout still open successfully under 0.12.6. There is no active command or runtime conflict.
- 2026-10-28: Completed the configuration-level Neovim 0.12 breaking-change audit. Diagnostic jump behavior passed under both 0.11.5 and 0.12.6, and a loaded-plugin `vim.deprecated` scan now reports only `copilot-cmp`'s upstream `client.is_stopped` call. That plugin warning is safe on 0.12 but remains a tracked compatibility concern before Neovim 0.13.
- 2026-10-28: Final Phase 13.3 smoke tests passed: both 0.11.5 and 0.12.6 start with the updated configuration, 0.12 attaches Intelephense with project settings, `git diff --check` is clean, and the measured 0.12 headless startup reached `NVIM STARTED` in approximately 43 ms with the existing locked plugins.
- 2026-10-28: Updated plugins in an isolated Neovim 0.12 worktree and XDG environment. The candidate passed automated loading checks for LSP, completion, Telescope/fzf, Mason integration, formatting, linting, Gitsigns, indent guides, autopairs, surround, UFO, Undotree, and PHP/HTML/CSS/JavaScript Tree-sitter parsers and custom queries. Copilot's new native language-server download completed in isolation and the client authenticated successfully. Mason-managed tools were not updated.
- 2026-10-28: Kept `nvim-treesitter` and `nvim-treesitter-textobjects` on their frozen `master` branches. Their rewritten `main` branches remove the `nvim-treesitter.configs` API used by this configuration and therefore require a separate migration; explicit branch pins prevent an ordinary plugin sync from crossing that boundary. Migrated the updated nvim-surround v4 configuration to its unchanged default mappings by removing the retired `opts.keymaps` field.
- 2026-10-28: Disabled lazy.nvim's LuaRocks integration because no configured plugin requires LuaRocks. This removes the irrelevant missing-hererocks health error without adding a system dependency. Candidate health otherwise reports only optional missing language runtimes, the pending inline-Copilot migration, and the `copilot-cmp` deprecation that will disappear with that migration.
- 2026-10-28: Adopted the isolated candidate's reviewed plugin lockfile and compatibility adjustments into the main worktree. Production plugin directories remain unchanged until the 0.12 activation step, preserving the working 0.11 rollback environment.
- 2026-10-28: Chose the post-activation native-feature direction for 0.12. Keep lazy.nvim because the configuration still benefits from its event/command loading, dependency ordering, and build hooks; do not mix the runtime upgrade with an experimental `vim.pack` migration. After activating 0.12, replace `mbbill/undotree` with the native optional package and trial native insert-mode/LSP completion plus built-in snippets. Copilot will use inline suggestions with a separate accept key rather than appearing in the completion popup; remove the cmp stack only after interactive language and editing validation.
- 2026-10-28: Activated Neovim 0.12.6 with the user-level selector `~/.local/bin/nvim`, which precedes the older `/opt` PATH entry. The root-owned `/opt/neovim` directory could not be reorganized without an interactive sudo password, so 0.11.5 remains intact at `/opt/neovim/nvim-linux-x86_64/bin/nvim`; rollback is `ln -sfn /opt/neovim/nvim-linux-x86_64/bin/nvim ~/.local/bin/nvim`.
- 2026-10-28: Implemented the 0.12 native editing trial. Native `nvim.undotree` now owns `<leader>u`; native autocomplete combines LSP omnifunc results and bounded buffer sources after a 150 ms delay, requires explicit selection, accepts with `<C-y>`, and uses built-in snippet tabstops. Copilot now supplies separate inline ghost text accepted with `<M-l>` and hides while the completion popup is visible. Removed the nvim-cmp, cmp source, LuaSnip, copilot-cmp, and external Undotree specs, integrations, lock entries, and highlight groups.
- 2026-10-28: Refined native completion after the PTY smoke test showed buffer words sorting ahead of a valid PHP method. LSP-attached buffers now autocomplete exclusively from the LSP omnifunc; `<C-x><C-n>` remains the explicit current-buffer word workflow, and non-LSP buffers retain bounded automatic word completion. This makes the first selectable language result predictable without losing native fallback completion.
- 2026-10-28: Added narrow 0.12 guards around the new `autocomplete` option, source match limits, and optional native Undotree package. The retained 0.11.5 binary can therefore still start the configuration for emergency rollback, although native autocomplete and Undotree are intentionally unavailable until the selector is returned to 0.12.6.
- 2026-10-28: Final smoke testing found that loading `nvim.undotree` before lazy.nvim setup left its command registered but removed its Lua module path. Moved `packadd nvim.undotree` after lazy.nvim finalizes `runtimepath`. The same test showed nvim-ts-autotag attaching to Markdown despite the intended filetype restriction; global autotag actions are now disabled and explicitly enabled only for HTML/PHP/JS/TS/Vue tag-bearing filetypes.
- 2026-10-28: Active-runtime validation passed through the normal `nvim` selector: native Undotree opens, netrw and Telescope load, PHP native completion offers and accepts `greetExternal()` with snippet syntax, Copilot's inline client is authenticated, Markdown no longer runs autotag, and HTML autotag exits Insert mode without errors. `vim.deprecated` is clean. Remaining health warnings are optional missing language runtimes and the harmless unknown `markdown.mdx` filetype hint.
- 2026-10-28: Marked `neovim-migration-guide.md` as historical planning input. Its original Neo-tree, cmp, and plugin assumptions are retained for provenance but no longer compete with the active architecture documents.
- 2026-10-28: User normal-use checks passed for the 0.12 native completion, snippet, autopairs, and inline Copilot workflow, closing the Phase 13.9 settling gate. Began Phase 14 with the confirmed netrw-only model: `p` starts previewing, cursor movement updates an existing preview, directories show read-only immediate-child summaries, and leaving netrw closes the preview.
- 2026-10-28: Revalidated bundled netrw v184. Its documented `netrw#Call()` bridge exposes `NetrwGetWord`, `NetrwTreePath`, `NetrwBrowseChgDir`, and `NetrwPreview`. In tree view, `NetrwTreePath` returns the selected directory path or the selected file's parent, allowing local paths to be resolved without parsing indentation or mutating netrw's expand/collapse state. Remote URI targets will retain native fallback behavior.
- 2026-10-28: Implemented Phase 14's plugin-free netrw preview extension in `lua/netrw-preview.lua`. Pressing `p` previews files through netrw's native machinery or shows a read-only immediate-child directory summary; while that preview window exists, a 90 ms buffer-local cursor debounce replaces it with the current selection. Manual `:pclose` stops updates, and leaving or wiping the explorer releases its preview and timer. Directory summaries are local-filesystem only, mirror netrw's active hide pattern, cap output at 200 entries, and never traverse symbolic links; remote URI schemes fall back to netrw's native preview path.
- 2026-10-28: Automated Phase 14 checks passed for file/directory transitions, manual close and reopen, all four file-open actions, tree expansion and collapse, rapid cursor movement, spaces, Unicode, symlinks, empty and unreadable directories, and the 200-entry cap. A PTY-backed launch on a directory confirmed Neovim 0.12 enters the configured netrw tree, so no special `filetype=directory` handling is required. Separate-tab, deleted-entry, and real SSH checks remain as interactive validation.
- 2026-10-28: An isolated lifecycle rerun exposed netrw's focus hop into an existing preview window while replacing a directory summary through its native preview function. The extension now explicitly restores the netrw window after that call, preserving tree-focused `j`/`k` navigation. Leave cleanup waits 20 ms before checking the settled current window and ignores callbacks raised while the native preview call is active, then closes only after a real explorer exit. The transient re-entrancy guard is not preview-mode state, and the cleanup timer is released when the netrw buffer is wiped.
- 2026-10-28: Fixed a reproducible Neovim 0.12 Tree-sitter race during rapid cross-language netrw previews. Native `:pedit` unloads the previous preview buffer while its asynchronous parser may still be running, producing `TSNode:range()` errors. Preview-only file buffers are now kept loaded but unlisted for the rest of the Neovim session, with Tree-sitter highlighting suspended in favor of Vim syntax; avoiding parser detachment is necessary because delayed unloading reproduces the same race. Pre-existing buffer state is preserved, while entering a previewed file for real editing restores listed-buffer behavior and Tree-sitter immediately. An 80-step Markdown/Lua/JSON preview stress test, edit restoration check, retained-buffer check, and pre-existing-buffer check all pass.
- 2026-10-28: Disabled diagnostic rendering in netrw file preview buffers. The preview metadata records each buffer's prior diagnostic state and restores it when the file is opened for editing, keeping ephemeral previews quiet without overriding buffers where diagnostics were already disabled.
- 2026-10-28: Made netrw file previews intentionally plain after CSS and HTML exposed residual colour. The remaining decoration was Vim regex syntax plus indent-blankline extmarks, not diagnostics or Tree-sitter. Preview buffers now clear `syntax` and disable their buffer-local indent guides, then restore both recorded states on real edit.
- 2026-10-28: Added buffer-local netrw `<M-j>` / `<M-k>` mappings to scroll the current preview with native `<C-e>` / `<C-y>` behavior while focus remains in the tree. The mappings accept Vim counts and quietly do nothing when no preview exists.
- 2026-10-28: Hardened preview scrolling after an interactive over-scroll surfaced a window-local error that did not reproduce headlessly. The temporary preview-window call is now protected, so stale-window and boundary failures are treated like native top/bottom clamping instead of reaching the command line.
- 2026-10-28: Excluded netrw from automatic session persistence after `:mksession` serialized its generated `NetrwTreeListing` name and restored an empty explorer buffer. Before the existing exit-time save, preview windows and netrw windows are removed; a netrw-only tab is dropped, while the sole remaining netrw window is replaced with an ordinary file buffer or a blank fallback. Netrw is intentionally not rebuilt on load.
- 2026-10-28: Changed the enhanced netrw `p` mapping into a true toggle. With no preview it opens the selected file or directory and begins live cursor updates; with an existing preview it closes that window and stops updates, matching manual `:pclose` behavior.
- 2026-10-28: Declared the migration complete after sustained daily use of the modern Lua configuration. Interactive navigation, language tooling, native completion, Copilot, formatting, linting, and the enhanced netrw workflow are considered validated. Real SSH URI previews and use of stock netrw without this configuration remain optional portability checks, not release gates. This decision also ends the requirement to retain migration-only archives and fixtures in the live repository; their removal will be handled as a separate, explicit cleanup.
- 2026-10-28: Replaced the migration-era `AGENTS.md` with a concise maintenance guide for the established configuration. Removed completed phase tracking, obsolete validation-workspace instructions, resolved issue notes, and the target-tree description; retained current architectural guardrails, documentation expectations, a smoke-test command, and a short history note.
- 2026-10-28: Retired the completed migration's legacy configuration, vendored plugin archive, UltiSnips collection, validation workspaces, root test assets, superseded planning documents, and phase labels in active Lua comments. Git history now provides the historical archive. Persistent undo storage moved from the configuration repository to `stdpath('state')/undo`; the active README, architectural log, lockfile, colorscheme, spell data, and templates remain.
- 2026-10-28: Diagnosed the Tree-sitter `TSNode:range()` failure as the known incompatibility between Neovim 0.12 and nvim-treesitter's locked legacy `master` branch. Migrated nvim-treesitter and nvim-treesitter-textobjects to their rewritten `main` branches: native Neovim highlighting and incremental selection replace the old modules, textobjects use explicit mappings, indentation uses the new plugin expression when a query exists, and autotag retains its direct supported setup. Installed the verified official Tree-sitter CLI 0.26.9 binary in `~/.local/bin` after the system package required interactive sudo and Cargo compilation required unavailable libclang. Reinstalled all configured parsers in the new site directory and removed obsolete generated parser copies from the plugin checkout. The plugin audit confirmed every plugin is current for its configured branch or release constraint; nvim-surround remains on the latest release rather than its newer unreleased `main` commit.
- 2026-10-28: Fixed stale deleted files persisting across session recreation. Deleting `Session.vim` did not clear the in-memory argument list or listed buffers, so `:mksession` serialized the removed diagnostics fixture again. Session load and exit-time save now discard unmodified nonexistent local-file buffers and missing argument-list entries before restoring or writing the session; real files, URI buffers, unnamed buffers, and modified buffers remain protected.
- 2026-10-28: Re-enabled highlighted netrw file previews after migrating to the Neovim 0.12-compatible nvim-treesitter `main` branch. Preview buffers retain native Tree-sitter or Vim syntax highlighting, while diagnostics and indent guides remain suppressed. The existing loaded-but-unlisted buffer lifecycle protection remains in place so the original unload behavior is not restored. An automated 80-transition Markdown/Lua/JSON/CSS/HTML stress test and subsequent normal use passed without `TSNode:range()` errors, so the trial was adopted as the permanent behavior.
## Project-Local Configuration
The final implementation uses Neovim's native `exrc` support with `secure` enabled. A project's trusted `.nvim.lua` returns an `lsp` table keyed by server name. An `LspAttach` callback securely reads settings from the client's actual root directory and sends them with `workspace/didChangeConfiguration`, so settings do not depend on Neovim's current working directory.
Example `.nvim.lua`:
```lua
return {
lsp = {
intelephense = {
settings = {
intelephense = {
environment = {
includePaths = {
"wp-content/themes/my-theme",
"wp-content/plugins/custom-plugin",
},
},
},
},
},
},
}
```
Neoconf and the earlier layered JSON proposal were rejected. They remain in the chronological decision entries above only as superseded history.
## LSP Scope (initial)
- Core servers: `lua_ls`, `tsserver`, `html`, `cssls`, `jsonls`, `bashls`
- PHP: `intelephense` (decision)
- Markdown: `marksman` (decision)
- Keep configuration minimal; defer language-specific tuning.
## Text & Editing Settings
- `spelllang = en_gb`
- `listchars` and `showbreak` will reuse legacy values.
- `autocomplete` with `completeopt = menu,menuone,noselect,popup,fuzzy` for Neovim 0.12 native completion.
## Maintenance Reminders
- Follow `AGENTS.md` for current maintenance rules.
- Update `README.md` when user-facing behavior changes.
- Record significant architectural decisions in this log.
- Keep changes focused and verify affected workflows.

552
README.md
View file

@ -1,552 +0,0 @@
# Neovim Config Reference
Living reference for session management, keymaps, commands, and plugin-specific features in this config.
The active runtime is Neovim 0.12.6 through `~/.local/bin/nvim`. Neovim 0.11.5 remains available at `/opt/neovim/nvim-linux-x86_64/bin/nvim` for emergency rollback; the config starts there with 0.12-only native completion and Undotree disabled.
Persistent undo files are stored outside this repository in
`stdpath('state')/undo` (normally `~/.local/state/nvim/undo`).
## Session Management
Session support is automatic but user-controlled:
**Manual session creation** (one-time):
```vim
:mksession # Creates Session.vim in current directory
```
**Auto-load/save** (via `lua/autocmds.lua`):
- On startup: auto-loads `Session.vim` if it exists (unless files specified on command line)
- On exit: auto-saves to `Session.vim` if it already exists
- Netrw and its preview windows are omitted from the saved layout; generated `NetrwTreeListing` buffers are not restorable
- Missing files are removed from restored buffers and argument lists, so deleted files cannot become persistent empty session buffers
- Sessions are per-directory and opt-in (just create one to start using)
**Stop using a session**:
```bash
rm Session.vim # Won't auto-create a new one
```
## Keymaps
### `:KeymapsGuide` (`lua/keymaps_reference.lua`)
| Command | Description |
| --- | --- |
| `:KeymapsGuide` | Open a floating window with a dynamically generated list of all user-defined keymaps |
### `lua/keymaps.lua`
Core keymaps available globally (not plugin-specific). These provide fallbacks for LSP features and diagnostic navigation.
| Mode | Key | Description | Notes |
| --- | --- | --- | --- |
| n | `gd` | Vim goto-definition fallback | Executes `normal! gd` for tags/include jumps when no LSP |
| n | `gD` | Vim goto-declaration fallback | Executes `normal! gD` |
| 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 |
| n | `<leader>xE` | Diagnostics → all errors | Quickfix filtered to errors |
| n | `[d` | Diagnostics: previous item | Uses `vim.diagnostic.jump` with a negative count |
| n | `]d` | Diagnostics: next item | Uses `vim.diagnostic.jump` with a positive count |
| 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 |
| n | `<C-6>` / `<C-^>` | Jump to alternate file, skipping netrw explorer buffers |
**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` | Toggle the selected file/directory preview and live cursor updates |
| `j` / `k` | Update an existing preview to the newly selected entry |
| `<M-j>` / `<M-k>` | Scroll the preview down/up without leaving netrw; accepts a count |
| `:pclose` / `<C-w>z` | Close the preview and stop live updates |
| `P` | Open file in previous window |
| `o` | Open file in horizontal split below |
| `v` | Open file in vertical split |
| `t` | Open file in a new tab |
Regular files use netrw's native preview. Directories show a read-only summary of
their immediate children, including type, file size, modification time, and a
total for immediate files. Directories appear first, output is capped at 200
entries, and symbolic links are displayed without following them recursively.
File previews retain Tree-sitter or Vim syntax highlighting while suppressing
diagnostics and indent guides. The buffer's prior diagnostic and indent-guide
state returns automatically when a previewed file is opened for editing.
Preview-only file buffers remain loaded but unlisted until Neovim exits, retaining
the lifecycle protection added for rapid preview replacement.
Opening a file with `<CR>`, `o`, `v`, or `t`, or otherwise leaving netrw, closes
the preview. Closing it manually or pressing `p` again disables live updates
until `p` is pressed again. Enhanced directory summaries apply to local paths
(including local paths when Neovim runs over SSH); netrw URI browsing retains
its native preview behavior.
##### File Operations
| Key | Description |
| --- | --- |
| `%` | Create new file (prompts for name) |
| `d` | Create new directory (prompts for name) |
| `D` | Delete file/directory under cursor |
| `R` | Rename/move file under cursor |
##### Marking & Batch Operations
| Key | Description |
| --- | --- |
| `mf` | Mark file (for batch operations) |
| `mr` | Mark files using regex pattern |
| `mu` | Unmark all marked files |
| `mt` | Set mark target directory (for move/copy destination) |
| `mc` | Copy marked files to target directory |
| `mm` | Move marked files to target directory |
| `qf` | Display file info |
## Plugin Reference
### LSP `lua/plugins/lsp.lua`
#### Keymaps
Buffer-local keymaps available when an LSP client attaches:
| Mode | Key | Description |
| --- | --- | --- |
| n | `gd` | LSP: go to definition |
| n | `gr` | LSP: references |
| n | `gD` | LSP: declaration |
| n | `gI` | LSP: implementation |
| n | `K` | LSP hover |
### Conform (Formatting) `lua/plugins/conform.lua`
#### Keymaps
| Mode | Key | Description |
| --- | --- | --- |
| n | `<leader>lt` | Toggle format-on-save flag |
| n | `<leader>lf` | Format current buffer (synchronous) |
| v | `<leader>lf` | Format visual selection |
### Nvim-lint `lua/plugins/nvim-lint.lua`
#### Commands
| Command | Description |
| --- | --- |
| `:MarkdownLintEnable` | Enable automatic markdown linting (runs on BufEnter, BufWritePost, InsertLeave) |
| `:MarkdownLintDisable` | Disable automatic markdown linting and clear diagnostics |
**Note:** Markdown linting is disabled by default. Spellcheck is always enabled for markdown files.
### Gitsigns `lua/plugins/gitsigns.lua`
#### Keymaps
Buffer-local keymaps available when inside a git repository:
| Mode | Key | Description |
| --- | --- | --- |
| n | `]h` | Next hunk (`expr` mapping that respects `diff` windows) |
| n | `[h` | Previous hunk |
| n | `<leader>hs` | Stage current hunk |
| n | `<leader>hr` | Reset current hunk |
| v | `<leader>hs` | Stage visually selected range as hunk |
| v | `<leader>hr` | Reset visually selected range |
| n | `<leader>hS` | Stage entire buffer |
| n | `<leader>hu` | Undo last staged hunk |
| n | `<leader>hR` | Reset entire buffer |
| 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`
#### Keymaps
##### Launcher mappings (normal mode)
| Key | Description |
| --- | --- |
| `<leader>ff` | Find files |
| `<leader>fg` | Live grep |
| `<leader>fb` | Open buffers picker (`<C-d>` deletes buffers in picker) |
| `<leader>fh` | Help tags |
| `<leader>fr` | Recent files (oldfiles) |
| `<leader>/` | Fuzzy search current buffer |
| `<leader>fk` / `<leader>?` | Telescope keymaps picker |
| `<leader>fc` | Commands picker |
| `<leader>fs` | LSP document symbols |
| `<leader>fS` | LSP workspace symbols |
##### Telescope prompt mappings (`defaults.mappings`)
| Mode | Key | Description |
| --- | --- | --- |
| i | `<C-n>` / `<C-j>` | Move selection down |
| i | `<C-p>` / `<C-k>` | Move selection up |
| i | `<C-c>` | Close picker |
| i | `<CR>` | Accept selection |
| i | `<C-x>` | Open selection in horizontal split |
| i | `<C-v>` | Open selection in vertical split |
| i | `<C-t>` | Open selection in tab |
| n | `<Esc>` | Close picker |
| n | `<CR>` | Accept selection |
| n | `<C-x>` | Horizontal split |
| n | `<C-v>` | Vertical split |
| n | `<C-t>` | New tab |
| n | `j` | Move selection down |
| n | `k` | Move selection up |
##### Picker-specific overrides
| Context | Mode | Key | Description |
| --- | --- | --- | --- |
| `buffers` picker | i | `<C-d>` | Delete highlighted buffer |
### UFO (Folding) `lua/plugins/ufo.lua`
#### Keymaps
| Mode | Key | Description |
| --- | --- | --- |
| n | `zR` | Open all folds |
| n | `zM` | Close all folds |
| n | `zr` | Open folds except configured kinds |
| n | `zm` | Close folds with configured kinds |
| n | `K` | Peek fold under cursor; falls back to LSP hover |
| preview window | `<C-u>` / `<C-d>` | Scroll within UFO preview |
| preview window | `[` / `]` | Jump to top/bottom of preview |
### Native Undotree `lua/keymaps.lua`
#### Keymaps
| Mode | Key | Description |
| --- | --- | --- |
| n | `<leader>u` | Toggle Neovim's native Undotree panel (`:Undotree`) |
### Treesitter `lua/plugins/treesitter.lua`
The Neovim 0.12-compatible `main` branches are used for both nvim-treesitter
and nvim-treesitter-textobjects. Highlighting and incremental selection use
Neovim's native APIs; textobjects use explicit plugin mappings. Parser builds
require `tree-sitter-cli` 0.26.1 or newer (`tree-sitter --version`).
#### Keymaps
##### Incremental selection
| Key | Description |
| --- | --- |
| `+` | Initialize/expand selection to the parent node |
| `g+` | Expand to scope |
| `-` | Shrink selection to the child node |
##### Textobject selection
| Key | Target |
| --- | --- |
| `af` / `if` | Function outer / inner |
| `ac` / `ic` | Class outer / inner |
| `aa` / `ia` | Parameter outer / inner |
| `ai` / `ii` | Conditional outer / inner |
| `al` / `il` | Loop outer / inner |
| `a/` | Comment outer |
##### Textobject movement
| Key | Action |
| --- | --- |
| `]f` / `]F` | Next function start / end |
| `[f` / `[F` | Previous function start / end |
| `]c` / `]C` | Next class start / end |
| `[c` / `[C` | Previous class start / end |
| `]a` / `]A` | Next parameter start / end |
| `[a` / `[A` | Previous parameter start / end |
##### Parameter swapping
| Key | Description |
| --- | --- |
| `<leader>a` | Swap parameter with next |
| `<leader>A` | Swap parameter with previous |
### Comment.nvim `lua/plugins/comment.lua`
#### Keymaps
| Mapping | Description |
| --- | --- |
| `gcc` | Toggle line comment |
| `gbc` | Toggle block comment |
| `gc` | Operator-pending line comment |
| `gb` | Operator-pending block comment |
| `gcO` / `gco` / `gcA` | Insert comment above / below / end-of-line |
### Nvim-surround `lua/plugins/surround.lua`
The configuration uses nvim-surround v4's default mappings.
#### Keymaps
| Mode | Mapping | Description |
| --- | --- | --- |
| normal | `ys{motion}{char}` | Add surround to motion |
| normal | `yss` | Surround current line |
| normal | `yS` | Surround motion with linewise behavior |
| normal | `ySS` | Surround current line linewise |
| insert | `<C-g>s` | Surround following text object |
| insert | `<C-g>S` | Surround current line |
| visual | `S` | Surround selection |
| visual line | `gS` | Surround visual-line selection |
| normal | `ds{char}` | Delete surround |
| normal | `cs{old}{new}` | Change surround |
| normal | `cS{old}{new}` | Change surround (linewise) |
### Native Completion `lua/completion.lua`
#### Keymaps
| Mode | Key | Description |
| --- | --- | --- |
| insert | `<C-Space>` | Manually trigger LSP completion |
| insert | `<C-n>` | Next completion item |
| insert | `<C-p>` | Previous completion item |
| insert | `<C-y>` | Accept the explicitly selected completion item |
| insert | `<C-e>` | Dismiss the completion menu |
| insert | `<C-x><C-n>` | Complete words from the current buffer |
| insert | `<C-x><C-f>` | Complete a filesystem path |
| insert/select | `<Tab>` / `<S-Tab>` | Jump forward/backward through native snippet tabstops |
Completion opens automatically after a 150 ms delay, uses fuzzy matching, and does not preselect an item. LSP buffers prioritize language-aware results exclusively; buffer words remain available explicitly with `<C-x><C-n>`. Buffers without an LSP autocomplete from a bounded set of nearby words. Native completion is disabled in Telescope prompts so it does not obscure picker results.
### Copilot Inline Suggestions `lua/plugins/copilot.lua`
Copilot appears as ghost text rather than as an item in the native completion menu. Suggestions are hidden while that menu is visible.
| Mode | Key | Description |
| --- | --- | --- |
| insert | `<M-l>` | Accept the visible Copilot suggestion |
| insert | `<M-]>` | Show the next Copilot suggestion |
| insert | `<M-[>` | Show the previous Copilot suggestion |
| insert | `<C-]>` | Dismiss the Copilot suggestion |
### Nvim-autopairs `lua/plugins/autopairs.lua`
#### Keymaps
| Mode | Key | Description |
| --- | --- | --- |
| insert | `<M-e>` | Fast wrap the previous text with a pair |
## Configuration
### Colorscheme
`paper-tonic-modern` is built into this repository and loaded from `colors/paper-tonic-modern.lua`. Its modular highlight definitions live under `lua/paper-tonic-modern/` and cover the editor UI, traditional syntax, Tree-sitter captures, LSP diagnostics, and the configured plugins.
The theme follows the system light/dark preference at startup. Use `<leader>bg` to toggle the background manually. Custom HTML and CSS Tree-sitter queries keep matching classes and IDs visually consistent across both languages.
### Project-Local LSP Settings
Native `exrc` and `secure` support are enabled. A trusted `.nvim.lua` can return an `lsp` table keyed by server name; settings are securely read from the attached LSP client's root and sent through `workspace/didChangeConfiguration`.
```lua
return {
lsp = {
intelephense = {
settings = {
intelephense = {
environment = {
includePaths = { "vendor/package/src" },
},
},
},
},
},
}
```
### 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**: `ruff_format`
**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)

149
Session.ts.vim Normal file
View file

@ -0,0 +1,149 @@
let SessionLoad = 1
let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1
let v:this_session=expand("<sfile>:p")
silent only
silent tabonly
cd ~/.config/nvim
if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''
let s:wipebuf = bufnr('%')
endif
set shortmess=aoO
badd +36 init.tabline.vim
badd +22 init.auto-window.vim
badd +1 coc-settings.json
badd +184 init.vim
badd +1 ftplugin/php.vim
badd +2 after/ftplugin/php.vim
badd +1 after/ftplugin/html.vim
badd +1 after/ftplugin/javascript.vim
badd +8 after/ftplugin/python.vim
badd +1 after/ftplugin/sass.vim
badd +1 after/ftplugin/vim.vim
badd +1 ftplugin/vue.vim
badd +1 ftplugin/sass.vim
badd +1 ftplugin/javascript.vim
badd +1 ftplugin/css.vim
badd +50 init.plugins.vim
badd +1 after/indent/php.vim
badd +10 init.commands.vim
badd +7 lua/init-plugins.lua
badd +1 after/queries/ecma/highlights.scm
badd +16 after/queries/html/highlights.scm
badd +2 after/queries/css/highlights.scm
argglobal
%argdel
$argadd init.tabline.vim
edit after/queries/css/highlights.scm
let s:save_splitbelow = &splitbelow
let s:save_splitright = &splitright
set splitbelow splitright
wincmd _ | wincmd |
vsplit
1wincmd h
wincmd _ | wincmd |
split
1wincmd k
wincmd w
wincmd w
let &splitbelow = s:save_splitbelow
let &splitright = s:save_splitright
wincmd t
let s:save_winminheight = &winminheight
let s:save_winminwidth = &winminwidth
set winminheight=0
set winheight=1
set winminwidth=0
set winwidth=1
exe '1resize ' . ((&lines * 22 + 25) / 50)
exe 'vert 1resize ' . ((&columns * 103 + 105) / 211)
exe '2resize ' . ((&lines * 22 + 25) / 50)
exe 'vert 2resize ' . ((&columns * 103 + 105) / 211)
exe 'vert 3resize ' . ((&columns * 107 + 105) / 211)
argglobal
balt after/queries/html/highlights.scm
setlocal fdm=indent
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=99
setlocal fml=1
setlocal fdn=9
setlocal fen
let s:l = 3 - ((2 * winheight(0) + 11) / 22)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 3
normal! 023|
lcd ~/.config/nvim
wincmd w
argglobal
if bufexists("/usr/share/nvim/runtime/doc/treesitter.txt") | buffer /usr/share/nvim/runtime/doc/treesitter.txt | else | edit /usr/share/nvim/runtime/doc/treesitter.txt | endif
if &buftype ==# 'terminal'
silent file /usr/share/nvim/runtime/doc/treesitter.txt
endif
setlocal fdm=manual
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=99
setlocal fml=1
setlocal fdn=9
setlocal nofen
silent! normal! zE
let &fdl = &fdl
let s:l = 196 - ((5 * winheight(0) + 11) / 22)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 196
normal! 050|
lcd ~/.config/nvim
wincmd w
argglobal
if bufexists("~/.config/nvim/after/indent/php.vim") | buffer ~/.config/nvim/after/indent/php.vim | else | edit ~/.config/nvim/after/indent/php.vim | endif
if &buftype ==# 'terminal'
silent file ~/.config/nvim/after/indent/php.vim
endif
setlocal fdm=marker
setlocal fde=0
setlocal fmr={{{,}}}
setlocal fdi=#
setlocal fdl=99
setlocal fml=1
setlocal fdn=9
setlocal fen
let s:l = 4 - ((3 * winheight(0) + 22) / 45)
if s:l < 1 | let s:l = 1 | endif
keepjumps exe s:l
normal! zt
keepjumps 4
normal! 03|
lcd ~/.config/nvim
wincmd w
exe '1resize ' . ((&lines * 22 + 25) / 50)
exe 'vert 1resize ' . ((&columns * 103 + 105) / 211)
exe '2resize ' . ((&lines * 22 + 25) / 50)
exe 'vert 2resize ' . ((&columns * 103 + 105) / 211)
exe 'vert 3resize ' . ((&columns * 107 + 105) / 211)
if exists(':tcd') == 2 | tcd ~/.config/nvim | endif
tabnext 1
if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0&& getbufvar(s:wipebuf, '&buftype') isnot# 'terminal'
silent exe 'bwipe ' . s:wipebuf
endif
unlet! s:wipebuf
set winheight=1 winwidth=20 shortmess=aoOT
let &winminheight = s:save_winminheight
let &winminwidth = s:save_winminwidth
let s:sx = expand("<sfile>:p:r")."x.vim"
if filereadable(s:sx)
exe "source " . fnameescape(s:sx)
endif
let &g:so = s:so_save | let &g:siso = s:siso_save
set hlsearch
let g:this_session = v:this_session
let g:this_obsession = v:this_session
let g:this_obsession_status = 2
doautoall SessionLoadPost
unlet SessionLoad
" vim: set ft=vim :

3
UltiSnips/all.snippets Normal file
View file

@ -0,0 +1,3 @@
snippet tp "{% partial ... " i
{% partial '$1'$0 %}
endsnippet

265
UltiSnips/css.snippets Normal file
View file

@ -0,0 +1,265 @@
snippet abs "pabs position: absolute;"
position: absolute;
endsnippet
snippet fix "position: fixed;"
position: fixed;
endsnippet
snippet rel "position: relative;"
position: relative;
endsnippet
snippet row "flex-direction: row;"
flex-direction: row;
endsnippet
snippet col "flex-direction: column;"
flex-direction: column;
endsnippet
snippet over "overflow: hidden;"
overflow: $0;
endsnippet
snippet style "/* stylelint-disable-next-line ... */"
/* stylelint-disable-next-line ${1:declaration-block-no-shorthand-property-overrides} */
endsnippet
snippet @m "@media ( ..."
@media (${1:min-width}: ${2:$bp-${3:s}-min}) {
$0
}
endsnippet
snippet @max "@media (max-width: ..."
@media (max-width: ${2:$bp-${3:s}-max}) {
$0
}
endsnippet
snippet @min "@media (min-width: ..."
@media (min-width: ${2:$bp-${3:s}-min}) {
$0
}
endsnippet
snippet lg "linear-gradient( ... )"
linear-gradient(
${1:to bottom},
${2:transparent},
${3:transparent}
)$0
endsnippet
snippet bglg "background: linear-gradient ..."
background-image: linear-gradient(
${2:to bottom},
${3:transparent},
${4:transparent}
);
endsnippet
snippet flex "display: flex ..."
display: flex;${1:
flex-direction: ${2:column};}
justify-content: ${3:center};
align-items: ${4:center};
endsnippet
snippet w "width: ;"
width: ${1:100%};
endsnippet
snippet h "height: ;"
height: ${1:100%};
endsnippet
snippet wh "width: ; height: ;"
width: ${1:100%};
height: $1;
endsnippet
snippet mxw "max-width: ;"
max-width: ${1:100%};
endsnippet
snippet mxh "max-height: ;"
max-height: ${1:100%};
endsnippet
snippet mnw "min-width: ;"
min-width: ${1:100%};
endsnippet
snippet mnh "min-height: ;"
min-height: ${1:100%};
endsnippet
snippet bgc "background-color: ;"
background-color: ${1:transparent};
endsnippet
snippet t "top: ... ;"
top: ${1:0};
endsnippet
snippet b "bottom: ... ;"
bottom: ${1:0};
endsnippet
snippet l "left: ... ;"
left: ${1:0};
endsnippet
snippet r "right: ... ;"
right: ${1:0};
endsnippet
snippet bd "border: ... ;"
border: ${1:1px} ${2:solid} ${3:currentColor};
endsnippet
snippet bdt "border-top: ... ;"
border-top: ${1:1px} ${2:solid} ${3:currentColor};
endsnippet
snippet bdb "border-bottom: ... ;"
border-bottom: ${1:1px} ${2:solid} ${3:currentColor};
endsnippet
snippet bdl "border-left: ... ;"
border-left: ${1:1px} ${2:solid} ${3:currentColor};
endsnippet
snippet bdr "border-right: ... ;"
border-right: ${1:1px} ${2:solid} ${3:currentColor};
endsnippet
snippet br "border-radius: ... ;"
border-radius: ${1:50%};
endsnippet
snippet m "margin: ... ;"
margin: ${1:0};
endsnippet
snippet mt "margin-top: ... ;"
margin-top: ${1:0};
endsnippet
snippet mb "margin-bottom: ... ;"
margin-bottom: ${1:0};
endsnippet
snippet ml "margin-left: ... ;"
margin-left: ${1:0};
endsnippet
snippet mr "margin-right: ... ;"
margin-right: ${1:0};
endsnippet
snippet p "padding: ... ;"
padding: ${1:0};
endsnippet
snippet pt "padding-top: ... ;"
padding-top: ${1:0};
endsnippet
snippet pb "padding-bottom: ... ;"
padding-bottom: ${1:0};
endsnippet
snippet pl "padding-left: ... ;"
padding-left: ${1:0};
endsnippet
snippet pr "padding-right: ... ;"
padding-right: ${1:0};
endsnippet
snippet fs "font-size: ... ;"
font-size: ${1:1}${2:rem};
endsnippet
snippet fw "font-weight: ... ;"
font-weight: ${1:bold};
endsnippet
snippet o "opacity: ... ;"
opacity: ${1:0};
endsnippet
snippet grid "display: grid ..."
display: grid;
grid-template-columns: $1;
grid-template-rows: $0;
endsnippet
snippet gr "grid-row: ... ;"
grid-row: $0;
endsnippet
snippet gc "grid-column: ... ;"
grid-column: $0;
endsnippet
snippet rgbw "rgba( ... )"
rgba(${1:#fff}, ${2:0.5})
endsnippet
snippet rgbb "rgba( ... )"
rgba(${1:#000}, ${2:0.5})
endsnippet
snippet before "::before { ... }"
${1:&}::before {
content: '$2';
display: ${3:block};
position: ${4:absolute};
width: ${5:100%};
height: ${6:100%};
${7:top}: ${8:0};
${9:left}: ${10:0};
}
endsnippet
snippet after "::after { ... }"
${1:&}::after {
content: '$2';
display: ${3:block};
position: ${4:absolute};
width: ${5:100%};
height: ${6:100%};
${7:top}: ${8:0};
${9:left}: ${10:0};
}
endsnippet
snippet befaft "::before, ::after { ... }"
${1:&}::before {
$1::after {
content: '$2';
display: ${3:block};
position: ${4:absolute};
width: ${5:100%};
height: ${6:100%};
${7:top}: ${8:0};
${9:left}: ${10:0};
$0
}
endsnippet
snippet bs "box-shadow ...;"
box-shadow: ${1:0} ${2:0} $3px $4px ${5:#000} ${6:inset};
endsnippet
snippet pabs "Position: absolute ..."
position: absolute;
inset: 0;
endsnippet
snippet v "var(--...)"
var(--$0)
endsnippet
snippet ftscss "/* vim: set filetype=scss: */"
/* vim: set filetype=scss: */
endsnippet

View file

@ -0,0 +1,3 @@
snippet polycolor "${colors.color}"
\$\{colors.${1:foreground}}$0
endsnippet

3
UltiSnips/eruby.snippets Normal file
View file

@ -0,0 +1,3 @@
snippet cldef "Colour definition"
$1 = ["#$2", $3, "$4"]
endsnippet

106
UltiSnips/html.snippets Normal file
View file

@ -0,0 +1,106 @@
snippet html5 "HTML% boilerplate."
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge" />${2:
<link rel="stylesheet" href="${3:./assets/css/style.css}">}
<title>$1</title>
</head>
<body>$0 ${4:
<script src="${5:./assets/js/index.js}"></script>}
</body>
</html>
endsnippet
snippet option "<option value=..."
<option value="$1">${2:$1}</option>$0
endsnippet
snippet c "class= ... "
class="$1"$0
endsnippet
snippet plc "<img src='https://via.placeholder.it ..."
<img class="$1" src="https://via.placeholder.com/$2" alt="">
endsnippet
snippet tag "wrap with tag"
<$1$0>
${VISUAL}
</$1>
endsnippet
snippet tagi "wrap with tag inline"
<$1$0>${VISUAL}</$1>
endsnippet
snippet ftphp "<!-- vim: set filetype=php -->"
<!-- vim: set filetype=php: -->
endsnippet
snippet wp "<!-- wp:... {...}"
<!-- wp:$1 ${2:\{"$3":$4\}} -->
$0
<!-- /wp:$1 -->
endsnippet
snippet wp/ "<!-- wp:... {...}"
<!-- wp:$1 ${2:\{"$3":$4\}} /-->$0
endsnippet
snippet wp:spacer "<!-- wp:spacer ..."
<!-- wp:spacer {"height":$1} -->
<div style="height: $1px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->$0
endsnippet
snippet wp:heading "<!-- wp:heading {..."
<!-- wp:heading {"className":"$1"} -->
<h${2:2} class="$1">$3</h$2>
<!-- /wp:heading -->$0
endsnippet
snippet wp:comments "<!-- wp:comments-query-loop"
<!-- wp:comments-query-loop -->
<div class="wp-block-comments-query-loop">
<!-- wp:comment-template -->
<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column \{"width":"40px"\} -->
<div class="wp-block-column" style="flex-basis:40px">
<!-- wp:avatar \{"size":40,"style":\{"border":\{"radius":"20px"\}\}\} /--></div>
<!-- /wp:column -->
<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:comment-author-name /-->
<!-- wp:group \{"style":\{"spacing":\{"margin":\{"top":"0px","bottom":"0px"\}\}\},"layout":\{"type":"flex"\}\} -->
<div class="wp-block-group" style="margin-top:0px;margin-bottom:0px">
<!-- wp:comment-date /-->
<!-- wp:comment-edit-link /-->
</div>
<!-- /wp:group -->
<!-- wp:comment-content /-->
<!-- wp:comment-reply-link /-->
</div>
<!-- /wp:column --></div>
<!-- /wp:columns -->
<!-- /wp:comment-template -->
<!-- wp:comments-pagination -->
<!-- wp:comments-pagination-previous /-->
<!-- wp:comments-pagination-numbers /-->
<!-- wp:comments-pagination-next /-->
<!-- /wp:comments-pagination -->
</div>
<!-- /wp:comments-query-loop -->
endsnippet
snippet wp:comment-form "<!-- wp:post-comments-form"
<!-- wp:post-comments-form /-->
endsnippet

View file

@ -0,0 +1,183 @@
snippet todo "// TODO"
// TODO $0
endsnippet
snippet fn "function .."
function $1($2) {
$0
}
endsnippet
snippet if "if ( ... ) { ... }"
if ($1) {
$0
}
endsnippet
snippet cl "console.log( ... )"
console.log($0);
endsnippet
snippet cw "console.warn( ... )"
console.warn($0);
endsnippet
snippet f "for (...)"
for (let index=0; index < $1; index += 1) {
$0
}
endsnippet
snippet fr "for (...)"
for (let index=${1:0}; index ${2:<} $3; index ${4:+=} ${5:1}) {
$0
}
endsnippet
snippet .fe "...forEach( ... );" i
.forEach(($1) => {
$0
});
endsnippet
snippet wae "window.addEventListener( ... );"
window.addEventListener('$1', ($2) => {
$0
});
endsnippet
snippet .ael "...addEventListener( ... );" i
.addEventListener('$1', ($2) => {
$0
});
endsnippet
snippet st "setTimeout( ... )"
setTimeout(() => {$0
}, ${1:1000});
endsnippet
snippet dq "document.querySelector( ... )"
document.querySelector($0)
endsnippet
snippet dqa "document.querySelectorAll( ... )"
document.querySelectorAll($0)
endsnippet
snippet cdq "const ... = document.querySelector( ... )"
const $1 = document.querySelector($0);
endsnippet
snippet cdqa "const ... = document.querySelectorAll( ... )"
const $1 = document.querySelectorAll($0);
endsnippet
snippet jsx "import React ..."
import * as React from "react";${1:
import { css } from "@emotion/react";}
const `!p snip.rv = snip.basename` = ($2) => {
return (
<div>$0
</div>
);
};
export default `!p snip.rv = snip.basename`;
endsnippet
snippet isi "import { StaticImage } ..."
import { StaticImage } from "gatsby-plugin-image";
endsnippet
snippet simg "<StaticImage ... />"
<StaticImage src="$1" alt="$2" />$0
endsnippet
snippet st. "styled."
const $1 = styled.${2:div}\`
$0
\`;
endsnippet
snippet icss "import ... from ..."
import \{ css \} from "@emotion/react";
endsnippet
snippet ccs "const ..Css=css``"
const $1Css = css\`
$0
\`;
endsnippet
snippet cse "css={css``}"
css={css\`$0\`}
endsnippet
snippet css "css={..Css}"
css={$0Css}
endsnippet
snippet th "this.handle..."
this.handle${1:Click} = this.handle$1.bind(this);
endsnippet
snippet tb "this ... = ... .bind(this);"
this.$1 = this.$1.bind(this);
endsnippet
snippet wprb
import { registerBlockType } from '@wordpress/blocks';
const blockStyle = {
color: red,
backgroundColor: pink,
};
registerBlockType( '${1:namespace}/${2:block-name}', {
title: '$3',
icon: '${4:universal-access-alt}',
category: '${5:layout}',
example: {},
edit( { className } ) {
return <div className={ className }>Hello World, step 1 (from the editor).</div>;
},
save() {
return <div style={ blockStyle }>Hello World, step 1 (from the frontend).</div>;
},
} );
endsnippet
snippet rtw "React + twin.macro"
import * as React from "react"
import 'twin.macro'
const `!p snip.rv = snip.basename` = ({ children }) => {
return (
<$1 tw="$0">
{children}
</$1>
)
}
export default `!p snip.rv = snip.basename`
endsnippet
snippet ue "useEffect(() => {"
useEffect(() => {
$0
}, ${1:[]});
endsnippet
snippet ule "useLayoutEffect(() => {"
useLayoutEffect(() => {
$0
}, ${1:[]});
endsnippet
snippet imp "import ... from ..."
import $1 from "${2:.}/$1";
endsnippet

View file

@ -0,0 +1 @@
extends css, javascript

33
UltiSnips/jinja.snippets Normal file
View file

@ -0,0 +1,33 @@
extends html
snippet com "{# .. #}"
{# $0 #}
endsnippet
snippet import "{% import ... %}"
{% import "$1" as $2 %}
endsnippet
snippet macro "{% macro ...%}"
{% macro $1(${2:$3="$4"}) %}
$0
{% endmacro %}
endsnippet
snippet blockin "{% block .. %}"
{% block $1 %}$0{% endblock %}
endsnippet
snippet block "{% block .. %}"
{% block $1 %}
$0
{% endblock %}
endsnippet
snippet include "{% include .. %}"
{% include "$0" %}
endsnippet
snippet extends "{% extends .. %}"
{% extends "$0" %}
endsnippet

19
UltiSnips/jst.snippets Normal file
View file

@ -0,0 +1,19 @@
snippet req "<%- require( ..."
<%- require('!!ejs-compiled-loader?{}!./$1.ejs')($0) %>
endsnippet
snippet % "<% ... %>"
<% $0 %>
endsnippet
snippet = "<= ... =>"
<%= $0 %>
endsnippet
snippet - "<- ... ->"
<%- $0 %>
endsnippet
snippet # "<# ... #>"
<%# $0 %>
endsnippet

View file

@ -0,0 +1,83 @@
snippet l "[...](...)"
[${1:title}](${2:url})$0
endsnippet
snippet c "``` ..."
\`\`\`$0
\`\`\`
endsnippet
# latex
snippet vec "vector 1x2" i
\\begin{bmatrix} $1 \\\\ $2 \\end{bmatrix}$0
endsnippet
snippet vec3 "vector 1x3" i
\\begin{bmatrix} $1 \\\\ $2 \\\\ $3 \\end{bmatrix}$0
endsnippet
snippet mx "matrix 2x2" i
\\begin{bmatrix} $1 & $2 \\\\ $3 & $4 \\end{bmatrix}$0
endsnippet
snippet mx3 "matrix 3x3" i
\\begin{bmatrix} $1 & $2 & $3 \\\\ $4 & $5 & $6 \\\\ $7 & $8 & $9 \\end{bmatrix}$0
endsnippet
snippet lim "limit" i
\\lim{$1 \\to $2}$0
endsnippet
snippet sum "sum" i
\\sum_{$1}^{$2}$0
endsnippet
snippet fr "fraction" i
\\frac{$1}{$2}$0
endsnippet
snippet sq "square root" i
\\sqrt{$1}$0
endsnippet
snippet rt "root" i
\\sqrt[${1:3}]{${2:x}}$0
endsnippet
snippet br "{}" i
\\lbrace $1 \\rbrace$0
endsnippet
snippet | "|...|"
\\lvert $1 \\rvert$0
endsnippet
snippet || "|...|"
\\lVert $1 \\rVert$0
endsnippet
snippet ds "displaystyle" i
\\displaystyle$0
endsnippet
snippet lmb "lambda" i
\\lambda$0
endsnippet
snippet imp "implies" i
\\Rightarrow
endsnippet
snippet int "integral" i
\\int_{$1}^{$2} $0
endsnippet
snippet web "web snippet"
[$1]($2)
\`\`\`
$3
\`\`\`
---$0
endsnippet

View file

@ -0,0 +1,3 @@
snippet pl "{% partial ... %}"
{% partial "$0" %}
endsnippet

95
UltiSnips/php.snippets Normal file
View file

@ -0,0 +1,95 @@
extends html
snippet doc "/**"
/**
* $1
*/$0
endsnippet
snippet pre "<pre>print_r()</pre>"
echo "<pre>";
print_r($0);
echo "</pre>";
endsnippet
snippet if "if () {}"
if ($1) {
$0
}
endsnippet
snippet fe "foreach () {}"
foreach (\$${1:array} as \$${2:item}) {
$0
}
endsnippet
snippet pp "<?php ... ?>"
<?php $0; ?>
endsnippet
snippet pe "<?php ... ?>"
<?php echo "$0"; ?>
endsnippet
snippet ph "<?php ... ?>"
<?php esc_html( $0 ); ?>
endsnippet
snippet pf "public function ..."
public function $1($2) {
$0
}
endsnippet
snippet pt "protected function ..."
protected function $1($2) {
$0
}
endsnippet
snippet pv "private function ..."
private function $1($2) {
$0
}
endsnippet
snippet psf "public static function ..."
public static function $1($2) {
$0
}
endsnippet
snippet prsf "public static function ..."
private static function $1($2) {
$0
}
endsnippet
snippet ''= "'...' => "
'$1' => $0,
endsnippet
snippet apm "add_post_meta(..."
add_post_meta( ${1:2720}, '__DEBUG__$2', $0);
endsnippet
snippet upm "update_post_meta(..."
update_post_meta( ${1:2720}, '__DEBUG__$1', $0);
endsnippet
snippet sw "switch ..."
switch ($1) {
case $2:
$3;
break;
default:
$0
}
endsnippet
snippet cs "case ..."
case $1:
$0;
break;
endsnippet

19
UltiSnips/python.snippets Normal file
View file

@ -0,0 +1,19 @@
snippet qxconfig "qutebrowser xResources config"
c.colors.messages.$1 = xresources['*${2:background}']$0
endsnippet
snippet main "if __name__ == .."
def main():
$0
if __name__ == "__main__":
main()
endsnippet
snippet ld "logger.debug()"
logger.debug("$1".format($2))
endsnippet
snippet li "logger.info()"
logger.info("$1".format($2))
endsnippet

49
UltiSnips/scss.snippets Normal file
View file

@ -0,0 +1,49 @@
extends css
snippet @i "@include"
@include $0;
endsnippet
snippet imt "@include margin-t( <top> );"
@include margin-t(${0:1});
endsnippet
snippet imb "@include margin-b( <bottom> );"
@include margin-b(${0:1});
endsnippet
snippet imv "@include margin-v( <top>, <bottom> );"
@include margin-v($0);
endsnippet
snippet im "@include margin( <top>, <bottom>, <left>, <right> );"
@include margin($0);
endsnippet
snippet ipt "@include pad-t( <top> );"
@include pad-t(${0:1});
endsnippet
snippet ipb "@include pad-b( <bottom> );"
@include pad-b(${0:1});
endsnippet
snippet iph "@include pad-v( <left>, <right> );"
@include pad-h($0);
endsnippet
snippet ipv "@include pad-v( <top>, <bottom> );"
@include pad-v($0);
endsnippet
snippet ipl "@include pad-l( <left> );"
@include pad-l(${0:1});
endsnippet
snippet ipr "@include pad-r( <right> );"
@include pad-r(${0:1});
endsnippet
snippet ip "@include pad( <top>, <bottom>, <left>, <right> );"
@include pad($0);
endsnippet

3
UltiSnips/sh.snippets Normal file
View file

@ -0,0 +1,3 @@
snippet polybashcolor "%{{F{color}}}" i
%\{\{F\{color${1:4}}}}
endsnippet

7
UltiSnips/vim.snippets Normal file
View file

@ -0,0 +1,7 @@
snippet scolor "SetColor('', .."
SetColor('$1', '$2', '$3', '$4', '$5', '$6')$0
endsnippet
snippet cscolor "call SetColor('', .."
call SetColor('$1', '$2', '$3', '$4', '$5', '$6')$0
endsnippet

154
UltiSnips/vue.snippets Normal file
View file

@ -0,0 +1,154 @@
extends html, javascript, scss
snippet vue "Default template"
<template>
$0
</template>
<script>
export default {
props: {
},
data () {
return {
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
</style>
endsnippet
snippet byinput "Beufy Input Field"
<div class="field"> <!-- $1 -->
<label class="label">$1</label>
<div class="control">
<input class="input"
name="$2"
${3:type="${4:text}"}
v-model="${5:$2}">
</div>
</div>$0
endsnippet
snippet byselect "Buefy Select Field"
<div class="field"> <!-- $1 -->
<label class="label">$1</label>
<div class="control">
<div class="select">
<select name="$2"
:v-model="$2">
$0
</select>
</div>
</div>
</div>
endsnippet
snippet nt "this.$nextTick( () => { ... })"
this.$nextTick( () => {
$0
})
endsnippet
snippet props "props: { ... }"
props: {
$1: {
type: ${2:Boolean},
required: ${3:false},
default() {
return ${4:false};
},
},
},$0
endsnippet
snippet prop "property: { ... }"
$1: {
type: ${2:Boolean},
required: ${3:false},
default() {
return ${4:false};
},
},$0
endsnippet
snippet comps "components { ... }"
components: {
$0,
},
endsnippet
snippet data "data() { return { ... } }"
data() {
return {
$1: $2,$0
};
},
endsnippet
snippet compd "computed: { ... }"
computed: {
$1() {
$0
},
},
endsnippet
snippet meth "methods: { ... }"
methods: {
$1() {
$0
},
},
endsnippet
snippet style "<style ..."
<style ${1:lang="${2:scss}"} ${3:scoped}>
$0
</style>
endsnippet
snippet impc "import ... from '~/components...';"
import $1 from '~/components/$1';$0
endsnippet
snippet tran "<transition ..."
<transition name="$1">
${VISUAL}$0
</transition>
endsnippet
snippet .tran "vue transition selector"
.$1 {
&-enter-active {
transition: ${2:opacity} ${3:1}s;
}
&-leave-active {
transition: $2 ${4:1}s;
}
&-enter,
&-leave-to {
$2: ${5:0};
}
}$0
endsnippet
snippet script "<script> ...</script>"
<script>
export default {
name: '$0',
};
</script>
endsnippet
snippet s ":style='{ ... }"
:style="{ '$1': $2 }"$0
endsnippet

1
after/ftplugin/help.vim Normal file
View file

@ -0,0 +1 @@
nmap <buffer> q :q<CR>

1
after/ftplugin/html.vim Normal file
View file

@ -0,0 +1 @@
setlocal foldnestmax=5

View file

@ -0,0 +1,107 @@
" https://gist.github.com/romainl/1f50c55b0ee92e5420de51e6c4b38491
nnoremap <silent> <space>cF :call CocAction('runCommand', 'eslint.executeAutofix')<CR>
setlocal includeexpr=RESOLVE(v:fname)
function! RESOLVE(module) abort
let built_ins = [
\ "assert",
\ "async_hooks",
\ "child_process",
\ "cluster",
\ "crypto",
\ "dns",
\ "domain",
\ "events",
\ "fs",
\ "http",
\ "http2",
\ "https",
\ "inspector",
\ "net",
\ "os",
\ "path",
\ "perf_hooks",
\ "punycode",
\ "querystring",
\ "readline",
\ "stream",
\ "string_decoder",
\ "tls",
\ "tty",
\ "dgram",
\ "url",
\ "util",
\ "v8",
\ "vm",
\ "zlib" ]
" There's no source file for built-in node modules.
" Example: var http = require('http');
if index(built_ins, a:module) != -1
return 0
endif
" Handling absolute and relative paths.
" Example: var foo = require('./foo.js');
if a:module =~ '^\/' || a:module =~ '^\.\{1,2}\/'
let root = fnamemodify(substitute(finddir("node_modules", ".;"), '/node_modules', '', ''), ':p:h:h')
if a:module =~ '^\.\{1,2}\/'
let module = substitute(a:module, '^\.\{1,2}\/', '', '')
endif
let filename = 0
try
let filename = findfile(module, root)
catch
try
let filename = findfile(module . ".js", root)
catch
try
let filename = findfile(module . "/index.js", root)
catch
let filename = 0
endtry
endtry
endtry
return filename
endif
" Handling vendored-in modules.
" Example: var _ = require('lodash');
for dir in NODE_MODULES_PATHS()
" require('module') => node_modules/module/package.json#main => node_modules/module/**/...
if filereadable(dir . "/" . a:module . "/package.json")
let package = json_decode(join(readfile(dir . "/" . a:module . "/package.json")))
return dir . "/" . a:module . "/" . substitute(get(package, "main", "index.js"), '^\.\{1,2}\/', '', '')
endif
" require('module') => node_modules/module/index.js
if filereadable(dir . "/" . a:module . "/index.js")
return dir . "/" . a:module . "/index.js"
endif
" require('module') => node_modules/module.js
if filereadable(dir . "/" . a:module . ".js")
return dir . "/" . a:module . ".js"
endif
endfor
" well, well, well
return 0
endfunction
function! NODE_MODULES_PATHS() abort
" node_modules/
let node_modules = finddir("node_modules", ".;")
" node_modules/foo/node_modules/
let basic_dirs = extend([node_modules], globpath(node_modules, "*/node_modules", 1, 1))
" node_modules/@foo/bar/node_modules/
let all_dirs = extend(basic_dirs, globpath(node_modules, "@*/*/node_modules", 1, 1))
return all_dirs
endfunction

1
after/ftplugin/netrw.vim Normal file
View file

@ -0,0 +1 @@
setl bufhidden=hide

View file

@ -1,62 +0,0 @@
-- PHP-specific settings and enhancements
-- Loaded automatically for PHP files
-- Enable includeexpr for intelligent gf (goto file) behavior
-- Handles common WordPress/PHP path patterns like:
-- require_once __DIR__ . '/../path/to/file.php';
-- include __FILE__ . '/relative/path.php';
vim.opt_local.includeexpr = "v:lua.resolve_php_gf_path(v:fname)"
-- Global function to resolve PHP file paths for gf command
function _G.resolve_php_gf_path(fname)
local line = vim.api.nvim_get_current_line()
local current_dir = vim.fn.expand('%:p:h')
local current_file = vim.fn.expand('%:p')
-- Pattern 1: __DIR__ . '/path' or __DIR__ . '/../path'
-- Most common in modern PHP/WordPress
local path = line:match("__DIR__%s*%.%s*['\"]([^'\"]+)['\"]")
if path then
path = current_dir .. path
return vim.fn.simplify(path)
end
-- Pattern 2: __FILE__ . '/path'
-- Less common but appears in legacy WordPress code
-- __FILE__ resolves to the current file's full path, so we use its directory
path = line:match("__FILE__%s*%.%s*['\"]([^'\"]+)['\"]")
if path then
-- __FILE__ is the file itself, but when concatenated with a path,
-- it's typically used like __DIR__, so we use the directory
path = current_dir .. path
return vim.fn.simplify(path)
end
-- Pattern 3: dirname(__FILE__) . '/path'
-- Old-style equivalent to __DIR__
path = line:match("dirname%s*%(%s*__FILE__%s*%)%s*%.%s*['\"]([^'\"]+)['\"]")
if path then
path = current_dir .. path
return vim.fn.simplify(path)
end
-- Pattern 4: Simple quoted path in require/include statements
-- Fallback for basic relative paths
path = line:match("require[_%w]*%s*['\"]([^'\"]+)['\"]")
if path then
-- Try relative to current file's directory
path = current_dir .. '/' .. path
return vim.fn.simplify(path)
end
path = line:match("include[_%w]*%s*['\"]([^'\"]+)['\"]")
if path then
-- Try relative to current file's directory
path = current_dir .. '/' .. path
return vim.fn.simplify(path)
end
-- Fallback: return as-is (normal gf behavior)
-- This allows standard gf to work for simple filenames
return fname
end

2
after/ftplugin/php.vim Normal file
View file

@ -0,0 +1,2 @@
setlocal iskeyword+=$
setlocal autoindent

11
after/ftplugin/python.vim Normal file
View file

@ -0,0 +1,11 @@
setlocal tabstop=4
setlocal textwidth=88
setlocal foldmethod=indent
setlocal foldnestmax=1
setlocal colorcolumn=88
noremap <F4> :w<CR>:silent !clear;python %<CR> :redraw!<CR>
nnoremap <silent> <buffer> <F5> :call SaveAndExecute('python')<CR>

2
after/ftplugin/qf.vim Normal file
View file

@ -0,0 +1,2 @@
nmap <buffer> o <CR>
nmap <buffer> q :q<CR>

1
after/ftplugin/sass.vim Normal file
View file

@ -0,0 +1 @@
setlocal includeexpr=substitute(substitute(substitute(v:fname,'\%(.*/\|^\)\zs','_',''),'^[\\~@]\/','./',''),'^[\\~@]','./node_modules/','')

2
after/ftplugin/vim.vim Normal file
View file

@ -0,0 +1,2 @@
setlocal foldmethod=marker

4
after/indent/php.vim Normal file
View file

@ -0,0 +1,4 @@
" https://github.com/nvim-treesitter/nvim-treesitter/issues/462#issuecomment-700278736
setlocal indentexpr =
setlocal autoindent
setlocal smartindent

View file

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

View file

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

View file

@ -1,42 +1,18 @@
;extends (id_selector (id_name) @CssIdentifier)
(id_selector (id_name)) @CssIdSelector
; CSS Selectors - ID selectors (#my-id)
; Priority: Must override default @constant capture
(id_selector "#" @punctuation.delimiter)
(id_selector (id_name) @CssIdentifier (#set! priority 200))
; CSS Selectors - Class selectors (.my-class)
(class_selector "." @punctuation.delimiter)
(class_selector (class_name) @CssClassName (#set! priority 200))
; CSS Selectors - Pseudo-class selectors (:hover, :focus, etc.)
(pseudo_class_selector ":" @punctuation.delimiter)
(pseudo_class_selector (class_name) @cssPseudoClass (#set! priority 200))
; CSS Selectors - Pseudo-element selectors (::before, ::after)
(pseudo_element_selector "::" @punctuation.delimiter)
(pseudo_element_selector (tag_name) @cssPseudoElement (#set! priority 200))
; CSS Selectors - Nesting selector (&)
(nesting_selector) @cssNestingSelector
; CSS Selectors - Universal selector (*)
(universal_selector) @CssUniversalSelector
; CSS Selectors - Tag/element selectors (div, p, etc.)
(tag_name) @HtmlTagName (tag_name) @HtmlTagName
; CSS Properties (class_selector (class_name) @CssClassName)
((property_name) @CssProp) (selectors (pseudo_class_selector (class_name) @cssPseudoClass))
(nesting_selector) @cssNestingSelector
; CSS Property values ; need to find out how to make this more specific?
(declaration (property_name) (_) @CssPropertyValue) (universal_selector) @CssUniversalSelector
((property_name) (_)) @CssProp
; CSS Units (px, em, rem, %, etc.)
(unit) @CssUnit (unit) @CssUnit
; CSS Media queries (declaration (property_name) (_) @CssPropertyValue)
(media_statement
(feature_query (media_statement (feature_query (feature_name) @cssMediaFeatureName (_ (unit) @cssMediaQueryValueUnit) @cssMediaQueryValue) @cssMediaQuery)
(feature_name) @cssMediaFeatureName
(_ (unit) @cssMediaQueryValueUnit) @cssMediaQueryValue) @cssMediaQuery)

View file

View file

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

View file

@ -1,20 +1,17 @@
;extends (start_tag
; HTML class attribute values - should match CSS .class-name color
; Priority: Must override default @string capture (priority 99)
(attribute (attribute
(attribute_name) @ClassNameAttribute (#eq? @ClassNameAttribute "class") (attribute_name) @ClassNameAttribute (#eq? @ClassNameAttribute "class")
(quoted_attribute_value (quoted_attribute_value
(attribute_value) @CssClassName (#set! priority 200))) (attribute_value) @CssClassName )))
; HTML id attribute values - should match CSS #id-name color (start_tag
(attribute (attribute
(attribute_name) @IdAttribute (#eq? @IdAttribute "id") (attribute_name) @IdAttribute (#eq? @IdAttribute "id")
(quoted_attribute_value (quoted_attribute_value
(attribute_value) @CssIdentifier (#set! priority 200))) (attribute_value) @CssIdentifier )))
; HTML data-* attributes (start_tag
(attribute (attribute
(attribute_name) @DataAttribute (#match? @DataAttribute "^data-") (attribute_name) @DataAttribute (#match? @DataAttribute "^data-")
(quoted_attribute_value (quoted_attribute_value
(attribute_value) @DataAttributeValue (#set! priority 200))) (attribute_value) @DataAttributeValue )))

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

79
colors/README.txt Normal file
View file

@ -0,0 +1,79 @@
README.txt for color scheme files
These files are used for the ":colorscheme" command. They appear in the
Edit/Color Scheme menu in the GUI.
Hints for writing a color scheme file:
There are two basic ways to define a color scheme:
1. Define a new Normal color and set the 'background' option accordingly.
set background={light or dark}
highlight clear
highlight Normal ...
...
2. Use the default Normal color and automatically adjust to the value of
'background'.
highlight clear Normal
set background&
highlight clear
if &background == "light"
highlight Error ...
...
else
highlight Error ...
...
endif
You can use ":highlight clear" to reset everything to the defaults, and then
change the groups that you want differently. This also will work for groups
that are added in later versions of Vim.
Note that ":highlight clear" uses the value of 'background', thus set it
before this command.
Some attributes (e.g., bold) might be set in the defaults that you want
removed in your color scheme. Use something like "gui=NONE" to remove the
attributes.
In case you want to set 'background' depending on the colorscheme selected,
this autocmd might be useful:
autocmd SourcePre */colors/blue_sky.vim set background=dark
Replace "blue_sky" with the name of the colorscheme.
In case you want to tweak a colorscheme after it was loaded, check out the
ColorScheme autocmd event.
To customize a colorscheme use another name, e.g. "~/.vim/colors/mine.vim",
and use `:runtime` to load the original colorscheme:
" load the "evening" colorscheme
runtime colors/evening.vim
" change the color of statements
hi Statement ctermfg=Blue guifg=Blue
To see which highlight group is used where, find the help for
"highlight-groups" and "group-name".
You can use ":highlight" to find out the current colors. Exception: the
ctermfg and ctermbg values are numbers, which are only valid for the current
terminal. Use the color names instead. See ":help cterm-colors".
The default color settings can be found in the source file src/syntax.c.
Search for "highlight_init".
If you think you have a color scheme that is good enough to be used by others,
please check the following items:
- Does it work in a color terminal as well as in the GUI?
- Is "g:colors_name" set to a meaningful value? In case of doubt you can do
it this way:
let g:colors_name = expand('<sfile>:t:r')
- Is 'background' either used or appropriately set to "light" or "dark"?
- Try setting 'hlsearch' and searching for a pattern, is the match easy to
spot?
- Split a window with ":split" and ":vsplit". Are the status lines and
vertical separators clearly visible?
- In the GUI, is it easy to find the cursor, also in a file with lots of
syntax highlighting?
- Do not use hard coded escape sequences, these will not work in other
terminals. Always use color names or #RRGGBB for the GUI.

View file

@ -1,20 +0,0 @@
-- Paper Tonic Modern
-- A paper-like colorscheme for Neovim supporting both light and dark modes
-- Forked from the original Paper Tonic with modern Lua implementation
-- Reset highlights and syntax
vim.cmd('highlight clear')
if vim.fn.exists('syntax_on') then
vim.cmd('syntax reset')
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
-- Load the colorscheme
require('paper-tonic-modern').load()

1
ftdetect/astro.vim Normal file
View file

@ -0,0 +1 @@
autocmd BufRead,BufEnter *.astro set filetype=astro

1
ftplugin/css.vim Normal file
View file

@ -0,0 +1 @@
setlocal iskeyword+=-

2
ftplugin/eruby.vim Normal file
View file

@ -0,0 +1,2 @@
nnoremap <buffer> <F5> :call ft#compileeruby#CompileEruby(0)<CR>
nnoremap <buffer> <F6> :call ft#compileeruby#CompileEruby(1)<CR>

1
ftplugin/fish.vim Normal file
View file

@ -0,0 +1 @@
:compiler fish

1
ftplugin/help.vim Normal file
View file

@ -0,0 +1 @@
setlocal keywordprg=:help

0
ftplugin/html.vim Normal file
View file

1
ftplugin/javascript.vim Normal file
View file

@ -0,0 +1 @@
setlocal iskeyword+=$

20
ftplugin/markdown.vim Normal file
View file

@ -0,0 +1,20 @@
setlocal spell
" markdown : jump to next heading
" https://gist.github.com/romainl/ac63e108c3d11084be62b3c04156c263
function! s:JumpToNextHeading(direction, count)
let col = col(".")
silent execute a:direction == "up" ? '?^#' : '/^#'
if a:count > 1
silent execute "normal! " . repeat("n", a:direction == "up" && col != 1 ? a:count : a:count - 1)
endif
silent execute "normal! " . col . "|"
unlet col
endfunction
nnoremap <buffer> <silent> ]] :<C-u>call <SID>JumpToNextHeading("down", v:count1)<CR>
nnoremap <buffer> <silent> [[ :<C-u>call <SID>JumpToNextHeading("up", v:count1)<CR>

8
ftplugin/octobercms.vim Normal file
View file

@ -0,0 +1,8 @@
function! StripSpecialM() abort
:%s/ $//e
endfunction
augroup RemoveSpecialM
autocmd BufWritePre <buffer> call StripSpecialM()
augroup END

2
ftplugin/ruby.vim Normal file
View file

@ -0,0 +1,2 @@
nnoremap <silent> <buffer> <F5> :call SaveAndExecute('ruby')<CR>

1
ftplugin/sass.vim Normal file
View file

@ -0,0 +1 @@
setlocal iskeyword+=$

2
ftplugin/sh.vim Normal file
View file

@ -0,0 +1,2 @@
setlocal makeprg=shellcheck\ -f\ gcc\ %
nnoremap <buffer> <F5> :w !sh<cr>

4
ftplugin/vim.vim Normal file
View file

@ -0,0 +1,4 @@
setlocal keywordprg=:help
setlocal iskeyword+=:
nnoremap <buffer> <F5> :w \| source %<cr>

8
ftplugin/vue.vim Normal file
View file

@ -0,0 +1,8 @@
setlocal includeexpr=substitute(substitute(v:fname,'^[\\~@]\/','./',''),'^[\\~@]','./node_modules/','')
setlocal suffixesadd=.js,.vue,.scss
setlocal complete=.,w,b,u,i
setlocal include=\\%(\\<require\\s*(\\s*\\\|\\<import\\>[^;\"']*\\)[\"']\\zs[^\"']*
setlocal foldnestmax=3

34
init.auto-window.vim Normal file
View file

@ -0,0 +1,34 @@
"auto_window_settings {{{
function! OnWinEnter() abort"{{{
if &buftype != ''
setlocal colorcolumn=0
elseif exists('b:colorcolumn_restore')
execute 'setlocal colorcolumn=' . b:colorcolumn_restore
endif
if &buftype == 'terminal'
setlocal nonumber norelativenumber
else
setlocal number relativenumber
endif
setlocal cursorline
setlocal cursorcolumn
endfunction
"}}}
function! OnWinLeave() abort"{{{
let b:colorcolumn_restore = &colorcolumn
setlocal colorcolumn=0
setlocal nocursorline
setlocal nocursorcolumn
setlocal norelativenumber
endfunction
"}}}
augroup auto_window_settings "{{{
autocmd!
autocmd WinEnter * call OnWinEnter()
autocmd WinLeave * call OnWinLeave()
augroup END
"}}} augroup auto_window_settings
" }}} auto_window_settings

27
init.commands.vim Normal file
View file

@ -0,0 +1,27 @@
" SpacesToTabs {{{
" TODO - ake a proper regex substitution
function! s:spacestotabs()
%s/^ / /e
%s/^ / /e
%s/^ / /e
%s/^ / /e
%s/^ / /e
%s/^ / /e
endfunction
command SpacesToTabs call s:spacestotabs()
"}}}
" TrimWhitespace {{{
command! -range=% TrimWhitespace let b:wv = winsaveview() |
\ keeppattern <line1>,<line2>s/\s\+$// |
\ call winrestview(b:wv)
"}}}
" Scratch, ScratchVertical {{{
command! Scratch new | setlocal buftype=nofile | setlocal bufhidden=hide | setlocal noswapfile
command! ScratchVertical vnew | setlocal buftype=nofile | setlocal bufhidden=hide | setlocal noswapfile
"}}}
"
"

52
init.fold-text.vim Normal file
View file

@ -0,0 +1,52 @@
function! MyFoldText()
if !exists('g:foldtext_column')
let g:foldtext_column = 80 " column to right align foldtext with
endif
if !exists('b:foldtext_column')
let b:foldtext_column = g:foldtext_column " column to right align foldtext with
endif
if !exists('g:foldtext_maxcolumn')
let g:foldtext_maxcolumn = 120
endif
let l:linecount = v:foldend - v:foldstart
" don't display foldmarker braces
" put one of the braces in brackets so vim doesn't treat
" it as an actual fold marker
let l:line = substitute(getline(v:foldstart), '"\?{\({\){', '', '')
" don't display vim comment quotation marks
" TODO other comment markers
let l:line = substitute(l:line, "\^\"\\s\\?", '', '')
" TODO replace spaces with tabs if appropriate
" replace tabs with spaces if expandtab
if &expandtab == 0
let l:tabwidth = &tabstop
let l:tabspace = ' '[1:l:tabwidth]
let l:line = substitute(l:line, ' ', l:tabspace, 'g')
endif
" let l:postfix = l:linecount . ' ' . substitute(v:folddashes, '-', '•', 'g')
let l:postfix = l:linecount . ' ' . substitute(v:folddashes, '-', '↓', 'g')
while strchars(l:postfix) < 7
let l:postfix = ' ' . l:postfix
endwhile
" let l:postfix = ' ↓ ' . l:postfix
let l:len_line = len(l:line)
let l:len_postfix = strchars(l:postfix)
if l:len_line + l:len_postfix <= b:foldtext_column
let l:padding = ' '[l:len_line + l:len_postfix + 0:b:foldtext_column - 1]
let l:foldtext = l:line . l:padding . l:postfix
else
let l:sniptext = ' ⋯'
let l:foldtext = l:line[:b:foldtext_column - 1 - strchars(l:sniptext) - l:len_postfix] . l:sniptext . l:postfix
endif
return l:foldtext
endfunction
set foldtext=MyFoldText()

434
init.full.vim Normal file
View file

@ -0,0 +1,434 @@
" set ruby program to global one. Need to investigate if this will cause
" problems if I ever use ruby virtual environments or RVM or similar.
let g:ruby_host_prog = '~/.gem/ruby/2.7.0/bin/neovim-ruby-host'
source ~/.config/nvim/init.plugins.vim
source ~/.config/nvim/init.tabline.vim
source ~/.config/nvim/init.auto-window.vim
source ~/.config/nvim/init.fold-text.vim
source ~/.config/nvim/init.commands.vim
"
" require lua init modules
lua <<EOF
-- require('init-lsp')
EOF
" TODO - maybe separate some of these into own files too
" #functions {{{
function! <SID>SynStack()"{{{
if !exists('*synstack')
return
endif
echo map(synstack(line('.'), col('.')), 'synIDattr(v:val,"name")') '-> ' . synIDattr(synIDtrans(synID(line('.'),col('.'),1)), 'name' )
endfunc
" nmap <space>pp :call <SID>SynStack()<CR>
nmap <silent><space>pp :TSHighlightCapturesUnderCursor<CR>
"}}}
function! s:RunShellCommand(cmdline) abort"{{{
" Shell command
" http://vim.wikia.com/wiki/VimTip1599
let l:expanded_cmdline = a:cmdline
for l:part in split(a:cmdline, ' ')
if l:part[0] =~ '\v[%#<]'
let l:expanded_part = fnameescape(expand(l:part))
let l:expanded_cmdline = substitute(l:expanded_cmdline, l:part, l:expanded_part, '')
endif
endfor
if g:shell_scratch_buffer_nr > -1
let l:win_nr = bufwinnr(g:shell_scratch_buffer_nr)
if l:win_nr < 0
execute 'bdelete' g:shell_scratch_buffer_nr
top new
let g:shell_scratch_buffer_nr = bufnr('%')
else
execute l:win_nr. ' wincmd w'
setlocal modifiable
%delete _
endif
else
top new
let g:shell_scratch_buffer_nr = bufnr('%')
endif
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap
nnoremap <buffer> q :bdelete<CR>
augroup ResetShellBufferNr
autocmd! * <buffer>
autocmd BufUnload <buffer> let g:shell_scratch_buffer_nr = -1
augroup END
" call setline(1, 'You entered: ' . a:cmdline)
" call setline(2, 'Expanded Form: ' .l:expanded_cmdline)
" call setline(3,substitute(getline(2),'.','=','g'))
execute '$read !'. l:expanded_cmdline
1
setlocal nomodifiable
if !exists('b:shell_line_count')
let b:shell_line_count = line('$')
if b:shell_line_count > 25
let b:shell_line_count = 20
endif
execute 'resize' b:shell_line_count + 1
endif
wincmd p
endfunction
command! -complete=shellcmd -nargs=+ Shell call s:RunShellCommand(<q-args>)
let g:shell_scratch_buffer_nr = -1
"}}}
function! SaveAndExecute(ex_command) abort "{{{
" https://stackoverflow.com/a/40195855
" ex_command: command to run to execute file
" SOURCE [reusable window]: https://github.com/fatih/vim-go/blob/master/autoload/go/ui.vim
" save and reload current file
silent execute 'update | edit'
" get file path of current file
let s:current_buffer_file_path = expand('%')
let s:output_buffer_name = 'Output'
let s:output_buffer_filetype = 'output'
" reuse existing buffer window if it exists otherwise create a new one
if !exists('c:buf_nr') || !bufexists(s:buf_nr) || bufwinnr(s:buf_nr) == -1
silent execute 'top new ' . s:output_buffer_name
let s:buf_nr = bufnr('%')
elseif bufwinnr(s:buf_nr) != bufwinnr('%')
silent execute bufwinnr(s:buf_nr) . 'wincmd w'
endif
silent execute 'setlocal filetype=' . s:output_buffer_filetype
setlocal bufhidden=delete
setlocal buftype=nofile
setlocal noswapfile
setlocal nobuflisted
setlocal winfixheight
setlocal cursorline " make it easy to distinguish
" setlocal nonumber
" setlocal norelativenumber
setlocal showbreak=""
nnoremap <silent> <buffer> q :bdelete!<CR>'.zz
" clear the buffer
setlocal noreadonly
" setlocal modifiable
%delete _
" add the console output
silent execute '.!'. a:ex_command . ' ' . shellescape(s:current_buffer_file_path, 1)
" resize window to content length
" Note: This is annoying because if you print a lot of lines then your code buffer is forced to a height of one line every time you run this function.
" However without this line the buffer starts off as a default size and if you resize the buffer then it keeps that custom size after repeated runs of this function.
" But if you close the output buffer then it returns to using the default size when its recreated
"execute 'resize' . line('$')
" make the buffer non modifiable
setlocal readonly
" setlocal nomodifiable
endfunction
"}}}
function! JsIncludeExpr(file)"{{{
" substitute(substitute(v:fname,'^[\\~@]\/','./',''),'^[\\~@]','./node_modules/','')
return substitute(substitute(a:file,'^[\\~@]\/','./',''),'^[\\~@]','./node_modules/','')
endfunction
"}}}
function! Redir(cmd) "{{{
for win in range(1, winnr('$'))
if getwinvar(win, 'scratch')
execute win . 'windo close'
endif
endfor
if a:cmd =~ '^!'
let output = system(matchstr(a:cmd, '^!\zs.*'))
else
redir => output
execute a:cmd
redir END
endif
vnew
let w:scratch = 1
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
call setline(1, split(output, "\n"))
endfunction
command! -nargs=1 -complete=command Redir silent call Redir(<q-args>)
" Usage:
" :Redir hi ............. show the full output of command ':hi' in a scratch window
" :Redir !ls -al ........ show the full output of command ':!ls -al' in a scratch window
"}}}
"}}}
function! ToggleHighlights() abort "{{{
if ! exists('b:toggle_highlights_state')
" state 1 is normal, state 0 is faded
let b:toggle_highlights_state = 1
else
let b:toggle_highlights_state = ! b:toggle_highlights_state
endif
if b:toggle_highlights_state == 1
hi! link Comment None
else
hi! link Comment CommentDim
endif
endfunction
"}}}
"----------------------------------------------------------------------------}}}
" #settings {{{
scriptencoding utf-8
set ttyfast
set dictionary=spell
" set thesaurus+=/usr/share/dict/mthesaur.txt
augroup Dictionary
au!
au FileType markdown,text setlocal dictionary=/usr/share/dict/aspell-dump.txt
augroup END
" disable background color erase
" https://sunaku.github.io/vim-256color-bce.html
set t_ut=
syntax on
set fillchars=stl:\ ,stlnc:\ ,vert:\|,fold:\
set guioptions-=mTrLb
" set guioptions+=c
if exists('+termguicolors')
let &t_8f = "\<Esc>[38;2;%lu;%lu;%lum"
let &t_8b = "\<Esc>[48;2;%lu;%lu;%lum"
set termguicolors
endif
colorscheme paper-tonic
set updatetime=100
set timeoutlen=500
set lazyredraw
" https://parceljs.org/hmr.html#safe-write
set backupcopy=yes
set title
" the ;/home/* tells vim to stop searching at /home/* (I hope!)
set tags=./.tags,.tags,./tags-external,.tags-external;/home/*
" persisitent undo file
set undofile
set undodir=~/.config/nvim/undodir
set backupdir=~/.nvim/tmpdir
set directory=~/.nvim/tmpdir
set viewoptions-=options
set nosplitbelow nosplitright
set switchbuf=uselast
set ignorecase
set smartcase
set wildmenu
set wildmode=longest:full,full
set wildignore+=**/node_modules/**,**/dist/**,**/.git/**,**/build/**
" Use ag over grep
if executable('ag')
set grepprg=ag\ --nogroup\ --nocolor\ --ignore\ node_modules
endif
set hidden
set hlsearch
set completeopt=menuone,noselect
set nospell
set spelllang=en_gb
set diffopt+=vertical
let g:tex_flavor = 'latex'
set rnu nu
set tabstop=2
set softtabstop=-1
set shiftwidth=0
set shiftround
set noexpandtab
set autoindent
set breakindent
set showbreak=\ \ ↳\
set formatoptions=cq
set wrapmargin=0
setlocal cursorline
set foldcolumn=0
set signcolumn=yes
set colorcolumn=80,120
set iskeyword+=-
set scrolloff=4
set scrolljump=4
set showcmd
set incsearch
set inccommand=nosplit
set laststatus=2
set shortmess=aoOT
set cmdheight=3
set foldnestmax=9
set foldlevelstart=99
set showmode
set mouse=a
let g:netrw_preview = 1
set listchars=eol:¬,tab:│\ ,trail:~,extends:>,precedes:<,space:·
set fillchars=stl:\ ,stlnc:\ ,vert:┃,fold:\
set statusline=\
" buffer number
set statusline+=[%n]\ \
" line of total_lines and percentage through file
set statusline+=%-20(%l\ of\ %L\ (%p%%)%)
" column, virtual column
set statusline+=%-10(:%c\ %((%V)%)%)
" separator, highlight color
set statusline+=%=%#StatusLineNC#
" CoC status
if exists('*coc#exists')
set statusline+=%{coc#status()}
endif
" separator
set statusline+=%=%#StatusLine#
set statusline+=%y\
set statusline+=%r\ %m\
set statusline+=%{&buftype==''?expand('%f'):''}
set statusline+=\
set conceallevel=0
"----------------------------------------------------------------------------}}}
" #mappings {{{
let g:mapleader = ' '
" miscallaneous {{{
nnoremap <cr>l :colorscheme paper-tonic<cr>
nnoremap <cr>d :colorscheme monotonous-dark<cr>
nnoremap 0 ^
nnoremap <silent><space>cs :let @/=""<cr>
nnoremap <silent><expr> <space>nh (&hls && v:hlsearch ? ':nohls' : ':set hls')."\n"
" Focus on current fold, close the rest
nnoremap <silent> <space>zz zMzvzt
" open quickfix window of TODOs
nnoremap <space>td :grep -RE '(TODO\\|FIXME)' .<CR>:botright cwindow<CR>:echo len(getqflist()) 'TODOs'<CR>
" sync highlighting from start
"}}}
" location list and quickfix mappings {{{
nnoremap <space>lo :botright lwindow<CR>
nnoremap <up> :lprev<CR>zv
nnoremap <down> :lnext<CR>zv
nnoremap <space>lc :lclose<CR>
nnoremap <space>lh :lhistory<CR>
nnoremap <space>lp :lolder<CR>
nnoremap <space>ln :lnewer<CR>
nnoremap <space>qo :botright cwindow<CR>
nnoremap <left> :cprev<CR>zv
nnoremap <right> :cnext<CR>zv
nnoremap <space>qc :cclose<CR>
nnoremap <space>qh :chistory<CR>
nnoremap <space>qp :colder<CR>
nnoremap <space>qn :cnewer<CR>
nnoremap <space>qt :tabnew \| cwindow<CR>
"}}}
"----------------------------------------------------------------------------}}}
" #abbreviations {{{
iabbrev adn and
iabbrev waht what
iabbrev tehn then
iabbrev functin function
iabbrev positin position
"----------------------------------------------------------------------------}}}
" #autocommands {{{
" template files {{{
augroup Templates
autocmd!
autocmd BufNewFile *.sh 0read ~/.config/nvim/templates/template.sh | normal G
autocmd BufNewFile *.html 0read ~/.config/nvim/templates/template.html | normal gg
autocmd BufNewFile *.css 0read ~/.config/nvim/templates/template.css | normal G
autocmd BufNewFile *.scss 0read ~/.config/nvim/templates/template.css | normal G
augroup END
" }}}
" persistent folds {{{
augroup AutoSaveFolds
autocmd!
autocmd BufWrite ?* mkview
autocmd BufRead ?* silent! loadview
augroup END
" }}}
" Show trailing whitepace and spaces before a tab: {{{
augroup ShowWhiteSpaceErrors
autocmd!
autocmd Syntax * syn match ExtraWhitespace /\s\+$\| \+\ze\t/ containedin=ALL
augroup END
" }}}
" Automatically reload .vimrc if changed {{{
augroup ReloadVimrc
autocmd!
autocmd BufWritePost .vimrc,_vimrc,vimrc,.gvimrc,_gvimrc,gvimrc so $MYVIMRC | if has('gui_running') | so $MYGVIMRC | endif
augroup END
" }}}
" Open quickfix window{{{
" augroup QuickFixAutoload
" autocmd!
" autocmd QuickFixCmdPost [^l]* nested botright cwindow
" autocmd QuickFixCmdPost l* nested botright lwindow
" augroup END
augroup OnTerminalOpen
autocmd!
autocmd TermOpen * setlocal nonumber norelativenumber
augroup END
" }}}
"----------------------------------------------------------------------------}}}
"
"
" require lua init
lua <<EOF
require('init')
EOF
" vim: set foldmethod=marker:

View file

@ -1,37 +0,0 @@
-- Leader keys early
vim.g.mapleader = ' '
vim.g.maplocalleader = ' '
-- Load basic settings, keymaps, autocmds (kept minimal for now)
require('settings')
require('completion')
require('netrw-config')
require('keymaps')
require('autocmds')
require('abbreviations')
require('keymaps_reference')
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
if not vim.uv.fs_stat(lazypath) then
vim.fn.system({
'git', 'clone', '--filter=blob:none', '--single-branch',
'https://github.com/folke/lazy.nvim.git', lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
-- Setup plugin system (plugins will be added incrementally)
require('lazy').setup({
spec = {
{ import = 'plugins' },
},
rocks = {
enabled = false,
},
})
-- Load optional native packages after lazy.nvim has finalized runtimepath.
if vim.fn.has('nvim-0.12') == 1 then
vim.cmd.packadd('nvim.undotree')
end

364
init.plugins.vim Normal file
View file

@ -0,0 +1,364 @@
" https://github.com/junegunn/vim-plug
if empty(glob('~/.config/nvim/autoload/plug.vim'))"
silent !curl -fLo ~/.config/nvim/autoload/plug.vim --create-dirs
\ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
endif
call plug#begin('~/.config/nvim/bundle')
" evaluating
" https://github.com/simrat39/rust-tools.nvim
Plug 'simrat39/rust-tools.nvim'
" evaluating
" https://github.com/wuelnerdotexe/vim-astro
" do i need this and https://github.com/virchau13/tree-sitter-astro ????
Plug 'wuelnerdotexe/vim-astro'
" https://github.com/kevinhwang91/
Plug 'kevinhwang91/promise-async'
Plug 'kevinhwang91/nvim-ufo'
" colorscheme in develpment paper-tonic
Plug '~/projects/nvim-paper-tonic'
" #nvim-treesitter {{{
" https://github.com/nvim-treesitter/nvim-treesitter
" Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'} " update the parsers on update
" config needs to be after call to plug#end
" Treesitter uses a different parser for every language, which needs to be generated
" If the language is already supported by nvim-treesitter, you can install it with
" :TSInstall <language_to_install>
" - can use tab completion with this
" Parsers not on this list can be added manually
"
" If you update nvim-treesitter and want to make sure the parser is at the latest compatible version (as specified in nvim-treesitter's lockfile.json), use
" :TSUpdate {language}.
" To update all parsers unconditionally, use
" :TSUpdate all or just :TSUpdate.
"
" Each module provides a distinct tree-sitter-based feature such as highlighting, indentation, or folding; see :h nvim-treesitter-modules or "Available modules" below for a list of modules and their options.
" All modules are disabled by default and need to be activated explicitly in your init.vim
"
" Each module can also be enabled or disabled interactively through the following commands:
" :TSBufEnable {module} " enable module on current buffer
" :TSBufDisable {module} " disable module on current buffer
" :TSEnableAll {module} [{ft}] " enable module on every buffer. If filetype is specified, enable only for this filetype.
" :TSDisableAll {module} [{ft}] " disable module on every buffer. If filetype is specified, disable only for this filetype.
" :TSModuleInfo [{module}] " list information about modules state for each filetype
"
" Check :h nvim-treesitter-commands for a list of all available commands. It may be necessary to reload the buffer (e.g., via :e) after enabling a module interactively.
"}}}
" #playground (temporary) {{{
" https://github.com/nvim-treesitter/playground
" Plug 'nvim-treesitter/playground'
"}}}
" #vim-lspconfig {{{
" https://github.com/neovim/nvim-lspconfig
" https://github.com/neovim/nvim-lspconfig/wiki
Plug 'neovim/nvim-lspconfig'
"}}}
" #plenary.nvim {{{
" https://github.com/nvim-lua/plenary.nvim
" required for null-ls
Plug 'nvim-lua/plenary.nvim'
"}}}
" #null-ls.nvim {{{
" https://github.com/jose-elias-alvarez/null-ls.nvim
Plug 'jose-elias-alvarez/null-ls.nvim'
"}}}
" #nvim-cmp {{{
" https://github.com/hrsh7th/nvim-cmp/
" completion
" Plug 'hrsh7th/nvim-cmp', { 'branch': 'main' }
" Plug 'hrsh7th/cmp-nvim-lsp', { 'branch': 'main' }
" Plug 'hrsh7th/cmp-buffer', { 'branch': 'main' }
" Plug 'hrsh7th/cmp-path', { 'branch': 'main' }
" Plug 'uga-rosa/cmp-dictionary', { 'branch': 'main' }
" let g:cmp_dictionary_exact = -1
" Plug 'quangnguyen30192/cmp-nvim-ultisnips', { 'branch': 'main' }
"}}}
" #indent-blankline.nvim {{{
" https://github.com/lukas-reineke/indent-blankline.nvim
Plug 'lukas-reineke/indent-blankline.nvim'
"}}}
" #iamcco/markdown-preview.nvim {{{
" https://github.com/iamcco/markdown-preview.nvim
Plug 'iamcco/markdown-preview.nvim', { 'do': { -> mkdp#util#install() }, 'for': ['markdown', 'vim-plug']}
let g:mkdp_browser = '/usr/local/bin/chromium-app'
let g:mkdp_echo_preview_url = 1
nmap <space>m <Plug>MarkdownPreviewToggle
" commands
" MarkdownPreview
" MarkdownPreviewStop
" MarkdownPreviewToggle
"}}}
" keeping
" #vim-hexokinase {{{
" https://github.com/chrisbra/Colorizer
" display colors asynchronously
Plug 'chrisbra/Colorizer'
" let g:colorizer_use_virtual_text = 1
nnoremap <silent> <cr>cc :ColorToggle<CR>
"}}}
" #nvim-autopairs {{{
" https://github.com/windwp/nvim-autopairs
" automatic quote, paranthesis,etc closing.
Plug 'windwp/nvim-autopairs'
"}}}
" #nvim-ts-autotag {{{
" https://github.com/windwp/nvim-ts-autotag
" automatic html tag closing/renaming.
" Plug 'windwp/nvim-ts-autotag', { 'branch': 'main' }
"}}}
" #emmet-vim {{{
" https://github.com/mattn/emmet-vim
Plug 'mattn/emmet-vim'
let g:user_emmet_leader_key=',,'
let g:user_emmet_settings = {
\ 'indentation': ' ',
\}
"}}}
" #gitgutter {{{
" https://github.com/airblade/vim-gitgutter/blob/master/README.mkd
" show git diff in sign column
Plug 'airblade/vim-gitgutter'
nmap ]h <Plug>(GitGutterNextHunk)
nmap [h <Plug>(GitGutterPrevHunk)
omap ih <Plug>(GitGutterTextObjectInnerPending)
omap ah <Plug>(GitGutterTextObjectOuterPending)
xmap ih <Plug>(GitGutterTextObjectInnerVisual)
xmap ah <Plug>(GitGutterTextObjectOuterVisual)
"}}}
" #MatchTagAlways{{{
" always highlights xml/html tags enclosing cursor
" https://github.com/Valloric/MatchTagAlways
Plug 'Valloric/MatchTagAlways'
let g:mta_filetypes = {
\ 'jinja' : 1,
\ 'html' : 1,
\ 'vue' : 1,
\ 'xhtml' : 1,
\ 'xml' : 1,
\}
"}}}
" #quickscope{{{
" highlights unique characters when using f, F, etc
" https://github.com/unblevable/quick-scope
Plug 'unblevable/quick-scope'
let g:qs_highlight_on_keys = ['f', 'F', 't', 'T']
"}}}
" #switch {{{
"https://github.com/AndrewRadev/switch.vim
Plug 'AndrewRadev/switch.vim'
let g:switch_mapping = '<space>-'
let g:switch_custom_definitions = [
\ ['0', '1'],
\ ['ease-in', 'ease-out', 'ease-in-out', 'linear'],
\ ['auto', 'none'],
\ ['left', 'right'],
\ ['top', 'bottom'],
\ ['row', 'column'],
\ ['relative', 'absolute', 'fixed'],
\ ['private', 'public', 'protected']
\ ]
"}}}
" #ultisnips {{{
" https://github.com/sirver/UltiSnips
Plug 'SirVer/ultisnips'
" and some snippets
" https://github.com/honza/vim-snippets
" Plug 'honza/vim-snippets'
nnoremap <space>ul :call ListUltisnips()<cr>
inoremap jkul <c-o>:call ListUltisnips()<cr>
let g:UltiSnipsSnippetsDir='~/.vim/UltiSnips'
" Trigger configuration. Do not use <tab> if you use https://github.com/Valloric/YouCompleteMe.
let g:UltiSnipsExpandTrigger='<tab>'
let g:UltiSnipsJumpForwardTrigger='<tab>'
let g:UltiSnipsJumpBackwardTrigger='<s-tab>'
let g:UltiSnipsEditSplit='horizontal'
function! ListUltisnips() abort"{{{
let l:snips = UltiSnips#SnippetsInCurrentScope(1)
let l:keylist = sort(keys(l:snips))
echo ' --------------------------------------------------'
for l:key in l:keylist
echo printf(" %-10s\t%s", l:key, l:snips[l:key])
endfor
echo '---------------------------------------------------'
endfunction
"}}}
function! CompleteSnippets(findstart, base)"{{{
if a:findstart
let l:line = getline('.')
let l:start = col('.') - 1
while l:start > 0 && l:line[l:start - 1] =~ '\a'
let l:start -= 1
endwhile
return l:start
else
let l:res = []
let l:snips = UltiSnips#SnippetsInCurrentScope(1)
let l:keylist = sort(keys(l:snips))
for l:key in l:keylist
if l:key =~ '^' . a:base
let l:item = {'word': l:key, 'menu': l:snips[l:key]}
call add(l:res, l:item)
endif
endfor
return l:res
endif
endfunction
"}}}
set completefunc=CompleteSnippets
"}}}
" #Netrw {{{
let g:netrw_liststyle=3
" let g:netrw_list_hide=netrw_gitignore#Hide()
let g:netrw_preview = 0
let g:netrw_winsize = 50
let g:netrw_altv = &spr
let g:netrw_alto = 0
let g:netrw_altfile = 1
let g:netrw_mousemaps = 0
let g:netrw_keepdir = 0
"}}}
" #Obsession{{{
" fork of tpope/vim-obsession
Plug 'studio-vx/vim-obsession'
"}}}
" #tcomment_vim{{{
" https://github.com/tomtom/tcomment_vim
Plug 'tomtom/tcomment_vim'
"}}}
" #vim-rsi{{{
"https://github.com/tpope/vim-rsi
Plug 'tpope/vim-rsi'
"}}}
" #vim-repeat{{{
" https://github.com/tpope/vim-repeat
" makes repeat . work for more stuff
Plug 'tpope/vim-repeat'
"}}}
" #vim-surround{{{
Plug 'tpope/vim-surround'
"}}}
" #vim-wakatime{{{
Plug 'wakatime/vim-wakatime'
"}}}
" reconfigure and learn to use properly
" #vim-fzf {{{
" https://github.com/junegunn/fzf.vim
Plug 'junegunn/fzf'
Plug 'junegunn/fzf.vim'
nnoremap <space>fg :GFiles<CR>
nnoremap <space>ff :Files<CR>
nnoremap <space>fh :History<CR>
nnoremap <space>fb :Buffers<CR>
nnoremap <space>fH :Helptags<CR>
let g:fzf_preview_window = 'right:60%'
let g:fzf_colors =
\ { 'fg': ['fg', 'Normal'],
\ 'bg': ['bg', 'Normal'],
\ 'hl': ['fg', 'Label'],
\ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
\ 'bg+': ['bg', 'CursorLine', 'CursorColumn'],
\ 'hl+': ['fg', 'WildMenu'],
\ 'info': ['fg', 'Comment'],
\ 'border': ['fg', 'Comment'],
\ 'prompt': ['fg', 'Constant'],
\ 'pointer': ['fg', 'Special'],
\ 'marker': ['fg', 'Keyword'],
\ 'spinner': ['fg', 'Label'],
\ 'header': ['fg', 'Comment'] }
" Rg that doesn't ignore .gitignore or any other files
command! -bang -nargs=* Rgall
\ call fzf#vim#grep(
\ 'rg -uuu --column --line-number --no-heading --color=always --smart-case -- '.shellescape(<q-args>), 1,
\ fzf#vim#with_preview(), <bang>0)
"}}}
" #vim-shellcheck{{{
" https://github.com/itspriddle/vim-shellcheck
Plug 'itspriddle/vim-shellcheck'
"}}}
" #vim-markdown{{{
" https://github.com/plasticboy/vim-markdown
Plug 'plasticboy/vim-markdown'
let g:vim_markdown_toc_autofit = 1
let g:vim_markdown_fenced_languages = ['bash=sh']
"}}}
" #undotree {{{
Plug 'mbbill/undotree'
nnoremap <space>ut :UndotreeToggle<cr>
"}}}
" #vim-floaterm{{{
" https://github.com/voldikss/floaterm
" allows to open floating terminal
Plug 'voldikss/vim-floaterm'
let g:floaterm_opener = 'edit'
" nnoremap <space>lf :FloatermNew --height=0.9 --width=0.9 lf<CR>
nnoremap <space>lg :FloatermNew --height=0.9 --width=0.9 lazygit<CR>
"}}}
" #vim-Jinja2-Syntax{{{
" https://github.com/studio-vx/Vim-Jinja2-Syntax
" original: https://github.com/Glench/Vim-Jinja2-Syntax
Plug 'studio-vx/Vim-Jinja2-Syntax'
"}}}
" ------------------------------------------------------------------------------
call plug#end()
runtime macros/matchit.vim
" require lua init modules
lua <<EOF
-- require('init-plugins')
-- require('init-treesitter')
-- require('init-indent-blankline')
-- require('init-cmp')
EOF
" vim: set fdm=marker ft=vim:

69
init.tabline.vim Normal file
View file

@ -0,0 +1,69 @@
" Rename tabs to show tab number.
" based on:
" http://stackoverflow.com/questions/5927952/whats-implementation-of-vims-default-tabline-function
function! MyTabLine() "{{{
let l:customtabline = ''
let l:currenttabnum = tabpagenr()
let l:lasttabnumber = tabpagenr('$')
let l:tabnumber = 1
while l:tabnumber <= l:lasttabnumber
let l:buflist = tabpagebuflist(l:tabnumber)
let l:winnr = tabpagewinnr(l:tabnumber)
if l:lasttabnumber > 1
let l:customtabline .= '%' . l:tabnumber . 'T'
let l:customtabline .= (l:tabnumber == l:currenttabnum ? '%1*' : '%2*')
let l:customtabline .= (l:tabnumber == l:currenttabnum ? '%#TabLineNumSel#' : '%#TabLineNum#')
let l:customtabline .= ' ' . l:tabnumber . ' '
let l:customtabline .= (l:tabnumber == l:currenttabnum ? '%#TabLineItemSel#' : '%#TabLineItem#')
endif
let l:bufnr = l:buflist[l:winnr - 1]
let l:file = bufname(l:bufnr)
let l:buftype = getbufvar(l:bufnr, '&buftype')
if l:buftype ==# 'help'
let l:file = 'help:' . fnamemodify(l:file, ':t:r')
elseif l:buftype ==# 'quickfix'
let l:file = 'quickfix'
elseif l:buftype ==# 'nofile'
if l:file =~# '\/.'
let l:file = substitute(l:file, '.*\/\ze.', '', '')
endif
else
let l:file = fnamemodify(l:file, ':p:t')
endif
if l:file ==# ''
let l:file = "''"
endif
let l:customtabline .= l:file . '%T%#TabLineFill# '
let l:tabnumber = l:tabnumber + 1
endwhile
let l:customtabline .= '%T%#TabLineFill#%='
let l:customtabline .= '%#TabLineItem# %{getcwd()}% '
if exists('*ObsessionStatus')
let l:customtabline .= "%#TabLineItem# %{ObsessionStatus(fnamemodify(v:this_session,':t'),'---')} %*"
endif
return l:customtabline
endfunction
"}}}
set tabline=%!MyTabLine()
set showtabline=2
" vim: set foldmethod=marker:

6
init.vim Normal file
View file

@ -0,0 +1,6 @@
if exists('g:vscode')
" VSCode extension
source ~/.config/nvim/init.vscode.vim
else
source ~/.config/nvim/init.full.vim
endif

11
init.vscode.vim Normal file
View file

@ -0,0 +1,11 @@
set undofile
set undodir=~/.config/nvim/undodir
set backupdir=~/.nvim/tmpdir
set directory=~/.nvim/tmpdir
set ignorecase
set smartcase
set iskeyword+=-
set scrolloff=4

View file

@ -1,23 +0,0 @@
{
"Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" },
"conform.nvim": { "branch": "master", "commit": "016802de402556da54c36bd7359b441266b01cdd" },
"copilot.lua": { "branch": "master", "commit": "82607bbb8528222a9a28bfcab0aceb3a7fed7e85" },
"gitsigns.nvim": { "branch": "main", "commit": "d4854fa9a94b5c8b37dd209fb4a19e98e764186f" },
"indent-blankline.nvim": { "branch": "master", "commit": "f1e186e44d3b7f9ae918008e2c28ce37c6023d2d" },
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
"mason-lspconfig.nvim": { "branch": "main", "commit": "a338f44149f5673882c32629697d67b0c0547aec" },
"mason-tool-installer.nvim": { "branch": "main", "commit": "443f1ef8b5e6bf47045cb2217b6f748a223cf7dc" },
"mason.nvim": { "branch": "main", "commit": "2a6940af80375532e5e9e7c1f2fc6319a1b7a69d" },
"nvim-autopairs": { "branch": "master", "commit": "3172f40c16563db871a58f20d205c710ad782311" },
"nvim-lint": { "branch": "master", "commit": "f6230a5c06ba59e6059c14637f1dbe29a7978b9b" },
"nvim-lspconfig": { "branch": "master", "commit": "3bccb6fa49a3a75bfb6d92fed63b13a71a85cfaf" },
"nvim-surround": { "branch": "main", "commit": "2e93e154de9ff326def6480a4358bfc149d5da2c" },
"nvim-treesitter": { "branch": "main", "commit": "a2f694478be07f89c2d03cc17858a8745a400c4b" },
"nvim-treesitter-textobjects": { "branch": "main", "commit": "5c7b0263797dfd1bd6202f2b219f3b53a80b2187" },
"nvim-ts-autotag": { "branch": "main", "commit": "d7220b55e16ed3c04e88086256def5d282429493" },
"nvim-ufo": { "branch": "main", "commit": "ab3eb124062422d276fae49e0dd63b3ad1062cfc" },
"plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
"promise-async": { "branch": "main", "commit": "119e8961014c9bfaf1487bf3c2a393d254f337e2" },
"telescope-fzf-native.nvim": { "branch": "main", "commit": "b25b749b9db64d375d782094e2b9dce53ad53a40" },
"telescope.nvim": { "branch": "0.1.x", "commit": "a0bbec21143c7bc5f8bb02e0005fa0b982edc026" }
}

View file

@ -1,10 +0,0 @@
-- Abbreviations (common typo corrections)
-- Insert-mode abbreviations for frequent typos
vim.cmd.iabbrev('adn', 'and')
vim.cmd.iabbrev('waht', 'what')
vim.cmd.iabbrev('tehn', 'then')
vim.cmd.iabbrev('functin', 'function')
vim.cmd.iabbrev('positin', 'position')
return {}

View file

@ -1,279 +0,0 @@
-- Non-plugin autocommands (minimal placeholder for bootstrap)
local aug = vim.api.nvim_create_augroup("UserDefaults", { clear = true })
-- Templates (auto-populate new files from templates)
local template_aug = vim.api.nvim_create_augroup("Templates", { clear = true })
vim.api.nvim_create_autocmd("BufNewFile", {
group = template_aug,
pattern = "*.sh",
callback = function()
-- Read template at top of file, then move to end
vim.cmd("0read ~/.config/nvim/templates/template.sh")
vim.cmd("normal! G")
end,
})
-- Per-filetype indentation settings to match formatters
-- PHP: WordPress coding standards (tabs, displayed as 2 spaces)
vim.api.nvim_create_autocmd("FileType", {
group = aug,
pattern = "php",
callback = function()
vim.opt_local.expandtab = false -- Use actual tabs (WordPress standard)
vim.opt_local.tabstop = 2 -- Display tabs as 2 spaces wide
vim.opt_local.shiftwidth = 2 -- Indent/outdent by 2 columns (one tab)
vim.opt_local.softtabstop = 2 -- Tab key inserts 2 columns (one tab)
end,
})
-- JavaScript, TypeScript, JSON, CSS: Prettier defaults (2 spaces)
vim.api.nvim_create_autocmd("FileType", {
group = aug,
pattern = { "javascript", "javascriptreact", "typescript", "typescriptreact", "json", "css", "scss", "html" },
callback = function()
vim.opt_local.expandtab = true -- Use spaces (Prettier default)
vim.opt_local.tabstop = 2 -- Display as 2 spaces
vim.opt_local.shiftwidth = 2 -- Indent by 2
vim.opt_local.softtabstop = 2
end,
})
-- Lua: stylua defaults (2 spaces)
vim.api.nvim_create_autocmd("FileType", {
group = aug,
pattern = "lua",
callback = function()
vim.opt_local.expandtab = true -- Use spaces (common for Lua configs)
vim.opt_local.tabstop = 2 -- Display as 2 spaces
vim.opt_local.shiftwidth = 2 -- Indent by 2
vim.opt_local.softtabstop = 2
end,
})
-- Markdown: Prettier defaults (2 spaces) + spellcheck
vim.api.nvim_create_autocmd("FileType", {
group = aug,
pattern = "markdown",
callback = function()
vim.opt_local.expandtab = true -- Use spaces
vim.opt_local.tabstop = 2 -- Display as 2 spaces
vim.opt_local.shiftwidth = 2 -- Indent by 2
vim.opt_local.softtabstop = 2
vim.opt_local.spell = true -- Enable spellcheck for markdown
end,
})
-- Python: Black defaults (4 spaces)
vim.api.nvim_create_autocmd("FileType", {
group = aug,
pattern = "python",
callback = function()
vim.opt_local.expandtab = true -- Use spaces (PEP 8)
vim.opt_local.tabstop = 4 -- Display as 4 spaces
vim.opt_local.shiftwidth = 4 -- Indent by 4
vim.opt_local.softtabstop = 4
end,
})
-- Colorize diagnostic severity in quickfix/location list
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)
vim.fn.matchadd("qfWarning", "\\<warning\\>", 10)
vim.fn.matchadd("qfInfo", "\\<info\\>", 10)
vim.fn.matchadd("qfHint", "\\<hint\\>", 10)
vim.fn.matchadd("qfNote", "\\<note\\>", 10)
-- Also match uppercase variants
vim.fn.matchadd("qfError", "\\<Error\\>", 10)
vim.fn.matchadd("qfWarning", "\\<Warning\\>", 10)
vim.fn.matchadd("qfInfo", "\\<Info\\>", 10)
vim.fn.matchadd("qfHint", "\\<Hint\\>", 10)
vim.fn.matchadd("qfNote", "\\<Note\\>", 10)
end,
})
-- Session management: auto-load on startup, auto-save on exit (only if Session.vim exists)
local session_aug = vim.api.nvim_create_augroup("SessionManagement", { clear = true })
local sanitize_session_files
-- Auto-load Session.vim if it exists in the current directory
vim.api.nvim_create_autocmd("VimEnter", {
group = session_aug,
pattern = "*",
once = true,
callback = function()
-- Only auto-load if:
-- 1. Session.vim exists in cwd
-- 2. No files were specified on command line (vim.fn.argc() == 0)
-- 3. Not started with nvim -S (check if we already loaded a session)
if vim.fn.argc() == 0 and vim.fn.filereadable("Session.vim") == 1 and vim.v.this_session == "" then
vim.cmd("silent! source Session.vim")
end
end,
})
-- Ensure filetype is detected for current buffer after session load
vim.api.nvim_create_autocmd("SessionLoadPost", {
group = session_aug,
pattern = "*",
callback = function()
sanitize_session_files()
-- 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,
})
local function is_netrw_session_buffer(buf)
return vim.api.nvim_buf_is_valid(buf)
and (vim.bo[buf].filetype == "netrw" or vim.api.nvim_buf_get_name(buf):match("NetrwTreeListing$") ~= nil)
end
local function session_fallback_buffer(excluded)
excluded = excluded or {}
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
local name = vim.api.nvim_buf_get_name(buf)
if
vim.api.nvim_buf_is_valid(buf)
and not excluded[buf]
and vim.bo[buf].buflisted
and vim.bo[buf].buftype == ""
and name ~= ""
and vim.uv.fs_stat(name) ~= nil
and not is_netrw_session_buffer(buf)
then
return buf
end
end
return vim.api.nvim_create_buf(true, false)
end
local function is_local_file_path(path)
return path ~= "" and path:match("^%a[%w+.-]*://") == nil
end
sanitize_session_files = function()
local missing = {}
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_is_valid(buf) and vim.bo[buf].buftype == "" then
local name = vim.api.nvim_buf_get_name(buf)
if is_local_file_path(name) and vim.uv.fs_stat(name) == nil and not vim.bo[buf].modified then
missing[buf] = true
end
end
end
if next(missing) then
local fallback
for _, win in ipairs(vim.api.nvim_list_wins()) do
local buf = vim.api.nvim_win_get_buf(win)
if missing[buf] then
fallback = fallback or session_fallback_buffer(missing)
vim.api.nvim_win_set_buf(win, fallback)
end
end
for buf in pairs(missing) do
if vim.api.nvim_buf_is_valid(buf) then
pcall(vim.api.nvim_buf_delete, buf, { force = true })
end
end
end
local args = vim.fn.argv()
local existing = {}
for _, arg in ipairs(args) do
local path = is_local_file_path(arg) and vim.fn.fnamemodify(arg, ":p") or nil
if not path or vim.uv.fs_stat(path) ~= nil then
table.insert(existing, arg)
end
end
if #existing ~= #args then
vim.cmd("%argdelete")
for _, arg in ipairs(existing) do
vim.cmd.argadd({ args = { arg } })
end
end
end
local function close_session_tab(tab)
if vim.api.nvim_tabpage_close then
pcall(vim.api.nvim_tabpage_close, tab, true)
return
end
pcall(function()
vim.api.nvim_set_current_tabpage(tab)
vim.cmd("tabclose!")
end)
end
local function omit_netrw_from_session()
local tabs_with_netrw = {}
for _, tab in ipairs(vim.api.nvim_list_tabpages()) do
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(tab)) do
if is_netrw_session_buffer(vim.api.nvim_win_get_buf(win)) then
tabs_with_netrw[tab] = true
break
end
end
end
for tab in pairs(tabs_with_netrw) do
if vim.api.nvim_tabpage_is_valid(tab) then
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(tab)) do
if vim.api.nvim_win_is_valid(win) and vim.wo[win].previewwindow then
pcall(vim.api.nvim_win_close, win, true)
end
end
if vim.api.nvim_tabpage_is_valid(tab) then
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(tab)) do
if vim.api.nvim_win_is_valid(win) and is_netrw_session_buffer(vim.api.nvim_win_get_buf(win)) then
if #vim.api.nvim_tabpage_list_wins(tab) > 1 then
pcall(vim.api.nvim_win_close, win, true)
elseif #vim.api.nvim_list_tabpages() > 1 then
close_session_tab(tab)
else
vim.api.nvim_win_set_buf(win, session_fallback_buffer())
end
end
end
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,
pattern = "*",
callback = function()
-- Only save if Session.vim exists (user manually created it)
if vim.fn.filereadable("Session.vim") == 1 then
omit_netrw_from_session()
sanitize_session_files()
vim.cmd("mksession! Session.vim")
end
end,
})
return {}

View file

@ -1,21 +0,0 @@
-- Neovim 0.12 native insert-mode completion.
if vim.fn.has("nvim-0.12") == 0 then
return
end
vim.opt.autocomplete = true
vim.opt.autocompletedelay = 150
vim.opt.completeopt = { "menu", "menuone", "noselect", "popup", "fuzzy" }
vim.opt.complete = { ".^10", "w^5", "b^5", "u^5" }
vim.api.nvim_create_autocmd("FileType", {
pattern = "TelescopePrompt",
callback = function(args)
vim.bo[args.buf].autocomplete = false
end,
desc = "Disable native completion in Telescope prompts",
})
vim.keymap.set("i", "<C-Space>", function()
vim.lsp.completion.get()
end, { desc = "Completion: Trigger LSP suggestions", silent = true })

49
lua/init-cmp.lua Normal file
View file

@ -0,0 +1,49 @@
local cmp = require 'cmp'
cmp.setup({
window = {documentation = cmp.config.window.bordered({border = "single"})},
formatting = {
format = function(entry, vim_item)
-- set a name for each source
vim_item.menu = ({
nvim_lsp = "[LSP]",
path = "[Path]",
buffer = "[Buffer]",
ultisnips = "[UltiSnips]"
})[entry.source.name]
return vim_item
end
},
completion = {
keyword_length = 1,
max_item_count = 20
-- autocomplete = false
},
snippet = {expand = function(args) vim.fn["UltiSnips#Anon"](args.body) end},
mapping = {
['<C-u>'] = cmp.mapping.scroll_docs(-4),
['<C-d>'] = cmp.mapping.scroll_docs(4),
["<C-n>"] = cmp.mapping({
i = function()
if cmp.visible() then
cmp.select_next_item({behavior = cmp.SelectBehavior.Insert})
else
cmp.complete()
end
end
})
},
sources = {
{name = 'nvim_lsp', max_item_count = 8, keyword_length = 3},
{name = 'path'},
{name = 'buffer',
option = {
keyword_length = 4,
max_item_count = 20,
get_bufnrs = function()
return vim.api.nvim_list_bufs()
end
}},
{name = 'ultisnips', priority_weight = 10, keyword_length = 1}
}
})

View file

@ -0,0 +1,5 @@
require("indent_blankline").setup {
space_char_blankline = " ",
show_current_context = true,
}

230
lua/init-lsp.lua Normal file
View file

@ -0,0 +1,230 @@
vim.cmd [[
sign define LspDiagnosticsSignError text= texthl= linehl= numhl=LspDiagnosticsLineNrError
sign define LspDiagnosticsSignWarning text= texthl= linehl= numhl=LspDiagnosticsLineNrWarning
sign define LspDiagnosticsSignInfo text= texthl= linehl= numhl=LspDiagnosticsLineNrInformation
sign define LspDiagnosticsSignHint text= texthl= linehl= numhl=LspDiagnosticsLineNrHint
]]
--
-- lspconfig
local nvim_lsp = require('lspconfig')
-- Use a loop to conveniently call 'setup' on multiple servers and
-- map buffer local keybindings when the language server attaches
local servers = {
-- astro
-- npm install -g @astrojs/language-server
'astro', -- npm i -g bash-language-server
-- no formatting support
'bashls', -- npm i -g vscode-langservers-extracted
-- no formatting support
'cssls', -- npm install -g intelephense
-- suports formatting
'intelephense',
-- https://phpactor.readthedocs.io/en/master/usage/standalone.html
'phpactor', -- npm install -g pyright
-- static type checker
'pyright', -- https://github.com/neovim/nvim-lspconfig/wiki
-- 'tailwindcss',
-- npm install -g typescript typescript-language-server
-- supports formatting
'tsserver', -- npm install -g vim-language-server
-- no formatting support
'vimls', -- vue
-- use vetur
'yamlls' -- no formatting support
-- is now automatically started/configured by rust-tools
-- sudo pacman -Syu rust-analyzer
-- 'rust_analyzer'
}
-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
local on_attach = function(client, bufnr)
local function buf_set_keymap(...)
vim.api.nvim_buf_set_keymap(bufnr, ...)
end
local function buf_set_option(...)
vim.api.nvim_buf_set_option(bufnr, ...)
end
-- Enable completion triggered by <c-x><c-o>
buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')
-- Mappings.
local opts = {noremap = true, silent = true}
-- See `:help vim.lsp.*` for documentation on any of the below functions
buf_set_keymap('n', '[d',
'<cmd>lua vim.diagnostic.goto_prev({float = {...}})<CR>',
opts)
buf_set_keymap('n', ']d',
'<cmd>lua vim.diagnostic.goto_next({float = {...}})<CR>',
opts)
buf_set_keymap('n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', opts)
buf_set_keymap('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
buf_set_keymap('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts)
buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
buf_set_keymap('n', 'gl',
'<cmd>lua vim.diagnostic.open_float(buffer, {{opts}, scope="line"})<CR>',
opts)
buf_set_keymap('n', 'gh', '<cmd>lua vim.lsp.buf.hover()<CR>', opts)
buf_set_keymap('n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>',
opts)
buf_set_keymap('n', '<space>T',
'<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
buf_set_keymap('n', '<space>l', '<cmd>lua vim.diagnostic.setloclist()<CR>',
opts)
buf_set_keymap('n', '<space>q', '<cmd>lua vim.diagnostic.setqflist()<CR>',
opts)
buf_set_keymap('n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>',
opts)
buf_set_keymap('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
buf_set_keymap('n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>',
opts)
-- buf_set_keymap('n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
-- buf_set_keymap('n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
-- buf_set_keymap('n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
if client.name == 'phpactor' then
client.server_capabilities.find_references = false;
end
if client.name == 'tsserver' or client.name == 'intelephense' then
client.server_capabilities.document_formatting = false;
-- print(vim.inspect(client.server_capabilities));
end
if client.server_capabilities.document_formatting then
vim.cmd [[augroup lsp_formatting]]
vim.cmd [[autocmd!]]
vim.cmd [[autocmd BufWritePre <buffer> :lua vim.lsp.buf.formatting_seq_sync({}, 3000)]]
vim.cmd [[augroup END]]
end
if client.server_capabilities.document_highlight then
vim.cmd [[
augroup lsp_document_highlight
autocmd! * <buffer>
autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
augroup END
]]
end
end
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').update_capabilities(capabilities)
capabilities.textDocument.completion.completionItem.snippetSupport = true
for _, lsp in ipairs(servers) do
nvim_lsp[lsp].setup {
on_attach = on_attach,
capabilities = capabilities,
flags = {debounce_text_changes = 150}
}
end
local runtime_path = vim.split(package.path, ';')
table.insert(runtime_path, "lua/?.lua")
table.insert(runtime_path, "lua/?/init.lua")
require'lspconfig'.sumneko_lua.setup {
settings = {
Lua = {
runtime = {
-- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
version = 'LuaJIT',
-- Setup your lua path
path = runtime_path
},
diagnostics = {
-- Get the language server to recognize the `vim` global
globals = {'vim'}
},
workspace = {
-- Make the server aware of Neovim runtime files
library = vim.api.nvim_get_runtime_file("", true)
},
-- Do not send telemetry data containing a randomized but unique identifier
telemetry = {enable = false}
}
}
}
-- toml
-- 'taplo'
-- -- sudo pacman -Syu taplo-cli
require'lspconfig'.taplo.setup {
on_attach = on_attach,
capabilities = capabilities,
flags = {debounce_text_changes = 150},
cmd = {"taplo", "lsp", "stdio"}
}
-- rust-tools
require('rust-tools').setup({
server = {
on_attach = on_attach,
capabilities = capabilities,
flags = {debounce_text_changes = 150},
settings = {["rust-analyzer"] = {checkOnSave = {command = "clippy"}}}
}
})
-- inlay hints
require('rust-tools').inlay_hints.enable()
------
-- null-ls
local null_ls = require("null-ls")
local null_ls_utils = require("null-ls.utils")
local root = null_ls_utils.get_root()
local eslint_command = root .. "/node_modules/eslint/bin/eslint.js"
-- print(eslint_command);
local sources = {
null_ls.builtins.formatting.prettierd,
null_ls.builtins.diagnostics.eslint.with({
condition = function(utils)
local has_file = utils.root_has_file({
"node_modules/@wordpress/scripts/config/.eslintrc.js"
})
-- print("condition")
-- print(has_file)
return has_file
end,
extra_args = {
"--no-eslintrc", "--config ",
root .. "/node_modules/@wordpress/scripts/config/.eslintrc.js",
"--ignore-path ",
root .. "/node_modules/@wordpress/scripts/config/.eslintignore"
}
}), --
-- null_ls.builtins.diagnostics.eslint.with({
-- filetypes = { "javascript", "javascriptreact", "typescript", "typescriptreact", "vue", "svelte", "astro" },
-- command = eslint_command
-- }),
-- null_ls.builtins.diagnostics.eslint.with({
-- condition = function(utils)
-- local has_file1 = utils.root_has_file({".eslintrc.js"})
-- local has_file2 = utils.root_has_file({"gatsby-config.js"})
-- print("condition")
-- print(has_file1)
-- print(has_file2)
-- return has_file1 and has_file2
-- end
-- }), --
null_ls.builtins.diagnostics.phpcs.with({
condition = function(utils)
return not utils.root_has_file({"vendor/bin/phpcs"})
end,
args = {"--standard=PSR12", "--report=json", "-s", "-"}
}), --
null_ls.builtins.formatting.black, -- black
null_ls.builtins.diagnostics.shellcheck, -- shellcheck,
null_ls.builtins.diagnostics.flake8, -- flake8
null_ls.builtins.formatting.lua_format -- luaFormatter
}
null_ls.setup({sources = sources, on_attach = on_attach})

35
lua/init-plugins.lua Normal file
View file

@ -0,0 +1,35 @@
-- nvim-autopairs
require('nvim-autopairs').setup {}
------
-- cmp
require("cmp").setup({
map_cr = false, -- map <CR> on insert mode
map_complete = false, -- it will auto insert `(` after select function or method item
auto_select = true -- automatically select the first item
})
------
-- nvim-ts-autotag
require'nvim-treesitter.configs'.setup {
autotag = {
enable = true,
filetypes = {
-- not working for astro files
'html', 'javascript', 'typescript', 'javascriptreact', 'typescriptreact', 'svelte', 'vue', 'tsx', 'jsx', 'rescript', 'astro',
'xml',
'php',
'markdown',
'glimmer','handlebars','hbs'
},
}
}
------
-- nvim-ufo
require('ufo').setup({
provider_selector = function(bufnr, filetype, buftype)
return {'treesitter', 'indent'}
end
})
------

14
lua/init-treesitter.lua Normal file
View file

@ -0,0 +1,14 @@
require'nvim-treesitter.configs'.setup {
-- ensure_installed = "maintained", -- one of "all", "maintained" (parsers with maintainers), or a list of languages
ignore_install = {"c"}, -- List of parsers to ignore installing
highlight = {
enable = true, -- false will disable the whole extension
disable = {"c", "rust"}, -- list of language that will be disabled
-- Setting this to true will run `:h syntax` and tree-sitter at the same time.
-- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
-- Using this option may slow down your editor, and you may see some duplicate highlights.
-- Instead of true it can also be a list of languages
additional_vim_regex_highlighting = false
},
indent = {enable = true}
}

38
lua/init.lua Normal file
View file

@ -0,0 +1,38 @@
vim.o.foldmethod = 'expr'
vim.o.foldexpr = 'nvim_treesitter#foldexpr()'
vim.diagnostic.config({
virtual_text = false,
signs = true,
underline = false,
update_in_insert = false,
severity_sort = true,
float = {
border = "single",
source = "always" -- Or "if_many"
}
})
local signs = {Error = "● ", Warn = "● ", Hint = "● ", Info = "● "}
for type, icon in pairs(signs) do
local hl = "DiagnosticSign" .. type
vim.fn.sign_define(hl, {text = icon, texthl = hl, numhl = hl})
end
local border = {
{"┌", "FloatBorder"}, {"─", "FloatBorder"}, {"┐", "FloatBorder"},
{"│", "FloatBorder"}, {"┘", "FloatBorder"}, {"─", "FloatBorder"},
{"└", "FloatBorder"}, {"│", "FloatBorder"}
}
vim.lsp.handlers["textDocument/hover"] =
vim.lsp.with(vim.lsp.handlers.hover, {border = "single"})
vim.lsp.handlers["textDocument/signatureHelp"] =
vim.lsp.with(vim.lsp.handlers.signature_help, {border = "single"})
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
return orig_util_open_floating_preview(contents, syntax, opts, ...)
end

View file

@ -1,92 +0,0 @@
-- Non-plugin and global helper keymaps
--
-- NOTE: LSP keymaps are primarily handled in lua/plugins/lsp.lua via LspAttach.
-- Buffer-local keymaps set there take precedence over these global fallbacks.
--
-- These global keymaps provide fallback behavior for buffers without LSP attached,
-- ensuring that common navigation keys still work with built-in Vim functionality
-- (e.g., ctags, included files, man pages).
local map = vim.keymap.set
-- Neovim 0.12's optional native undo-tree viewer.
if vim.fn.has('nvim-0.12') == 1 then
map('n', '<leader>u', '<cmd>Undotree<cr>', { desc = 'Undotree: Toggle native undo tree', silent = true })
end
-- Fallback navigation keymaps for non-LSP buffers
-- When LSP attaches, buffer-local keymaps in lsp.lua override these
map('n', 'gd', function()
-- Built-in: jump to definition via tags or included files
vim.cmd("normal! gd")
end, { desc = 'Vim: Go to definition (built-in fallback)', silent = true })
map('n', 'gD', function()
-- Built-in: jump to declaration (global)
vim.cmd("normal! gD")
end, { desc = 'Vim: Go to declaration (built-in fallback)', silent = true })
-- LSP-only keymaps (no useful fallback for these in non-LSP contexts)
map('n', 'gr', '<Nop>', { desc = 'LSP: References (requires LSP)', silent = true })
map('n', 'gI', '<Nop>', { desc = 'LSP: Implementation (requires LSP)', silent = true })
-- K: Hover/Help with sensible fallback
map('n', 'K', function()
-- Built-in K uses 'keywordprg' (defaults to 'man' for shell scripts, etc.)
-- LSP buffers override this with vim.lsp.buf.hover() in lsp.lua
if vim.bo.keywordprg ~= '' then
vim.cmd("normal! K")
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 })
map('n', '<leader>xe', function()
vim.diagnostic.setloclist({ severity = vim.diagnostic.severity.ERROR })
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.jump({ count = -1, float = false })
end, { desc = 'Diagnostics: Previous diagnostic', silent = true })
map('n', ']d', function()
vim.diagnostic.jump({ count = 1, float = false })
end, { desc = 'Diagnostics: Next diagnostic', silent = true })
map('n', '<leader>xd', function()
pcall(vim.diagnostic.open_float)
end, { desc = 'Diagnostics: Show diagnostic under cursor', silent = true })
map('n', '<leader>xt', function()
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'
end
end, { desc = 'UI: Toggle background (light/dark)', silent = true })

View file

@ -1,74 +0,0 @@
local M = {}
local reference_filename = "README.md"
local reference_path = vim.fn.stdpath("config") .. "/" .. reference_filename
local function read_reference()
local lines = {}
local fd = io.open(reference_path, "r")
if not fd then
return {
("Keymap reference file not found: %s"):format(reference_path),
"Create README.md to document mappings.",
}
end
for line in fd:lines() do
table.insert(lines, line)
end
fd:close()
if #lines == 0 then
lines = { "README.md is empty." }
end
return lines
end
local function open_float(lines)
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.bo[buf].buftype = "nofile"
vim.bo[buf].bufhidden = "wipe"
vim.bo[buf].filetype = "markdown"
vim.bo[buf].swapfile = false
vim.bo[buf].modifiable = false
local width = 0
for _, line in ipairs(lines) do
width = math.max(width, vim.fn.strdisplaywidth(line))
end
width = math.min(math.max(width + 4, 60), math.floor(vim.o.columns * 0.9))
local height = math.min(#lines, math.floor(vim.o.lines * 0.9))
if height < 10 then
height = math.min(10, vim.o.lines - 2)
end
local win_opts = {
relative = "editor",
width = width,
height = height,
row = math.floor((vim.o.lines - height) / 2),
col = math.floor((vim.o.columns - width) / 2),
style = "minimal",
border = "rounded",
}
local win = vim.api.nvim_open_win(buf, true, win_opts)
vim.keymap.set("n", "q", "<cmd>close<cr>", { buffer = buf, silent = true })
vim.keymap.set("n", "<Esc>", "<cmd>close<cr>", { buffer = buf, silent = true })
return win
end
function M.open_reference()
local lines = read_reference()
open_float(lines)
end
vim.api.nvim_create_user_command("KeymapsGuide", function()
M.open_reference()
end, { desc = "Show README.md keymap reference in a floating window" })
return M

View file

@ -1,140 +0,0 @@
local map = vim.keymap.set
local netrw_preview = require('netrw-preview')
local last_file_buf
local previous_file_buf
local function is_valid_buffer(buf)
return type(buf) == 'number' and buf > 0 and vim.api.nvim_buf_is_valid(buf)
end
local function is_real_file_buffer(buf)
return is_valid_buffer(buf)
and vim.bo[buf].buflisted
and vim.bo[buf].buftype == ''
and vim.bo[buf].filetype ~= 'netrw'
and vim.api.nvim_buf_get_name(buf) ~= ''
end
local function is_netrw_buffer(buf)
if not is_valid_buffer(buf) then
return false
end
return vim.bo[buf].filetype == 'netrw' or vim.api.nvim_buf_get_name(buf):match('NetrwTreeListing$') ~= nil
end
local function switch_to_buffer(buf)
vim.cmd('buffer ' .. buf)
end
local function switch_to_native_alternate()
pcall(vim.cmd, 'buffer #')
end
local function jump_to_alternate_file()
local alternate = vim.fn.bufnr('#')
if is_real_file_buffer(alternate) then
switch_to_buffer(alternate)
return
end
if is_netrw_buffer(alternate) and is_real_file_buffer(previous_file_buf) then
switch_to_buffer(previous_file_buf)
return
end
switch_to_native_alternate()
end
-- Netrw configuration for file browsing
-- Native Neovim file explorer (no plugins required)
-- Hide the banner (that annoying help text at the top)
vim.g.netrw_banner = 0
-- Tree view by default (3 = tree style)
-- Options: 0=thin, 1=long, 2=wide, 3=tree
vim.g.netrw_liststyle = 3
-- Preview in horizontal split (underneath)
vim.g.netrw_preview = 0
-- Open preview splits below (1=below, 0=above)
vim.g.netrw_alto = 0
-- Window size for preview splits (percentage for preview window)
-- 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
-- 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
-- Using 0 so Enter opens file in netrw window itself
vim.g.netrw_browse_split = 0
-- Sort order: name (empty), time (t), size (s), exten (e)
vim.g.netrw_sort_by = 'name'
-- Preserve netrw's normal listing filter; toggle dotfile visibility with 'gh'
vim.g.netrw_list_hide = [[^\.\.\=/\=$]]
-- Use human-readable file sizes
vim.g.netrw_sizestyle = 'H'
-- Netrw file explorer keymaps
-- Open netrw in new tab at current file's directory
map('n', '<leader>nt', function()
local current_file_dir = vim.fn.expand('%:p:h')
vim.cmd('tabnew')
vim.cmd('Explore ' .. vim.fn.fnameescape(current_file_dir))
end, { desc = 'Netrw: Tab at current file dir', silent = true })
-- Open netrw in new tab at project root (stored at startup)
map('n', '<leader>nT', 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 })
map('n', '<C-6>', jump_to_alternate_file, { desc = 'Buffer: Alternate file skipping netrw', silent = true })
map('n', '<C-^>', jump_to_alternate_file, { desc = 'Buffer: Alternate file skipping netrw', silent = true })
vim.api.nvim_create_autocmd('BufEnter', {
desc = 'Track recent real file buffers for alternate-buffer jumps',
callback = function(args)
local buf = args.buf
if is_real_file_buffer(buf) and buf ~= last_file_buf then
previous_file_buf = last_file_buf
last_file_buf = buf
end
end,
})
-- Enable line numbers in netrw buffers
vim.api.nvim_create_autocmd('FileType', {
pattern = 'netrw',
desc = 'Configure netrw buffer-local options',
callback = function()
local buf = vim.api.nvim_get_current_buf()
vim.bo.buflisted = false
vim.opt_local.number = true
vim.opt_local.relativenumber = true
-- netrw installs its own buffer mappings while building the listing.
-- Apply the preview extension after that setup has finished.
vim.schedule(function()
if is_valid_buffer(buf) and vim.bo[buf].filetype == 'netrw' then
netrw_preview.setup(buf)
end
end)
end,
})

View file

@ -1,728 +0,0 @@
local M = {}
local uv = vim.uv
local states = {}
local preview_buffers = {}
local preview_group = vim.api.nvim_create_augroup('netrw_live_preview', { clear = true })
local DEBOUNCE_MS = 90
local MAX_ENTRIES = 200
local PREVIEW_BUFFER_CLEANUP_MS = 2000
local function is_valid_buffer(buf)
return type(buf) == 'number' and buf > 0 and vim.api.nvim_buf_is_valid(buf)
end
local function is_valid_window(win)
return type(win) == 'number' and win > 0 and vim.api.nvim_win_is_valid(win)
end
local function is_valid_tab(tab)
return type(tab) == 'number' and tab > 0 and vim.api.nvim_tabpage_is_valid(tab)
end
local function is_remote_path(path)
return type(path) == 'string' and path:match('^%a[%w+.-]*://') ~= nil
end
local function netrw_call(name, ...)
return pcall(vim.fn['netrw#Call'], name, ...)
end
local function find_preview_window(tab)
if not is_valid_tab(tab) then
return nil
end
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(tab)) do
if is_valid_window(win) and vim.wo[win].previewwindow then
return win
end
end
end
local function close_timer(state, key)
key = key or 'timer'
local timer = state and state[key]
if not timer then
return
end
pcall(timer.stop, timer)
if not timer:is_closing() then
pcall(timer.close, timer)
end
state[key] = nil
end
local function indent_guides_enabled(buf)
local ok, config = pcall(require, 'ibl.config')
if not ok or not config.config then
return nil
end
return config.get_config(buf).enabled
end
local function set_indent_guides(buf, enabled)
if enabled == nil then
return
end
local ok, ibl = pcall(require, 'ibl')
if ok and ibl.initialized then
ibl.setup_buffer(buf, { enabled = enabled })
end
end
local function restore_preview_buffer(buf, for_editing)
local metadata = preview_buffers[buf]
if not metadata or not is_valid_buffer(buf) then
return
end
vim.b[buf].netrw_live_preview_buffer = nil
vim.bo[buf].bufhidden = metadata.bufhidden
vim.bo[buf].buflisted = for_editing or metadata.buflisted
vim.bo[buf].syntax = metadata.syntax
vim.diagnostic.enable(metadata.diagnostics_enabled, { bufnr = buf })
set_indent_guides(buf, metadata.indent_guides_enabled)
if metadata.treesitter_active then
pcall(vim.treesitter.start, buf)
end
preview_buffers[buf] = nil
end
local function schedule_preview_buffer_cleanup(state)
if not state or state.preview_cleanup_scheduled then
return
end
state.preview_cleanup_scheduled = true
vim.defer_fn(function()
state.preview_cleanup_scheduled = false
for buf, metadata in pairs(preview_buffers) do
local is_preview = is_valid_buffer(buf) and vim.b[buf].netrw_live_preview_buffer == true
local is_visible = is_valid_buffer(buf) and #vim.fn.win_findbuf(buf) > 0
if not is_preview or not is_visible then
if is_preview and metadata.loaded then
restore_preview_buffer(buf, false)
end
if not is_preview then
preview_buffers[buf] = nil
end
end
end
end, PREVIEW_BUFFER_CLEANUP_MS)
end
local function close_preview(state)
if not state then
return
end
local preview = find_preview_window(state.tab)
if preview then
pcall(vim.api.nvim_win_close, preview, false)
schedule_preview_buffer_cleanup(state)
end
end
local function normalize_local_path(path)
if type(path) ~= 'string' or path == '' then
return nil
end
local trimmed = path:gsub('/+$', '')
if trimmed == '' and path:sub(1, 1) == '/' then
trimmed = '/'
end
return vim.fs.normalize(trimmed)
end
local function selected_entry(buf, win)
if not is_valid_buffer(buf) or not is_valid_window(win) then
return nil, 'netrw buffer or window is no longer valid'
end
return vim.api.nvim_win_call(win, function()
local curdir = vim.b[buf].netrw_curdir
local ok_word, word = netrw_call('NetrwGetWord')
if not ok_word or type(word) ~= 'string' or word == '' then
return nil, 'netrw could not identify the selected entry'
end
if is_remote_path(curdir) then
return {
remote = true,
word = word,
}
end
local path
if vim.w[win].netrw_liststyle == 3 and type(vim.w[win].netrw_treetop) == 'string' then
local ok_tree, tree_path = netrw_call('NetrwTreePath', vim.w[win].netrw_treetop)
if not ok_tree or type(tree_path) ~= 'string' or tree_path == '' then
return nil, 'netrw could not resolve the selected tree path'
end
if word == '../' then
path = vim.fs.dirname(normalize_local_path(tree_path))
elseif word:sub(-1) == '/' then
path = tree_path
else
path = vim.fs.joinpath(tree_path, (word:gsub('[@*]$', '')))
end
else
local name = word:gsub('/$', ''):gsub('[@*]$', '')
path = vim.fs.isabs(name) and name or vim.fs.joinpath(curdir, name)
end
return {
path = normalize_local_path(path),
remote = false,
word = word,
}
end)
end
local function human_size(bytes)
if type(bytes) ~= 'number' then
return '—'
end
local units = { 'B', 'K', 'M', 'G', 'T' }
local value = bytes
local unit = 1
while value >= 1024 and unit < #units do
value = value / 1024
unit = unit + 1
end
if unit == 1 then
return ('%d%s'):format(value, units[unit])
end
local formatted = ('%.1f'):format(value):gsub('%.0$', '')
return formatted .. units[unit]
end
local function modified_time(stat)
local seconds = stat and stat.mtime and stat.mtime.sec
if type(seconds) ~= 'number' then
return '—'
end
return os.date('%Y-%m-%d %H:%M', seconds)
end
local function visible_in_netrw(name, entry_type)
local hide_mode = tonumber(vim.g.netrw_hide) or 0
local pattern = vim.g.netrw_list_hide
if hide_mode == 0 or type(pattern) ~= 'string' or pattern == '' then
return true
end
local candidate = name .. (entry_type == 'directory' and '/' or '')
local hidden = vim.fn.match(candidate, pattern) >= 0
return (hide_mode == 1 and not hidden) or (hide_mode == 2 and hidden)
end
local function entry_details(directory, name, hinted_type)
local path = vim.fs.joinpath(directory, name)
local stat = uv.fs_lstat(path)
local entry_type = stat and stat.type or hinted_type or 'unknown'
local display_name = name
local size = '—'
if entry_type == 'directory' then
display_name = display_name .. '/'
elseif entry_type == 'file' then
size = human_size(stat and stat.size)
elseif entry_type == 'link' then
local target = uv.fs_readlink(path)
display_name = display_name .. '@'
if target then
display_name = display_name .. ' -> ' .. target
end
end
return {
name = name,
display_name = display_name,
path = path,
type = entry_type,
size = size,
bytes = entry_type == 'file' and stat and stat.size or 0,
modified = modified_time(stat),
}
end
local function plural(count, singular, plural_form)
return ('%d %s'):format(count, count == 1 and singular or plural_form)
end
local function directory_lines(path)
local scan, scan_error = uv.fs_scandir(path)
if not scan then
return {
path .. '/',
'',
'Unable to read directory:',
tostring(scan_error or 'unknown error'),
}
end
local entries = {}
local counts = { directory = 0, file = 0, link = 0, other = 0 }
local total_file_size = 0
while true do
local name, hinted_type = uv.fs_scandir_next(scan)
if not name then
break
end
if visible_in_netrw(name, hinted_type) then
local entry = entry_details(path, name, hinted_type)
if entry.type == 'directory' then
counts.directory = counts.directory + 1
elseif entry.type == 'file' then
counts.file = counts.file + 1
total_file_size = total_file_size + (entry.bytes or 0)
elseif entry.type == 'link' then
counts.link = counts.link + 1
else
counts.other = counts.other + 1
end
table.insert(entries, entry)
end
end
local ranks = { directory = 1, file = 2, link = 3 }
table.sort(entries, function(a, b)
local a_rank = ranks[a.type] or 4
local b_rank = ranks[b.type] or 4
if a_rank ~= b_rank then
return a_rank < b_rank
end
return a.name:lower() < b.name:lower()
end)
local lines = {
path == '/' and '/' or path .. '/',
'',
('%-5s %8s %-16s %s'):format('Type', 'Size', 'Modified', 'Name'),
('%-5s %8s %-16s %s'):format('-----', '--------', '----------------', '----'),
}
for index, entry in ipairs(entries) do
if index > MAX_ENTRIES then
break
end
local label = entry.type == 'directory' and 'dir'
or entry.type == 'file' and 'file'
or entry.type == 'link' and 'link'
or 'other'
table.insert(lines, ('%-5s %8s %-16s %s'):format(
label,
entry.size,
entry.modified,
entry.display_name
))
end
if #entries == 0 then
table.insert(lines, '(empty)')
elseif #entries > MAX_ENTRIES then
table.insert(lines, ('… showing first %d of %d entries'):format(MAX_ENTRIES, #entries))
end
table.insert(lines, '')
local summary = {
plural(counts.directory, 'directory', 'directories'),
plural(counts.file, 'file', 'files'),
}
if counts.link > 0 then
table.insert(summary, plural(counts.link, 'link', 'links'))
end
if counts.other > 0 then
table.insert(summary, plural(counts.other, 'other entry', 'other entries'))
end
table.insert(lines, table.concat(summary, ', ') .. ', ' .. human_size(total_file_size) .. ' in immediate files')
return lines
end
local function symlink_lines(path, stat)
local target, read_error = uv.fs_readlink(path)
return {
path .. '@',
'',
'Type: symbolic link',
'Target: ' .. tostring(target or read_error or 'unavailable'),
'Modified: ' .. modified_time(stat),
'',
'The target is not traversed in directory previews.',
}
end
local function unavailable_lines(path, error_message)
return {
tostring(path or '(unknown entry)'),
'',
'Preview unavailable:',
tostring(error_message or 'entry no longer exists'),
}
end
local function ensure_summary_buffer(state)
if is_valid_buffer(state.summary_buf) then
return state.summary_buf
end
local buf = vim.api.nvim_create_buf(false, true)
vim.bo[buf].buftype = 'nofile'
vim.bo[buf].bufhidden = 'wipe'
vim.bo[buf].swapfile = false
vim.bo[buf].filetype = 'netrw-preview'
vim.b[buf].netrw_directory_preview = true
state.summary_buf = buf
return buf
end
local function set_summary_lines(buf, lines)
vim.bo[buf].readonly = false
vim.bo[buf].modifiable = true
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.bo[buf].modified = false
vim.bo[buf].modifiable = false
vim.bo[buf].readonly = true
end
local function configure_summary_window(win)
vim.wo[win].previewwindow = true
vim.wo[win].number = false
vim.wo[win].relativenumber = false
vim.wo[win].cursorline = false
vim.wo[win].wrap = false
vim.wo[win].list = false
vim.wo[win].spell = false
vim.wo[win].signcolumn = 'no'
end
local function show_summary(state, netrw_win, lines)
local buf = ensure_summary_buffer(state)
set_summary_lines(buf, lines)
local preview = find_preview_window(state.tab)
if preview then
local ok = pcall(vim.api.nvim_win_set_buf, preview, buf)
if not ok then
return false
end
else
local ok, created = pcall(vim.api.nvim_open_win, buf, false, {
split = 'below',
win = netrw_win,
noautocmd = true,
})
if not ok then
return false
end
preview = created
end
configure_summary_window(preview)
vim.api.nvim_win_set_cursor(preview, { 1, 0 })
return true
end
local function native_preview(entry)
local path = entry.path
if entry.remote then
-- Match netrw's own `p` mapping arguments for local and remote listings.
local ok_path, resolved = netrw_call('NetrwBrowseChgDir', 1, entry.word, 1, 1)
if not ok_path then
return false
end
path = resolved
end
if type(path) ~= 'string' or path == '' then
return false
end
local ok = netrw_call('NetrwPreview', path)
return ok
end
local function show_native_preview(state, entry)
state.native_preview_active = true
local existing_buf = entry.path and vim.fn.bufnr(entry.path) or -1
local existing = is_valid_buffer(existing_buf) and {
buf = existing_buf,
loaded = vim.api.nvim_buf_is_loaded(existing_buf),
buflisted = vim.bo[existing_buf].buflisted,
bufhidden = vim.bo[existing_buf].bufhidden,
syntax = vim.bo[existing_buf].syntax,
treesitter_active = vim.treesitter.highlighter.active[existing_buf] ~= nil,
diagnostics_enabled = vim.diagnostic.is_enabled({ bufnr = existing_buf }),
indent_guides_enabled = indent_guides_enabled(existing_buf),
} or nil
local ok, shown = pcall(native_preview, entry)
local preview = find_preview_window(state.tab)
if preview then
local preview_buf = vim.api.nvim_win_get_buf(preview)
local preview_only = #vim.fn.win_findbuf(preview_buf) == 1
if vim.bo[preview_buf].buftype == '' and preview_only then
preview_buffers[preview_buf] = preview_buffers[preview_buf] or {
loaded = existing ~= nil and existing.buf == preview_buf and existing.loaded,
buflisted = existing ~= nil and existing.buf == preview_buf and existing.buflisted or false,
bufhidden = existing ~= nil and existing.buf == preview_buf and existing.bufhidden or '',
syntax = existing ~= nil and existing.buf == preview_buf and existing.syntax or '',
treesitter_active = existing ~= nil
and existing.buf == preview_buf
and existing.loaded
and existing.treesitter_active
or vim.treesitter.highlighter.active[preview_buf] ~= nil,
diagnostics_enabled = existing ~= nil
and existing.buf == preview_buf
and existing.diagnostics_enabled
or vim.diagnostic.is_enabled({ bufnr = preview_buf }),
indent_guides_enabled = existing ~= nil
and existing.buf == preview_buf
and existing.indent_guides_enabled
or indent_guides_enabled(preview_buf),
}
vim.bo[preview_buf].bufhidden = 'hide'
vim.bo[preview_buf].buflisted = preview_buffers[preview_buf].buflisted
vim.b[preview_buf].netrw_live_preview_buffer = true
vim.diagnostic.enable(false, { bufnr = preview_buf })
set_indent_guides(preview_buf, false)
end
end
if is_valid_window(state.win) then
pcall(vim.api.nvim_set_current_win, state.win)
end
state.native_preview_active = false
return ok and shown
end
local function preview_selected(state, allow_create)
if not state or not is_valid_buffer(state.buf) or not is_valid_window(state.win) then
return
end
if vim.api.nvim_win_get_buf(state.win) ~= state.buf then
return
end
if not find_preview_window(state.tab) and not allow_create then
return
end
local entry, resolve_error = selected_entry(state.buf, state.win)
if not entry then
show_summary(state, state.win, unavailable_lines(nil, resolve_error))
return
end
if entry.remote then
show_native_preview(state, entry)
return
end
local stat, stat_error = uv.fs_lstat(entry.path)
if not stat then
show_summary(state, state.win, unavailable_lines(entry.path, stat_error))
return
end
if stat.type == 'directory' then
show_summary(state, state.win, directory_lines(entry.path))
elseif stat.type == 'link' then
show_summary(state, state.win, symlink_lines(entry.path, stat))
elseif stat.type == 'file' then
show_native_preview(state, entry)
else
show_summary(state, state.win, unavailable_lines(entry.path, 'unsupported entry type: ' .. stat.type))
end
end
local function schedule_preview(state)
if not state then
return
end
if not find_preview_window(state.tab) then
schedule_preview_buffer_cleanup(state)
return
end
if not state.timer then
state.timer = uv.new_timer()
end
state.timer:stop()
state.timer:start(DEBOUNCE_MS, 0, vim.schedule_wrap(function()
preview_selected(state, false)
end))
end
vim.api.nvim_create_autocmd('BufEnter', {
group = preview_group,
desc = 'Restore normal editing behavior for a file previously used as a netrw preview',
callback = function(args)
local buf = args.buf
if not is_valid_buffer(buf) or vim.b[buf].netrw_live_preview_buffer ~= true then
return
end
local win = vim.api.nvim_get_current_win()
if is_valid_window(win) and vim.wo[win].previewwindow then
return
end
if preview_buffers[buf] then
restore_preview_buffer(buf, true)
end
end,
})
local function state_for(buf)
local state = states[buf]
if not state then
state = { buf = buf }
states[buf] = state
end
return state
end
local function schedule_leave_cleanup(state)
close_timer(state, 'leave_timer')
state.leave_timer = uv.new_timer()
state.leave_timer:start(20, 0, vim.schedule_wrap(function()
close_timer(state, 'leave_timer')
if state.native_preview_active then
return
end
local current_win = vim.api.nvim_get_current_win()
local still_in_explorer = is_valid_window(current_win)
and current_win == state.win
and vim.api.nvim_win_get_buf(current_win) == state.buf
if not still_in_explorer then
close_preview(state)
end
end))
end
local function scroll_preview(direction, count)
local preview = find_preview_window(vim.api.nvim_get_current_tabpage())
if not preview then
return
end
local key = vim.keycode(direction > 0 and '<C-e>' or '<C-y>')
pcall(vim.api.nvim_win_call, preview, function()
vim.cmd.normal({ args = { tostring(count) .. key }, bang = true })
end)
end
function M.setup(buf)
if vim.fn.has('nvim-0.12') == 0 or not is_valid_buffer(buf) then
return false
end
if vim.b[buf].netrw_live_preview_configured then
return true
end
if vim.fn.exists('*netrw#Call') ~= 1 then
return false
end
vim.b[buf].netrw_live_preview_configured = true
local state = state_for(buf)
vim.keymap.set('n', 'p', function()
state.win = vim.api.nvim_get_current_win()
state.tab = vim.api.nvim_get_current_tabpage()
if find_preview_window(state.tab) then
close_timer(state)
close_preview(state)
return
end
preview_selected(state, true)
end, {
buffer = buf,
desc = 'Netrw: Toggle preview and live updates',
nowait = true,
silent = true,
})
vim.keymap.set('n', '<M-j>', function()
scroll_preview(1, vim.v.count1)
end, {
buffer = buf,
desc = 'Netrw: Scroll preview down',
nowait = true,
silent = true,
})
vim.keymap.set('n', '<M-k>', function()
scroll_preview(-1, vim.v.count1)
end, {
buffer = buf,
desc = 'Netrw: Scroll preview up',
nowait = true,
silent = true,
})
vim.api.nvim_create_autocmd('CursorMoved', {
group = preview_group,
buffer = buf,
desc = 'Update an existing netrw preview after cursor movement',
callback = function()
state.win = vim.api.nvim_get_current_win()
state.tab = vim.api.nvim_get_current_tabpage()
schedule_preview(state)
end,
})
vim.api.nvim_create_autocmd({ 'BufLeave', 'WinLeave' }, {
group = preview_group,
buffer = buf,
desc = 'Close the netrw preview when leaving the explorer',
callback = function()
close_timer(state)
schedule_leave_cleanup(state)
end,
})
vim.api.nvim_create_autocmd('BufWipeout', {
group = preview_group,
buffer = buf,
once = true,
desc = 'Release netrw preview resources',
callback = function()
close_timer(state)
close_timer(state, 'leave_timer')
close_preview(state)
states[buf] = nil
end,
})
return true
end
return M

View file

@ -1,270 +0,0 @@
-- Paper Tonic Modern - Color Palette
-- Extracted from original Paper Tonic colorscheme
-- Supports both light (paper-like) and dark modes
local M = {}
-- Color format: { hex, 256-color, ansi }
-- hex: Hexadecimal color code for GUI (GVim/MacVim/terminal with true color)
-- 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 = {'#27292c', 234, 'black'}
M.bg_darkest = {'#ffffff', 255, 'white'} -- Inverted: lightest for strong contrast
M.bg_ui = {'#353535', 236, 'darkgray'}
else
-- Light mode: pure white paper
M.bg = {'#ffffff', 255, 'white'}
M.bg_darkest = {'#505050', 244, 'gray'}
M.bg_ui = {'#efefef', 0, 'darkgray'}
end
-- ============================================================================
-- Highlight Backgrounds (general)
-- ============================================================================
if is_dark then
-- Dark mode: lighter backgrounds for highlights
M.bg_hl_strong = {"#3a3a3a", 237, "darkgray"}
M.bg_hl = {"#2a2a2a", 236, "darkgray"}
M.bg_hl_weak = {"#252525", 235, "darkgray"}
-- Special highlight backgrounds (cyan/blue tones)
M.bg_hl_special_strong = {"#1a4a5a", 24, "darkblue"}
M.bg_hl_special = {"#0a3a4a", 23, "darkblue"}
M.bg_hl_special_weak = {"#1a3545", 23, "darkblue"}
-- Alternative special highlight (green tones)
M.bg_hl_special_alt_strong = {"#1a5a2a", 28, "darkgreen"}
M.bg_hl_special_alt = {"#0a4a1a", 22, "darkgreen"}
else
-- Light mode: neutral gray tones
M.bg_hl_strong = {"#dddddd", 17, "white"}
M.bg_hl = {"#eeeeee", 250, "white"}
M.bg_hl_weak = {"#f7f2f2", 250, "white"}
-- Special highlight backgrounds (cyan/blue tones)
M.bg_hl_special_strong = {"#a3e0ff", 17, "cyan"}
M.bg_hl_special = {"#d4f0ff", 250, "cyan"}
M.bg_hl_special_weak = {"#e0eaff", 250, "cyan"}
-- Alternative special highlight (green tones)
M.bg_hl_special_alt_strong = {"#74f283", 17, "cyan"}
M.bg_hl_special_alt = {"#bff2cd", 250, "cyan"}
end
-- ============================================================================
-- Status Backgrounds (error, success, modified, fail)
-- ============================================================================
if is_dark then
-- Dark mode: darker tinted backgrounds
M.bg_error = {'#4a2020', 52, 'darkred'}
M.bg_error_weak = {'#3a1a1a', 52, 'darkred'}
M.bg_success = {'#1a3a1a', 22, 'darkgreen'}
M.bg_modified = {'#1a1a3a', 17, 'darkblue'}
M.bg_fail = {'#3a1a1a', 52, 'darkred'}
else
-- Light mode: light tinted backgrounds
M.bg_error = {'#ffd7d7', 196, 'white'}
M.bg_error_weak = {'#ffefef', 196, 'white'}
M.bg_success = {'#e0ece0', 196, 'white'}
M.bg_modified = {'#e0e0ec', 196, 'white'}
M.bg_fail = {'#ece0e0', 196, 'white'}
end
-- ============================================================================
-- Foreground Colors (text)
-- ============================================================================
if is_dark then
-- Dark mode: light text (inverted from light mode)
M.fg_stronger = {'#e0e0e0', 253, 'white'} -- Lightest text
M.fg_strong = {'#c0c0c0', 250, 'white'} -- Light text
M.fg = {'#a0a0a0', 248, 'gray'} -- Normal text (default)
M.fg_weak = {'#808080', 244, 'gray'} -- Dimmer text (comments, less important)
M.fg_weaker = {'#606060', 241, 'darkgray'} -- Dimmest text (very subtle)
M.fg_exception = {'#d08080', 174, 'red'} -- Light reddish for errors/exceptions
else
-- Light mode: dark text on paper
M.fg_stronger = {'#444444', 236, 'darkgrey'} -- Darkest text
M.fg_strong = {'#666666', 236, 'darkgrey'} -- Dark text
M.fg = {'#8c8c8c', 244, 'gray'} -- Normal text (default)
M.fg_weak = {'#9d9d9d', 251, 'gray'} -- Light text (comments, less important)
M.fg_weaker = {'#bbbbbb', 251, 'gray'} -- Lightest text (very subtle)
M.fg_exception = {'#7c4444', 251, 'gray'} -- Reddish-brown for errors/exceptions
end
-- ============================================================================
-- Status Foreground Colors (Git/Diff - muted natural tones)
-- ============================================================================
if is_dark then
-- Dark mode: lighter muted tones
M.success = {'#a0d0a0', 114, 'green'} -- Success/addition (muted green)
M.modified = {'#a0a0d0', 110, 'blue'} -- Modified/change (muted blue)
M.fail = {'#d0a0a0', 174, 'red'} -- Fail/deletion (muted red)
else
-- Light mode: natural muted colors
M.success = {'#89af89', 196, 'white'} -- Success/addition (muted green)
M.modified = {'#8989af', 196, 'white'} -- Modified/change (muted blue)
M.fail = {'#af8989', 196, 'white'} -- Fail/deletion (muted red)
end
-- ============================================================================
-- Quickfix/Location List Diagnostic Colors (Muted tones like git/diff)
-- ============================================================================
if is_dark then
-- Dark mode: lighter muted tones
M.qf_error = {'#d07070', 167, 'red'} -- Error (lighter muted red)
M.qf_warn = {'#d0a080', 173, 'yellow'} -- Warning (muted orange)
M.qf_info = {'#80b0d0', 110, 'blue'} -- Info (muted blue)
M.qf_hint = {'#80d0d0', 116, 'cyan'} -- Hint (lighter muted cyan)
else
-- Light mode: darker muted tones
M.qf_error = {'#af3f3f', 196, 'white'} -- Error (darker muted red for more contrast)
M.qf_warn = {'#af7f5f', 196, 'white'} -- Warning (muted orange)
M.qf_info = {'#5f8faf', 196, 'white'} -- Info (muted blue)
M.qf_hint = {'#5fafaf', 196, 'white'} -- Hint (lighter muted blue)
end
-- ============================================================================
-- Diagnostic/Alert Colors (Fluorescent/Neon - intentionally jarring)
-- ============================================================================
-- These colors are designed to be NOTICED, not blend in with code
-- Fluorescent/neon aesthetic
-- In light mode: bright, synthetic, stands out on white paper ("highlighter marker")
-- In dark mode: slightly toned down but still prominent
if is_dark then
-- Dark mode: still bright but not as harsh
M.diag_error = {'#ff4488', 204, 'red'} -- Bright pink-red
M.diag_warn = {'#ff8833', 208, 'yellow'} -- Bright orange
M.diag_info = {'#44ccff', 81, 'cyan'} -- Bright cyan
M.diag_hint = {'#88ddff', 117, 'cyan'} -- Softer cyan
M.diag_hint_dark = {'#44aacc', 74, 'cyan'} -- Medium cyan
M.diag_weak = {'#ffaa66', 215, 'yellow'} -- Lighter orange
-- Diagnostic backgrounds
M.bg_diag_error = {'#3a1a25', 52, 'darkred'}
M.bg_diag_warn = {'#3a2a1a', 58, 'brown'}
M.bg_diag_info = {'#1a2a3a', 17, 'darkblue'}
M.bg_diag_hint = {'#1a2f3a', 23, 'darkblue'}
M.question = {'#44ccff', 81, 'cyan'}
else
-- Light mode: fluorescent colors for maximum visibility
M.diag_error = {'#ff0066', 197, 'red'} -- Hot pink-red (screams "error!")
M.diag_warn = {'#ff6600', 202, 'red'} -- Fluorescent orange (warnings)
M.diag_info = {'#00ccff', 45, 'cyan'} -- Bright fluorescent cyan (info)
M.diag_hint = {'#66e0ff', 81, 'cyan'} -- Softer fluorescent cyan (hint)
M.diag_hint_dark = {'#0099cc', 38, 'cyan'} -- Darker cyan for UI elements
M.diag_weak = {'#ff9933', 208, 'yellow'} -- Lighter orange (spelling, etc.)
-- Diagnostic backgrounds
M.bg_diag_error = {'#ffe6f0', 224, 'white'}
M.bg_diag_warn = {'#fff0e6', 223, 'white'}
M.bg_diag_info = {'#e6f9ff', 195, 'white'}
M.bg_diag_hint = {'#f0fcff', 195, 'white'}
M.question = {'#00ccff', 45, 'cyan'}
end
-- ============================================================================
-- Primary Accent Colors (brownish-red tones)
-- ============================================================================
-- 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
-- ============================================================================
-- Language-Specific Color Palettes (for mixed-language contexts)
-- ============================================================================
-- IMPORTANT: Each language maintains its color identity regardless of where it appears.
-- A language always uses the same color, whether standalone or embedded in another language.
-- This creates consistent visual language recognition across all contexts.
--
-- Example: In a PHP file with HTML and CSS:
-- - PHP code: primary (brownish-red) - everywhere, including <?php ?> blocks
-- - 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
-- ============================================================================
-- Helper Constants
-- ============================================================================
-- Common constants for transparent/inherited values
M.NONE = 'NONE'
M.FG = 'FG'
M.BG = 'BG'
return M

View file

@ -1,145 +0,0 @@
-- Paper Tonic Modern - Editor Highlight Groups
-- Base Vim/Neovim UI elements
local c = require('paper-tonic-modern.colors')
return {
-- ============================================================================
-- Core Editor Elements
-- ============================================================================
Normal = { fg = c.fg, bg = c.bg },
NormalFloat = { fg = c.fg, bg = c.bg },
FloatBorder = { fg = c.diag_hint_dark, bg = c.bg },
-- ============================================================================
-- Cursor & Line Highlighting
-- ============================================================================
Cursor = { reverse = true },
CursorIM = 'Cursor',
CursorLine = { bg = c.bg_hl },
CursorLineNr = { fg = c.fg_weak, bold = true },
CursorColumn = { bg = c.bg_hl },
-- ============================================================================
-- Line Numbers & Columns
-- ============================================================================
LineNr = { fg = c.fg_weak },
SignColumn = { fg = c.NONE, bg = c.NONE },
ColorColumn = { bg = c.bg_hl_weak },
-- ============================================================================
-- Visual Selection & Search
-- ============================================================================
Visual = { bg = c.bg_hl_special },
VisualNOS = { fg = c.fg_strong, bg = c.bg_hl_special, bold = true },
Search = { bg = c.bg_hl_special },
IncSearch = { reverse = true },
MatchParen = { bg = c.bg_hl_special_alt_strong, bold = true, underline = true, sp = c.fg_stronger },
-- ============================================================================
-- Status Line & Tab Line
-- ============================================================================
StatusLine = { fg = c.fg, bg = c.bg_ui, italic = true },
StatusLineNC = { fg = c.fg_weaker, bg = c.bg_ui, italic = true },
StatusLineTerm = 'StatusLine',
StatusLineTermNC = 'StatusLineNC',
TabLine = 'StatusLineNC',
TabLineFill = { fg = c.bg_ui, bg = c.bg_ui },
TabLineSel = 'StatusLine',
-- ============================================================================
-- Window Separators & Misc UI
-- ============================================================================
VertSplit = { fg = c.bg_hl_strong },
Title = { bold = true },
Directory = { fg = c.primary_weak, bold = true },
-- ============================================================================
-- 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 },
-- ============================================================================
-- Folds
-- ============================================================================
Folded = { fg = c.diag_hint_dark, bg = c.NONE, bold = true },
CursorLineFold = { fg = c.diag_hint_dark, bg = c.bg_hl, bold = true },
FoldColumn = { fg = c.fg_weak },
-- ============================================================================
-- Diffs
-- ============================================================================
DiffAdd = { bg = c.bg_success },
DiffChange = { bg = c.bg_modified },
DiffDelete = { bg = c.bg_fail },
DiffText = { fg = c.fg_stronger, bg = c.bg_success },
-- ============================================================================
-- 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
-- ============================================================================
-- Messages & Prompts
-- ============================================================================
ErrorMsg = { fg = c.diag_error, bold = true },
WarningMsg = { fg = c.diag_warn, bold = true },
Question = { fg = c.question, bold = true },
ModeMsg = { fg = c.question },
MoreMsg = { fg = c.question },
-- ============================================================================
-- Special Characters & Whitespace
-- ============================================================================
NonText = { fg = c.fg_weak },
EndOfBuffer = 'Comment',
Whitespace = 'NonText',
SpecialKey = 'SpecialChar',
Conceal = 'Noise',
-- ============================================================================
-- QuickFix & Location List
-- ============================================================================
QuickFixLine = { bg = c.bg_hl_special_weak, bold = true },
-- Diagnostic severity highlighting in quickfix/location list
-- These are custom syntax groups defined in autocmds.lua (using matchadd)
-- Uses muted colors (similar tone to git/diff) - not the fluorescent editor diagnostic colors
qfError = { fg = c.qf_error, bold = true }, -- "error" word (muted red)
qfWarning = { fg = c.qf_warn, bold = true }, -- "warning" word (muted orange)
qfInfo = { fg = c.qf_info, bold = true }, -- "info" word (muted blue)
qfHint = { fg = c.qf_hint, bold = true }, -- "hint" word (lighter muted blue)
qfNote = { fg = c.qf_hint, bold = true }, -- "note" word (same as hint)
-- ============================================================================
-- Debug
-- ============================================================================
debugPC = 'ColorColumn',
debugBreakpoint = 'ErrorMsg',
}

View file

@ -1,80 +0,0 @@
-- Paper Tonic Modern - LSP Highlight Groups
-- LSP diagnostics, references, and UI elements
local c = require('paper-tonic-modern.colors')
return {
-- ============================================================================
-- LSP Diagnostics
-- ============================================================================
-- Error diagnostics (fluorescent hot pink with light pink background)
DiagnosticError = { fg = c.diag_error }, -- Hot pink #ff0066
DiagnosticUnderlineError = { bg = c.bg_diag_error }, -- Light pink background instead of undercurl
DiagnosticSignError = { fg = c.diag_error, bg = c.bg },
DiagnosticFloatingError = { fg = c.diag_error },
DiagnosticVirtualTextError = { fg = c.diag_error }, -- No background
-- Warning diagnostics (fluorescent orange with light orange background)
DiagnosticWarn = { fg = c.diag_warn }, -- Fluorescent orange #ff6600
DiagnosticUnderlineWarn = { bg = c.bg_diag_warn }, -- Light orange background instead of undercurl
DiagnosticSignWarn = { fg = c.diag_warn, bg = c.bg },
DiagnosticFloatingWarn = { fg = c.diag_warn },
DiagnosticVirtualTextWarn = { fg = c.diag_warn }, -- No background
-- Info diagnostics (bright fluorescent cyan with light cyan background)
DiagnosticInfo = { fg = c.diag_info }, -- Bright cyan #00ccff
DiagnosticUnderlineInfo = { bg = c.bg_diag_info }, -- Light cyan background instead of underline
DiagnosticSignInfo = { fg = c.diag_info, bg = c.bg },
DiagnosticFloatingInfo = { fg = c.diag_info },
DiagnosticVirtualTextInfo = { fg = c.diag_info }, -- No background
-- Hint diagnostics (softer fluorescent cyan with very light cyan background)
DiagnosticHint = { fg = c.diag_hint }, -- Soft cyan #66e0ff
DiagnosticUnderlineHint = { bg = c.bg_diag_hint }, -- Very light cyan background instead of underline
DiagnosticSignHint = { fg = c.diag_hint, bg = c.bg },
DiagnosticFloatingHint = { fg = c.diag_hint },
DiagnosticVirtualTextHint = { fg = c.diag_hint }, -- No background
-- ============================================================================
-- LSP References (document highlight)
-- ============================================================================
-- Text under cursor (current symbol)
LspReferenceText = { bg = c.bg_hl_special_weak }, -- Subtle cyan #e0eaff
-- Read references (where symbol is read)
LspReferenceRead = { bg = c.bg_hl_special }, -- Cyan #d4f0ff
-- Write references (where symbol is written/modified)
LspReferenceWrite = { bg = c.bg_hl_special_strong }, -- Strong cyan #a3e0ff
-- ============================================================================
-- LSP Signature Help
-- ============================================================================
LspSignatureActiveParameter = { fg = c.primary_strong, bold = true },
-- ============================================================================
-- LSP Semantic Tokens (fallback groups)
-- ============================================================================
-- These are fallback groups for LSP semantic tokens
-- TreeSitter groups take precedence when both are available
['@lsp.type.class'] = '@type',
['@lsp.type.decorator'] = '@function',
['@lsp.type.enum'] = '@type',
['@lsp.type.enumMember'] = '@constant',
['@lsp.type.function'] = '@function',
['@lsp.type.interface'] = '@type',
['@lsp.type.macro'] = '@macro',
['@lsp.type.method'] = '@function.method',
['@lsp.type.namespace'] = '@module',
['@lsp.type.parameter'] = '@variable.parameter',
['@lsp.type.property'] = '@property',
['@lsp.type.struct'] = '@type',
['@lsp.type.type'] = '@type',
['@lsp.type.typeParameter'] = '@type',
['@lsp.type.variable'] = '@variable',
}

View file

@ -1,103 +0,0 @@
-- Paper Tonic Modern - Plugin Highlight Groups
-- Highlights for configured plugins (Telescope, Gitsigns, etc.)
local c = require('paper-tonic-modern.colors')
return {
-- ============================================================================
-- Telescope
-- ============================================================================
TelescopeNormal = { fg = c.fg, bg = c.bg },
TelescopeBorder = { fg = c.fg_weak, bg = c.bg },
TelescopeTitle = { fg = c.primary_strong, bold = true },
-- Selection
TelescopeSelection = { fg = c.fg_stronger, bg = c.bg_hl },
TelescopeSelectionCaret = { fg = c.primary, bg = c.bg_hl },
-- Matching
TelescopeMatching = { fg = c.primary, bold = true },
-- Prompts
TelescopePromptNormal = { fg = c.fg, bg = c.bg_ui },
TelescopePromptBorder = { fg = c.fg_weak, bg = c.bg_ui },
TelescopePromptTitle = { fg = c.primary_strong, bold = true },
TelescopePromptPrefix = { fg = c.primary },
TelescopePromptCounter = { fg = c.fg_weak },
-- Results
TelescopeResultsNormal = { fg = c.fg, bg = c.bg },
TelescopeResultsBorder = { fg = c.fg_weak, bg = c.bg },
TelescopeResultsTitle = { fg = c.fg_strong, bold = true },
-- Preview
TelescopePreviewNormal = { fg = c.fg, bg = c.bg },
TelescopePreviewBorder = { fg = c.fg_weak, bg = c.bg },
TelescopePreviewTitle = { fg = c.fg_strong, bold = true },
-- ============================================================================
-- Gitsigns
-- ============================================================================
-- Signs in the gutter
GitSignsAdd = { fg = c.success, bg = c.bg }, -- Green
GitSignsChange = { fg = c.modified, bg = c.bg }, -- Blue
GitSignsDelete = { fg = c.fail, bg = c.bg }, -- Red
-- Line highlights (when enabled)
GitSignsAddLn = { bg = c.bg_success },
GitSignsChangeLn = { bg = c.bg_modified },
GitSignsDeleteLn = { bg = c.bg_fail },
-- Virtual text (inline blame, etc.)
GitSignsCurrentLineBlame = { fg = c.fg_weak, italic = true },
-- Diff highlights
GitSignsAddInline = { bg = c.bg_hl_special_alt },
GitSignsChangeInline = { bg = c.bg_hl_special },
GitSignsDeleteInline = { bg = c.bg_error_weak },
-- ============================================================================
-- Comment.nvim
-- ============================================================================
-- Uses default Comment group
-- ============================================================================
-- nvim-surround
-- ============================================================================
-- Uses default highlighting
-- ============================================================================
-- nvim-autopairs
-- ============================================================================
-- Uses default highlighting
-- ============================================================================
-- indent-blankline.nvim
-- ============================================================================
IblIndent = { fg = c.fg_weaker },
IblScope = { fg = c.fg_weak },
-- ============================================================================
-- nvim-ufo (folding)
-- ============================================================================
UfoFoldedBg = { bg = c.bg_hl_weak },
UfoFoldedFg = { fg = c.diag_hint_dark },
UfoCursorFoldedLine = { bg = c.bg_hl, fg = c.diag_hint_dark },
UfoPreviewBorder = { fg = c.fg_weak },
UfoPreviewNormal = { bg = c.bg_ui },
UfoPreviewCursorLine = { bg = c.bg_hl },
-- ============================================================================
-- undotree
-- ============================================================================
-- Uses default highlighting (mainly reuses diff colors)
}

View file

@ -1,94 +0,0 @@
-- Paper Tonic Modern - Semantic Highlight Groups
-- Custom semantic captures for language-specific highlighting
-- These are defined in after/queries/*/highlights.scm files
local c = require('paper-tonic-modern.colors')
return {
-- ============================================================================
-- CSS Semantic Captures
-- ============================================================================
-- Class selectors in CSS (.class-name)
-- Uses green (c2) to match CSS language color
['@CssClassName'] = { fg = c.c2 },
-- ID selectors in CSS (#id-name)
-- Uses stronger green (c2_strong) with bold for emphasis
['@CssIdentifier'] = { fg = c.c2_strong, bold = true },
-- ID selector wrapper (the whole #id construct)
['@CssIdSelector'] = 'SpecialChar',
-- Pseudo-class selectors (:hover, :focus, etc.)
-- Uses bold primary_strong (brownish-red) to distinguish from green class/ID selectors
-- These are behavioral modifiers, not structural selectors
['@cssPseudoClass'] = { fg = c.primary_strong, bold = true },
-- Pseudo-element selectors (::before, ::after, etc.)
-- Similar to pseudo-classes but for virtual elements
['@cssPseudoElement'] = { fg = c.primary_strong, bold = true },
-- Nesting selector (& in SCSS/modern CSS)
['@cssNestingSelector'] = { fg = c.c2_strong },
-- Universal selector (*)
['@CssUniversalSelector'] = 'SpecialChar',
-- CSS properties
['@CssProp'] = { fg = c.primary_weak },
-- CSS property values
['@CssPropertyValue'] = { fg = c.fg, bold = true },
-- CSS units (px, em, rem, %, etc.)
['@CssUnit'] = 'Delimiter',
-- Media query features
['@cssMediaFeatureName'] = 'Function',
['@cssMediaQuery'] = 'Function',
['@cssMediaQueryValue'] = 'cssAtRule',
['@cssMediaQueryValueUnit'] = 'cssMediaQueryValue',
-- HTML tag names in CSS selectors (div, p, span, etc.)
['@HtmlTagName'] = { fg = c.c3 },
-- ============================================================================
-- HTML Semantic Captures
-- ============================================================================
-- IMPORTANT: Cross-language consistency
-- Class names in HTML class="" should use the SAME color as CSS .class
-- This creates visual consistency when working with HTML and CSS together
-- Class attribute in HTML (class="...")
-- Uses @CssClassName to match CSS .class-name color (green c2)
['@ClassNameAttribute'] = { fg = c.fg_weak }, -- The attribute name itself
-- The class value uses @CssClassName (handled by query, falls back to green c2)
-- ID attribute in HTML (id="...")
-- Uses @CssIdentifier to match CSS #id-name color (strong green c2_strong)
['@IdAttribute'] = { fg = c.fg_weak }, -- The attribute name itself
-- The id value uses @CssIdentifier (handled by query, falls back to c2_strong)
-- Data attributes (data-*)
['@DataAttribute'] = { fg = c.fg_weak },
['@DataAttributeValue'] = 'Function',
-- ============================================================================
-- Cross-Language Consistency Notes
-- ============================================================================
-- The custom queries in after/queries/ ensure:
-- 1. CSS .my-class and HTML class="my-class" both use @CssClassName (green c2)
-- 2. CSS #my-id and HTML id="my-id" both use @CssIdentifier (green c2_strong)
-- 3. This makes it easy to visually match selectors between HTML and CSS files
--
-- Example:
-- HTML: <div class="container" id="main">
-- "container" → green (c2)
-- "main" → strong green (c2_strong, bold)
--
-- CSS: .container { ... } → green (c2)
-- #main { ... } → strong green (c2_strong, bold)
}

View file

@ -1,225 +0,0 @@
-- Paper Tonic Modern - Syntax Highlight Groups
-- Traditional Vim syntax groups
local c = require('paper-tonic-modern.colors')
return {
-- ============================================================================
-- Text Analysis
-- ============================================================================
Comment = { fg = c.fg_weaker, italic = true, bold = true },
-- ============================================================================
-- Literals
-- ============================================================================
Constant = { fg = c.fg_strong, italic = true },
String = { fg = c.fg_strong, italic = true },
Character = 'String',
Number = 'String',
Boolean = 'String',
Float = 'Number',
-- ============================================================================
-- Identifiers
-- ============================================================================
Identifier = { fg = c.primary_weak, bold = true },
Function = { fg = c.primary_strong, bold = true },
-- ============================================================================
-- Syntax
-- ============================================================================
Statement = { fg = c.fg_weak },
Conditional = { fg = c.fg_strong, bold = true },
Repeat = 'Conditional',
Label = { fg = c.fg_stronger, bold = true },
Operator = { fg = c.fg, bold = true },
Keyword = { fg = c.fg_weaker, bold = true },
Exception = { fg = c.fg_exception, bold = true },
-- ============================================================================
-- Preprocessor
-- ============================================================================
PreProc = { fg = c.fg_weak },
Include = { fg = c.fg_weak, bold = true },
Define = 'Include',
Macro = { fg = c.fg, bold = true },
PreCondit = 'Macro',
-- ============================================================================
-- Types
-- ============================================================================
Type = { fg = c.primary, bold = true },
StorageClass = { fg = c.primary },
Structure = 'StorageClass',
Typedef = 'StorageClass',
-- ============================================================================
-- Special
-- ============================================================================
Special = { fg = c.primary_stronger },
SpecialChar = { fg = c.primary_stronger, bold = true },
Tag = { fg = c.primary_strong, bold = true },
Delimiter = { fg = c.fg_weaker },
SpecialComment = { fg = c.fg_exception, bold = true },
Debug = 'WarningMsg',
-- ============================================================================
-- Miscellaneous
-- ============================================================================
Underlined = { underline = true },
Ignore = { fg = c.fg_weak },
Error = { fg = c.diag_error, bold = true },
Todo = { fg = c.diag_error },
-- ============================================================================
-- Language-Specific: CSS
-- ============================================================================
cssAtRule = 'Label',
cssAttr = 'Keyword',
cssAttrComma = 'Delimiter',
cssAttrRegion = 'Keyword',
cssBraces = 'Delimiter',
cssClassName = { fg = c.c2 },
cssClassNameDot = 'Delimiter',
cssFlexibleBoxAttr = 'cssAttr',
cssFunctionComma = 'Delimiter',
cssImportant = 'Exception',
cssProp = { fg = c.primary_weak },
cssPseudoClass = { fg = c.c2_weak },
cssPseudoClassId = 'Operator',
cssSelectorOp = 'Operator',
cssTagName = 'htmlTagName',
cssUnitDecorators = 'Type',
-- SCSS
scssAmpersand = 'cssNestingSelector',
scssAttribute = 'Delimiter',
scssBoolean = 'Boolean',
scssDefault = 'Keyword',
scssElse = 'scssIf',
scssMixinName = 'Function',
scssIf = 'PreCondit',
scssInclude = 'Include',
scssSelectorChar = 'Delimiter',
scssDefinition = 'PreProc',
scssSelectorName = 'cssClassName',
scssVariable = 'Define',
scssVariableAssignment = 'Operator',
-- Custom CSS (from custom queries)
cssIdentifier = { fg = c.c2_strong, bold = true },
cssIdSelector = 'SpecialChar',
cssNestingSelector = { fg = c.c2_strong },
cssUniversalSelector = 'SpecialChar',
cssPropertyValue = { fg = c.fg, bold = true },
cssMediaQuery = 'Function',
cssMediaQueryValue = 'cssAtRule',
cssMediaQueryValueUnit = 'cssMediaQueryValue',
cssMediaFeatureName = 'cssMediaQuery',
cssUnit = 'Delimiter',
-- ============================================================================
-- Language-Specific: HTML
-- ============================================================================
htmlArg = 'Label',
htmlBold = { bold = true },
htmlTitle = { fg = c.fg_stronger, bold = true },
htmlEndTag = 'htmlTag',
htmlH1 = 'markdownH1',
htmlH2 = 'markdownH2',
htmlH3 = 'markdownH3',
htmlH4 = 'markdownH4',
htmlH5 = 'markdownH5',
htmlH6 = 'markdownH6',
htmlItalic = { italic = true },
htmlSpecialTagName = 'Keyword',
htmlTag = { fg = c.c3 },
htmlTagName = { fg = c.c3 },
htmlTagN = 'htmlTagName',
-- ============================================================================
-- Language-Specific: Lua
-- ============================================================================
luaBraces = 'Structure',
luaBrackets = 'Delimiter',
luaBuiltin = 'Keyword',
luaComma = 'Delimiter',
luaFuncArgName = 'Identifier',
luaFuncCall = 'Function',
luaFuncKeyword = 'Type',
luaFuncName = 'Function',
luaFuncParens = 'Delimiter',
luaFuncTable = 'Structure',
luaIn = 'Repeat',
luaLocal = 'Type',
luaParens = 'Delimiter',
-- ============================================================================
-- Language-Specific: Markdown
-- ============================================================================
markdownH1 = { fg = c.primary_strong, bold = true },
markdownH2 = { fg = c.primary, bold = true },
markdownH3 = { fg = c.primary_weak, bold = true },
markdownH4 = { fg = c.primary_weak },
markdownH5 = { fg = c.primary_weak },
markdownH6 = { fg = c.primary_weak },
markdownBold = { bold = true },
markdownItalic = { italic = true },
markdownCode = { fg = c.fg_strong },
markdownCodeBlock = 'markdownCode',
markdownCodeDelimiter = 'Delimiter',
markdownUrl = { fg = c.c3, underline = true },
markdownLink = { fg = c.primary },
markdownLinkText = { fg = c.primary },
-- ============================================================================
-- Language-Specific: JSON
-- ============================================================================
jsonBraces = 'luaBraces',
jsonEscape = 'SpecialChar',
jsonKeywordMatch = 'Operator',
jsonNull = 'Constant',
jsonQuote = 'Delimiter',
jsonString = 'String',
jsonStringSQError = 'Exception',
-- ============================================================================
-- Language-Specific: JavaScript/TypeScript
-- ============================================================================
jsFuncBlock = 'Function',
jsObjectKey = 'Type',
jsReturn = 'Keyword',
jsVariableDef = 'Identifier',
-- ============================================================================
-- Language-Specific: PHP
-- ============================================================================
phpVarSelector = 'Identifier',
phpIdentifier = 'Identifier',
phpFunction = 'Function',
phpClass = 'Type',
phpKeyword = 'Keyword',
phpSuperglobal = 'Special',
phpMemberSelector = 'Operator',
-- ============================================================================
-- Noise (low-priority syntax elements)
-- ============================================================================
Noise = { fg = c.fg_weaker },
}

View file

@ -1,297 +0,0 @@
-- Paper Tonic Modern - TreeSitter Highlight Groups
-- Modern @* highlight groups for TreeSitter
local c = require('paper-tonic-modern.colors')
return {
-- ============================================================================
-- Core TreeSitter Groups
-- ============================================================================
['@variable'] = { fg = c.primary_weak },
['@variable.builtin'] = { fg = c.primary, bold = true },
['@variable.parameter'] = { fg = c.fg },
['@variable.member'] = { fg = c.primary_weak },
['@constant'] = { fg = c.fg_strong, italic = true },
['@constant.builtin'] = { fg = c.fg_strong, italic = true, bold = true },
['@constant.macro'] = { fg = c.fg, bold = true },
['@module'] = { fg = c.primary },
['@label'] = { fg = c.fg_stronger, bold = true },
-- ============================================================================
-- Literals
-- ============================================================================
['@string'] = { fg = c.fg_strong, italic = true },
['@string.documentation'] = { fg = c.fg_weak, italic = true },
['@string.regex'] = { fg = c.fg_strong, bold = true },
['@string.escape'] = { fg = c.primary_stronger, bold = true },
['@string.special'] = { fg = c.primary_stronger },
['@string.special.symbol'] = { fg = c.primary },
['@character'] = '@string',
['@character.special'] = '@string.escape',
['@number'] = { fg = c.fg_strong, italic = true },
['@number.float'] = '@number',
['@boolean'] = { fg = c.fg_strong, italic = true },
-- ============================================================================
-- Functions
-- ============================================================================
['@function'] = { fg = c.primary_strong, bold = true },
['@function.builtin'] = { fg = c.primary_stronger },
['@function.call'] = '@function',
['@function.macro'] = { fg = c.fg, bold = true },
['@function.method'] = '@function',
['@function.method.call'] = '@function',
['@constructor'] = { fg = c.primary, bold = true },
-- ============================================================================
-- Keywords
-- ============================================================================
['@keyword'] = { fg = c.fg_weaker, bold = true },
['@keyword.function'] = { fg = c.primary, bold = true },
['@keyword.operator'] = { fg = c.fg, bold = true },
['@keyword.return'] = { fg = c.fg_strong, bold = true },
['@keyword.conditional'] = { fg = c.fg_strong, bold = true },
['@keyword.repeat'] = '@keyword.conditional',
['@keyword.import'] = { fg = c.fg_weak, bold = true },
['@keyword.exception'] = { fg = c.fg_exception, bold = true },
-- ============================================================================
-- Operators & Punctuation
-- ============================================================================
['@operator'] = { fg = c.fg, bold = true },
['@punctuation.delimiter'] = { fg = c.fg_weaker },
['@punctuation.bracket'] = { fg = c.fg_weaker },
['@punctuation.special'] = { fg = c.primary_stronger },
-- ============================================================================
-- Types
-- ============================================================================
['@type'] = { fg = c.primary, bold = true },
['@type.builtin'] = { fg = c.primary, bold = true },
['@type.definition'] = '@type',
['@type.qualifier'] = { fg = c.primary },
['@attribute'] = { fg = c.fg_weak },
['@property'] = { fg = c.primary_weak },
-- ============================================================================
-- Identifiers
-- ============================================================================
['@variable.parameter'] = { fg = c.fg },
['@variable.member'] = { fg = c.primary_weak },
-- ============================================================================
-- Text & Comments
-- ============================================================================
['@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 },
-- ============================================================================
-- Markup (Markdown, etc.)
-- ============================================================================
['@markup.strong'] = { bold = true },
['@markup.italic'] = { italic = true },
['@markup.underline'] = { underline = true },
['@markup.strike'] = { strikethrough = true },
['@markup.heading'] = { fg = c.primary_strong, bold = true },
['@markup.heading.1'] = { fg = c.primary_strong, bold = true },
['@markup.heading.2'] = { fg = c.primary, bold = true },
['@markup.heading.3'] = { fg = c.primary_weak, bold = true },
['@markup.heading.4'] = { fg = c.primary_weak },
['@markup.heading.5'] = { fg = c.primary_weak },
['@markup.heading.6'] = { fg = c.primary_weak },
['@markup.link'] = { fg = c.primary },
['@markup.link.label'] = { fg = c.primary },
['@markup.link.url'] = { fg = c.c3, underline = true },
['@markup.raw'] = { fg = c.fg_strong },
['@markup.raw.block'] = '@markup.raw',
['@markup.list'] = { fg = c.fg_strong },
['@markup.list.checked'] = { fg = c.success },
['@markup.list.unchecked'] = { fg = c.fg_weak },
-- ============================================================================
-- Language-Specific: CSS (Green - c2)
-- ============================================================================
['@property.css'] = { fg = c.primary_weak },
['@type.css'] = { fg = c.c2 }, -- Tag selectors
['@string.css'] = { fg = c.fg, bold = true },
['@number.css'] = { fg = c.fg, bold = true },
['@function.css'] = { fg = c.fg_strong },
['@operator.css'] = 'Noise',
-- CSS-specific TreeSitter captures
['@variable.css'] = { fg = c.c2_weak }, -- CSS variables (--custom-property)
['@constant.css'] = { fg = c.fg_strong },
-- ============================================================================
-- Language-Specific: SCSS/Sass (Green - c2)
-- ============================================================================
['@property.scss'] = { fg = c.primary_weak },
['@type.scss'] = { fg = c.c2 },
['@variable.scss'] = { fg = c.c2_weak }, -- SCSS variables ($variable)
['@function.scss'] = { fg = c.fg_strong },
-- ============================================================================
-- Language-Specific: HTML (Blue - c3)
-- ============================================================================
['@tag.html'] = { fg = c.c3 },
['@tag.attribute.html'] = { fg = c.fg_weak },
['@tag.delimiter.html'] = { fg = c.c3 },
['@string.html'] = { fg = c.fg_strong },
['@constant.builtin.html'] = { fg = c.c3 }, -- DOCTYPE, etc.
-- ============================================================================
-- Language-Specific: PHP (Primary - brownish-red)
-- ============================================================================
['@variable.php'] = { fg = c.primary_weak },
['@variable.builtin.php'] = { fg = c.primary, bold = true }, -- $this, $GLOBALS, etc.
['@function.php'] = { fg = c.primary_strong, bold = true },
['@function.method.php'] = { fg = c.primary_strong, bold = true },
['@keyword.php'] = { fg = c.fg_weaker, bold = true },
['@keyword.function.php'] = { fg = c.primary, bold = true },
['@type.php'] = { fg = c.primary, bold = true },
['@constant.php'] = { fg = c.fg_strong, italic = true },
['@constant.builtin.php'] = { fg = c.fg_strong, italic = true, bold = true },
['@property.php'] = { fg = c.primary_weak },
['@variable.member.php'] = { fg = c.primary_weak },
-- ============================================================================
-- Language-Specific: JavaScript/TypeScript (Primary - brownish-red)
-- ============================================================================
['@variable.javascript'] = { fg = c.primary_weak },
['@variable.builtin.javascript'] = { fg = c.primary, bold = true }, -- this, arguments, etc.
['@function.javascript'] = { fg = c.primary_strong, bold = true },
['@keyword.javascript'] = { fg = c.fg_weaker, bold = true },
['@keyword.function.javascript'] = { fg = c.primary, bold = true },
['@property.javascript'] = { fg = c.primary_weak },
['@constant.builtin.javascript'] = { fg = c.fg_strong, italic = true, bold = true },
['@variable.typescript'] = { fg = c.primary_weak },
['@variable.builtin.typescript'] = { fg = c.primary, bold = true },
['@function.typescript'] = { fg = c.primary_strong, bold = true },
['@keyword.typescript'] = { fg = c.fg_weaker, bold = true },
['@keyword.function.typescript'] = { fg = c.primary, bold = true },
['@property.typescript'] = { fg = c.primary_weak },
['@type.typescript'] = { fg = c.primary, bold = true },
-- JSX/TSX (HTML in JS - keep HTML blue)
['@tag.tsx'] = { fg = c.c3 },
['@tag.attribute.tsx'] = { fg = c.fg_weak },
['@tag.delimiter.tsx'] = { fg = c.c3 },
['@tag.jsx'] = { fg = c.c3 },
['@tag.attribute.jsx'] = { fg = c.fg_weak },
['@tag.delimiter.jsx'] = { fg = c.c3 },
-- ============================================================================
-- Language-Specific: Lua (Primary - brownish-red)
-- ============================================================================
['@variable.lua'] = { fg = c.primary_weak },
['@variable.builtin.lua'] = { fg = c.primary, bold = true }, -- _G, _VERSION, etc.
['@function.lua'] = { fg = c.primary_strong, bold = true },
['@keyword.lua'] = { fg = c.fg_weaker, bold = true },
['@keyword.function.lua'] = { fg = c.primary, bold = true },
['@property.lua'] = { fg = c.primary_weak },
-- ============================================================================
-- Language-Specific: Markdown (Primary for structure)
-- ============================================================================
['@markup.heading.markdown'] = { fg = c.primary_strong, bold = true },
['@markup.heading.1.markdown'] = { fg = c.primary_strong, bold = true },
['@markup.heading.2.markdown'] = { fg = c.primary, bold = true },
['@markup.heading.3.markdown'] = { fg = c.primary_weak, bold = true },
['@markup.heading.4.markdown'] = { fg = c.primary_weak },
['@markup.heading.5.markdown'] = { fg = c.primary_weak },
['@markup.heading.6.markdown'] = { fg = c.primary_weak },
['@markup.link.markdown'] = { fg = c.primary },
['@markup.link.url.markdown'] = { fg = c.c3, underline = true },
['@markup.raw.markdown'] = { fg = c.fg_strong },
['@markup.raw.block.markdown'] = { fg = c.fg_strong },
-- Code blocks in markdown use their language colors
['@markup.raw.markdown_inline'] = { fg = c.fg_strong },
-- ============================================================================
-- Language-Specific: JSON (Neutral colors)
-- ============================================================================
['@property.json'] = { fg = c.primary_weak },
['@string.json'] = { fg = c.fg_strong, italic = true },
['@number.json'] = { fg = c.fg_strong, italic = true },
['@constant.builtin.json'] = { fg = c.fg_strong, italic = true }, -- true, false, null
-- ============================================================================
-- Language-Specific: Bash/Shell (Primary - brownish-red)
-- ============================================================================
['@variable.bash'] = { fg = c.primary_weak },
['@variable.builtin.bash'] = { fg = c.primary, bold = true },
['@function.bash'] = { fg = c.primary_strong, bold = true },
['@function.builtin.bash'] = { fg = c.primary_stronger },
['@keyword.bash'] = { fg = c.fg_weaker, bold = true },
['@string.bash'] = { fg = c.fg_strong, italic = true },
-- ============================================================================
-- Template Languages (Magenta - c5)
-- ============================================================================
-- Twig/Jinja/Django template syntax
['@punctuation.special.twig'] = { fg = c.c5 },
['@keyword.twig'] = { fg = c.c5, bold = true },
['@function.twig'] = { fg = c.c5, bold = true },
['@punctuation.special.jinja'] = { fg = c.c5 },
['@keyword.jinja'] = { fg = c.c5, bold = true },
['@function.jinja'] = { fg = c.c5, bold = true },
-- ============================================================================
-- Diagnostics & Diffs
-- ============================================================================
['@diff.plus'] = { fg = c.success },
['@diff.minus'] = { fg = c.fail },
['@diff.delta'] = { fg = c.modified },
-- ============================================================================
-- Special
-- ============================================================================
['@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
}

View file

@ -1,140 +0,0 @@
-- Paper Tonic Modern - Main Module
-- Provides colorscheme loading and highlight helper functions
local M = {}
-- ============================================================================
-- 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
-- Already a string like 'NONE', 'FG', 'BG'
return color_def
end
if type(color_def) ~= 'table' then
return 'NONE'
end
-- mode: 'gui' for hex, 'cterm' for 256-color
if mode == 'gui' then
return color_def[1] or 'NONE'
elseif mode == 'cterm' then
return color_def[2] or 'NONE'
else
return 'NONE'
end
end
-- Set a highlight group
-- spec = {
-- fg = color_def, -- Foreground color
-- bg = color_def, -- Background color
-- sp = color_def, -- Special color (for undercurl, etc.)
-- bold = boolean,
-- italic = boolean,
-- underline = boolean,
-- undercurl = boolean,
-- strikethrough = boolean,
-- reverse = boolean,
-- standout = boolean,
-- }
function M.highlight(group, spec)
if type(spec) == 'string' then
-- Link to another group
vim.api.nvim_set_hl(0, group, { link = spec })
return
end
local hl = {}
-- Colors
if spec.fg then
hl.fg = get_color(spec.fg, 'gui')
hl.ctermfg = get_color(spec.fg, 'cterm')
end
if spec.bg then
hl.bg = get_color(spec.bg, 'gui')
hl.ctermbg = get_color(spec.bg, 'cterm')
end
if spec.sp then
hl.sp = get_color(spec.sp, 'gui')
end
-- Styles
if spec.bold then hl.bold = true end
if spec.italic then hl.italic = true end
if spec.underline then hl.underline = true end
if spec.undercurl then hl.undercurl = true end
if spec.strikethrough then hl.strikethrough = true end
if spec.reverse then hl.reverse = true end
if spec.standout then hl.standout = true end
vim.api.nvim_set_hl(0, group, hl)
end
-- Convenience function to set multiple highlight groups
function M.highlight_all(groups)
for group, spec in pairs(groups) do
M.highlight(group, spec)
end
end
-- ============================================================================
-- Load Colorscheme
-- ============================================================================
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 }
-- Base editor highlights (Normal, StatusLine, etc.)
local editor = require('paper-tonic-modern.groups.editor')
M.highlight_all(editor)
-- Syntax highlights (Comment, Function, String, etc.)
local syntax = require('paper-tonic-modern.groups.syntax')
M.highlight_all(syntax)
-- TreeSitter highlights (@variable, @function, etc.)
local treesitter = require('paper-tonic-modern.groups.treesitter')
M.highlight_all(treesitter)
-- Semantic highlights (custom captures for CSS/HTML)
local semantic = require('paper-tonic-modern.groups.semantic')
M.highlight_all(semantic)
-- LSP highlights (diagnostics, references)
local lsp = require('paper-tonic-modern.groups.lsp')
M.highlight_all(lsp)
-- Plugin highlights (Telescope, Gitsigns, etc.)
local plugins = require('paper-tonic-modern.groups.plugins')
M.highlight_all(plugins)
-- Export colors for external use
M.colors = colors
end
return M

View file

@ -1,29 +0,0 @@
-- nvim-autopairs for auto-closing brackets, quotes, etc.
return {
'windwp/nvim-autopairs',
event = 'InsertEnter',
config = function()
local autopairs = require('nvim-autopairs')
autopairs.setup({
check_ts = true, -- Use treesitter to check for valid pair
ts_config = {
lua = { 'string' }, -- Don't add pairs in lua string treesitter nodes
javascript = { 'template_string' }, -- Don't add pairs in JS template strings
php = { 'string' }, -- Don't add pairs in PHP strings
},
disable_filetype = { 'TelescopePrompt', 'vim' },
fast_wrap = {
map = '<M-e>', -- Alt+e for fast wrap
chars = { '{', '[', '(', '"', "'" },
pattern = [=[[%'%"%>%]%)%}%,]]=],
end_key = '$',
keys = 'qwertyuiopzxcvbnmasdfghjkl',
check_comma = true,
highlight = 'Search',
highlight_grey = 'Comment',
},
})
end,
}

View file

@ -1,10 +0,0 @@
-- Paper Tonic Modern colorscheme
-- Built into this config at colors/paper-tonic-modern.lua
-- No plugin spec needed - just set the colorscheme
-- Set colorscheme directly without lazy.nvim
vim.cmd.colorscheme('paper-tonic-modern')
-- Return empty table so lazy.nvim doesn't complain
return {}

View file

@ -1,24 +0,0 @@
-- Comment.nvim for commenting
return {
'numToStr/Comment.nvim',
event = 'VeryLazy',
opts = {
-- Use default keymaps: gcc (line), gc (visual), gbc (block), gb (visual block)
padding = true, -- Add space between comment and text
sticky = true, -- Keep cursor position after commenting
ignore = '^$', -- Ignore empty lines
toggler = {
line = 'gcc', -- Line comment toggle
block = 'gbc', -- Block comment toggle
},
opleader = {
line = 'gc', -- Line comment operator
block = 'gb', -- Block comment operator
},
extra = {
above = 'gcO', -- Add comment on line above
below = 'gco', -- Add comment on line below
eol = 'gcA', -- Add comment at end of line
},
},
}

View file

@ -1,77 +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" },
-- Python
python = { "ruff_format" },
},
-- 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,
}

Some files were not shown because too many files have changed in this diff Show more