16 KiB
Neovim redesign and cleanup plan
Governing intent
Make Neovim the primary editor and turn its configuration into a small, readable, cohesive system. Refactoring, simplification, runtime performance, reliability and UX are equal deliverables; do not bolt new plugins onto the current structure.
Use Helix's philosophy as the decision framework throughout implementation, including unforeseen choices:
- Prefer a few orthogonal concepts over aliases and special cases.
- Make related commands symmetric, mnemonic and context-aware.
- Provide one canonical interface for each task; hide plugin identity.
- Make first use behave like later use; load order must not affect behavior.
- Keep the screen quiet and reveal information when relevant or requested.
- Prefer native Neovim behavior when it fits this model.
- Remove code, configuration and plugins that do not justify their complexity.
These principles govern the detailed plan. If a minor implementation detail conflicts with them, choose the simpler coherent solution and report the deviation. If resolving a conflict changes the user-facing command model, dependencies or scope, stop and request a plan amendment. Do not use this discretion to expand scope.
Context and scope
- Neovim module:
modules/capabilities/neovim/default.nix. - Lua root:
modules/capabilities/neovim/lua-config. modules/profiles/host-base.nixcurrently selectsprimary-editor-helix; therefore the active shell has nonvim.- Locked versions include Neovim 0.12.4 and Kulala 6.29.0. Target them; add no older-version compatibility.
- Retain
nix-wrapper-modules,lz.nand the custom Svelte Treesitter grammar. - First-class languages: Nix, TypeScript/JavaScript, Svelte, Rust and Python.
- Also retain Lua, JSON, CSS, HTML, Astro, Typst, TOML, YAML, Markdown/
zkand snippets. Remove Dafny. - Add Oil, Grug-far, Dadbod UI, SQL completion, Kulala request/environment/import support and
zk-nvim. - Add YAML/JSON schema support and automatic web-tag closing/renaming.
- Exclude sessions, embedded-terminal workflow beyond leaving terminal mode, Git porcelain, debugging, test runners, AI tooling and graphical ER modeling.
- Preserve unrelated worktree changes, including
modules/capabilities/desktop-defaults.nixif still modified.
Known defects and cleanup targets:
- Nix plugin groups and Lua modules do not share a clear capability structure; Kulala is in
ui.lua, whilelsp.luaalso owns Telescope loading, diagnostics and breadcrumbs. options.luacontains mappings and an autocmd.- Telescope dependency arrays and manual
trigger_loadcalls are duplicated. Code-action UI changes after Telescope first loads. - Treesitter starts through three overlapping events.
- Navic uses a global callback evaluated by winbars.
- LSP attach callbacks recreate shared autocmd groups.
project.nvimchanges global cwd;direnv.vimruns on every buffer entry although the launching shell owns the environment.- Dadbod is absent; Kulala installs global mappings; several installed plugins lack explicit setup.
Required structure and code quality
Before editing, inventory every Neovim option, mapping, autocmd, plugin, runtime package and server. Delete duplicates, unused entries and configuration that restates defaults.
Align Nix plugin groups with these Lua modules:
init.lua: orderedrequirecalls only.options.lua: options only.keymaps.lua: plugin-independent mappings and intentional default removals.autocmds.lua: editor-wide autocmds.plugins/editing.lua: mini.ai/surround/pairs/icons, indentation detection, Treesitter, text objects, rainbow delimiters, automatic tag handling and Grug-far.plugins/navigation.lua: Telescope, FZF,ui-selectand Oil.plugins/diagnostics.lua: native diagnostic presentation and Trouble.plugins/lsp.lua: servers, attach/detach behavior and Navic attachment.plugins/completion.lua,formatting.lua,ui.lua,database.lua,http.lua,notes.lua: one concern each.
Enforce these rules:
- One owner per option, mapping, autocmd and plugin setup.
- Keep plugin-backed mappings in their plugin specification; declare Which-key groups centrally.
- Give every lazy plugin an explicit
keys,cmd,ftoreventtrigger. Only dependency entries may omit one. - Use
lz.ndeclaratively. Remove scatteredtrigger_loadwrappers. - Create each augroup once and scope buffer behavior with
buffer = args.buf. - Keep state local; add no
_Gcallbacks. - Add a helper only when at least two capabilities use it and it reduces conceptual complexity.
- Comments explain non-obvious constraints or decisions, not syntax. Delete commented-out code.
- Prefer current native APIs and defaults. Add no speculative guards, fallbacks or compatibility layers.
- Move Kanagawa setup into
ui.lua; delete the generated Lua string and_kanagawa-theme.nix. Usenix-infoonly for Nix expressions and store paths. - Group Nix packages by capability/language and remove obsolete entries.
Interaction model
| Task | Canonical interface |
|---|---|
| Filesystem operations | Oil editable directory buffer |
| Files, text, symbols, references, diagnostics, commands | Telescope |
| Project-wide text or regex replacement | Grug-far |
| Syntax movement/selection in the current buffer | Treesitter |
| Code relationships and semantic refactors | LSP; Telescope renders result collections |
| Persistent diagnostics/promoted results | Trouble |
| Database inspection/queries | Dadbod UI |
| HTTP requests | Kulala |
| Note capture and discovery | zk-nvim; Telescope renders note collections |
Use buffers, not tabs or sessions, as the open-file model. Root project searches at vim.fs.root(0, { ".git" }), falling back to the process cwd; never change cwd implicitly. Keep Telescope's default result behavior. Provide one Telescope action that sends results to quickfix and opens Trouble.
Keep LSP rename and code actions as the semantic project-wide refactoring interface. Use Grug-far for explicit text or regular-expression replacement; do not add an integration layer between them.
Make Oil the directory handler. <leader>e opens the current file's directory. Keep filesystem-change confirmation.
Keymap and discovery contract
Set <leader> to space and <localleader> to comma. Every custom mapping needs an action-oriented desc. Remove displaced plugin/native aliases.
g…:gddefinition,gDdeclaration,grreferences,gIimplementation,gytype definition,gOdocument symbols,Khover.[x/]x: previous/next diagnosticd, Git hunkh, functionf, class/typec, parametera. Replace conflicting defaults for these exact keys as one family.<leader>s…: Telescope project files/grep, cursor word, current buffer, recent files, workspace symbols, diagnostics, help, commands, keymaps and resume.<leader>c…:cacode action,crrename,cfformat,cRproject-wide replacement. Remove displacedgr*LSP mappings.<leader>d…: persistent workspace/buffer diagnostics. Remove<leader>x…Trouble and location-list diagnostic mappings.<leader>b…: buffer picker/delete. Remove the duplicate<leader><leader>picker.<leader>q…: database workspace;qotoggles Dadbod UI.<leader>r…: global HTTP facilities, including the scratchpad.<leader>n…: central notes;nncaptures,nffinds notes andntfinds tags. Inside azknote,niinserts a link.<leader>u…: UI toggles only.gl: diagnostic details;<C-h/j/k/l>: window focus;<Esc><Esc>: leave terminal mode.- Keep native
gg,Gand word motions. Do not aliasgeto end-of-file. Replace native mappings only as part of a complete family.
Provide operator-pending and visual Treesitter objects: if/af function, ic/ac class/type, ia/aa parameter, ii/ai conditional, il/al loop, ib/ab block. Retain ci<object> grammar and make structural bracket movements repeatable.
Context-local mappings:
- SQL:
,rexecute query/selection,,ssave query,,pedit bind parameters. - HTTP:
,rexecute request at cursor,,aexecute file,,eselect environment. - Dadbod drawer/output: keep normal motions and
<CR>; describe local mutation/refresh actions instead of adding globals.
Configure Which-key with the helix preset and automatic normal, visual, select and operator-pending triggers. Enable its operator, motion, text-object, window, navigation, g, z, mark, register and spelling documentation. Define groups once without duplicating mappings. Use Telescope keymap, command and help pickers as the full-catalog fallback; do not remap single-key built-ins only to display them.
Feature changes
Editing, navigation and UI
- Stop configuring
mini.files; addpkgs.vimPlugins.oil-nvim. - Remove
project.nvim,direnv.vimandmini.indentscope. - Start Treesitter once on
FileTypefor normal file buffers. Configure text objects and rainbow delimiters explicitly. - Add
nvim-ts-autotagfor Svelte, Astro, HTML, JSX and TSX only. - Retain mini.ai/surround/pairs/icons and indentation detection. Benchmark
mini.hipatterns; retain hex/TODO highlighting only if it adds no measurable scrolling cost. - Configure Grug-far as the only project-wide text/regex replacement interface. Open it with the cursor word or visual selection when available and retain its editable-buffer workflow.
- Reduce Gitsigns to signs, hunk navigation and preview.
- Keep Kanagawa Wave and colorful semantic completion. Use opaque surfaces and one border style for popups.
- Use absolute line numbers, no tabline and markers only for tabs, trailing spaces and non-breaking spaces.
- Use one global statusline containing mode, branch, filename, diagnostics, filetype, location and a truncated Navic breadcrumb when available. Remove diff/LSP-status duplication and all winbars.
LSP, completion and formatting
- Remove the Dafny package/server. Keep current servers for retained languages.
- Add
yaml-language-server(yamlls) andSchemaStore.nvimfor schema-aware YAML and JSON completion and validation. Disable LSP formatting in favor of Conform. - Remove
oxfmtas an LSP; invoke Conform'soxfmtCLI formatter. - Remove
typst-preview.nvim,websocatand theirnix-infosettings. Invoke the locked Tinymist server's preview commands. - Enable supported inlay hints by default with one toggle. Retain semantic tokens unless profiling identifies them as a bottleneck.
- Show diagnostics through signs, underlines and severity sorting. Remove virtual lines; use
glfor text. - Install one buffer-local document-highlight autocmd set on attach; remove it on detach without affecting other buffers.
- Ensure every Telescope-backed LSP action and
telescope-ui-selectworks on first invocation. - Retain Blink, colorful-menu and friendly-snippets with
<C-j>/<C-k>selection. Use Blink's nativevim.snippetsupport and convert the custom Nix snippets to VS Code JSON; remove LuaSnip. - Define one Conform pipeline per filetype. Prefer packaged CLIs; use LSP formatting only as an explicit fallback. Run one Ruff fix plus Ruff format for Python and one Oxfmt pass for supported web files.
- Keep format-on-save synchronous with a finite timeout; keep manual formatting asynchronous.
Databases and HTTP
- Add
vim-dadbod,vim-dadbod-ui,vim-dadbod-completionandblink-compat; load them on database commands/filetypes only. - Add wrapper-local
postgresql,mariadbandmongosh. PostgreSQL is primary; retain MySQL and MongoDB adapters. - Integrate relational SQL completion into Blink through
blink.compat; do not enable it for MongoDB. - Expose schemas, tables, views, constraints/index helpers, saved queries, results and relational foreign-key jumps.
- Use
DATABASE_URLas the default project connection when set. Never store credentials in Nix or committed files. Store ad-hoc saved queries under XDG data; keep schema/migration SQL in projects. - Treat MongoDB as collection/document exploration, not relational navigation.
- Move Kulala from
ui.luatohttp.lua; load it onhttp/restfiles and explicit commands only. Disable its global mappings. - Add wrapper-local
kulala-core,kulala-fmt,curl,jqandopenssl. Pass the packaged core path throughnix-info; disable backend downloads. - Use the Nix-provided
httpTreesitter grammar; disable Kulala parser downloads/builds. - Use JetBrains-compatible
.httpfiles. Commit non-secrethttp-client.env.json; keep credentials in ignoredhttp-client.private.env.jsonor shell/direnv variables. - Support conversion with
kulala-fmt convert --from openapi --to http -o <output> <spec>. Add no editor wrapper until repeated use justifies it.
Notes
- Add wrapper-local
zkandzk-nvimwith Telescope as its picker. Letzk-nvimstartzk lsponly for Markdown buffers inside a notebook; do not also configurezkthroughnvim-lspconfig. - Set
ZK_NOTEBOOK_DIRto~/sync/notes. Global capture and discovery always target this notebook; new notes use its existing filename and template rules and never prompt for a directory. - Use the standard LSP mappings to follow links, find backlinks and preview linked notes. Add only the three global mappings and notebook-local insert-link mapping defined above.
Nix integration
Keep the wrapper self-contained; do not rely on login-shell packages. Align Nix groups with the Lua structure and delete unused declarations. After validation, change host-base.nix to primary-editor-neovim and remove its Helix cache import; retain the optional Helix capability.
Performance and reliability
- Before refactoring, build the current wrapper without switching the running system. Record median headless startup time over repeated runs. Profile scrolling in representative large Nix, TypeScript/Svelte, Rust and Python buffers with LSP attached.
- Remove known hot-path duplication: repeated Treesitter starts, winbar evaluation, virtual-line layout changes, unused visual scanners, the formatter-only LSP and the separate Typst preview process layer.
- Re-profile after editing/navigation, LSP and UI changes. Add no size thresholds, caches, debounce wrappers or feature cutoffs without a measured problem.
- Keep inlay hints, semantic tokens and required servers unless measurements identify a specific offender.
- Temporary files, non-file buffers, inaccessible parent directories and files outside Git repositories must not change cwd or emit configuration errors.
Verification and completion
- Run
nix fmt,nix flake check --no-buildand no-switch builds forzenith,polarisandorion. - Run the built wrapper headlessly with no arguments and with Nix, TypeScript, JavaScript, Svelte, Rust and Python files. Run
:checkhealth; resolve errors from configured providers instead of suppressing them. - In fresh processes, test every semantic mapping, code action before any other Telescope command, Trouble promotion, Oil directory handling, Grug-far replacement, YAML/JSON schemas, automatic tag handling, central note capture/discovery, native Tinymist preview, SQL completion/query actions and Kulala request/environment actions.
- Open a temporary prompt file outside a project and confirm no workspace, cwd or access error.
- With disposable data, test Oil multi-create/rename/move/copy/delete, Dadbod against PostgreSQL, MySQL/MongoDB client and adapter loading, a local Kulala request and a small OpenAPI conversion.
- Repeat startup and attached-LSP scrolling measurements; report before/after results and any remaining bottleneck.
- Apply the governing Helix principles throughout, not only at final review. Completion requires no duplicate mapping/setup, misplaced capability, lazy plugin that fails on first action or retained code without a current purpose.