Skip to content

About

My neovim-init.lua Configuration

Topics

Resources

Contributing

Stars

2 stars

Watchers

1 watching

Forks

Repository files navigation

neovim-init.lua

Personal Neovim configuration repository.

This repo started from the broader "Neovim from scratch" style of setup, but it has diverged heavily and should now be treated as a standalone personal config rather than a tutorial or general-purpose starter.

Overview

The configuration is organized around lazy.nvim and split into many small plugin specs under lua/plugins/. User-specific bootstrapping and behavior live under lua/user/.

Highlights of the current setup:

  • lazy.nvim auto-bootstrap in lua/user/lazy.lua
  • LSP, completion, treesitter, testing, task, search, terminal, UI, and language-specific modules
  • Default completion engine set in lua/user/config.lua (blink by default, optional nvim-cmp)
  • Mason-based LSP/tool management in lua/plugins/lsp/mason.lua
  • Theme-heavy setup with many colorschemes, a randomizer, and theme-aware custom highlight logic
  • Frontend-aware behavior for GUI clients and modern terminals
  • Extra non-Neovim tool configs under external/ and file templates under templates/

Distinctive parts

  • Theme-first workflow: lua/plugins/general/colorscheme-randomizer.lua is wired into a large colorscheme collection, and several custom highlight paths derive their colors from the active scheme instead of pinning a fixed palette.
  • Semantic token styling is unusually opinionated: lua/user/lsp/semantic_tokens.lua generates ccls-style id0..id19 rainbow semantic token groups from existing theme colors, so semantic highlighting stays aligned with the current colorscheme.
  • UI rendering changes based on the current frontend or terminal. lua/util/client.lua detects Neovide, Neovim-Qt, Goneovim, Kitty, WezTerm, Ghostty, Alacritty, and others; lua/plugins/lsp/symbol-usage.lua switches between bubble, label, and plain-text renderers accordingly.
  • The repo tracks surrounding tooling as part of the setup, not as an afterthought: external/config/ keeps related GUI and terminal configs, and templates/vim-template stores file templates for common C and C++ entry points.
  • Personal workflow keymaps are part of the repo identity, especially quick theme rotation and lightweight selection highlighting in lua/user/keymaps.lua.

Requirements

Use a recent Neovim version. This config uses newer APIs such as vim.lsp.config, vim.lsp.enable, and modern semantic token handling, and it currently tracks nvim-treesitter's main branch, so 0.12+ / nightly is the practical baseline.

Recommended dependencies:

  • git
  • curl
  • tar
  • a Nerd Font
  • ripgrep
  • fd or another fast file finder
  • tree-sitter-cli installed via your system package manager, not npm. This repo uses nvim-treesitter's main branch, and parser install/update expects the CLI to be available.
  • native build tools for plugins and parsers:
    • Unix: a C/C++ compiler (gcc or clang), make, and cmake
    • Windows: MSVC Build Tools or clang-cl, plus cmake
  • node 22+ if you keep copilot.lua enabled. The plugin requires Node.js and curl; this repo does not call npm directly.
  • ruby if you keep Mason automatic installation enabled, because the default Mason LSP list includes solargraph
  • language runtimes and external tools you actually use (python, clangd, latex, etc.)

Depending on platform and feature usage, a Rust toolchain (cargo/rustc) may also be useful for Rust-specific workflows or plugins that need a local Rust build.

Some plugins have extra optional requirements. For example, workspace substitution with nvim-rip-substitute benefits from ripgrep with pcre2 support.

Installation

Clone the repo into Neovim's config directory:

git clone https://github.com/tan-wei/neovim-init.lua.git ~/.config/nvim

Start Neovim:

nvim

Before first launch, make sure the bootstrap dependencies above are installed. This config will bootstrap lazy.nvim, build native plugins, update Treesitter parsers, and let Mason install its default toolchain on startup.

After startup, these commands are usually the first things worth checking:

:checkhealth
:Mason

Install optional terminal configurations separately:

just install-kitty
just install-ghostty
just install-alacritty
just install-wezterm
just install-neovide

The installer detects Linux, macOS, and WSL, and overwrites the matching files under $XDG_CONFIG_HOME (or ~/.config). For Kitty, Ghostty, and Alacritty, set DOTFILES_PLATFORM to linux, macos, or wsl to select a platform explicitly. WezTerm selects its platform module at runtime. If a tool's config directory is a symlink directly to its tracked source directory, the installer replaces that directory symlink with an installed copy.

just install-neovide installs external/config/neovide/config.toml. Neovide's config file has no include directive, so the installer concatenates the base file with the matching platform fragment (linux.toml, macos.toml, or wsl.toml) into $XDG_CONFIG_HOME/neovide/config.toml. The platform fragments carry the per-OS [font] block that mirrors the guifont branch in lua/user/neovide.lua; the remaining g:neovide_* globals have no config file equivalent and stay there. Note that a Windows-side Neovide reads %APPDATA%\neovide\config.toml, so run the recipe from the environment that starts Neovide.

The base file also sets startup-message-capture = false. Neovide normally externalizes ext_messages/ext_cmdline while capturing startup messages, which makes noice.nvim report You're using a GUI that uses ext_messages. Disabling the capture avoids that without CLI flags or environment variables. The trade-off is that startup errors before the first grid render use Neovim's own press-enter prompt instead of Neovide's overlay.

Layout

  • init.lua: startup entrypoint
  • lua/user: user config, options, keymaps, autocommands, LSP setup, semantic token customization
  • lua/plugins: modular plugin specs grouped by domain
  • lazy-lock.json: pinned plugin versions, it's intentional ignore in git, to keep everything latest, :-P
  • external/: related tool configs such as kitty, ghostty, alacritty, wezterm, neovide, and goneovim
  • templates/: file templates
  • spell/: custom spell additions

Sessions

Auto-session saves the complete window layout, including nvim-tree windows. On Neovim 0.13+, nvim-tree rebuilds its own windows when a session is restored, preserving which tabs had the tree open. Saving a session does not close and reopen the tree. When no session is restored at startup, the tree opens without taking focus; a failed restore does not trigger this fallback.

Auto-session's unsupported-window cleanup preserves the NvimTree filetype. Otherwise automatic saves on exit would close the tree before saving and overwrite a manually saved open-tree layout with a closed-tree layout.

The nvim-tree configuration also adapts the current upstream one-shot SessionLoadPost handler to support repeated session switches, while coalescing duplicate events. Auto-session loads nvim-tree before restoring any layout.

Sessions previously saved with the close-before-save workaround have no tree window records. Open the tree where desired and save the session again to record that layout.

Run just session-check for the session regression suite. Each group runs in a fresh Neovim process with temporary session, config, state, cache and fixture directories. The checks use installed plugins without touching existing sessions or project repositories. Failures identify the group and return a nonzero status.

Group Coverage
layout Named sessions, split orientation, files, active editor, cursor, local options, pinned buffers and title
startup Automatic cwd restore, no-session fallback, no unrelated last-session restore, saved tree visibility
filters Auto-create disabled, existing-session autosave, enable switches, suppressed directories, bypass filetypes and unsupported-window cleanup
directory Active-buffer guard, clean directory restore, LSP stop and separator/statusline hooks
git Branch-specific session files and restore, named-session autosave; uses a temporary Git repository
scope Real Scope tab-local buffer membership save and restore
overseer Task serialization, quoting, opt-in at save/restore, disposal, invalid tasks and stale companion-file cleanup
hooks Outline close, project-local config sourcing, dropbar preload and CMake/statusline refresh
errors Missing/corrupt sessions, error reporting, autosave safety and no startup fallback after failure
commands AutoSession save, restore, delete and toggle
terminal Terminal split preservation and job recreation
tree Per-tab visibility, repeated and fresh-process restores, focus preservation and no blank placeholders
shutdown q, qa, wq, tree focus, multiple tabs/splits, exit autosave, and last-buffer deletion through real Snacks bufdelete

The session engine, nvim-tree, Scope, terminal handling and buffer deletion run against real installed code. External-effect boundaries (task execution, LSP clients, trusted project-config loading, Outline, dropbar loading, CMake and UI refresh) use recording test doubles. These checks verify the configured hooks, not actual language servers, builds, task processes or picker UI. Git branch watcher automation is not tested because it is not enabled in this config.

Jujutsu

Install Jujutsu (jj) on PATH. Existing Git plugins remain available; no repository is automatically initialized or migrated.

  • NicolasGB/jj.nvim is the main JJ interface, loaded by :J and its companion commands. Use :J log, :J status, :J describe, :J new, :J bookmark create, :J split, or :J resolve. :Jdiff compares the current file with its parent using native Neovim diff. Destructive-action confirmations remain enabled. No global keymaps are added; the existing <leader>j jump namespace is unchanged.

  • MrDwarf7/lazyjui.nvim opens jjui, a lazygit-like terminal interface, with :LazyJui. It uses a 90% width/height floating window with no transparency. Default global keymaps are disabled to preserve the jump namespace. Run it from your JJ workspace. Install the separate jjui executable on PATH, for example brew install jjui, paru -S jjui-bin on Arch, or go install github.com/idursun/jjui/cmd/jjui@latest (add $(go env GOPATH)/bin to PATH). Use :checkhealth lazyjui to check dependencies.

  • julienvincent/hunk.nvim adds file/hunk/line selection for interactive splitting and squashing. It loads on :HunkDiffEditor / :HunkDiffView and uses a compact file tree with quit confirmation. To let JJ launch it, merge this into your JJ user config (jj config edit --user):

     [ui]
     diff-editor = [
     	"nvim", "-c",
     	'lua vim.api.nvim_cmd({cmd="HunkDiffEditor", args=vim.fn.argv()}, {})',
     	"--", "$left", "$right", "$output",
     ]

    This uses Neovim's argument list for paths, including paths containing spaces. JJ launches a separate Neovim instance, including when invoked from :J split. In hunk, a selects a line, A selects a hunk, <leader><CR> accepts, and q aborts. The JJ user config is not changed automatically.

Lualine keeps the existing Git branch/status components in Git-only repositories. Inside a JJ workspace (including colocated Git/JJ repositories), they are replaced by a theme-aware JJ badge: change ID, * for a nonempty change or ∅ for an empty one, and prominent conflict/divergence warnings. Bookmarks appear above 100 columns; the first description line appears above 150 columns. Long text is truncated without breaking UTF-8. JJ ... means loading and JJ ? means the query failed. Click the badge to open status for that workspace, even when the editor's current directory points elsewhere. Git blame is hidden there because Git HEAD is not JJ's working-copy change.

JJ status queries are asynchronous, cached per workspace, and throttled to at most once per second on buffer entry, save, focus return, terminal leave, directory change, or cursor idle. Like jj status, the query snapshots saved working-copy changes; unsaved buffer edits are not included. Redrawing Lualine never runs JJ.

Other community options include jiejie.nvim (Fugitive-style), neojj (Magit-style), and jj-diffconflicts (dedicated merge tool). These are not installed alongside the primary interface to avoid overlapping workflows. vcsigns.nvim is worth considering for native JJ gutter signs, especially in non-colocated repositories; the existing Gitsigns setup is retained, so its Git-based signs/actions are not JJ-aware.

Customization

The lowest-friction entry points are:

If you want to remove or swap functionality, the corresponding plugin specs are usually under a matching folder in lua/plugins/.

Keymap inventory

This section is the repo-level source of truth for repo-owned keymaps.

Maintenance contract:

  1. Any change that adds, removes, or repurposes a keymap in lua/user/keymap/plain.lua, lua/user/keymap/lazy.lua, lua/user/keymap/buffer.lua, lua/user/keymap/which_key.lua, lua/user/autocommands.lua, or plugin specs with M.keys must update this section in the same change.
  2. If a key replaces a core Neovim mapping, keep the Overrides builtin and Replaced meaning columns in sync. For builtin-like normal-mode slots (g*, z*, <C-w>*, single-key, Ctrl-letter), add conflict.builtin or at least conflict.note in the registry entry so the audit knows the slot was reviewed.
  3. Centralized [ / ] entries must declare whether they are bracket motion or bracket action via symbol = { ... }. Bracket motions are expected to be previous/next pairs and repeat through demicolon; bracket actions must opt out with symbol.repeatable = false.
  4. Centralized > / < / = entries are treated as operator-style actions, not navigation prefixes. They must declare symbol = { family = "operator", role = "action", repeatable = false, ... }.
  5. ( / ) / { / } stay reserved for native motions rather than repo-defined prefix families.
  6. Buffer-local mappings should be documented with their scope, not only by key.

Scope notes:

  • This inventory focuses on repo-owned mappings and repo-generated prefixes.
  • It does not fully expand upstream default plugin mappings inherited from plugins, such as nvim-tree's default_on_attach() defaults.
  • mapleader and maplocalleader are both \, configured in lua/user/config.lua.
  • Plugin-local bracket families that are still generated outside the centralized registry, such as toggle.nvim's [o / ]o, are documented here but are not yet covered by the symbol-family audit.

Symbol families

  • [ / ] are the repo's navigation family. Use them for previous/next style motions like [b / ]b; centralized motion entries should be repeatable with demicolon.
  • [ / ] may still host actions when Neovim or a plugin already uses that syntax, such as Yanky's [p / ]p, but those entries should explicitly opt out of repeatability.
  • > / < / = are operator-style action families. Keep them for transforms such as Yanky's shifted or filtered puts, not for next/previous navigation.
  • ( / ) / { / } are kept for native motions rather than repo-owned prefix namespaces.

Ctrl / Alt / Function / Special keys

Mode Key Current meaning Overrides builtin Replaced meaning / notes
n <C-Up>, <C-Down>, <C-Left>, <C-Right> Resize splits No Repo-owned resize shortcuts
n <C-h>, <C-j>, <C-k>, <C-l> Treewalker structural navigation No Uses nonstandard control slots for syntax-aware movement
all <C-\\> Toggle floating terminal No Set by toggleterm open_mapping
n <C-w>z, <C-w>_, <C-w>|, <C-w>= windows.nvim maximize / equalize helpers Yes Replaces core window commands with plugin implementations
n, v <C-a>, <C-x> dial.nvim increment / decrement Yes Replaces core numeric increment / decrement with dial logic
n <C-n>, <C-p> Yank history next / previous Yes Replaces normal-mode slots with Yanky ring navigation
i, s <C-n>, <C-p> LuaSnip next / previous choice Yes Replaces insert/select completion-style navigation
n, v, x <A-j>, <A-k> Move line / block up or down No Repo-owned move.nvim mappings
n, v <A-h>, <A-l> Move character / block left or right No Repo-owned move.nvim mappings
n, o, x <A-w>, <A-e>, <A-b> Spider subword motions No Repo-owned alternative word motions
n, x <A-q> Toggle multicursor No Repo-owned multicursor entrypoint
i <A-e> nvim-autopairs fast-wrap No Repo-owned insert helper
n, x <Up>, <Down> Add cursor above / below Yes Replaces plain cursor movement with multicursor actions
n <C-LeftMouse>, <C-LeftDrag>, <C-LeftRelease> Multicursor mouse interactions Yes Replaces core mouse selection behavior
n <S-h>, <S-l> Previous / next buffer Yes Replaces H / L screen-line motions
n <F1> Toggle profiling No Repo-owned profile.nvim helper
n <F2> Live rename No Repo-owned rename helper
v <F3>, <F4> Add / remove highlight No Repo-owned highlighting helpers
n <F5> OverseerRun No Repo-owned task runner shortcut
n <F6> DAP continue No Repo-owned debug helper
n <F8> Randomize colorscheme No Repo-owned theme rotation shortcut
n <F9>, <F10>, <F11> DAP breakpoint / step over / step into No Repo-owned debug helpers
n <F17>, <F23> DAP terminate / step out No Repo-owned debug helpers

g* keys

Mode Key Current meaning Overrides builtin Replaced meaning / notes
n gD LSP declaration Yes Replaces core declaration jump with LSP declaration
n gd LSP definition Yes Replaces core local-definition jump with LSP definition
n gI LSP implementation No Repo-owned LSP slot
n gr LSP references No Repo-owned LSP slot
n gl Diagnostic float No Repo-owned diagnostics slot
n, x ga EasyAlign Yes Replaces core character info display
n, x gx Browse open under cursor Yes Replaces builtin open-under-cursor behavior with gx.nvim's :Browse
n, v g<C-a>, g<C-x> dial.nvim gnormal / gvisual increment / decrement Yes Replaces core g<C-a> / g<C-x> numeric operations
n, x gcr coerce motion / visual transform entrypoint No Repo-owned transform slot
n, x gS mini.splitjoin toggle split/join No Split or join function arguments, arrays, dicts
n, x g= mini.operators evaluate text Yes Replaces core g= format with Lua expression evaluation
n, x gm mini.operators multiply (duplicate) text No Repo-owned duplicate operator
n, x gs mini.operators sort text Yes Replaces core gs sleep with sort operator

z* and fold-related keys

Mode Key Current meaning Overrides builtin Replaced meaning / notes
n zR, zM, zr, zm ufo fold open / close helpers Yes Replaces core fold controls with ufo implementations
n zC fold-cycle close_all() Yes Replaces core recursive fold close
n K Peek folded lines, else hover Yes Replaces core keywordprg / help behavior; also shadowed by LSP buffer-local K
n <Tab>, <S-Tab> fold-cycle open / close Yes for <Tab> <Tab> replaces jump-list forward (<C-i> equivalent); <S-Tab> is a repo-owned slot

[ / ] / operator families

Mode Key Current meaning Overrides builtin Replaced meaning / notes
n [b, ]b Previous / next buffer No Bracket motion family; repeatable with demicolon
n, x [e, ]e Previous / next delimiter No sort.nvim bracket motion family; repeatable with demicolon
n [S, ]S Previous / next scrollview mark No Bracket motion family; repeatable with demicolon
n [g, ]g Previous / next git hunk No Bracket motion family; repeatable with demicolon
n [x, ]x Previous / next merge conflict No diffs.nvim buffer-local maps in conflicted files
n [r, ]r Previous / next reference No Refjump motion family; repeatable with demicolon
n [p, ]p, [P, ]P Yanky indent-aware put Yes Bracket action family; intentionally non-repeatable
n >p, <p, >P, <P, =p, =P Yanky shift / filter put helpers Yes Operator-style action family; intentionally non-repeatable
n, x [m, ]m, [M, ]M Multicursor match add / skip No Repo-owned multicursor navigation
n [t, ]t, [T, ]T Previous / next test, previous / next failed test No Repo-owned neotest motions; custom demicolon repeat wrapper
n [o{option}, ]o{option} toggle.nvim previous / next option state No Plugin-generated toggle prefix family

Bare keys and text-object families

Mode Key Current meaning Overrides builtin Replaced meaning / notes
n, x y Yanky yank Yes Replaces core yank with yank-ring aware yank
n p, P, gp Yanky put family Yes Replaces core put family with yank-ring aware put
n gzd, gzt, gzi, gzr goto-preview definition / type / implementation / references No Requires LSP support for the requested method
n gzP Close goto-preview windows No Closes all goto-preview windows, not only the current one
n n, N hlslens-enhanced next / previous match Yes Replaces builtin n / N with hlslens overlay
n *, # hlslens-enhanced next / previous word match Yes Replaces builtin * / # with hlslens overlay
n g*, g# hlslens-enhanced next / previous partial-word match Yes Replaces builtin g* / g# with hlslens overlay
n, x, o f, F, t, T Flash character motions Yes Replaces builtin character motions; repeats through demicolon
n cr coerce current word No Repo-owned transform entrypoint
n co, ct, cb, c0 Resolve conflict with ours / theirs / both / neither No diffs.nvim buffer-local maps in conflicted files
i jk, kj Exit insert mode No Repo-owned insert escape shortcuts
v <, > Reindent and keep selection Yes Extends core indent behavior to preserve selection
v p Black-hole delete then paste Yes Avoids clobbering unnamed register
x J, K Move selected block down / up Yes Replaces core visual/block behavior
n - Open Oil float Yes Replaces core first-nonblank line motion
n q in qf, help, man, lspinfo buffers Close auxiliary window Yes Buffer-local override of macro recording key
n yo{option}, yos toggle.nvim toggle option / dashboard No Plugin-generated option prefix family
o, x various-textobjs family Extra text objects for indentation, subword, brackets, quotes, values, keys, numbers, diagnostics, folds, chain members, notebook cells, filepaths, colors, and more Mixed Exact leaf list lives in lua/plugins/edit/nvim-various-textobjs.lua

<leader> families

<leader> is \\. The table below lists repo-owned namespaces and leaf keys.

Prefix Leaf keys Current meaning Overrides builtin Notes
<leader>[, <leader>] [, ] Swap syntax sibling left / right No sibling-swap.nvim; repeatable with .
<leader>A A Alpha dashboard No Standalone action
<leader>n na NodeAction No Tree-sitter node actions
<leader>b bb, bn, bi, bI, bc, bp, bP, bf, bh, bl, bm, bM, bsd, bsl, bst, bsr Buffer navigation, close, pick, menu, sort, indent-blankline toggle No Buffer namespace
<leader>C Cs, Ci, CI, Ca, CS, Ct, Cm, CD, Cf, Cc, C3, C5 clangd / C++ helper actions No C++ namespace
<leader>c c Close current buffer No Standalone action
<leader>d d Dropbar pick No Plain d is a standalone action, but d* also hosts DAP keys
<leader>d* dp, dP, dR, dl DAP condition breakpoint, log point, REPL, run last No Debug namespace piggybacks on <leader>d
<leader>e e Toggle NvimTree No Standalone action
<leader>F F Live grep with args No Standalone action
<leader>f f Find files No Standalone action
<leader>g gB, gb, gc, gd, gl, gj, gk, gp, gr, gR, gs, gu, go, gv Git blame, branches, commits, diff, LazyGit, hunk operations, status, VGit previews No Git namespace; gv opens the VGit group
<leader>gv gvd, gvf, gvb, gvh, gvl, gvc, gvs VGit project/file diff, blame, file history, commit log/overview, stashes No Read-only view entry points; staging/resetting stays with Gitsigns
<leader>h h Clear search highlight No Standalone action
<leader>H H Clear search highlight and export last search to quickfix No Standalone action
<leader>j jj, jk, jcj, jck, jgj, jgk, jhj, jhk, jqj, jqk, jv, jl Portal jumplist / changelist / grapple / harpoon / quickfix jumps, mini.jump2d visible word / line No Jump namespace; jv / jl from mini.jump2d
<leader>L Ld, Lr Linediff and reset No Line-diff namespace
<leader>l la, ld, lD, lf, lF, lh, li, lI, lj, lk, ll, lo, lq, lr, ls, lS LSP code actions, diagnostics, symbols, format, hover, rename, signature, outline No LSP namespace
<leader>M Mg, Mj, Ms, Mt Grapple mark / select / scopes / tags No Marks namespace
<leader>m ma, mm, mn, mp, m1, m2, m3, m4 Harpoon toggle, menu, prev/next, select slot No Harpoon namespace
<leader>o o Smart open (frecency) No Standalone action
<leader>p pd, pc, pu, pU, pf, ps, pv, pt, po Overlook peek / close / restore / focus / split / vsplit / tab / current window No Popup namespace
<leader>q qq, qp, qs, qe, qy, qd Macro start/stop, play, switch slot, edit, yank, delete No Macro namespace
<leader>R Rr, Rs, RS, Rf, Rh, Rc, Rl REPL run / restart / sniprun / focus / hide / send file / send until cursor No REPL namespace
<leader>r rc, rf, rs RunCode / RunFile / stop runner No Run namespace
<leader>s ss, sS, sy, sg, sf, si, sa, sb, sc, sC, sh, sk, sM, so, sp, sr, sR Flash, grug-far, yank history, AST grep, buffer search, colorschemes, commands, help, keymaps, man, frecent files, pickers, recent files, registers No Search namespace
<leader>T Tf, Th, Tv Float / horizontal / vertical terminal No Terminal namespace
<leader>t ta, tA, tm, tg, tr, tu, tn, td, tl, tc, ts, to, tp, tw, tj, tk CSV column alignment, table mode, TOC, neotest run / Rust-only debug / output / panel / watch / failed-test jumps No ta / tA are CSV/TSV buffer-local; tj / tk currently look typo-prone in config
<leader>w wl, ws, wd, wt, wf, wb Session management plus MoveWord forward / backward No Namespace drift: workspace/session and MoveWord share the same prefix
<leader>x xg, xr CellularAutomaton effects No Repo-owned extras/effects namespace
<leader>v vv, vl, vj, vk mini.visits select path / label / next / previous No Visits namespace
<leader>y yc, yy, yt Yazi cwd / open / toggle No Yazi namespace
<leader><Up>, <leader><Down> ↑, ↓ Multicursor skip cursor above / below No Only meaningful for multicursor workflow

Buffer-local and conditional mappings

Scope Key Current meaning Overrides builtin Notes
CSV/TSV buffer <leader>ta, <leader>tA Align / clear virtual column spacing No Buffer-local registry entries applied by decisive.nvim on FileType
LSP-attached buffer K Pure LSP hover Yes Buffer-local registry entry in lua/user/keymap/buffer.lua, applied from lua/user/lsp/handlers.lua and shadowing the global ufo K
Non-LSP buffer K Peek folded lines, else hover Yes Global lazy-registry entry from lua/user/keymap/lazy.lua
qf, help, man, lspinfo, transient nofile floats q Close window Yes Buffer-local helper from lua/user/autocommands.lua
checkhealth tab q Close tab Yes Buffer-local helper from lua/user/autocommands.lua with a safe fallback to close the window if it is the last tab
Terminal buffers <Esc>, jk, <C-h>, <C-j>, <C-k>, <C-l> Leave terminal mode or move between windows Yes Buffer-local helpers from lua/plugins/terminal/toggleterm.lua
NvimTree buffer P, <Esc>, <C-f>, <C-b>, <Tab> Preview, close preview, preview scroll, smart expand / preview Mixed Repo-local additions only; upstream default_on_attach() maps are not expanded here

Known drift and conditional precedence

  • Global K from lua/user/keymap/lazy.lua is shadowed by buffer-local LSP K from lua/user/keymap/buffer.lua, so fold-preview-on-K disappears after LSP attach.
  • <leader>d is both a standalone Dropbar action and the parent prefix for DAP leaf keys.
  • <leader>w mixes workspace/session actions with MoveWord (wf, wb), so the namespace is not semantically pure.

Conflict detection workflow

Manual tools currently available in this repo:

  1. just keymap-audit runs the repo-owned headless audit and prints a report without failing the shell.
  2. just keymap-audit-check runs the same audit but exits non-zero when duplicate repo registrations or required conflict annotations are missing.
  3. just keymap-docs reports central mappings that do not appear in the README inventory tables.
  4. just keymap-docs-check exits non-zero when a central registry or which-key mapping is missing from the README inventory tables.
  5. :KeyAnalyzer <leader>, :KeyAnalyzer <C->, and :KeyAnalyzer <M-> visualize occupied vs free keys by prefix.
  6. :FzfLua keymaps shows the runtime keymap list after lazy loading.
  7. :verbose nmap {lhs}, :verbose xmap {lhs}, :verbose imap {lhs}, and friends show which mapping currently wins and where it was defined.

Practical limitations:

  • lua/plugins/keymap/key-analyzer.lua installs meznaric/key-analyzer.nvim, but that plugin is an analysis / visualization aid, not an automatic conflict warning system.
  • just keymap-audit / just keymap-audit-check currently focus on repo-internal duplicate registrations that survive startup and lazy loading, plus missing builtin-conflict annotations, reviewed conflict.note metadata for builtin-like normal-mode slots, and missing symbol-family metadata for centralized [ / ] and > / < / = entries. They still do not try to infer every upstream plugin default mapping.
  • just keymap-docs / just keymap-docs-check currently cover the centralized plain, lazy, buffer, and which-key registries that back the README inventory tables. Plugin-generated or attach-time families that are still documented only at a coarse summary level remain outside that strict coverage.
  • Upstream key-analyzer.nvim reads mappings via vim.api.nvim_get_keymap(), so it does not fully cover built-in families such as <C-w> and z, and it does not show buffer-local mappings.
  • which-key.nvim improves discoverability, but it does not validate conflicts or duplicate ownership.
  • There is currently no plugin in this repo that will reliably warn on every repo-internal conflict as soon as a mapping changes, especially across lazy-loaded, plugin-generated, and buffer-local mappings.

If automatic warnings become a requirement, the more reliable next step is a repo-owned audit command or headless script that:

  1. collects global and buffer-local mappings,
  2. groups them by mode and lhs,
  3. flags duplicate lhs ownership,
  4. and checks whether builtin overrides or reviewed builtin-like slots are explicitly documented in this section and registry metadata.

Project-local config

This repo also supports project-local configuration through klen/nvim-config-local. The current setup looks for these files in the current working directory and its parent directories:

  • .nvim.lua
  • .nvimrc
  • .exrc
  • .nvim/nvim.lua

The default starting point is .nvim/nvim.lua in your project root.

Use project-local config when something should only apply to one repository, such as:

  • setting vim.g.* globals for Vim plugins
  • enabling or disabling repo-specific linters
  • adding project-only autocommands, keymaps, or environment tweaks

Typical flow:

  1. Run :ConfigLocalCreate from the project root to prepare an unsaved .nvim/nvim.lua buffer.
  2. Fill the TODO fields and adjust any defaults for the project.
  3. Write the file, then allow it when prompted.

If the file contents or path change later, Neovim will ask you to trust it again.

Useful commands:

  • :ConfigLocalCreate to prepare an unsaved .nvim/nvim.lua for an empty project, including TODO project fields plus header/template, linter, formatting, Markdown, and LaTeX defaults
  • :ConfigLocalEdit to open or create the local config file
  • :ConfigLocalSource to source the current project config again
  • :ConfigLocalTrust to mark the current project config as trusted
  • :ConfigLocalDeny to deny the current project config

Template maintenance:

  • Project-local fields are declared in lua/util/project_config.lua.
  • When adding a new project-level vim.g.* setting, add it to that registry with a runtime default and, when the generated config should prompt the user, a separate template TODO value.
  • Runtime consumers should read these values with require("util.project_config").get "setting_name". :ConfigLocalCreate renders the template from the same registry.

Example: plugin globals for one project only

vim.g.tex_flavor = "latex"
vim.g.vim_markdown_folding_disabled = 1

Example: repo-specific linter toggles

vim.g.linters = {
	clangtidy = true,
	cppcheck = false,
	cpplint = false,
}

This config is read by lua/plugins/linter/nvim-lint.lua.

Example: arbitrary project-local Lua

vim.env.CC = "clang"

vim.api.nvim_create_autocmd("FileType", {
	pattern = "markdown",
	callback = function()
		vim.opt_local.wrap = true
		vim.opt_local.spell = true
	end,
})

The main tradeoff is flexibility versus discoverability: these files can run arbitrary Lua or Vimscript, which makes them powerful, but they do not have the schema validation and completion that more structured project-local config systems provide.

Project debugging

Run :DebugConfigCreate from a project root and select a template in templates/debug. Empty prepares JSONC skeletons; the other choices cover C, C++, Python, Rust, Go, JavaScript, TypeScript, Lua, Ruby, and Bash. Check each template's defaults before saving:

  • C/C++ offer GDB or CodeLLDB with a CMake build task; set the actual executable and build directory. Rust uses CodeLLDB and cargo build; set the binary name in target/debug. Go uses Delve and expects a Go package at the project root.
  • Python uses debugpy and python3; change python to the project's interpreter if needed. JavaScript runs the current file with Node.js; TypeScript additionally needs tsx installed in the project (npm install --save-dev tsx). These are Node scripts, not browser configs.
  • Lua has separate templates: Lua (standalone / Lua 5.3) launches the current file with lua5.3 and the local Lua debugger; change the interpreter to a supported Lua 5.1-5.3 or LuaJIT if needed (not Lua 5.4/5.5). Lua (Neovim / OSV attach) instead connects to another Neovim instance running :lua require("osv").launch({port = 8086}); it does not start a Lua script. Ruby runs the current file through rdbg (install the Ruby debug gem). Bash uses the Mason-provided bash-debug-adapter and bashdb; it also needs bash, cat, mkfifo, and pkill on PATH.

New launch templates pause at entry without a breakpoint when supported: GDB uses stopAtBeginningOfMainSubprogram, while CodeLLDB, Python, Go, Node.js, and standalone Lua use stopOnEntry. Bash's adapter has no supported entry pause setting. Neovim Lua and Ruby use attach configurations, so they cannot generally guarantee a pause before the target starts running. Existing project .vscode/launch.json files are not changed when templates are updated.

Mason is configured to install debugpy, CodeLLDB, Delve, js-debug-adapter, and bash-debug-adapter; the standalone Lua debugger is built by lazy.nvim with npm. GDB, Node.js, Cargo, Go, Ruby/debug, and shell tools must be available separately. Both .vscode/launch.json and .vscode/tasks.json are prepared as unsaved buffers: the command opens launch.json, and you can switch to the tasks.json buffer to edit it before :write on each file. Canceling the selection creates nothing; existing files and unsaved drafts are not overwritten. nvim-dap reads launch configurations and Overseer reads tasks from this fixed directory; .nvim/nvim.lua remains for trusted project-local Lua settings.

Notes

  • This is a personal config repo, not a polished distribution.
  • The code is a more reliable source of truth than old documentation copied from upstream templates.
  • lazy-lock.json is ignored in git, so plugin versions are always latest (if break, should be fixed ASAP).

About

My neovim-init.lua Configuration

Topics

Resources

Contributing

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages