Skip to content
NoetherVim is alpha. Breaking changes land without deprecation shims. These docs track main.

Reference

*noethervim.txt* NoetherVim distribution user guide Author: Chiarandini License: MIT
CONTENTS *noethervim-contents* 1. Introduction ........................ |noethervim| 2. Installation ........................ |noethervim-install| 3. Quickstart .......................... |noethervim-quickstart| 4. User configuration .................. |noethervim-user-config| 4.1 Directory structure ............. |noethervim-user-dir| 4.2 Options ......................... |noethervim-user-options| 4.3 Keymaps ......................... |noethervim-user-keymaps| 4.4 Autocommands .................... |noethervim-user-autocmds| 4.5 Highlights ...................... |noethervim-user-highlights| 4.6 Bundle data table ............... |noethervim-user-config-data| 4.7 Plugins ......................... |noethervim-user-plugins| 4.8 LSP servers ..................... |noethervim-user-lsp| 4.9 Imperative overrides ............ |noethervim-user-overrides| 5. Bundles ............................. |noethervim-bundles| 6. Inspection .......................... |noethervim-inspect| 7. Comparison .......................... |noethervim-diff| 8. Toggling ............................ |noethervim-toggle| 9. Keymap namespaces ................... |noethervim-keymaps| 9.1 Filetype keymaps ................ |noethervim-ftplugin| 9.2 Statusline ...................... |noethervim-statusline| 10. Commands ............................ |noethervim-commands| 11. Completion sources .................. |noethervim-completion| 12. Health check ........................ |noethervim-health| 13. FAQ ................................. |noethervim-faq|
*noethervim* *neothervim* NoetherVim is an opinionated Neovim distribution built on lazy.nvim. It ships sensible defaults, 30+ core plugins, and 39 opt-in bundles, with a user configuration system that survives upstream updates. This help file covers in-editor usage and reference. For design rationale, system requirements, and installation, see the README in the project root.
*noethervim-install* *neothervim-install* *noethervim-nvim-appname* *noethervim-uninstall* For initial installation (backup paths, the bootstrap `curl`, NVIM_APPNAME side-by-side trials, and uninstall / reset commands), see the README in the project root. Once Neovim is running, this help file is the reference. Updating: ~ Run inside Neovim. This updates the distro and all plugins: >vim :Lazy update
*noethervim-quickstart* Four commands, in the order worth running them: ~ SearchLeader+? Every active keymap, grouped by namespace. `<CR>` on a line jumps to where it was defined. See |noethervim-guide|. `:checkhealth noethervim` Confirms your terminal, tools and config are in order. See |noethervim-health|. SearchLeader+cb Enable bundles. See |noethervim-bundle-toggle|. SearchLeader+ct Write `lua/user/` config templates. Start with `user/config.example.lua`, which is an annotated list of everything the distribution lets you change about its own behavior. See |noethervim-templates|. Both the bundle and template flows show a diff prompt before writing. The keymap prefixes are the fastest thing to learn: |noethervim-keymaps|. A longer walkthrough for a first session, including the prefixes and the defaults worth reviewing, lives in `docs/onboarding/first-session.md` in the project root; `docs/onboarding/mathematicians.md` continues it for LaTeX work.
*noethervim-user-config* NoetherVim is designed so that the source code is its own documentation. Browse core source files with SearchLeader+cf or `:NoetherVim files`, and live-grep them with SearchLeader+cg. All source files open read-only to prevent accidental edits. See |noethervim-inspect| for the full set of navigation commands. NoetherVim loads user override files automatically after each core module. User files live in your config directory's `lua/user/` folder and are never touched by upstream updates. *noethervim-load-order* Loading happens in three stages. ~ Stage 1 collects plugin specs. Nothing runs yet; your `user/plugins/` files are merged last, so your `opts` win over both the distro's and any bundle's. A bundle sits in between: it can change a core plugin's `opts`, and you can change a bundle's. Stage 2 runs the configuration files, distro first and yours second at every step: noethervim/options.lua -> user/options.lua noethervim/lsp/*.lua -> user/lsp/*.lua noethervim/keymaps.lua \ noethervim/toggles.lua > user/keymaps.lua noethervim/autocmds.lua -> user/autocmds.lua noethervim/highlights.lua \ colorscheme > user/highlights.lua user/overrides/*.lua (last-resort hooks) Stage 3 is deferred until after the screen is drawn: the extra commands, and each lazy-loaded plugin whenever its trigger fires. *noethervim-override-timing* What stage 3 means for overrides: ~ `user/overrides/*.lua` is the last thing in stage 2, not the last thing overall. It wins against everything the distribution sets in stage 2, and against a plugin's `keys` entries: once you remap one of those, it stays remapped whether or not the plugin later loads. It does NOT win against stage 3, which is mainly a lazy-loaded plugin's own `config` function and buffer-local LSP keymaps applied on `LspAttach`. Those run later and overwrite you. For those, override at the layer that runs at the same time: an `LspAttach` autocmd for LSP keymaps, or a `config` wrapper in your `user/plugins/` spec for a plugin's own bindings. ------------------------------------------------------------------------------ 4.1 DIRECTORY STRUCTURE *noethervim-user-dir* ~/.config/nvim/lua/user/ |-- config.lua Config data table (|noethervim-user-config-data|) |-- options.lua vim.o overrides |-- keymaps.lua Keymap overrides and additions |-- autocmds.lua Autocommand additions |-- highlights.lua Highlight overrides (after colorscheme) |-- lsp/ LSP server config overrides | `-- lua_ls.lua e.g., override lua_ls workspace settings |-- plugins/ Personal plugins and distro opts overrides | `-- snacks.lua e.g., override snacks.nvim opts `-- overrides/ Imperative after-hooks (last resort) `-- *.lua Template files are provided in the `templates/user/` directory of the NoetherVim repo. Copy the ones you need to your `lua/user/` and uncomment the relevant lines. ------------------------------------------------------------------------------ 4.2 OPTIONS *noethervim-user-options* Create `lua/user/options.lua`. It runs after `noethervim/options.lua`, so any global value you set overwrites the distro default: >lua -- lua/user/options.lua vim.o.scrolloff = 8 -- default: 4 vim.o.autochdir = true -- default: false *noethervim-option-layers* Note: this holds for "genuinely" global options. Filetype-scoped options set by the ftplugin layer (`formatoptions`, `comments`, `commentstring`, and often `textwidth`, `tabstop`, `shiftwidth`) are only seeded in `options.lua`; an ftplugin runs after it and sets the effective per-buffer value. Neovim's own bundled ftplugins do this too, so setting one of these in `user/options.lua` does not take effect for filetypes whose ftplugin sets it. To change the effective value, use the filetype layer: a `FileType` autocmd in `lua/user/autocmds.lua`, or an `after/ftplugin/<ft>.lua`. For example, to stop comment-leader continuation on <Enter> and o/O across filetypes: >lua vim.api.nvim_create_autocmd("FileType", { callback = function() vim.opt_local.formatoptions:remove({ "r", "o" }) end, }) Notable non-default choices: ~ `swapfile` is OFF; `undofile` is ON. NoetherVim favors persistent undo over swap-based crash recovery; in the author's experience, swap's "file changed on disk" prompts produced more false positives than they prevented lost work. Your mileage may differ; if you want swap back (e.g. on flaky SSH / VM sessions), set `vim.o.swapfile = true` in `user/options.lua`. `autowrite` ON, `autowriteall` OFF, `autoread` ON. Buffer-switch commands (`:next`, `:make`, `<C-^>`, `:buffer`) auto-save unsaved edits; reload commands (`:e`) do not, so running `:e` on a modified buffer errors with `E37: No write since last change`. The `autowriteall=false` guard prevents a specific data-loss scenario: if a buffer is modified while an external process also edits the file on disk, `autowriteall=true` would silently write the stale buffer first when you `:e`, overwriting the external changes, then reloading the result, destroying the external edits with no warning. With `autowriteall=false`, `:e` errors instead, forcing an explicit choice (`:e!` discards buffer edits; `:w!` overwrites disk). Treat `:e` as "load file into buffer," not "reload regardless of state." Flip with `vim.o.autowriteall = true` in `user/options.lua` if you prefer the classic "save everything automatically" behavior. See `noethervim/options.lua` for all defaults. ------------------------------------------------------------------------------ 4.3 KEYMAPS *noethervim-user-keymaps* Create `lua/user/keymaps.lua`. It runs after core keymaps and toggles: >lua -- Override a core keymap (last-write wins) vim.keymap.set("n", ";", ";") -- revert ; to default -- Remove a core keymap entirely vim.keymap.del("n", "<C-a>") -- Add personal keymaps vim.keymap.set("n", "<space>ev", "<cmd>e $MYVIMRC<cr>", { desc = "edit vimrc" }) NoetherVim keymap philosophy: SearchLeader fuzzy navigation / search (default: <Space>) <Leader> global actions <LocalLeader> filetype-specific actions <C-w> window navigation / management [x / ]x prev / next directional navigation [ox / ]ox toggle option on / off *g:mapsearchleader* g:mapsearchleader ~ The key prefix for all search/navigation keymaps. Defaults to `<Space>`. Works like |mapleader|; set it in your init.lua BEFORE `lazy.setup()`: >lua vim.g.mapsearchleader = "<C-s>" -- or ";", "\\", etc. All keymaps in the SearchLeader namespace (find, grep, LSP, git, config, diagnostics, etc.) will use the new prefix. which-key popups and the keymap diff picker automatically reflect the change. When unset, all search keymaps use `<Space>` (the default). ------------------------------------------------------------------------------ 4.4 AUTOCOMMANDS *noethervim-user-autocmds* Create `lua/user/autocmds.lua`. New autocommands are additive. To override a core autocmd, clear its augroup first: >lua -- Clear a core augroup and replace it vim.api.nvim_create_augroup("noethervim_q_close", { clear = true }) -- Then re-create with your preferred list *noethervim-q-close* q-to-close: ~ A bare `q` closes the window in filetypes you cannot usefully edit (help, man, lazy, mason, checkhealth, undotree, diff, and friends). Editable filetypes are excluded on purpose, because `q` there would shadow macro recording, so `oil`, `markdown` and the like are left alone. To ADD a filetype, set `q_close_filetypes` in `lua/user/config.lua`; see |noethervim-user-config-data|. To REMOVE one of the defaults, clear the `noethervim_q_close` augroup and re-create the autocmd from `require("noethervim.util.filetypes").q_close` minus the entries you do not want. `templates/user/autocmds.example.lua` has the full snippet. Core augroups (clear any of these to disable or replace the feature): ~ `noethervim_q_close` q-to-close in non-editing windows `noethervim_profiles` the writing / code filetype profiles `noethervim_lastplace` restore last cursor position on read `noethervim_autoread` reload buffers when focus or edits change `noethervim_term` terminal window tweaks (no numbers, scrolloff) `noethervim_diff_cleanup` auto diffoff when a diff half closes `noethervim_oil_float` Oil dual-pane navigation *noethervim-filetype-profiles* Filetype profiles (writing and code): ~ NoetherVim applies two FileType autocmds that layer opinionated settings on top of the globals: `noethervim_writing` Matches: tex, markdown, norg, text, gitcommit, gitsendemail, mail, rst, typst. Sets `wrap`, `linebreak`, `spell`, `conceallevel = 2`, appends `t` to `formatoptions` (auto-wrap at textwidth), and hides list chars. Also binds `<C-l>` in insert mode for spell-fix. Wrapped lines get a marker column (see |noethervim-wrap-marker|). `noethervim_code` Matches: every filetype NOT in the writing list, structured-text set (json, jsonc, yaml, toml), or special-buffer set (help, man, lspinfo, query, qf, oil, terminal, snacks_*, lazy, mason, checkhealth, notify, Trouble, dap / dap-float / dapui_*). Sets `list` on so trailing whitespace and tabs are visible, and `wrap` off, the counterpart to the writing profile turning it on. `[ow` therefore lasts until the window shows a buffer again, the same as `]ow` in a writing buffer. `formatoptions` omits `t` globally, so typing past textwidth in code does NOT auto-break mid-line; formatters handle line length instead. When `spell_in_code = true` is set in |noethervim-user-config-data|, also turns on `spell`; spellcheck is then scoped to comments and strings via treesitter @spell captures. Use `[os` / `]os` to toggle per-buffer and `zg` to add a word to your spellfile. For CamelCase identifier splitting, append `camel` to `spelloptions` yourself in `lua/user/options.lua`; the distro leaves `spelloptions` untouched. *noethervim-hard-wrap* The writing profile hard wraps: `formatoptions+t` breaks a line at 'textwidth' as you type. That is what you want for prose in version control, but it means a phrase can end up split across two lines, and `/brown fox` then finds nothing when the wrap falls between the words. Enable the `wrapsearch` bundle to make `/` and `?` match across the break; see |noethervim-bundles|. To stop hard wrapping instead, remove `t` from `formatoptions` in an `after/ftplugin/<ft>.lua`. FileType autocmds fire AFTER `ftplugin/*.lua`, so these profiles win over same-named ftplugin settings. To extend the lists (e.g. treat `vimwiki` as writing, or skip the code profile for a custom filetype), set `writing_filetypes` / `non_code_filetypes` in `lua/user/config.lua`; see |noethervim-user-config-data|. Both profiles are applied by a single augroup, `noethervim_profiles`. To override the whole profile, clear it and re-create: >lua vim.api.nvim_create_augroup("noethervim_profiles", { clear = true }) -- then register your own FileType autocmd The global `wrap` default is OFF; the writing profile re-enables it for writing filetypes. Global `list` is OFF; the code profile re-enables it for code filetypes. Listchars are styled in `noethervim/options.lua`. *noethervim-wrap-marker* Wherever 'wrap' is on, a continuation row is marked `↳` in the number column, so a line that ran past the window edge is never mistaken for a line of its own. Writing buffers wrap by default and so always show it; a code buffer shows it from the moment you press `[ow`, and stops when you press `]ow`. The marker column replaces 'number' while it is up, which is why it draws the line numbers itself and why `]on` still turns them off, leaving the marker on its own. It costs no text cell, so continuation rows stay lined up under the text they continue. 'showbreak' is left empty. Turning wrap off in a prose buffer swaps the marker column for the code layout's edge markers: `›` and `‹` where a line runs past the window, which Vim draws only while 'list' is on, so tabs and trailing space become visible with them. Prose that scrolls sideways has left the profile's assumption. A 'statuscolumn' you define in `lua/user/options.lua` is never touched, and switches the marker off for that window: >lua vim.o.statuscolumn = "%l " To go back to an inline 'showbreak' arrow, set both in `lua/user/options.lua`: >lua vim.o.showbreak = "↳" vim.o.statuscolumn = "%s%=%l " Setting 'statuscolumn' to an empty string does NOT opt out: empty means "nothing set yet", so the writing profile fills it in. Give it a value of your own, or clear the `noethervim_profiles` augroup entirely. ------------------------------------------------------------------------------ 4.5 HIGHLIGHTS *noethervim-user-highlights* Create `lua/user/highlights.lua`. This file runs AFTER the colorscheme is applied, so your overrides are not wiped by the theme: >lua vim.api.nvim_set_hl(0, "Normal", { bg = "#1d2021" }) vim.api.nvim_set_hl(0, "SnacksDashboardHeader", { fg = "#89b4fa" }) *noethervim-colorscheme-tweaks* Highlight tweaks: ~ Plain `nvim_set_hl` calls are lost when you switch colorschemes. Use the tweak helper instead; tweaks re-apply automatically on every colorscheme change. This is a core feature and needs no bundle: >lua -- lua/user/highlights.lua require("noethervim.util.colorscheme").tweak({ Comment = { italic = true }, CursorLine = { bg = "#1a1a2e" }, LineNr = { fg = "#555555" }, }) Common highlight groups to tweak: ~ `Normal` Main text and background `Comment` Code comments (try `italic = true`) `CursorLine` Line the cursor is on `LineNr` Line numbers `Visual` Visual selection `Search` Search matches `DiagnosticError` Error diagnostics `DiagnosticWarn` Warning diagnostics `@keyword` Treesitter keywords `@function` Treesitter functions `@string` Treesitter strings ------------------------------------------------------------------------------ 4.6 CONFIG DATA TABLE *noethervim-user-config-data* Create `lua/user/config.lua`. It returns a single table of values that `noethervim.setup()` and individual bundles read at runtime. This is NoetherVim's single user-facing configuration surface: colorscheme, statusline overrides, vault paths, feature flags, filetype lists. Keys you do not set fall back to the distro default; the file is optional and missing keys are not an error. Note: this file is for personal preferences. Arbitrary plugin overrides go in `lua/user/plugins/` instead (see |noethervim-user-plugins|). Which surface to reach for: ~ `config.lua` Values NoetherVim's own code branches on, with no `vim.o` or plugin-`opts` home of their own. `user/options.lua` Anything with a `vim.o` / `vim.opt` equivalent. `user/plugins/` Anything a plugin exposes through `opts`. Plugin options are not repeated in `config.lua`; go straight to the spec. `vim.g.*` Leaders and the dashboard opt-out, set in `init.lua`. These have to be in place before plugins load, which is earlier than `config.lua` is read. >lua -- lua/user/config.lua return { colorscheme = "gruvbox", colorscheme_persistence = false, statusline = { edge_style = "round", colors = {}, }, obsidian_vault = "~/Documents/MyVault/", completion_style = "supertab", blink_conservative_filetypes = { "tex", "latex" }, blink_conservative_size_kb = 500, drop = false, writing_filetypes = { "vimwiki", "quarto" }, non_code_filetypes = { "csv" }, q_close_filetypes = { "oil" }, spell_in_code = true, } Current keys: ~ `colorscheme` Colorscheme applied at startup unless persistence has restored a previously picked one. Default: `"gruvbox"`, which the distribution ships and pins. The dashboard and statusline fallback colors are chosen against it, so a fresh install looks coherent without any configuration. Naming a scheme that is not installed warns on startup and falls back to gruvbox. `colorscheme_persistence` If true, restore the colorscheme last picked with SearchLeader+C, in preference to `colorscheme` above. Set false to make `colorscheme` authoritative. Default: true. `:checkhealth noethervim` reports which of the two set the active scheme. `statusline_enabled` Set to `false` to skip NoetherVim's heirline statusline, tabline and winbar entirely, so a replacement (lualine, mini.statusline) dropped into `lua/user/plugins/` takes over with no conflict. Default: `true`. `statusline.edge_style` Shape of the colored mode block at the left of the statusline. See |noethervim-statusline-edge-style|. Default: `"round"`. `statusline.colors` Heirline color-table overrides. See |noethervim-statusline|. `statusline.extra_right` Extra heirline component specs appended to the right side of the main statusline. `statusline.tab_modified_indicator` Glyph rendered on a tab containing unsaved changes. Default `" ●"`. Common alternatives: `" [+]"`, `" *"`, `" "`, `" ◉"`. `statusline.mode_background` Set to `false` to keep one statusline background in every mode. On by default: the bar shifts to blue in insert, so the mode is legible from the shape of the bar and not only from the mode chip, which still changes colour either way. `statusline.filetype_profile` Set to `true` to render a marker for the filetype profile (writing or code) that claimed the current buffer: a pencil in blue for writing, angle brackets in purple for code, and nothing at all for a buffer neither profile claims. Clicking it reports the profile, the detected filetype, and the live values of the options the profile sets, so per-buffer toggles (`[ow`, `]os`, ...) show up too. `<C-w>sf` turns it on and off in a running session; this sets where it starts. See |noethervim-filetype-profiles|. Default: `false`. `statusline.git_click` Function called when the git branch/status block is clicked. The default opens lazygit when it is on PATH and falls back to snacks' git-status picker otherwise; lazygit is optional, not a prerequisite. `obsidian_vault` Path to your Obsidian vault. Read by the `obsidian` bundle. Default: `~/obsidian/`. `completion_style` Tab-key philosophy for the completion menu: `"snippet"` (default, Tab is reserved for LuaSnip jumps), `"supertab"` (Tab accepts visible menu item, falls back to snippet jump), or `"navigate"` (Tab cycles the menu without accepting). `blink_conservative_filetypes` Filetypes where keyword-triggered completion is suppressed. `<C-Space>` and LSP trigger characters (e.g. `\` in LaTeX) still work. Default: `{ "tex", "latex" }`. `blink_conservative_size_kb` Files larger than this (in KB) also get conservative mode, regardless of filetype. Default: `500`. `drop` Set to `false` to disable the seasonal `drop.nvim` animations from the `eye-candy` bundle. Default: enabled. `writing_filetypes` Extra filetypes added to the writing profile (wrap, linebreak, spell, conceallevel=2, formatoptions+t). Additive: defaults stay in place. See |noethervim-filetype-profiles|. Default: `{}`. `non_code_filetypes` Extra filetypes that skip both the writing and code profiles; their own ftplugin / buffer settings take over. Additive: defaults stay in place. Default: `{}`. `q_close_filetypes` Extra filetypes where a bare `q` closes the window. Additive: defaults stay in place. `{ "oil" }` is the common addition. See |noethervim-q-close|. Default: `{}`. `spell_in_code` If `true`, the code profile turns on `spell`. Spellcheck is scoped to comments and strings via treesitter `@spell` captures; identifiers are not flagged. `spelloptions` is left untouched; add `camel` yourself if you want CamelCase splitting. See |noethervim-filetype-profiles|. Default: `false`. `toggle_feedback` Channel for the confirmation message emitted when a bracket-prefix toggle (`[ow`, `]os`, etc.) fires. `"notify"` (default) routes through `vim.notify` and is picked up as a snacks toast. `"echo"` uses `nvim_echo` for the classic one-line cmdline message (still recorded in `:messages`). `"off"` suppresses the message entirely. See `templates/user/config.example.lua` for the annotated template. ------------------------------------------------------------------------------ 4.7 PLUGINS *noethervim-user-plugins* Create files in `lua/user/plugins/`. Each file returns a lazy.nvim spec table. This is where you both add new plugins and adjust the ones the distro already ships. To add a new plugin, write a normal lazy.nvim spec: >lua -- lua/user/plugins/pantran.lua return { { "potamides/pantran.nvim", cmd = "Pantran", opts = { default_engine = "argos" }, }, } To override a plugin the distro already loads, use the EXACT same repository string, and lazy.nvim deep-merges `opts` tables automatically: >lua -- lua/user/plugins/snacks.lua return { { "folke/snacks.nvim", opts = { picker = { layout = { preset = "vertical" } }, }, }, } You only need to specify the fields you want to change. Core defaults fill in everything else. To add extra keymaps to an existing plugin: >lua { "folke/snacks.nvim", keys = { { "<space>fg", function() Snacks.picker.grep() end, desc = "live grep" }, }, } lazy.nvim unions `keys`, `cmd`, `event`, and `ft` tables from all specs for the same plugin. If you prefer keeping all keymaps in one place, set them in `lua/user/keymaps.lua` instead. That file loads early, before lazy-loaded plugins, so map to a `<cmd>...<cr>` (which triggers the load) or wrap the call, (function() ... end) rather than referencing a plugin function that may not be loaded yet. Array-valued opts: ~ *noethervim-user-plugins-arrays* `vim.tbl_deep_extend` (the function lazy.nvim uses to merge `opts`) does NOT extend sequential arrays; it REPLACES them. Keys like `ensure_installed` (Mason / treesitter), `formatters_by_ft.<ft>` (conform), and `linters_by_ft.<ft>` (nvim-lint) all behave this way. Two patterns to extend an array key without replacing it: >lua -- Pattern 1: list the full set you want. -- (you must track upstream additions yourself.) { "neovim/nvim-lspconfig", opts = { ensure_installed = { "lua_ls", "basedpyright", "gopls" } } } -- Pattern 2: opts as a function, mutating in place. -- Picks up upstream additions automatically; preferred when the -- upstream list is long or distro-maintained. { "neovim/nvim-lspconfig", opts = function(_, opts) opts.ensure_installed = opts.ensure_installed or {} table.insert(opts.ensure_installed, "gopls") end } Pattern 2 receives the resolved upstream `opts` table as the second argument. Mutating it (rather than returning a new table) lets you apend your entries at runtime. NOTE: if you specify `config = function()` in your override spec, it REPLACES the upstream config function entirely. Only do this as a last resort. See `templates/user/plugins/example.lua` for more examples. ------------------------------------------------------------------------------ 4.8 LSP SERVERS *noethervim-user-lsp* Create files in `lua/user/lsp/`. This is where you both add new server configs and adjust the ones the distro already ships (see `lua/noethervim/lsp/` for the bundled list). To add a new server, call `vim.lsp.config()` and `vim.lsp.enable()`: >lua -- lua/user/lsp/gleam.lua vim.lsp.config("gleam", { cmd = { "gleam", "lsp" }, filetypes = { "gleam" }, root_markers = { "gleam.toml", ".git" }, }) vim.lsp.enable("gleam") To adjust a server the distro already configures, call `vim.lsp.config()` again with the same name, and Neovim deep-merges the tables: >lua -- lua/user/lsp/lua_ls.lua vim.lsp.config("lua_ls", { settings = { Lua = { workspace = { library = { "/my/custom/lib" } }, }, }, }) -- No need to call vim.lsp.enable() again. ------------------------------------------------------------------------------ 4.9 IMPERATIVE OVERRIDES *noethervim-user-overrides* Create files in `lua/user/overrides/`. These run at the very end of `noethervim.setup()`, after everything else has loaded. Use this only when opts merging and module hooks are insufficient: >lua -- lua/user/overrides/custom-diagnostics.lua vim.diagnostic.config({ virtual_text = { prefix = ">>" } }) "After everything else" means everything in stage 2 of |noethervim-load-order|. Overrides also beat plugin `keys` entries: once you remap one, lazy.nvim leaves it alone, before and after the plugin loads. They do NOT beat stage 3: a lazy-loaded plugin's own `config` function, or buffer-local LSP keymaps applied on `LspAttach`. Those run later and win. For a plugin's own bindings use |noethervim-user-plugins| (spec merging), which lazy.nvim times correctly; for LSP keymaps use your own `LspAttach` autocmd. See |noethervim-override-timing|.
*noethervim-bundles* *neothervim-bundles* Bundles are opt-in plugin groups. They live under `lua/noethervim/bundles/<category>/<name>.lua`; if you are using the init.lua template, enable them by uncommenting the corresponding `import` line in your `init.lua`: >lua { import = "noethervim.bundles.languages.latex" }, { import = "noethervim.bundles.tools.debug" }, *noethervim-bundle-toggle* You can also toggle bundles from the picker without editing `init.lua` by hand. Open `:NoetherVim bundles` (or SearchLeader+cb) and: <C-y> Enable bundle. Tries to uncomment its `import` line in `init.lua`; if the line is missing, proposes inserting a new one at the end of the `spec = { ... }` table. A floating diff window shows the exact change before any file is written, with [y]es / [n]o confirmation. <C-x> Disable bundle. Re-comments the active `import` line, again behind a diff prompt. <CR> Open the bundle source file (unchanged). <C-o> Seed a user override for this bundle in `user/plugins/<name>.lua`, listing the upstream spec's repo strings as commented stubs. Same as opening the source and running `:NoetherVim override`. See |noethervim-commands|. <F1> List every key the picker binds. Press <F1> or <Esc> to dismiss it; <Esc> only closes the picker itself once the list is gone. Works in every snacks picker, not just this one; see |noethervim-picker-help|. If `init.lua` has been restructured beyond recognition (multi-file specs, no detectable `spec = {` block), <C-y> falls back to copying the import line to the `+` and `"` registers and notifies you to paste it manually. After accepting a change, restart Neovim to load the bundle (or use `:Lazy reload` if you know what you are doing). *noethervim-stale-imports* If an enabled bundle has been removed or renamed upstream, lazy.nvim emits a `No specs found for module "..."` error during spec resolution. The stock `init.lua.example` bootstrap calls `require("noethervim.util").buffer_notify()` immediately before `lazy.setup(...)` so these errors surface as `snacks.notifier` notifications after VimEnter rather than landing on the cmdline as ErrorMsg. Error-level startup notifications stay on screen until dismissed, and say so: the toast carries `<Esc> dismiss` and, for a stale import, the key that opens `init.lua` (read from your keymaps, so a rebind shows yours). `:checkhealth noethervim` reports the same errors under "Spec errors", along with the current path when a bundle was renamed rather than removed, and flags an override left without a spec to extend under "Stranded overrides". Available bundles (grouped by category): ~ languages/ ~ `c-cpp` clangd for C and C++, plus c/cpp treesitter parsers. Requires: compile_commands.json for good results. `rust` rustaceanvim: enhanced Rust beyond rust-analyzer. `go` go.nvim: Go test gen, struct tags, interface impl. `java` nvim-jdtls: proper Java LSP (requires special setup). `python` venv-selector.nvim: virtual environment switching. `latex` VimTeX, img-clip, bibtex picker, LaTeX textobjects. `<LocalLeader>lf` makes the PDF viewer follow the cursor until pressed again. Some of its snippets expect a particular preamble; which ones, and what it looks like, are written up at https://nathanaelsrawley.com/noethervim/guides/latex-setup/ Requires: latexmk, TeX distribution. For citations, see the `zotero` bundle under writing/. `web-dev` JS/TS template string + inline color preview. tools/ ~ `debug` nvim-dap + UI. Language-agnostic: the Neovim Lua adapter is built in, and the Python, Go, JS/TS and C/C++ adapters come from their own language bundles when both bundles are enabled. `test` neotest test runner. Language-agnostic in the same way: Python, Go, Rust, Java, Jest and Vitest adapters come from their own language bundles when both bundles are enabled. See |noethervim-test-adapters|. `repl` iron.nvim interactive REPL. `task-runner` overseer.nvim + compiler.nvim. `database` vim-dadbod + UI + SQL completion via blink.cmp. `http` kulala.nvim HTTP/REST/gRPC/GraphQL client. `git` Fugitive, Flog, Fugit2, diffview, git-conflict. `ai` CodeCompanion (Anthropic, OpenAI, Gemini, Ollama, …). Default: Anthropic. Override adapter in user/plugins/. Requires: API key env var for cloud providers. `refactoring` Extract function/variable/block. `octo` GitHub PRs / issues / reviews via the `gh` CLI. `<C-w>O` opens the PR list. Requires `gh` (and a one-time `gh auth login`). `nvim-dev` Neovim config development: :StartupTime, :Luapad, vimls LSP for .vim files. navigation/ ~ `harpoon` Fast per-project file marks. `flash` Enhanced f/t and / motions with jump labels. Note: the flash bundle remaps `S` in normal mode from the core global-substitute shortcut to a flash jump. The core `S` behavior remains accessible via `:%s/`. `projects` Project switcher via snacks.picker. `editing-extras` Argument marking (argmark) + decorative comment boxes. `yanky` Yank ring: cycle through paste history with `<C-p>`/`<C-n>` after a paste; SearchLeader+y opens a fuzzy picker over the full history. writing/ ~ `markdown` render-markdown, markdown-preview, tables, math, img-clip (image paste from clipboard). `obsidian` Obsidian vault integration (obsidian.nvim). Recommended: also enable the markdown bundle. Configure vault path in lua/user/config.lua. `neorg` .norg wiki / note-taking. `zotero` Citation picker over a local Zotero library, for tex, markdown, quarto, typst, org and asciidoc. `<LocalLeader>z` opens it. Requires: Zotero, sqlite3. `wrapsearch` Make `/` and `?` match across hard-wrapped lines, so a phrase split by a wrap is still findable. The writing profile hard-wraps by default, so this is its counterpart. `g/` searches verbatim for one search. See |noethervim-filetype-profiles|. `presentation` Slide presentations (presenting.nvim) + showkeys. terminal/ ~ `better-term` Named terminal windows. `tmux` Tmux window naming. Note: vim-tmux-navigator remaps `<C-h/j/k/l>` to navigate both Neovim splits and tmux panes seamlessly. This overrides the core window-navigation shortcuts (which do the same thing for Neovim splits only). `remote-dev` distant.nvim SSH editing. ui/ ~ `colorscheme` Nine themes beyond the gruvbox core already ships. Mainstream picks: catppuccin, tokyonight, rose-pine, kanagawa. Long tail: onedark, nord, everforest, nightfox, solarized. All lazy; only the active one loads, but the nine cost ~18MB on disk, which is why they are opt-in. Switching themes (SearchLeader+C), persistence and highlight tweaks are core features and work without this bundle. See |noethervim-colorscheme-tweaks|. `eye-candy` Animations, scrollbar, code block visualizer. `minimap` Sidebar minimap with diagnostics and git signs. `helpview` Rendered :help pages. `tableaux` noethervim-tableaux: animated mathematical dashboard scenes (Sieve, Collatz, Lorenz, Game of Life, …). practice/ ~ `training` Vim motion and typing practice (vim-be-good, speedtyper, typr). `hardtime` Motion habit trainer. Each bundle file contains full documentation (plugins, keymaps, commands, and requirements) in its header comment. Browse them with `:NoetherVim bundles` or SearchLeader+cb. *noethervim-test-adapters* Test bundle: ~ Keymaps live under `<Leader>t`, the sibling of `<Leader>d` for debug: ~ `<Leader>tt` run the nearest test `<Leader>tf` run the current file `<Leader>ta` run every test under the working directory `<Leader>tl` re-run the last test `<Leader>tq` stop the running test `<Leader>ts` toggle the summary tree `<Leader>to` show output for the nearest test `<Leader>tO` toggle the output panel `<Leader>tw` toggle watch mode on the current file `<Leader>td` debug the nearest test (needs the `debug` bundle too) Adapters: ~ The `test` bundle installs neotest and no adapters. An adapter arrives from its language bundle, and only when both bundles are enabled: `languages/python` pytest / unittest, via neotest-python `languages/go` go test, via neotest-golang `languages/rust` cargo test, via rustaceanvim's own adapter `languages/java` JUnit, via neotest-java `languages/web-dev` Jest and Vitest So `test` on its own gives you the UI and nothing to run: `:Neotest run` finds no tests. The `debug` bundle is arranged the same way. For a language with no bundle, append to `adapters` from `user/plugins/`, using the function form of `opts`. An adapter is an object its plugin constructs, so the `require` has to run when neotest loads, not while specs are being collected: >lua { "nvim-neotest/neotest", dependencies = { "some/neotest-adapter" }, opts = function(_, opts) table.insert(opts.adapters, require("neotest-adapter")({})) end } *noethervim-inline-math* Inline math (markdown bundle): ~ The `markdown` bundle typesets `$...$` and `$$...$$` and draws the result over the source, in markdown buffers. Each equation is set by pdflatex or tectonic and converted by ImageMagick, then cached, so only its first appearance pays for the run. It needs a terminal that speaks the kitty graphics protocol (kitty, WezTerm, Ghostty). Move the cursor onto an equation's line to see the source again, unless 'concealcursor' covers the mode you are in. `[om` Render math in this buffer. In a tex or typst buffer this is the opt-in: the bundle claims markdown and leaves other filetypes to the PDF or preview they already have. `]om` Stop rendering math, in every buffer. Reach for this in a document where typesetting each equation costs more than reading the source. Configuring noethervim-tex (latex bundle): ~ The `latex` bundle includes `noethervim-tex`, which provides LaTeX snippets, treesitter navigation, preamble and figure completion sources, theorem-tag colouring, and a spell dictionary of common mathematical terms. Configure it via opts override in `user/plugins/`: >lua { "Chiarandini/NoetherVim-tex", opts = { -- Both off by default: `conventions` needs preamble declarations -- yours may not have, `acronyms` is one writer's vocabulary. snippets = { conventions = true, acronyms = true }, preamble = { folders = { "preamble", "~/my/preambles/" } }, extra_snippet_paths = { "~/shared-snippets/" }, textobjects = false, } } See `:help noethervim-tex` for the full option list. *noethervim-snippet-stop* *noethervim-spell-add* Adding a word: ~ `zg` adds the word under the cursor to your spellfile, and with it the possessive, which Vim does not derive for a lowercase entry and which you would otherwise come back and add by hand. `zG` does the same for this session only. Both report what they wrote, and adding a word twice does nothing. Vim already accepts the capitalised and all-caps forms of a lowercase entry, so those are never written. Nor is the lowercase form of a capitalised one: that would stop a proper noun being flagged in lowercase, and names are most of what gets added by hand. `<C-l>` in insert mode replaces the nearest misspelling behind the cursor with the first suggestion, leaving the cursor where you were typing. Getting out of a snippet: ~ `<C-u>` Stop every snippet in the buffer (insert, select) `<Leader>U` Same, from normal mode `:LuaSnipStop` Same, as a command `<Leader>u` Unlink only the snippet you are in `<C-u>` only stops snippets when there are snippets to stop. With none active and no marks left behind, it is Vim's own "delete what you have typed on this line", untouched. Stopping is not the same as unlinking. Unlinking drops one snippet and hands the cursor to its neighbor; LuaSnip's extmarks stay behind, so the buffer can end up with no active snippet but still carry marks that highlight text and interfere with later expansions. Stopping unlinks everything and clears those marks. Reach for it when a buffer still behaves as though a snippet were active. Buffer text is never touched, and snippets expand normally afterwards. *noethervim-snippet-reenter* Getting back into one: ~ `<Leader>j` Jump back into the snippet under the cursor Tabbing past the last placeholder finishes a snippet, and normally that is the end of it. `<Leader>j` reopens one you have already left, so a placeholder you filled in wrongly can be filled in again without expanding the snippet a second time. Put the cursor anywhere in the snippet's text and press it; `<Tab>` and `<S-Tab>` then move between its placeholders as they did the first time. A snippet nested inside another is reached the same way. Snippets stay reachable for as long as the buffer is open. If the cursor is not in one, `<Leader>j` says so and does nothing. *noethervim-snippets* Writing custom snippets: ~ LuaSnip loads user snippets from `stdpath("config")/LuaSnip/<filetype>/`. Create a directory for the target filetype (e.g. `LuaSnip/tex/`, `LuaSnip/python/`) and add `.lua` files inside it; each file should return a snippet list. LuaSnip auto-loads them alongside any plugin-provided snippets. Saving a snippet file puts it to work straight away. That holds for a file you have just created for a filetype you had no snippets for before, and it holds in every other Neovim you have open at the time. Nothing needs restarting. SearchLeader+es (`:LuaSnipEdit`) jumps to a snippet file for the current filetype. Every registered file is listed, yours and the ones plugins ship, so the picker doubles as a way to read how a bundled snippet is built. Entries read as owner and filename, for example: ~/.config/nvim · preamble.lua NoetherVim-Tex · commands.lua The filetype was chosen a prompt earlier, so the `LuaSnip/<ft>/` segment every entry shares is left out. The owner is the lazy.nvim plugin name, or the tree the file came from when it is one of yours. A file owned by a plugin opens with 'readonly' set: a snippet saved there is lost on the next |:Lazy| update, and in a `dev` checkout it dirties that repository and then loads twice alongside your own copy. Use `:w!` when you do mean to edit a plugin you maintain. When you have no snippet file of your own for the filetype, the list also offers one to create, marked `(new)`: ~/.config/nvim · rust.lua (new) Choosing it writes a skeleton and opens it. Do not empty the file: a Lua file that returns nil errors the next time LuaSnip loads that filetype. Writing the new file registers it immediately, so its snippets work in the same session without a restart. In a buffer with no 'filetype' there is nothing filetype-specific to edit, so the prompt offers only `all` and warns. Neovim maps `.rs` to `rust`, not `.rust`, so a scratch file named `tmp.rust` has no filetype at all; `:setfiletype` fixes it. For LaTeX snippets, `noethervim-tex` exposes helper functions: >lua local helper = require("noethervim-tex").luasnip_helper local tex_utils = helper.tex_utils -- in_mathzone, in_text, etc. local get_visual = helper.get_visual_node
*noethervim-inspect* Browse and search NoetherVim source code from within the editor. All commands are available as `:NoetherVim <subcommand>` and most have keymaps under the SearchLeader+c prefix (default: `<Space>c`; see |g:mapsearchleader|). Source files open as non-modifiable to prevent accidental edits to the distribution. SearchLeader+cf `:NoetherVim files` Browse NoetherVim source SearchLeader+cg `:NoetherVim grep` Live grep NoetherVim source SearchLeader+cu (snacks) Browse user config files SearchLeader+cb `:NoetherVim bundles` Browse bundle source files SearchLeader+ct `:NoetherVim templates` Write user-config templates `<Leader>e` `:NoetherVim override` Edit/create user override SearchLeader+cL (snacks) Lazy plugin list SearchLeader+? `:NoetherVim keymap-guide` Keymap namespace reference `<Leader>i` (inspect) Open init.lua for editing Run `:NoetherVim status` to see which user override files are active. *noethervim-templates* Templates: ~ SearchLeader+ct `:NoetherVim templates` Merge the bundled `templates/user/<name>.example.lua` files into your config as `lua/user/<name>.lua`. The picker shows each template with `[new]` or `[exists]` status; press `<C-y>` on a row to write it. A floating diff window shows the exact change before any file is created or overwritten: `y` or `<CR>` accepts, `n` rejects, `q` or `<Esc>` cancels. On accept the new file opens for editing, which is what you wanted the template for. `<CR>` in the picker opens the source template read-only, so you can read one without copying anything. If the destination directory does not exist (e.g. `lua/user/plugins/`), it is created on accept. *noethervim-guide* Keymap namespace guide: ~ SearchLeader+? `:NoetherVim keymap-guide` Opens a reference buffer showing all active keymaps organized by NoetherVim's namespace philosophy: SearchLeader fuzzy navigation and search (sub-grouped by topic) `[` / `]` directional navigation and option toggles (paired) `<C-w>` window and panel management `g` goto and LSP actions `Z` buffer management `<Leader>` global actions Normal / Insert / Visual / Command-line mode sections The guide is dynamic: it reads the current keymap state, so it reflects active bundles, user additions, and buffer-local LSP keymaps. Press `q` to close. Press `<CR>` on any keymap line to jump to its source definition in the distribution or user config. *noethervim-picker-help* Picker key list: ~ Every snacks picker binds `<F1>` to a list of the keys it defines, in both normal and insert mode, and says so in its title. Snacks also binds `?`, but only in normal mode; since pickers open with the prompt in insert mode, `?` typed there goes into the query instead. Where `<CR>` does something other than open what you picked, the prompt says which action it is -- `copy` in the registers picker, `git checkout` in the branch picker. Pickers that open a file or jump to a line leave the prompt bare, since naming the obvious would bury the ones worth reading. Dismiss the list with `<F1>` again or with `<Esc>`. `<Esc>` closes the picker itself only once the list is gone, so with the list open it takes two presses to leave. The list shows normal-mode and insert-mode mappings together, and where a key exists in both the normal-mode entry is the one displayed. Entries like `q`, `j`, `k`, `gg`, `G`, `/` and `<C-w>H/J/K/L` therefore apply to the results window rather than the prompt; reach it with `<A-w>`. *noethervim-picker-notifications* Notification history: ~ SearchLeader+fn lists past notifications. `<CR>` opens the one under the cursor in a float: the full message, wrapped, sized to fit, bordered in the colour of its level. `q` closes it. The float's filetype is `noethervim-notification`, so it answers to the q-to-close set (|noethervim-q-close|) and skips both filetype profiles.
*noethervim-diff* Compare your user overrides against NoetherVim defaults. SearchLeader+ck `:NoetherVim diff keymaps` Smart picker showing all keymaps annotated with: `[CORE]`: unchanged NoetherVim default `[USER]`: keymap added by your user/keymaps.lua `[OVERRIDE]`: core keymap you changed `[DELETED]`: core keymap you removed A second column names the bundle a keymap came from (`latex`, `debug`, `yanky`, ...) and stays blank for core and user keymaps. It disappears entirely when no bundle is enabled. The bundle name is searchable, so typing `latex` narrows the list to that bundle. `<CR>` opens the file and line that defined the key. Keymaps set by third-party plugins (surround, mini.ai, marks, ...) are hidden by default, since they are not NoetherVim's to explain; `<C-x>` toggles them in and out. On one of those, `<CR>` opens the spec file that installs the plugin rather than the plugin's own source: the spec is where you add `opts` or `keys` to change or disable the key, and the plugin's tree is overwritten on update. When the plugin cannot be identified, `<CR>` says so instead of guessing. Neovim's own defaults and `<Plug>` handles are never listed. Use SearchLeader+fk for a plain search over every key currently mapped. Buffer-local keymaps, marked `(buffer)`, are those of the buffer the picker was opened from, so LSP keymaps appear only when a server is attached there. Plugins that have not loaded yet have not registered their keymaps, so the list grows as a session goes on. SearchLeader+co `:NoetherVim diff options` Smart picker showing all tracked options (the set of options NoetherVim explicitly configures) annotated with: `[CORE]`: unchanged NoetherVim default `[OVERRIDE]`: option you changed in user/options.lua SearchLeader+ca `:NoetherVim diff autocmds` Smart picker showing every augroup NoetherVim declares, annotated with: `[CORE]`: unchanged NoetherVim default `[OVERRIDE]`: augroup you replaced from `lua/user/` `[CLEARED]`: augroup you emptied and did not refill `[INACTIVE]`: not in use this session, because the bundle is off or the plugin has not loaded. Expected, not a problem. `[USER]`: augroup you added Overriding an autocommand means clearing its augroup and re-registering, so this is the view that tells you whether that worked. `<CR>` opens the file and line that registered the handler. SearchLeader+cd `:NoetherVim diff` Smart picker listing all NoetherVim modules across Core, Plugin, Bundle, and LSP categories. Each entry shows whether a user override exists. Selecting a module opens the upstream file (readonly) and user override in a side-by-side vertical split. Also accepts a direct argument: `:NoetherVim diff snacks`
*noethervim-toggle* Disable all user overrides for debugging. Environment variable (before starting Neovim): >bash NOETHERVIM_NO_USER=1 nvim Vim global (set in init.lua before lazy.setup): >lua vim.g.noethervim_no_user = true When active, all `user/` module hooks, LSP overrides, and `user/overrides/` files are skipped. Plugin spec merging from `user/plugins/` is also skipped (via the conditional import in init.lua.example). This is NOT a runtime toggle; restart Neovim to apply. Check current status: `:NoetherVim status` *noethervim-dashboard* Disabling the startup dashboard: ~ The Snacks dashboard shown on empty-arg startup (`nvim` with no file) can be turned off with a single flag in your init.lua, set before `lazy.setup`: >lua vim.g.noethervim_dashboard = false With the flag set, `nvim` opens straight into an empty buffer. Set it to `true` or leave unset to keep the dashboard (default). *noethervim-auto-install* Declining toolchain auto-install: ~ Enabling a bundle is how you ask for the tools it drives. A language bundle names its language server, formatter, linter and debug adapter, and NoetherVim fetches through Mason whatever is missing, the same way language servers have always arrived. Enabling `languages/rust` together with `tools/debug` gets you codelldb; `languages/python` gets you black. To decline, set this in your init.lua before `lazy.setup`: >lua vim.g.noethervim_auto_install = false Nothing is then fetched on your behalf. Bundles still declare what they need, `:checkhealth noethervim` still names anything missing, and `:Mason` still installs on request. Set this when a toolchain is managed outside the editor, by Nix, by system packages, or by a project-local environment, where a second copy under Mason is at best redundant and at worst a version you did not choose. Language servers are unaffected: they arrive through `ensure_installed` and are not gated by this flag.
*noethervim-keymaps* *neothervim-keymaps* NoetherVim groups keymaps by prefix. Press any prefix and wait for which-key to show available actions, or use SearchLeader+fk to search all keymaps interactively. This section is the complete list. The bindings that replaced a Vim default are argued for individually, with a standalone snippet for each, at: https://nathanaelsrawley.com/noethervim/guides/notable-keybindings/ The which-key popup appears after a 1500 ms delay by default. To change this, add to `lua/user/plugins/whichkey.lua`: >lua return { { "folke/which-key.nvim", -- Remember that setting `config` would override, not extend, -- the behavior, and passing `opts= {...}` would override the -- default optoins opts = function(_, opts) vim.o.timeoutlen = 500 opts.delay = 500 return opts end, }, } Prefixes: ~ *noethervim-space* *noethervim-mapsearchleader* `SearchLeader` Search & navigation (default: `<Space>`) f(ind) g(rep) G(it) l(sp) d(iagnostics) c(onfig) D(ebug) e(ditor files) o(bsidian) w(iki) Configurable via |g:mapsearchleader|. *noethervim-leader* `<Leader>` (\) Global actions y/Y/p/P (clipboard) f(ormat) h(unk) d(ebug) t(est) r(un/REPL) R(efactor) m(inimap) b(ox) a(i) z(en) H(arpoon add) *noethervim-localleader* `<LocalLeader>` (,) Filetype-specific actions LaTeX: vimtex commands, image paste Neorg: wiki navigation *noethervim-ctrl-w* `<C-w>` Windows & panels l(azy) s(tatusline) t(erminal) F(ugit2) Q (Trouble diagnostics) <C-q> (quickfix) [d / ]d (diffview) <C-h> (harpoon) <C-e> (explorer) <C-o> (Oil) *noethervim-g* `g` LSP & go-to gd(efinition) gD(eclaration) gR(eferences float) gt(ype def) gi(mplementation) gs(ignature) gl(diagnostics) go(utline) gy(ank type at cursor) gr* = Neovim 0.12 defaults (grr/grn/gra…) gz / gZ (capitalize, see |noethervim-case|) SearchLeader+li / +lO (call hierarchy in/out) *noethervim-brackets* `[x` / `]x` Directional navigation d(iagnostic) t(ab) b(uffer) f(ile) q(uickfix / Trouble when open) T(odo) e(xchange line) <Space>(blank line) *noethervim-option-toggles* `[ox` / `]ox` Toggle options w(rap) s(pell) n(umbers) r(elative) h(lsearch) L(SP) b(ackground) c(ursorline) l(ist) t(extwidth) i(gnorecase) I(lluminate) T(reeSitter) D(irty whitespace/tidy) G(uide column/deadcolumn) G is off by default; toggle persists across sessions (stored in stdpath("state")). `[oC`/`]oC` toggle blink.cmp completion on/off `[om`/`]om` toggle inline math (|noethervim-inline-math|) *noethervim-Z* `Z` Closing things, arranged as a grid. Rows say how much you are willing to lose; columns say what you are closing. Vim's own `ZZ` and `ZQ` are the bottom and top of the first column. this window everything this buffer force `ZQ` :q! `ZW` :qa! `ZE` :bd! refuse dirty `ZA` :q `ZS` :qa `ZD` :bd save first `ZZ` :x `ZX` :wa|qa! `ZC` :w|bd `ZQ` and `ZZ` are Vim defaults, listed for the shape. `ZW` is the "kill nvim" key; `ZX` is the one that saves every buffer it can before forcing out. `ZR` (scratch sweep) deletes unnamed scratch buffers. *noethervim-running* Running code: ~ `<Leader>rf` Run the current file `<Leader>rp` Run the project around it `<Leader>rc` Run the current file in a floating window `<Leader>rT` Send the run command to a betterTerm terminal What each language runs is one table, so the four agree. `rf` and `rp` need the task-runner bundle; `rc` and `rT` are core, and `rT` also needs the better-term bundle. The difference between `rf` and `rp` is the project: in a Cargo crate `rf` runs `cargo run`, and on a loose .rs file it compiles that file alone. `rp` runs the project's own entry point (cargo, go.mod, npm, Maven, make) and does nothing when there is no project around the buffer. Interpreted languages have `rf` only. Version managers are honored: an interpreter is resolved through mise, asdf, pyenv, rbenv, nodenv or goenv for the buffer's directory before it runs, so a project pinned to an older Python gets that one. Other notable keymaps: ~ Normal: ~ `<C-a>` / `<C-x>` Enhanced increment/decrement (numbers, booleans, dates, operators, semver, via dial.nvim) `g<C-a>`/`g<C-x>` Sequential increment/decrement in visual selection `<C-/>` Toggle comment (builtin gc/gcc, all modes) `<C-S-r>` Comment lines and paste uncommented copy below `gz` / `gZ` Capitalize (see |noethervim-case|) Visual: ~ `gC` Invert comment per-line. Unlike `gc` (which picks one direction for the whole range based on the majority state), `gC` toggles each selected line independently: so a mixed selection ends up with every line in the *opposite* state. `s` Substitute without polluting register `S` Global search/replace (:%s/) `;` Command-line (replaces :) `n` / `N` Consistent search direction: `n` always forwards, `N` always backwards `-` Highlight word under cursor + count `L` Fold-peek (nvim-ufo); falls through to the default L motion (jump to last visible line) when not on a closed fold. `<F2>` Rename (with count, LSP) Visual: ~ `Y` Yank selection to clipboard `P` Paste clipboard (keep registers) `p` Paste over (keep register) `il` / `al` Line text objects (|noethervim-line-objects|) `<Down>` / `<Up>` Move block Insert: ~ `<M-BS>` Delete word backward `<C-v>` Paste from clipboard `<C-=>` Expression register Command-line: ~ `<C-l>` Insert cwd `<C-y>` Yank cmdline to clipboard `<C-o>` Redirect output to buffer *noethervim-line-objects* Line text objects ~ Vim has no text object for a line. `V` and `yy` are linewise, so what they put in a register comes back as a whole new line, and there is no operator target that stops at the line break. `al` The whole line, up to but not including the break. `il` The line without its indentation: first non-blank through last non-blank. Both take an operator and both work on a Visual selection. `yal` Yank a line as text, to drop inside another one with `p` instead of below it. `dal` Empty a line and keep it: the line stays, its contents go. `dil` The same, with the indentation left in place, ready to type the line again. `al` does nothing on an empty line, where the break is all there is. Both take a register prefix, so `"_dal` clears a line without disturbing the unnamed register and `"ayal` puts one in register `a`. *noethervim-case* Capitalization operators ~ Vim ships three case operators -- `gu`, `gU` and `g~` -- and no way to raise only the leading letter. NoetherVim adds the missing pair: `gz`{motion} Title Case: raise the first letter of every word. `gZ`{motion} Sentence case: raise the first letter of the region. Both work in Visual mode on the selection, take a count, and repeat with `.`. `gziw`, `gzip`, `gz$` and `gZ3w` all do what they read like. Title Case has the unshifted key because it is the one with no short manual equivalent. Raising a single leading letter is already two keys -- `~` on the character, or `guiw~` for a word -- while Title Casing a run by hand is one `~` per word. Both are conservative: every character after the first is left exactly as typed. `gZ` on `the PDE of LaTeX notes` gives `The PDE of LaTeX notes`, not `The pde of latex notes` -- which is what the usual `guiw~` workaround would leave you with. `gz` leaves the short function words lowercase unless they fall first or last in the run, so `of mice and men` becomes `Of Mice and Men`. The list lives in `util/case.lua` and can be replaced from `lua/user/`: >lua -- lua/user/overrides/case.lua local case = require("noethervim.util.case") case.minor_words = {} -- capitalize every word case.minor_words = { ["über"] = true } -- or supply your own set In tex buffers a leading control sequence is stepped over rather than walked into, so `gZ` on `\emph{the cat}` gives `\emph{The cat}` and not `\Emph{the cat}`. An accent macro is followed through -- `\'etale` becomes `\'Etale` -- and a bare macro is treated as the word it renders as, so `\LaTeX is great` is left alone. There is deliberately no `gzz` line-wise double. Mapping an operator and its doubled form together makes every `gz` wait out 'timeoutlen' first, which is the same annoyance `gc`/`gcc` is known for. Use `gzV` or `gzip`. Shadowed defaults: ~ *noethervim-shadowed* These core keymaps replace standard Vim behavior. The original functionality is available through the alternatives listed. `s` Substitute without register (default: same as `cl`) `S` Global `:%s/` replace (default: same as `cc`) `;` Command-line (replaces `:`) (default: repeat last f/F/t/T motion) `n` / `N` Always forward / always backward (default: next / prev in search direction: after `?pat`, default `n` goes up) `|` Vertical split on a scratch buffer (default: go to screen column) `_` Horizontal split on a scratch buffer (default: go N-1 lines down, first non-blank) `+` New tab on a scratch buffer (default: go to next line, first non-blank) `<C-w><C-q>` Open quickfix (default: close window: use `<C-w>q` or `:q`) `<C-w>Q` Toggle Trouble diagnostics panel `<C-w>t` Toggle a 12-line terminal along the bottom. Reuses the same terminal buffer each time rather than opening a new one, and drops you straight into terminal mode. `<Esc><Esc>` leaves terminal mode. (default: go to top window: use `1<C-w>w`) *noethervim-resize-arrows* `<Arrow>` Resize current window: push the near edge in the arrow direction (grow). Falls back to `hjkl` cursor motion when the tab has a single non-floating window (nothing to resize against) or the current window is a float. Counts pass through, so `5<Down>` jumps 5 lines via the `j` remap. (default: one-cell / one-line motion: use `hjkl`) `<S-Arrow>` Resize current window: pull the far edge in the arrow direction (shrink). No-op when no neighbor exists on the moving edge. (default: same one-cell / one-line motion) *noethervim-semicolon* Note on `;`: If you lean on `f{char}` / `t{char}` motions with `;` / `,` to repeat them, revert in `lua/user/keymaps.lua`: >lua vim.keymap.set({ "n", "v" }, ";", ";", { desc = "repeat f/t motion" }) If you don't reach for `;` / `,` repeat often, give the swap a try. The cmdline becomes a one-key hop and the habit sticks fast. ------------------------------------------------------------------------------ 9.1 FILETYPE KEYMAPS *noethervim-ftplugin* These keymaps activate only in their respective filetype, except for the "all filetypes" group below which applies globally. all filetypes (insert): ~ `<S-CR>` Smart newline (smart-enter.nvim). Continues the structure at the cursor: Markdown lists, and LaTeX environments, etc. Presets are appended when the Markdown and LaTeX bundles are enabled. Note: smart-enter's `<S-CR>` needs a terminal that sends it distinctly from `<CR>` (Kitty, WezTerm, Ghostty, Neovide, most GUIs); some send plain `<CR>` for both. tex: ~ `:PDF` Open the compiled PDF in the system viewer. This, `yP` and Oil's `gP` treat the PDF as a file and need no viewer configured; `<LocalLeader>lv` is the one that drives your viewer and syncs `yP` Put the compiled PDF on the system clipboard `<LocalLeader>lv` View the PDF at the cursor. Says so first when the PDF is behind the buffer, since SyncTeX resolves against the last build `<LocalLeader>lf` Toggle the PDF following the cursor `<LocalLeader>P` Paste an image from the clipboard as a figure `<C-S-c>` Citation from a .bib file (insert) `@` Preamble fragment completion, at the start of a line above `\begin{document}` `]g` `[g` Next / previous theorem environment; `]p` `[p` for proofs, `]x` `[x` for examples, and `]P` `]X` for their `\end` `ig` `ag` A paragraph that stops at display math, environments, `\item` and sectioning `zg` `zw` `z=` Spell add / mark wrong / suggest, understanding LaTeX accents: on `K\"ahler` these read the decoded word rather than the fragment `<S-CR>` Smart newline in environments (insert) `<C-l>` Auto-fix nearest spelling error (insert) `<LocalLeader>vw` VimTeX word count `gd` Jump to the label under the cursor (`\cref`, `\ref`, `\eqref`, ...) via the label cache; LSP definition when not on a prefixed label `<C-]>` Same label jump through the tag stack, so `<C-t>` returns. Works across subfiles and nested sub-books `:VimtexToggleMain` Switch between compiling the current subfile and the full project. Subfile projects (`subfiles.cls`) start in subfile mode `:VimtexStop` Stop compilation AND disarm the on-save auto-compile for the project; the next manual compile re-arms it. `:VimtexStopAll` does the same for every open project markdown: ~ `<S-CR>` Smart newline in lists (insert) `<C-l>` Auto-fix nearest spelling error (insert) qf (quickfix): ~ `q` Close quickfix window `<CR>` Jump to entry, leave the list open `<S-CR>` Jump to entry and close the list `<C-j>` / `<C-n>` Navigate down `<C-p>` Navigate up oil (file explorer, `<C-w><C-o>` or `:Oil`): ~ `g?` Show all Oil keymaps (including defaults) `<C-p>` Preview the entry under the cursor in a split, and keep it in step as you move. Press it again to close. The split side follows the window shape; set `preview_split` in a user override to pin it. `gf` Fuzzy find files in current directory `gG` Live grep in current directory `gt` Open a .tex file from this directory (latex bundle). A lone match opens; with several, a picker appears where `<CR>` opens the file and `<S-CR>` puts the Oil cursor on it without opening, ready for the usual Oil keys `gP` Open a .pdf from this directory in the system viewer (latex bundle). With several, a list appears to choose from `gV` / `g|` Second pane beside this one, for moving files between directories. Both open where you are; navigate each with Oil as usual. `<C-h>` / `<C-l>` switch panes, `q` or either key again leaves. A float gives two floats and a split gives two splits, so the pair matches what you were in. To copy: yank an entry's line in one pane, put it in the other, `:w` to commit -- Oil's ordinary edit-the-buffer model, now with both ends visible. `gd` Toggle detail view (permissions, size, mtime) `gX` Open directory in system file browser `gS` Create symlink in current directory `gz` Zip entry under cursor (normal) or selection (visual) Uses `zip` on macOS/Linux, `Compress-Archive` on Windows `gZ` Unzip .zip entry (normal) or selection (visual) Uses `unzip` on macOS/Linux, `Expand-Archive` on Windows `g.` Toggle hidden files `g\` Toggle trash `Y` (normal) Copy file under cursor to system clipboard `Y` (visual) Copy selected files to system clipboard (skips "../" and unsaved lines) `yp` `yd` `yn` Yank full path / parent dir / name to unnamed reg `Yp` `Yd` `Yn` Same, but to the system clipboard ("+) ------------------------------------------------------------------------------ 9.2 STATUSLINE *noethervim-statusline* NoetherVim uses heirline.nvim for its statusline. Toggle components with the `<C-w>s` prefix: `<C-w>sg` Toggle git component `<C-w>sp` Toggle PDF size indicator `<C-w>sl` Toggle LSP component `<C-w>sf` Toggle the filetype-profile marker. Starts from `statusline.filetype_profile`; see |noethervim-user-config-data|. `<C-w>sP` Split the path: project-relative directory as its own component, bare filename beside it `<C-w>s<C-p>` Toggle PDF mode Clickable components: ~ Every statusline component below responds to mouse clicks: Diagnostics count Open buffer diagnostics picker File name Open current directory in system file browser Modified indicator Diff unsaved changes against saved version Deleted-file flag Offer to write the buffer back (see below) New-file flag Report that nothing is at that path yet (see below) Scratch flag Offer to save the buffer under a name Directory indicator Explain the cwd mismatch, offer `:lcd` (see below) LSP indicator Open `:LspInfo` Git branch/status Open lazygit in a terminal Update indicator Open Lazy plugin manager *noethervim-statusline-mode-chip* Mode chip: ~ The colored block at the left of the bar names the current mode, and hands its space to a counter when there is one to show. Search: the match under the cursor and how many the pattern has in the buffer, as `3/12`. It clears with the highlighting, on `<Esc>` or `]oh`. Confirmed substitute (`:s/old/new/gc`): the prompt you are answering and how many the pass will offer, as `3/12`. The total is fixed when the run starts, and skipping with `n` advances the count the same as replacing. Neither counter is computed in buffers over 50000 lines. *noethervim-statusline-buffer-file* Buffer and file: ~ A buffer and the file it is named after are two different things, and they come apart more often than the editor usually admits: the file can be deleted, or changed underneath you, or never have existed. Vim says so once, on the cmdline, at the moment it happens, and then the buffer looks like any other. The flag slot left of the filename holds that state instead, for as long as it is true. Between them the flags answer one question, which is whether what you are looking at exists anywhere but this buffer: Deleted the file was there and is gone New the file is not there yet Modified the file is there and differs from the buffer Read-only the buffer will not write back Scratch there is no file, and no name to write to The directory indicator (below) answers the neighbouring question, of where a relative `:w` or `:e` would land. *noethervim-statusline-deleted* Deleted-file flag: ~ A red marker appears in the flag slot, left of the filename, when the file behind the buffer is deleted, whether through Oil, `rm` in a terminal, or another program. The buffer stays intact and holds the only surviving copy of the contents. Click it to write the buffer back and recreate the file. The flag clears on the next write or re-read. *noethervim-statusline-new-file* New-file flag: ~ A marker in the same slot when the buffer has a name but nothing is at that path yet, as after `:e notes/draft.md` on a file that does not exist. The buffer is the only copy, the same as for a deleted file, so closing without writing loses it. Click it for the full path. `:w` creates the file, and any parent directories it needs. The flag clears on the first write. *noethervim-statusline-directory* Directory indicator: ~ A small marker appears left of the filename when the buffer's directory is not the window's working directory. Dimmed when the file is somewhere under cwd, which is ordinary project navigation; in the warning color when it is outside cwd entirely. The marker is about relative paths, not saving in general. Neovim's working directory belongs to the window (falling back to the tab, then the global one), never to the buffer, so: :e docs/a.md cwd stays at the project root :w draft.md writes <root>/draft.md, not <root>/docs/draft.md A plain `:w` is never affected. Neovim expands a buffer's name to an absolute path when the buffer is created, so it always writes back to the file it read, whatever cwd has done since. Click the marker for the two paths and a `[L]` action that runs `:lcd` to the buffer's directory. `:lcd` rather than `:cd` on purpose: `:cd` from a window holding a window-local or tab-local directory discards both. `:pwd` and `haslocaldir()` report which scope is currently in force. Setting 'autochdir' makes cwd follow the buffer, which silences this marker permanently. It also makes every relative path resolve against whichever file you last visited, which is its own way to open or create a file somewhere unexpected. NoetherVim leaves 'autochdir' off, matching Neovim's default; `[oa` / `]oa` toggle it. VimTeX compile indicator (latex bundle): ~ The compile indicator follows the relevant project automatically. For \input children, it walks vimtex's state list to find the parent project so the status surfaces even when the buffer is a fragment. For subfile projects (`subfiles.cls`) it compiler statusline component marks `[parent]` / `[subfile]` / `[parent+sub]` so it's clear which side is compiling. While compiling it appends a rough percent against the last successful PDF size (cached at `stdpath('state')/noethervim/vimtex_baseline.json`). On success the compile duration is sent to fidget under the `vimtex` group; the statusline shows a persistent `compiled ✓` until the next compile. Snippet jump indicator: ~ While a LuaSnip snippet is active, the statusline shows which tabstop you are on out of how many, flanked by arrows for the directions that lead to another tabstop: a left arrow when an earlier tabstop exists, a right arrow when a later one does. So `1/3` with only a right arrow means two tabstops still to fill. The indicator disappears once the snippet is finished or left. The count describes the innermost group that actually holds more than one tabstop. Dynamic and choice nodes build groups of their own, so this is what you want in both directions: `:defn`, whose four tabstops are each wrapped in a dynamic node, reads `1/4` through `4/4`, and a `mat:3*3` matrix, whose nine cells all live inside a single dynamic node, reads `1/9` through `9/9`. Expanding a second snippet inside a tabstop of the first switches the indicator to the inner snippet's own count while you fill it, then returns to the outer snippet's numbering when you jump out. Customizing the statusline: ~ These keys apply to the default heirline statusline; if you replace the statusline (for example with lualine), they have no effect. Customize statusline colors and extra components in `lua/user/config.lua`: >lua -- lua/user/config.lua return { statusline = { edge_style = "round", -- see edge styles below colors = { ... }, -- override heirline color table extra_right = { ... }, -- heirline component specs }, } Available color keys (from `util/palette.lua`): ~ `mode_n`, `mode_i`, `mode_v`, `mode_c`, `mode_t`, `mode_R`, `green`, `blue`, `orange`, `red`, `purple`, `default_gray`, `lazy_updates`, `text_gray`, `profile_writing`, `profile_code` A `mode_` key names one mode and moves only that indicator. The mode variations are reachable the same way, by their short name: `mode_niI`, `mode_cv`, `mode_Rv`. Setting `green` instead moves the normal-mode indicator and everything else green on the bar with it. *noethervim-statusline-themes* Every component asks the palette for a role rather than for a colour, so overriding a key here moves everything that plays that role at once, and the bar keeps working when you change theme. Gruvbox is the theme the palette is tuned against, by hand, with each role given a value chosen for it. Under any other colorscheme the roles are derived from standard highlight groups instead: `String` for green, `Function` for blue, `DiagnosticError` for red, and so on. That is a best effort, and on themes that paint those groups unusually the result is legible rather than considered -- gruvbox itself paints `Function` green and `Statement` red, which is why the two filetype-profile roles are their own keys rather than reusing `blue` and `purple`. If a role lands badly on the theme you use, override it here; that is what these keys are for. Example, changing the normal-mode indicator to blue: >lua -- lua/user/config.lua return { statusline = { colors = { mode_n = "#458588" }, }, } *noethervim-statusline-edge-style* Edge style ~ The colored mode block at the left of the statusline (and, for some styles, an opening endcap on the right ruler block) follows the `statusline.edge_style` key in `lua/user/config.lua`. All glyphs come from the Nerd Font private use area, so a Powerline-capable terminal font is required. `round` (default) Rounded mode bubble, flush right edge. `slant` Slanted endcaps leaning right, so the mode block is a right-leaning parallelogram; right ruler also slanted. `slant_left` The mirror of it, leaning left. `slant_in` Edges lean towards each other, making the block a trapezoid rather than a parallelogram. `slant_out` Edges lean apart, the other trapezoid. `pointy` Triangle endcaps; right ruler gets a triangle opener. `straight` No endcap glyphs; mode block renders as a plain rectangle, right edge stays flush. `bubbly` Rounded endcaps on both sides (left mode block and right ruler block). An endcap at a section's left edge is filled on its right, and one at the right edge is filled on its left. Two that lean the same way give a parallelogram, two that lean opposite ways give a trapezoid, which is the whole of the shape space and why the slant family has four members. Switching styles takes effect on the next Neovim launch (the bar is assembled in heirline's `config` callback). Picking an unknown name falls back to `round` and emits a `vim.notify` warning during setup. For custom component authoring, see |heirline-cookbook|. *noethervim-statusline-busy-override* Greedy Busy-component takeover ~ The Busy component (animated spinner while `vim.bo.busy > 0`) can be greedily claimed by a bundle or by user config via `register_busy_override`. The override supplies a label, highlight and optional click handler, and temporarily replaces the default spinner rendering. The most recently registered override that returns a non-nil spec wins (last-write-wins), so user config overrides bundle defaults naturally: >lua require("noethervim.statusline").register_busy_override(function() -- return nil to yield; return a spec to claim the slot: return { icon = nil, -- falls back to the spinner label = "ai", -- shown after icon hl = { fg = "#c678dd", bold = true }, on_click = function() ... end, -- mouse handler } end) The animation timer only ticks while something is driving `vim.bo.busy` > 0, so overrides wanting animation should also increment busy on the relevant buffer. *noethervim-statusline-error-boundary* Error recovery ~ If any statusline component raises an error, the bar degrades to a `statusline recovering...` marker instead of letting the traceback replace the entire statusline. The full error is written to |:messages| for diagnosis. After a cooldown, the bar auto-retries; if the underlying state has cleared, normal rendering resumes. Override the cooldown with `vim.g.heirline_recovery_ms` (default `1000`). The same protection applies to the tabline, winbar, and statuscolumn. ------------------------------------------------------------------------------ 9.3 TABLINE *noethervim-tabline* NoetherVim ships a tab-based tabline (not a bufferline). Each tab shows its focused buffer's file icon, `project: filename` label (derived from git root), and a `●` modified indicator. Tabs have per-tab close buttons and shrink gracefully when many tabs are open. Tab management keymaps: ~ `+` Open a new tab on an empty scratch buffer `<C-w><A-h>` Move current tab left `<C-w><A-l>` Move current tab right `[t` / `]t` Previous / next tab `>t` / `<t` Move current tab right / left *noethervim-tabline-bufferline* Switching to a bufferline ~ NoetherVim defaults to a tabline because tabs and buffers serve different purposes in Vim's model: tabs are layout viewports, buffers are open files. Reaching for a bufferline to manage open files often signals that a fuzzy-finder or |:ls| would be a more effective habit. That said, if you prefer a bufferline, replacing the tabline is straightforward. See the example in `templates/user/plugins/bufferline.example.lua`, or drop this into your `lua/user/plugins/` directory: >lua { "akinsho/bufferline.nvim", event = "UIEnter", dependencies = "nvim-tree/nvim-web-devicons", opts = {}, config = function(_, opts) require("bufferline").setup(opts) -- Disable heirline's tabline so bufferline owns the area. vim.o.tabline = "" vim.o.showtabline = 2 end, }
*noethervim-commands* `:NoetherVim` [subcommand] Inspection and comparison commands. See |noethervim-inspect| and |noethervim-diff|. Run with no subcommand to print the full subcommand list with descriptions. `:NoetherVim override` Open the user override file corresponding to the current buffer's NoetherVim source file. Creates the file (and parent directories) if it does not exist, seeding it with minimal boilerplate. Opens the override in a vertical split so you can read the source alongside your customization. Path mapping: `plugins/<name>.lua` -> `user/plugins/<name>.lua` `bundles/<cat>/<name>.lua` -> `user/plugins/<name>.lua` `lsp/<name>.lua` -> `user/lsp/<name>.lua` `options.lua` etc. -> `user/<name>.lua` `ftplugin/<ft>.lua` -> `<config>/ftplugin/<ft>.lua` For internal modules (util/, sources/) or files outside the NoetherVim tree, a warning is shown. For a plugin or bundle, the seeded file lists every repo string the upstream spec declares, as commented-out stubs. lazy.nvim merges by repo string, so those are the keys an override attaches to; uncomment the one you want. `:NoetherVim bundles` binds `<C-o>` to the same thing, so you can seed an override straight from the catalogue without opening the source first. `:NoetherVim override!` Copy the upstream file wholesale instead of seeding stubs. A last resort, and the file says so in its own header: a full copy shadows every spec in the original, not just the ones you meant to change, so later upstream edits to any of them stop reaching you, and a copied `config = function()` no longer deep-merges the way `opts` does. An override that already exists is never overwritten, with or without the bang. *noethervim-override-drift* Both forms record which distribution file the override was written against, and a hash of its contents at that moment. The record lives in `stdpath('state')/noethervim/override-base.json`, never in the override itself: files under `lua/user/` are yours to rewrite, reformat or strip comments from, and a marker comment would stop working the moment you did. `:checkhealth noethervim` rehashes the upstream file and reports overrides whose upstream has moved on since, under "Override drift". Without it an upstream fix simply never arrives: the override keeps winning, and nothing says so. Compare one with `:NoetherVim diff` {name}, which opens upstream and your override side by side in diff mode. Once you have read the change, run `:NoetherVim override` again from the upstream file to re-baseline it and clear the warning. Deleting the override drops its record on the next health check. Overrides written by hand are not reported: there is no baseline to compare them against. Run `:NoetherVim override` from the upstream file to start tracking one. The record is per-machine. A configuration copied to a second machine arrives with no baselines, so nothing is reported there until an override is created or re-baselined on it. `:Reset[!]` Close all buffers, open dashboard. Use ! to force (discard unsaved changes). `:Redir` {cmd} Redirect command or shell output to a scratch buffer. Example: `:Redir !ls -la` `:DiffOrig` Diff the current buffer against the file on disk. Opens a vertical split with the on-disk version and enters diff mode on both sides. Useful when the buffer has unsaved changes or the file was modified externally. Close the scratch split with `:q` when done. `:LuaSnipEdit` Open a snippet file for the current filetype. See |noethervim-snippets|. `:LuaSnipStop` Stop every snippet in the buffer and clear LuaSnip's leftover marks. See |noethervim-snippet-stop|. `:LspRename` LSP rename with instance count notification. `:NoetherVimHighlightUnderCursor` Print every highlight layer covering the cursor: legacy syntax, treesitter captures, LSP semantic tokens, and ALL extmarks on the row (including virtual text from inlay hints, diagnostics, gitsigns blame, and nvim-dap-virtual-text). Useful for tweaking and debugging; the output names the exact group so you can override it in `user/highlights.lua`. Web search command: ~ *noethervim-web-search* `:Search` {query} Search the web with the current default engine. `:Search` {engine} {query} Search the web with a specific engine. `:Search set` {engine} Set the default search engine for this session. `:Search ?` Report the engine currently in use, with the full list of engines (the active one marked). `:Search set` with no engine does the same. Available engines: brave (default), duckduckgo, google, ecosia, github, startpage, reddit, stackoverflow, wikipedia, youtube. Tab-completion is available for engine names and the `set` / `?` subcommands.
*noethervim-completion* NoetherVim uses blink.cmp for completion. The default keybindings are designed around a "Tab jumps snippets, C-n/p navigates the menu" model. See |noethervim-completion-custom| below to change this. Default keybindings: ~ *noethervim-completion-keys* Insert mode: ~ `<C-Space>` Show menu / toggle documentation. Falls back to native |i_CTRL-N| when blink is disabled (|noethervim-option-toggles| `]oC`). `<C-n>` Next item `<C-p>` Previous item `<C-y>` Accept selected item `<C-e>` Dismiss menu `<Tab>` Jump to next snippet node (or expand snippet) `<S-Tab>` Jump to previous snippet node `<C-b>` Scroll documentation up `<C-f>` Scroll documentation down Cmdline mode: ~ `<Tab>` Select and insert next item (cycles through matches) `<S-Tab>` Select and insert previous item `<C-y>` Accept (inherited from insert mode) Tab key philosophy preset: ~ *noethervim-completion-style* Set `completion_style` in `lua/user/config.lua` to switch the entire Tab behavior without writing any keymap overrides: `"snippet"` (default) Tab is reserved for LuaSnip jumps. Pick from the menu with C-n/C-p and accept with C-y. Snippet expansion and menu navigation never compete for Tab. `"supertab"` Tab accepts the highlighted item (auto-selecting the top one if none is selected) and inserts a trailing space. Falls back to snippet_forward, then to literal Tab. IDE muscle memory. `"navigate"` Tab cycles forward through the menu (= C-n) without accepting; C-y or <CR> commits. S-Tab cycles backward. Matches the classic nvim-cmp default and most VSCode setups. Example: >lua -- lua/user/config.lua return { completion_style = "supertab" } The two philosophies people mostly go for are "supertab" and "snippet". The "navigate" is here mostly for users coming from older nvim-cmp configs. You can accept AI completion (Copilot/Codeium) using Tab in AI plugin's accept_word in your `lua/user/keymaps.lua` to shadow whichever preset you picked. Customizing individual keys: ~ *noethervim-completion-custom* Override in `user/plugins/` using opts merging. Each key maps to a list of actions; blink tries them in order and stops at the first that succeeds. Tab to accept (instead of snippet jump): >lua { "saghen/blink.cmp", opts = { keymap = { ["<Tab>"] = { "accept", "snippet_forward", "fallback" }, ["<S-Tab>"] = { "snippet_backward", "fallback" }, }, } } Enter to accept, Tab/S-Tab to navigate menu: >lua { "saghen/blink.cmp", opts = { keymap = { ["<CR>"] = { "accept", "fallback" }, ["<Tab>"] = { "select_next", "fallback" }, ["<S-Tab>"] = { "select_prev", "fallback" }, }, } } Use a blink preset (replaces all keymaps): >lua { "saghen/blink.cmp", opts = { keymap = { preset = "super-tab" }, } } Cmdline-only overrides (insert mode keymaps stay unchanged): >lua { "saghen/blink.cmp", opts = { cmdline = { keymap = { preset = "inherit", ["<Tab>"] = { "accept", "fallback" }, }, }, } } Custom completion sources: ~ NoetherVim ships three custom blink.cmp completion sources. images ~ Triggered when the entire line is "." in a .tex file. Lists image files from ./images/ and inserts a complete \begin{figure}...\end{figure} block. todos ~ Triggered by typing "@" at the start of a line. Inserts a language-appropriate comment prefix + keyword (TODO, FIXME, BUG, etc.). Supported filetypes: lua, vim, tex, python, typescript, javascript. user_config ~ Active only in `lua/user/config.lua`. Opening a quote after a field that takes one of a fixed set of strings lists that set: `colorscheme` every scheme installed right now, so it grows when the ui.colorscheme bundle is enabled `edge_style` the statusline edge presets Both are read at the moment you ask, not from a list kept here, so what you are offered is what the distribution will accept.
*noethervim-health* Run `:checkhealth noethervim` to verify your setup. Checks include: - Required tools: git, rg (ripgrep), fd - Optional tools: node, zoxide, lazygit, tree-sitter - Terminal: true color, `<S-CR>` support, Nerd Font (heuristic, see below) - LaTeX toolchain (if latex bundle enabled) - Snippets: how many are loaded per filetype, where your own live, and a warning when a trigger is defined more than once (which shows up as a duplicated entry in the completion menu) - Template version: warns if your init.lua is outdated - Override drift: warns when a file you overrode has changed upstream (see |noethervim-override-drift|) - User override status: lists loaded user modules - Configuration: user config dir, plugin dir, vault paths - Active bundles, spec errors, option drift, override conflicts - LSP servers and feature flags *noethervim-health-terminal* About the terminal section: ~ These results are a guess. A terminal cannot be asked what it supports, so the check goes by `$TERM` and `$TERM_PROGRAM`, both of which can be wrong, especially through a multiplexer. An unrecognized terminal is reported as unknown rather than broken. What it is checking for is whether `<S-CR>` arrives as its own key instead of a plain `<CR>`. Several keymaps need that, notably the list-continuation and environment-aware `<S-CR>` bindings. Terminals known to support it include kitty, Ghostty, WezTerm, Alacritty, foot and iTerm2 3.5+. Apple Terminal does not. To test it yourself, press `<S-CR>` in insert mode inside a markdown list: a new list item means it works. Under tmux, add `set -g extended-keys on`, otherwise `<S-CR>` never reaches Neovim regardless of the terminal. Nerd Font presence cannot be detected at all. The check prints sample glyphs instead; if they render as boxes, the font is missing.
*noethervim-faq* *neothervim-faq* Q: How do I add a new LSP server?~ A: Create `lua/user/lsp/<server>.lua` with `vim.lsp.config()` and `vim.lsp.enable()` calls. Add the server to Mason's ensure_installed via a plugin override: >lua { "neovim/nvim-lspconfig", opts = { ensure_installed = { "lua_ls", "basedpyright", ..., "gopls" } } } `ensure_installed` is an array, so the table-form override REPLACES the distro list, so include every server you want loaded. To extend the distro list instead, see |noethervim-user-plugins-arrays|. Q: How do I add a linter?~ A: NoetherVim uses nvim-lint alongside conform.nvim. Most linting is provided by LSP servers; nvim-lint fills the gap for non-LSP tools. Add linters via opts override in `user/plugins/`: >lua { "mfussenegger/nvim-lint", opts = { linters_by_ft = { sh = { "shellcheck" }, yaml = { "yamllint" } } } } `linters_by_ft.<ft>` is also an array; same caveat applies. See |noethervim-user-plugins-arrays|. *noethervim-diagnostic-display* Q: The error message beside my cursor is cut off. Can I see all of it?~ A: `gl` opens the full text in a float; `]d` and `[d` jump to the next or previous diagnostic and open it there. The inline text is a one-line preview, clipped where the window ends. To let it continue over the lines below instead: >lua -- lua/user/plugins/diagnostics.lua return { { "rachartier/tiny-inline-diagnostic.nvim", opts = { options = { overflow = { mode = "wrap" } } } }, } The continuation is drawn on top of the lines below, so while the cursor rests there a long message hides two or three lines of code. For a renderer that pushes those lines down rather than covering them, swap in Neovim's own: >lua -- lua/user/plugins/diagnostics.lua return { { "rachartier/tiny-inline-diagnostic.nvim", enabled = false }, { "neovim/nvim-lspconfig", opts = { diagnostic = { virtual_lines = { current_line = true }, } } }, } Q: How do I install a formatter or debugger?~ A: Both are managed by Mason, the same as LSP servers. Use `:Mason` to browse and install interactively. Formatters referenced by conform are installed automatically: >lua { "stevearc/conform.nvim", opts = { formatters_by_ft = { sh = { "shfmt" } } } } For debuggers, enable the `debug` bundle plus the bundle for the language you are debugging: `languages.python` brings debugpy, `languages.go` brings delve, `languages.web-dev` brings vscode-js-debug, `languages.c-cpp` brings codelldb. Neither half installs a debugger on its own, so enabling `debug` alone costs nothing but the UI. Test adapters pair the same way; see |noethervim-test-adapters|. *noethervim-formatting-core* Q: Which filetypes can I format without enabling anything?~ A: Thirteen, out of the box and with no bundle: `lua`, `python`, `bib`, `javascript`, `javascriptreact`, `typescript`, `typescriptreact`, `css`, `html`, `json`, `yaml`, `markdown` and `sh`. This is deliberate rather than an oversight about where language configuration belongs. Formatting a Python file is something you are entitled to expect from an editor you just installed, so it is core's to provide; a bundle deepens a subject rather than making a common file type work at all. The formatter binaries still come from Mason on first use, so nothing is downloaded for a language you never open. To change one, or to add a filetype, override conform in `user/plugins/`. `formatters_by_ft` is a table of arrays, so naming a filetype replaces its list rather than adding to it: >lua -- lua/user/plugins/conform.lua return { { "stevearc/conform.nvim", opts = { formatters_by_ft = { python = { "ruff_format" }, -- replaces black rust = { "rustfmt" }, -- adds a filetype core does not claim } } }, } See |noethervim-user-plugins-arrays| for why the array does not merge. Q: Does NoetherVim format on save?~ A: No; auto-formatting is deliberately off. Use `<Leader>ff` for explicit formatting (conform.nvim with LSP fallback). To opt into save-time formatting, override conform in `user/plugins/`: >lua { "stevearc/conform.nvim", opts = { format_on_save = { timeout_ms = 500, lsp_format = "fallback" }, } } Q: How do I change a picker keymap?~ A: NoetherVim uses snacks.picker as the core UI. Default picker keymaps live in bundles (e.g. `bundles/languages/latex.lua`) or core plugin files (e.g. `plugins/snacks.lua`). Override them in `user/keymaps.lua` or by specifying `keys` in your plugin override spec. Q: Why does my override have no effect?~ A: Check `:NoetherVim status` to verify your files are loaded. Check `:NoetherVim diff keymaps` or `:NoetherVim diff options` to see what changed. If using `NOETHERVIM_NO_USER`, your overrides are disabled. Q: How do I completely replace a plugin's config?~ A: Specify `config = function(_, opts) ... end` in your override spec. This replaces the upstream config entirely. Note in this case you must call the plugin's setup yourself. Q: Can I disable a plugin from core or a bundle?~ A: Use `cond = false` (or `enabled = false`) in your `user/plugins/` spec: >lua { "some/plugin", cond = false } Both work: lazy.nvim resolves these on the merged plugin (last-write- wins), and user fragments are always last. `cond = false` keeps the plugin in lazy's disabled list (visible in `:Lazy`); `enabled = false` removes it entirely. For bundles, comment out the bundle's `import` line in your init.lua to disable the entire bundle. Q: Why doesn't `p` paste from my system clipboard?~ A: By design. NoetherVim keeps `y` and `p` bound to Vim's unnamed register so transient edits (`ddp`, `xp`, `ciwp`) don't go to the OS clipboard. By default, there are keybindings for when you want the system clipboard: Normal, Visual <leader>y / <leader>Y yank (motion / line) <leader>p / <leader>P paste (after / before) Visual Y / P shorthand yank / paste Insert <C-v> paste (see caveat) Cmdline <C-y> yank cmdline to clipboard <C-r>* insert clipboard (built-in) Any mode (nvim defualt) "*y / "*p unmapped register access To opt into the "everything through OS clipboard" default, override in `lua/user/options.lua`: >lua vim.o.clipboard = "unnamedplus" Caveat: insert `<C-v>` shadows Vim's "insert next char literally" default. Substitutes: - `<C-q>`: literal-insert in most terminals - `<C-r>+`: insert clipboard register literally - cmdline `<C-v>{char}` is unshadowed; pairs well with `:verbose` when introspecting keymaps that contain literal control sequences Q: How do I change the default colorscheme?~ A: Set `colorscheme` in `lua/user/config.lua`: >lua -- lua/user/config.lua return { colorscheme = "tokyonight" } When the `colorscheme` bundle is enabled, your SearchLeader+C pick is persisted and takes priority over the configured default on subsequent launches. To reset the saved pick, delete `stdpath("data") .. "/noethervim_colorscheme"`. Q: Why is spell check on in markdown / tex / gitcommit files?~ A: Writing filetypes (tex, markdown, norg, text, gitcommit, rst, typst) get a "writing profile" that enables wrap, linebreak, spell, and `conceallevel = 2`. See |noethervim-filetype-profiles| for the full picture. Toggle spell per buffer with `[os` / `]os`. To disable the profile entirely, clear the augroup in `lua/user/autocmds.lua`: >lua vim.api.nvim_create_augroup("noethervim_writing", { clear = true }) Q: How do I restore my last session?~ A: Sessions auto-save per working directory via persistence.nvim. From the dashboard, press `r`. From anywhere else: >vim :lua require("persistence").load({ last = true }) Omit `{ last = true }` to load the session for the current CWD instead of the most recent one. Q: I typed `:NeotherVim` and it worked?~ A: Yes. Years of typing `neo-` will do that. The alias is there so you don't have to think about it. `:help neothervim` and `:checkhealth neothervim` work too. I did consider naming the distribution `NeotherVim`, but this felt it would inadvertently induce the Mandela-effect forevermore on the reading of the name `Noether`. Q: What is vim-abolish and how do I use it?~ A: NoetherVim ships a fork of vim-abolish that adds context-aware expansion. The fork exposes three features: 1. `:Subvert`: case-preserving find/replace (facility->building preserves Facility->Building, FACILITY->BUILDING) 2. `:Abolish`: smart auto-correct (`:Abolish teh the` corrects all case variants in insert mode) 3. Coercion operators: `crs` snake_case, `crm` MixedCase, `crc` camelCase, `cru` UPPER_CASE, `cr-` dash-case Add personal typo corrections in `lua/user/plugins/abolish.lua`. Context gating: in code buffers, :Abolish corrections only fire when the cursor is inside a comment or @spell-tagged region (typical string content). Prose buffers (tex, markdown, ...) always expand. Use `[oA` to force unconditional expansion in the current buffer and `]oA` to return to context-gated. To opt a specific :Abolish line out of gating entirely, pass `-expr=` explicitly: `:Abolish -expr= teh the` always expands Q: Can I make my `leader` and `searchleader` the same?~ A: Yes; simply map both to the same key in your `init.lua` file. You can map your LocalLeader to the same key as well.