Files
lux/PLAN.md
T
2026-09-04 19:49:42 +02:00

191 lines
16 KiB
Markdown

# 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.nix` currently selects `primary-editor-helix`; therefore the active shell has no `nvim`.
- Locked versions include Neovim 0.12.4 and Kulala 6.29.0. Target them; add no older-version compatibility.
- Retain `nix-wrapper-modules`, `lz.n` and 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/`zk` and 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.nix` if 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`, while `lsp.lua` also owns Telescope loading, diagnostics and breadcrumbs.
- `options.lua` contains mappings and an autocmd.
- Telescope dependency arrays and manual `trigger_load` calls 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.nvim` changes global cwd; `direnv.vim` runs 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`: ordered `require` calls 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-select` and 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`, `ft` or `event` trigger. Only dependency entries may omit one.
- Use `lz.n` declaratively. Remove scattered `trigger_load` wrappers.
- Create each augroup once and scope buffer behavior with `buffer = args.buf`.
- Keep state local; add no `_G` callbacks.
- 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`. Use `nix-info` only 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…`: `gd` definition, `gD` declaration, `gr` references, `gI` implementation, `gy` type definition, `gO` document symbols, `K` hover.
- `[x`/`]x`: previous/next diagnostic `d`, Git hunk `h`, function `f`, class/type `c`, parameter `a`. 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…`: `ca` code action, `cr` rename, `cf` format, `cR` project-wide replacement. Remove displaced `gr*` 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; `qo` toggles Dadbod UI.
- `<leader>r…`: global HTTP facilities, including the scratchpad.
- `<leader>n…`: central notes; `nn` captures, `nf` finds notes and `nt` finds tags. Inside a `zk` note, `ni` inserts 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`, `G` and word motions. Do not alias `ge` to 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: `,r` execute query/selection, `,s` save query, `,p` edit bind parameters.
- HTTP: `,r` execute request at cursor, `,a` execute file, `,e` select 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`; add `pkgs.vimPlugins.oil-nvim`.
- Remove `project.nvim`, `direnv.vim` and `mini.indentscope`.
- Start Treesitter once on `FileType` for normal file buffers. Configure text objects and rainbow delimiters explicitly.
- Add `nvim-ts-autotag` for 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`) and `SchemaStore.nvim` for schema-aware YAML and JSON completion and validation. Disable LSP formatting in favor of Conform.
- Remove `oxfmt` as an LSP; invoke Conform's `oxfmt` CLI formatter.
- Remove `typst-preview.nvim`, `websocat` and their `nix-info` settings. 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 `gl` for 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-select` works on first invocation.
- Retain Blink, colorful-menu and friendly-snippets with `<C-j>/<C-k>` selection. Use Blink's native `vim.snippet` support 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-completion` and `blink-compat`; load them on database commands/filetypes only.
- Add wrapper-local `postgresql`, `mariadb` and `mongosh`. 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_URL` as 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.lua` to `http.lua`; load it on `http`/`rest` files and explicit commands only. Disable its global mappings.
- Add wrapper-local `kulala-core`, `kulala-fmt`, `curl`, `jq` and `openssl`. Pass the packaged core path through `nix-info`; disable backend downloads.
- Use the Nix-provided `http` Treesitter grammar; disable Kulala parser downloads/builds.
- Use JetBrains-compatible `.http` files. Commit non-secret `http-client.env.json`; keep credentials in ignored `http-client.private.env.json` or 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 `zk` and `zk-nvim` with Telescope as its picker. Let `zk-nvim` start `zk lsp` only for Markdown buffers inside a notebook; do not also configure `zk` through `nvim-lspconfig`.
- Set `ZK_NOTEBOOK_DIR` to `~/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
1. 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.
2. 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.
3. Re-profile after editing/navigation, LSP and UI changes. Add no size thresholds, caches, debounce wrappers or feature cutoffs without a measured problem.
4. Keep inlay hints, semantic tokens and required servers unless measurements identify a specific offender.
5. 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-build` and no-switch builds for `zenith`, `polaris` and `orion`.
- 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.