Introduction
zenvim is a personal Neovim setup for polyglot development (Python, Go, Rust,
TypeScript/Svelte, Lua, Nix, shell and more). The whole configuration is
declared as Nixvim modules – there is
no hand-written init.lua. Every plugin, language server, formatter and linter
is pinned by Nix, so the same editor runs on any machine with a single
nix run.
nix run github:farbzen/zenvim.nix
What the flake provides
| Output | Purpose |
|---|---|
packages.<system>.full / .default | Neovim with every feature enabled |
packages.<system>.minimal | Neovim with core editing only |
packages.<system>.docs | This book as a static website |
nixvimModules.full / .default | The complete Nixvim configuration, to build your own variant |
nixvimModules.minimal | Profile module that switches all optional features off |
nixosModules.zenvim / .default | Adds programs.zenvim to NixOS |
checks.<system>.* | One check per profile; starts Neovim headless and fails on errors/warnings |
formatter.<system> | alejandra |
Supported systems are x86_64-linux and aarch64-linux.
Features
- Completion: blink.cmp with LSP, path, snippet, buffer and ripgrep sources.
- Modular: every language and its tooling, the AI integration, bufferline, mini.map, noice, persistence and the fun plugins can be switched on or off with a boolean option.
- Profiles:
full(everything, the default) andminimal(no languages, only the essentials). - Language support: 16 languages with LSP, inlay hints and navic breadcrumbs.
- Formatting & linting: conform and nvim-lint with per-filetype tools.
- Treesitter: all grammars prebuilt by Nix with highlighting, indentation and folding.
- Testing & debugging: neotest (Python, Rust, Plenary) with coverage, and nvim-dap with UI for Python, Go and LLDB.
- Navigation: snacks pickers, neo-tree, bufferline, mini.map and sessions.
- Git: gitsigns with inline blame and Lazygit integration.
- AI: claudecode in a floating snacks terminal.
- Look & feel: rose-pine (transparent outside Neovide), lualine, noice, tiny-inline-diagnostic and smart relative line numbers.
- Project tools win: bundled tools are appended to
PATH, so versions from a project’s devshell take precedence.
Where to go next
- Installation – run it, install it on NixOS or build a variant.
- Profiles and Options – choose what is enabled.
- Keymaps – every mapping at a glance.
- Languages and Plugins – what is configured and how.
Installation
Prerequisites
- Nix with the
nix-commandandflakesexperimental features enabled. - A Nerd Font in your terminal; icons in the statusline, bufferline, file explorer and diagnostics rely on it.
- An
x86_64-linuxoraarch64-linuxsystem.
Try it without installing
nix run github:farbzen/zenvim.nix # full profile
nix run github:farbzen/zenvim.nix#full
nix run github:farbzen/zenvim.nix#minimal
Arguments after -- are passed to Neovim:
nix run github:farbzen/zenvim.nix -- README.md
Install on NixOS
Add the flake as an input, import the NixOS module and enable it:
{
inputs.zenvim.url = "github:farbzen/zenvim.nix";
inputs.zenvim.inputs.nixpkgs.follows = "nixpkgs";
outputs = {nixpkgs, zenvim, ...}: {
nixosConfigurations.<host> = nixpkgs.lib.nixosSystem {
modules = [
zenvim.nixosModules.default
{
programs.zenvim = {
enable = true;
profile = "minimal";
settings.zenvim.languages.python.enable = true;
};
}
];
};
};
}
The module installs the resulting package into environment.systemPackages and,
unless defaultEditor = false, sets EDITOR=nvim. settings accepts any
Nixvim module, so besides the zenvim.* switches you can also set plain Nixvim
options there. All options are listed in the
NixOS options reference.
Note:
follows = "nixpkgs"makes zenvim build against your system’s nixpkgs. Leave it out to use the exact versions pinned in zenvim’sflake.lock.
Install with any other Nix setup
The packages are ordinary derivations, so they can be added to a Home Manager configuration or a devshell:
home.packages = [inputs.zenvim.packages.${pkgs.stdenv.hostPlatform.system}.full];
Build your own variant
nixvimModules.default contains the complete configuration. Combine it with a
profile and your own overrides and evaluate it with Nixvim:
let
configuration = inputs.nixvim.lib.evalNixvim {
system = "x86_64-linux";
modules = [
inputs.zenvim.nixvimModules.default
inputs.zenvim.nixvimModules.minimal
{nixpkgs.source = inputs.nixpkgs;}
{
zenvim.languages.python.enable = true;
zenvim.languages.python.dap.enable = false;
}
];
};
in
configuration.config.build.package
Because the variant is a Nixvim configuration, every Nixvim option is available
in your own modules as well, e.g. plugins.<name>.enable or opts.*.
Tools you provide yourself
A few tools are deliberately not bundled and are expected from the system or the project environment:
| Tool | Needed for |
|---|---|
claude | claudecode (zenvim.ai.claudecode) |
rust-analyzer | Rust LSP via rustaceanvim |
rustfmt | Rust formatting |
gofmt | Go formatting (together with bundled golines) |
staticcheck | Go linting |
ruff, isort | Python formatting and linting |
See Languages for what each language bundles.
Profiles
A profile is a Nixvim module that sets the default state of all options. zenvim ships two of them.
| Profile | Module | Package | Contents |
|---|---|---|---|
full | nixvimModules.full | packages.<system>.full | All languages with LSP, formatter, linter, DAP and test adapters, claudecode, bufferline and all extras. |
minimal | nixvimModules.minimal | packages.<system>.minimal | Core editing only: snacks, neo-tree, lualine, gitsigns, blink.cmp, treesitter, which-key – no languages, no AI. |
full is also exported as default, both for the module and for the package.
How the profiles work
nixvimModules.full is the actual configuration: it imports every module from
nixvim/ and all options default to true.
nixvimModules.minimal contains no configuration of its own. It only sets every
language and every optional feature to false – with lib.mkDefault:
{lib, ...}: let
disabled = {enable = lib.mkDefault false;};
in {
zenvim = {
languages = lib.genAttrs (lib.attrNames (import ../nixvim/languages/features.nix)) (_: disabled);
ai.claudecode = disabled;
bufferline = disabled;
# …
};
}
Because of mkDefault, a plain assignment in your own module wins without
mkForce. The minimal profile is therefore always used together with the full
module (nixvimModules.default + nixvimModules.minimal).
Starting from a profile
Start from minimal and switch single features on:
programs.zenvim = {
enable = true;
profile = "minimal";
settings = {
zenvim.languages.nix.enable = true;
zenvim.persistence.enable = true;
};
};
Or start from full and switch single features off:
programs.zenvim.settings = {
zenvim.ai.claudecode.enable = false;
zenvim.pentatonic.enable = false;
zenvim.languages.rust.dap.enable = false;
};
What is always enabled
Independent of the profile, the following is always part of the configuration: snacks, neo-tree, lualine with navic, gitsigns, blink.cmp with ripgrep source, treesitter with all grammars, which-key, nvim-surround, autoclose, nvim-cursorline, tiny-inline-diagnostic, todo-comments, vim-sleuth, direnv, web-devicons and the rose-pine colorscheme. See Plugins for details.
Options
All options are booleans and default to true, which corresponds to the full
profile. The minimal profile (nixvimModules.minimal) sets every option on
this page to false with lib.mkDefault, so single options can be switched
back on.
Languages
zenvim.languages.<language>.enable switches a language with all of its
tooling. Each sub-option defaults to the value of the language switch and can be
toggled separately, e.g. zenvim.languages.python.dap.enable = false.
| Language | Sub-options | Tooling |
|---|---|---|
lua | lsp, format, test | lua_ls + lazydev, stylua, neotest-plenary |
nix | lsp, format, lint | nil, alejandra, statix |
python | lsp, format, lint, dap, test | pyright, isort + ruff_format, ruff, nvim-dap-python, neotest-python |
go | lsp, format, lint, dap | gopls, gofmt + golines, staticcheck, nvim-dap-go |
rust | lsp, format, dap, test | rustaceanvim + crates, rustfmt, nvim-dap-lldb (codelldb), neotest-rust |
shell | lsp, format, lint | bash-language-server, shfmt, shellcheck |
html | lsp, format | vscode-html-language-server, prettierd (html, htmldjango) |
css | format, lint | prettierd, stylelint |
typescript | lsp, format, lint | typescript-tools, deno fmt (JS/TS), eslint |
svelte | lsp, format, lint | svelte-language-server, prettierd, eslint |
tailwind | lsp | tailwindcss-language-server |
json | lsp, format | vscode-json-language-server, deno fmt |
yaml | lsp | yaml-language-server |
sql | lsp | sqls |
docker | lsp | dockerfile-language-server |
markdown | format, render | deno fmt, render-markdown |
The shared tooling follows the languages: the LSP core (lspconfig, inlay hints,
K, <C-k>, <leader>lR, <leader>th), conform, nvim-lint, nvim-dap with
dap-ui and neotest with nvim-coverage are only enabled when at least one
language uses the respective feature. The DAP status in the winbar follows
nvim-dap as well.
Integrations and extras
| Option | Enables |
|---|---|
zenvim.ai.claudecode.enable | claudecode in a floating snacks terminal (<C-,>) |
zenvim.bufferline.enable | bufferline with <M-h>, <M-l>, <M-1>…<M-9>, <M-$> |
zenvim.miniMap.enable | mini.map minimap (<leader>mm) |
zenvim.noice.enable | noice UI, macro recording status in the winbar, <leader>na |
zenvim.persistence.enable | session management (<leader>ql, <leader>qL, <leader>qd) |
zenvim.tacho.enable | typing speed in the winbar |
zenvim.pentatonic.enable | pentatonic typing sounds |
zenvim.duck.enable | duck.nvim (<leader>dh, <leader>dc, <leader>dC) |
zenvim.cellularAutomaton.enable | cellular-automaton (<leader>fml, <leader>gol) |
NixOS module
nixosModules.zenvim (also available as nixosModules.default) provides
programs.zenvim:
| Option | Type | Default | Description |
|---|---|---|---|
programs.zenvim.enable | bool | false | Install zenvim. |
programs.zenvim.profile | "full" or "minimal" | "full" | Profile the configuration starts from. |
programs.zenvim.settings | Nixvim module | {} | Applied on top of the profile, e.g. to toggle zenvim.*. |
programs.zenvim.defaultEditor | bool | true | Set EDITOR to nvim. |
programs.zenvim.package | package (read-only) | – | The resulting package. |
programs.zenvim = {
enable = true;
profile = "minimal";
settings = {
zenvim.languages.nix.enable = true;
zenvim.languages.nix.lint.enable = false;
zenvim.tacho.enable = true;
};
};
Always enabled
snacks, neo-tree, lualine with navic, gitsigns, blink.cmp with ripgrep source, treesitter with all grammars, which-key, nvim-surround, autoclose, nvim-cursorline, tiny-inline-diagnostic, todo-comments, vim-sleuth, direnv, web-devicons and the rose-pine colorscheme.
Keymaps
<leader> and <localleader> are both set to Space. All mappings are in
normal mode unless stated otherwise. Mappings of optional features only exist
when the feature is enabled, see Options. Press <leader> and
wait to see the which-key popup.
Files, buffers and search
| Key | Action |
|---|---|
<C-o> | Open file explorer (neo-tree, floating) |
<C-M-o> | List recent projects |
<leader>ff | Find file |
<leader>fg | Find content in files |
<leader>fw | Find current word or selection (n, x) |
<leader>bo | Show open buffers |
<leader>bw | Close current buffer |
<leader>bc | Close all except the current buffer |
<M-h> | Jump to previous buffer |
<M-l> | Jump to next buffer |
<M-1>…<M-9> | Jump to buffer 1–9 in bufferline |
<M-$> | Jump to last buffer in bufferline |
<leader>s | Open scratchpad |
<leader>S | Select scratchpad |
<leader>mm | Toggle minimap |
LSP, diagnostics and formatting
| Key | Action |
|---|---|
K | Hover (when an LSP is attached) |
<C-k> | Signature documentation (LSP) |
gd | Go to definition |
<leader>lr | Show LSP references |
<leader>ls | Show LSP symbols |
<leader>lR | Restart LSP |
<leader>th | Toggle LSP inlay hints |
<leader>lf | Format current file |
<leader>ld | Show diagnostics for current buffer |
<leader>lD | Show diagnostics |
<leader>ll | Show location list |
<leader>lq | Show quickfix list |
<leader>lt | Show TODO comments |
<leader>lu | Show undo history |
Git
| Key | Action |
|---|---|
<leader>gg | Open Lazygit |
<leader>gb | Toggle git blame on current line |
<leader>ghp | Preview git hunk |
<leader>ghP | Preview git hunk inline |
<leader>ghs | Stage git hunk |
<leader>ghr | Reset git hunk |
Tests and debugging
| Key | Action |
|---|---|
<leader>tr | Run current test(s) |
<leader>td | Debug current test(s) |
<leader>ta | Attach to current test(s) |
<leader>to | Toggle test output panel |
<leader>ts | Toggle test summary |
<leader>tcs | Show test coverage |
<leader>tcc | Clear test coverage |
<leader>tcS | Show test coverage summary |
<F4> | DAP: Close |
<F5> | DAP: Start/Continue |
<F6> | DAP: Restart |
<F10> | DAP: Step over |
<F11> | DAP: Step into |
<F12> | DAP: Step out |
<leader>db | DAP: Toggle breakpoint |
<leader>dr | DAP: Open REPL |
<leader>du | DAP: Open UI |
Tools and UI
| Key | Action |
|---|---|
<C-,> | Toggle Claude Code (also hides it in terminal mode) |
<leader>tt | Toggle terminal |
<leader>ds | Dim code around the current block |
<leader>mp | Open markdown preview |
<leader>mt | Toggle markdown rendering |
<leader>nh | Show notification history |
<leader>na | Show Noice message history |
<leader>ql | Load the last session |
<leader>qL | Select a session to load |
<leader>qd | Disable persistence (no save on exit) |
<C-=> | Zoom in (Neovide only) |
<C--> | Zoom out (Neovide only) |
Just for fun
| Key | Action |
|---|---|
<leader>fml | Make it rain |
<leader>gol | Game of life |
<leader>dh | Hatch a duck |
<leader>dc | Cook a duck |
<leader>dC | Cook all ducks |
Completion (blink.cmp)
| Key | Action |
|---|---|
<CR> | Select and accept |
<Tab> | Select next item |
<S-Tab> | Select previous item |
<C-e> | Hide menu |
<C-d> | Scroll documentation down |
<C-u> | Scroll documentation up |
Commands
:Wand:Qbehave like:wand:q.
Languages
Every language has a switch zenvim.languages.<language>.enable and one
sub-option per feature it supports (lsp, format, lint, dap, test,
render). Sub-options default to the language switch and can be toggled on
their own:
zenvim.languages.python = {
enable = true;
dap.enable = false;
};
The supported features per language are declared in
nixvim/languages/features.nix. Shared plugins follow the languages: lspconfig,
conform, nvim-lint, nvim-dap with dap-ui and neotest with nvim-coverage are only
part of the configuration if at least one language enables the corresponding
feature (see Plugins).
How tools are provided
- Language servers are enabled with Nixvim’s
packageFallback = true. The server from nixpkgs is appended to the end ofPATH, so a server installed in the project (e.g. through a devshell) is used first. - Formatters and linters are added with
extraPackagesAfter, which also appends them toPATH. - Tools marked as not bundled below must come from the system or the project environment.
Overview
| Language | LSP | Format | Lint | DAP | Test |
|---|---|---|---|---|---|
lua | lua_ls + lazydev | stylua | – | – | neotest-plenary |
nix | nil | alejandra | statix | – | – |
python | pyright | isort + ruff_format (not bundled) | ruff (not bundled) | nvim-dap-python | neotest-python |
go | gopls | gofmt (not bundled) + golines | staticcheck (not bundled) | nvim-dap-go | – |
rust | rustaceanvim + crates | rustfmt (not bundled) | – | nvim-dap-lldb (codelldb) | neotest-rust |
shell | bash-language-server | shfmt | shellcheck | – | – |
html | vscode-html-language-server | prettierd | – | – | – |
css | – | prettierd | stylelint | – | – |
typescript | typescript-tools | deno fmt | eslint | – | – |
svelte | svelte-language-server | prettierd | eslint | – | – |
tailwind | tailwindcss-language-server | – | – | – | – |
json | vscode-json-language-server | deno fmt | – | – | – |
yaml | yaml-language-server | – | – | – | – |
sql | sqls | – | – | – | – |
docker | dockerfile-language-server | – | – | – | – |
markdown | – | deno fmt | – | – | – |
Details
Lua
- LSP:
lua_lswith signature help;vimandhlare known globals and themissing-fieldsdiagnostic is disabled. lazydev provides completion for the Neovim Lua API. - Format: stylua.
- Test: neotest-plenary for plenary/busted test files.
Nix
- LSP: nil.
- Format: alejandra.
- Lint: statix.
Python
- LSP: pyright.
- Format: isort, then
ruff format– both not bundled. - Lint: ruff – not bundled.
- DAP: nvim-dap-python.
- Test: neotest-python with
justMyCode = false, so debugging a test also steps into library code.
Go
- LSP: gopls.
- Format: gofmt (not bundled), then golines.
- Lint: staticcheck – not bundled.
- DAP: nvim-dap-go.
Rust
- LSP: rustaceanvim, plus crates.nvim for
Cargo.toml. rust-analyzer is not bundled, so the toolchain version of the project is used. - Format: rustfmt – not bundled.
- DAP: nvim-dap-lldb using the bundled
codelldbadapter from the vscode-lldb extension. - Test: neotest-rust.
Shell
Applies to the sh and bash filetypes.
- LSP: bash-language-server.
- Format: shfmt.
- Lint: shellcheck.
HTML
- LSP: vscode-html-language-server.
- Format: prettierd for
htmlandhtmldjango.
CSS
- Format: prettierd.
- Lint: stylelint.
TypeScript
Applies to the javascript and typescript filetypes.
- LSP: typescript-tools.nvim.
- Format:
deno fmt. - Lint: eslint.
Svelte
- LSP: svelte-language-server.
- Format: prettierd.
- Lint: eslint.
Tailwind, JSON, YAML, SQL, Docker
- Tailwind: tailwindcss-language-server.
- JSON: vscode-json-language-server; formatting with
deno fmt. - YAML: yaml-language-server.
- SQL: sqls. The default SQL omni-completion mappings of Neovim are disabled
(
omni_sql_no_default_maps). - Docker: dockerfile-language-server.
Markdown
- Format:
deno fmt. - Render: render-markdown.nvim. Rendering is off by default and toggled per
buffer with
<leader>mt;<leader>mpopens a preview.
Plugins
Each plugin is configured in its own module under nixvim/plugins/. Plugins
marked with an option can be switched off (see Options); all
others are always part of the configuration.
Navigation and search
snacks.nvim
Always enabled. Enabled snacks: animate, bigfile, dim, indent, input,
lazygit, notifier, picker, quickfile, scope, scratch (markdown
scratch buffers), statuscolumn, terminal and words.
The pickers cover files, grep, buffers, recent projects, undo history, LSP definitions/references/symbols, diagnostics, location and quickfix lists, TODO comments and notifications. See Keymaps.
neo-tree
Always enabled. Opens as a floating window (<C-o>) with rounded borders, shows
hidden and ignored files, follows the current file and closes when it is the
last window. Inside the tree, l opens and h closes a node.
bufferline
Option zenvim.bufferline.enable. Always visible, slope separators, LSP
diagnostic counts per buffer, close button on hover and an offset for the
neotest summary. Colors match rose-pine.
mini.map
Option zenvim.miniMap.enable. Minimap with search, diagnostic and gitsigns
integrations, toggled with <leader>mm.
persistence.nvim
Option zenvim.persistence.enable. Sessions are stored per directory and git
branch under stdpath("state")/sessions/ and only saved when at least one file
buffer is open.
Editing
| Plugin | Purpose |
|---|---|
| blink.cmp | Completion from LSP, path, snippets, buffer and ripgrep; Rust fuzzy matcher |
| blink-ripgrep | Project-wide word completion (from 3 characters, up to 1 MB files, ignore case) |
| treesitter | All grammars, highlighting, indentation and folding (foldlevel = 99) |
| autoclose | Auto-pairs, disabled when the cursor touches a character |
| nvim-surround | Add, change and delete surrounding pairs |
| nvim-cursorline | Highlights the cursor line number and underlines the word under the cursor (from 3 characters) |
| todo-comments | Highlights TODO, FIXME & co. and lists them in a picker (<leader>lt) |
| vim-sleuth | Detects indentation from the file |
| direnv | Loads .envrc environments, so project tools are found |
| which-key | Shows available mappings after pressing <leader> |
Language tooling
These plugins are only enabled if at least one language uses the respective feature.
| Plugin | Enabled when any language uses | Notes |
|---|---|---|
| lspconfig + inlay hints | lsp | K hover, <C-k> signature help, <leader>lR restart |
| conform.nvim | format | Formatting on demand with <leader>lf (async) |
| nvim-lint | lint | Runs on BufReadPost, BufWritePost and InsertLeave |
| nvim-dap + nvim-dap-ui | dap | UI at the bottom with console, watches and breakpoints |
| neotest + nvim-coverage | test | Rounded floating windows, coverage reloads automatically |
UI
lualine and navic
Always enabled. The statusline shows mode, file type and name, git diff,
diagnostics, the attached LSP clients, search count, branch, position and file
format. The winbar shows the working directory, navic breadcrumbs (depth 3) and,
depending on the enabled features, macro recording state (noice), DAP status
(any language with dap) and typing speed (tacho).
rose-pine
Always enabled, variant main. The background is transparent unless running in
Neovide.
tiny-inline-diagnostic
Always enabled and replaces Neovim’s virtual text: multi-line diagnostics with source, count and icons, disabled in neo-tree.
noice.nvim
Option zenvim.noice.enable. Presets: bottom search, command palette, LSP doc
borders, long messages in a split and inc-rename. <leader>na shows the message
history.
Git
- gitsigns (always): signs in the status column, inline blame of the current line, hunk preview, stage and reset.
- Lazygit (always): opened through snacks with
<leader>gg;lazygitis bundled.
AI
claudecode.nvim
Option zenvim.ai.claudecode.enable. Runs Claude Code in a floating snacks
terminal (90 % of the screen, rounded border). <C-,> opens or focuses it and,
inside the terminal, hides it again. The claude CLI is not bundled.
Just for fun
| Plugin | Option | What it does |
|---|---|---|
| tacho.nvim | zenvim.tacho.enable | Words per minute in the winbar |
| pentatonic.nvim | zenvim.pentatonic.enable | Plays pentatonic notes while typing (volume 5 %) |
| duck.nvim | zenvim.duck.enable | A duck that walks across the buffer |
| cellular-automaton | zenvim.cellularAutomaton.enable | “Make it rain” and “Game of life” animations |
Editor defaults
General settings live in nixvim/options.nix, nixvim/autocmds.nix,
nixvim/smartnumber.nix and nixvim/packages.nix. They are part of every
profile.
Options
| Setting | Value | Effect |
|---|---|---|
mapleader, maplocalleader | Space | Leader for all custom mappings |
clipboard | unnamedplus | Yank and paste use the system clipboard |
tabstop, shiftwidth | 4 | Default indentation (vim-sleuth adapts it per file) |
expandtab | on | Spaces instead of tabs |
wrap | off | No soft wrapping |
number | on | Line numbers, see smart numbers |
cursorlineopt | number | Only the line number of the cursor line is highlighted |
ignorecase, smartcase | on | Case-insensitive search unless it contains capitals |
hlsearch | on | Highlight search matches |
inccommand | split | Live preview of :s in a split |
undofile | on | Persistent undo history |
breakindent | on | Wrapped lines keep their indentation |
exrc | on | Loads trusted project-local .nvim.lua |
mousemoveevent | on | Hover effects (e.g. bufferline close button) |
termguicolors | on | 24-bit colors |
netrw is disabled in favour of neo-tree. Diagnostic signs use Nerd Font icons; inline diagnostic text comes from tiny-inline-diagnostic.
Autocommands and commands
formatoptionslosesc,randofor every file type, so comments are not continued automatically.- Empty, unnamed buffers are closed as soon as a real file is opened.
- Yanked text is briefly highlighted.
:Wand:Qbehave like:wand:q(with!support).
Smart relative line numbers
Relative line numbers are shown in the focused window in normal mode. They switch to absolute numbers in insert mode, on the command line, in unfocused windows and when Neovim loses focus. Pickers, terminals and neo-tree are excluded.
Neovide
When running in Neovide, zenvim sets:
- the font
JetBrainsMono Nerd Font(13 pt) withNoto Color Emojias fallback and a line spacing of 5, - 16 px padding, rounded floating windows and slight transparency,
- the
pixiedustcursor effect and short scroll animations, <C-=>and<C-->to zoom in and out.
The colorscheme is opaque in Neovide and transparent everywhere else.
Bundled tools
fd, gcc, lazygit, noto-fonts-color-emoji, ripgrep and tree-sitter
are added to the end of PATH (extraPackagesAfter). Like all bundled language
tools, they only serve as fallback: if a project’s devshell provides a different
version, that version is used.
Troubleshooting
Icons are missing or show as boxes
The statusline, bufferline, neo-tree and the diagnostic signs use Nerd Font
glyphs. Configure a Nerd Font in your terminal. In
Neovide, JetBrainsMono Nerd Font must be installed.
A formatter, linter or language server does nothing
- Check that the language and the feature are enabled, e.g.
zenvim.languages.go.lint.enable. Theminimalprofile disables all languages. - Check whether the tool is bundled.
claude,rust-analyzer,rustfmt,gofmt,staticcheck,ruffandisortare not – install them in the system or the project environment (see Languages). - Run
:checkhealth vim.lspor:ConformInfofor details.
The wrong tool version is used
Bundled tools are appended to the end of PATH, so a version from the project
environment always wins. That is intended: projects can pin their own formatter
or language server, e.g. through a devshell loaded by direnv. If you expect the
bundled version, check :!which <tool>.
The project environment is not picked up
direnv.vim loads .envrc files, but only after you allowed them with
direnv allow in the shell. Start Neovim from inside the project directory.
claudecode opens but nothing happens
The claude CLI is not bundled. Install Claude Code and make sure claude is
on PATH, or disable the integration with
zenvim.ai.claudecode.enable = false.
The configuration does not build
nix flake check builds one check per profile that starts Neovim headless and
fails on any error or warning. Run it to see the message:
nix flake check -L
Getting help
Open an issue at github.com/farbzen/zenvim.nix.
Nixvim options
Generated from the option declarations of the full profile.
zenvim.ai.claudecode.enable
Whether to enable the Claude Code integration.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.bufferline.enable
Whether to enable the bufferline.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.cellularAutomaton.enable
Whether to enable cellular-automaton.nvim.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.duck.enable
Whether to enable duck.nvim.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.css.enable
Whether to enable support for css.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.css.format.enable
Whether to enable the formatter for css.
Type: boolean
Default:
config.zenvim.languages.css.enable
Declared by:
zenvim.languages.css.lint.enable
Whether to enable the linter for css.
Type: boolean
Default:
config.zenvim.languages.css.enable
Declared by:
zenvim.languages.docker.enable
Whether to enable support for docker.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.docker.lsp.enable
Whether to enable the language server for docker.
Type: boolean
Default:
config.zenvim.languages.docker.enable
Declared by:
zenvim.languages.go.enable
Whether to enable support for go.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.go.dap.enable
Whether to enable the debug adapter for go.
Type: boolean
Default:
config.zenvim.languages.go.enable
Declared by:
zenvim.languages.go.format.enable
Whether to enable the formatter for go.
Type: boolean
Default:
config.zenvim.languages.go.enable
Declared by:
zenvim.languages.go.lint.enable
Whether to enable the linter for go.
Type: boolean
Default:
config.zenvim.languages.go.enable
Declared by:
zenvim.languages.go.lsp.enable
Whether to enable the language server for go.
Type: boolean
Default:
config.zenvim.languages.go.enable
Declared by:
zenvim.languages.html.enable
Whether to enable support for html.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.html.format.enable
Whether to enable the formatter for html.
Type: boolean
Default:
config.zenvim.languages.html.enable
Declared by:
zenvim.languages.html.lsp.enable
Whether to enable the language server for html.
Type: boolean
Default:
config.zenvim.languages.html.enable
Declared by:
zenvim.languages.json.enable
Whether to enable support for json.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.json.format.enable
Whether to enable the formatter for json.
Type: boolean
Default:
config.zenvim.languages.json.enable
Declared by:
zenvim.languages.json.lsp.enable
Whether to enable the language server for json.
Type: boolean
Default:
config.zenvim.languages.json.enable
Declared by:
zenvim.languages.lua.enable
Whether to enable support for lua.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.lua.format.enable
Whether to enable the formatter for lua.
Type: boolean
Default:
config.zenvim.languages.lua.enable
Declared by:
zenvim.languages.lua.lsp.enable
Whether to enable the language server for lua.
Type: boolean
Default:
config.zenvim.languages.lua.enable
Declared by:
zenvim.languages.lua.test.enable
Whether to enable the test adapter for lua.
Type: boolean
Default:
config.zenvim.languages.lua.enable
Declared by:
zenvim.languages.markdown.enable
Whether to enable support for markdown.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.markdown.format.enable
Whether to enable the formatter for markdown.
Type: boolean
Default:
config.zenvim.languages.markdown.enable
Declared by:
zenvim.languages.markdown.render.enable
Whether to enable the in-buffer rendering for markdown.
Type: boolean
Default:
config.zenvim.languages.markdown.enable
Declared by:
zenvim.languages.nix.enable
Whether to enable support for nix.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.nix.format.enable
Whether to enable the formatter for nix.
Type: boolean
Default:
config.zenvim.languages.nix.enable
Declared by:
zenvim.languages.nix.lint.enable
Whether to enable the linter for nix.
Type: boolean
Default:
config.zenvim.languages.nix.enable
Declared by:
zenvim.languages.nix.lsp.enable
Whether to enable the language server for nix.
Type: boolean
Default:
config.zenvim.languages.nix.enable
Declared by:
zenvim.languages.python.enable
Whether to enable support for python.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.python.dap.enable
Whether to enable the debug adapter for python.
Type: boolean
Default:
config.zenvim.languages.python.enable
Declared by:
zenvim.languages.python.format.enable
Whether to enable the formatter for python.
Type: boolean
Default:
config.zenvim.languages.python.enable
Declared by:
zenvim.languages.python.lint.enable
Whether to enable the linter for python.
Type: boolean
Default:
config.zenvim.languages.python.enable
Declared by:
zenvim.languages.python.lsp.enable
Whether to enable the language server for python.
Type: boolean
Default:
config.zenvim.languages.python.enable
Declared by:
zenvim.languages.python.test.enable
Whether to enable the test adapter for python.
Type: boolean
Default:
config.zenvim.languages.python.enable
Declared by:
zenvim.languages.rust.enable
Whether to enable support for rust.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.rust.dap.enable
Whether to enable the debug adapter for rust.
Type: boolean
Default:
config.zenvim.languages.rust.enable
Declared by:
zenvim.languages.rust.format.enable
Whether to enable the formatter for rust.
Type: boolean
Default:
config.zenvim.languages.rust.enable
Declared by:
zenvim.languages.rust.lsp.enable
Whether to enable the language server for rust.
Type: boolean
Default:
config.zenvim.languages.rust.enable
Declared by:
zenvim.languages.rust.test.enable
Whether to enable the test adapter for rust.
Type: boolean
Default:
config.zenvim.languages.rust.enable
Declared by:
zenvim.languages.shell.enable
Whether to enable support for shell.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.shell.format.enable
Whether to enable the formatter for shell.
Type: boolean
Default:
config.zenvim.languages.shell.enable
Declared by:
zenvim.languages.shell.lint.enable
Whether to enable the linter for shell.
Type: boolean
Default:
config.zenvim.languages.shell.enable
Declared by:
zenvim.languages.shell.lsp.enable
Whether to enable the language server for shell.
Type: boolean
Default:
config.zenvim.languages.shell.enable
Declared by:
zenvim.languages.sql.enable
Whether to enable support for sql.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.sql.lsp.enable
Whether to enable the language server for sql.
Type: boolean
Default:
config.zenvim.languages.sql.enable
Declared by:
zenvim.languages.svelte.enable
Whether to enable support for svelte.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.svelte.format.enable
Whether to enable the formatter for svelte.
Type: boolean
Default:
config.zenvim.languages.svelte.enable
Declared by:
zenvim.languages.svelte.lint.enable
Whether to enable the linter for svelte.
Type: boolean
Default:
config.zenvim.languages.svelte.enable
Declared by:
zenvim.languages.svelte.lsp.enable
Whether to enable the language server for svelte.
Type: boolean
Default:
config.zenvim.languages.svelte.enable
Declared by:
zenvim.languages.tailwind.enable
Whether to enable support for tailwind.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.tailwind.lsp.enable
Whether to enable the language server for tailwind.
Type: boolean
Default:
config.zenvim.languages.tailwind.enable
Declared by:
zenvim.languages.typescript.enable
Whether to enable support for typescript.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.typescript.format.enable
Whether to enable the formatter for typescript.
Type: boolean
Default:
config.zenvim.languages.typescript.enable
Declared by:
zenvim.languages.typescript.lint.enable
Whether to enable the linter for typescript.
Type: boolean
Default:
config.zenvim.languages.typescript.enable
Declared by:
zenvim.languages.typescript.lsp.enable
Whether to enable the language server for typescript.
Type: boolean
Default:
config.zenvim.languages.typescript.enable
Declared by:
zenvim.languages.yaml.enable
Whether to enable support for yaml.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.languages.yaml.lsp.enable
Whether to enable the language server for yaml.
Type: boolean
Default:
config.zenvim.languages.yaml.enable
Declared by:
zenvim.miniMap.enable
Whether to enable the mini.map minimap.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.noice.enable
Whether to enable noice.nvim.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.pentatonic.enable
Whether to enable pentatonic.nvim.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.persistence.enable
Whether to enable session management with persistence.nvim.
Type: boolean
Default:
true
Example:
true
Declared by:
zenvim.tacho.enable
Whether to enable the tacho.nvim typing speed meter.
Type: boolean
Default:
true
Example:
true
Declared by:
NixOS options
Generated from the option declarations of nixosModules.zenvim.
programs.zenvim.enable
Whether to enable zenvim, a Neovim configuration built with Nixvim.
Type: boolean
Default:
false
Example:
true
Declared by:
programs.zenvim.package
The resulting zenvim package.
Type: package (read only)
Declared by:
programs.zenvim.defaultEditor
Whether to set EDITOR to nvim.
Type: boolean
Default:
true
Declared by:
programs.zenvim.profile
The zenvim profile the configuration starts from.
Type: one of “full”, “minimal”
Default:
"full"
Declared by:
programs.zenvim.settings
Nixvim module applied on top of the selected profile, e.g. to toggle single zenvim.* options.
Type: module
Default:
{ }
Example:
{
zenvim.languages.python.enable = true;
zenvim.ai.claudecode.enable = false;
}
Declared by:
Development
Repository layout
| Path | Content |
|---|---|
flake.nix | Flake outputs, built with flake-parts and the Nixvim flake module |
nixvim/*.nix | General settings: options, autocommands, smart numbers, packages |
nixvim/plugins/ | One module per plugin, imported by default.nix |
nixvim/languages/ | One module per language, plus features.nix and default.nix |
profiles/full.nix | Imports everything from nixvim/ |
profiles/minimal.nix | Sets all optional features to lib.mkDefault false |
modules/nixos.nix | The NixOS module programs.zenvim |
nix/docs.nix | Builds this book, including the generated option reference |
docs/, book.toml | Sources of this book |
Everyday commands
nix run . # run the full profile from the checkout
nix run .#minimal # run the minimal profile
nix flake check # start every profile headless, fail on errors/warnings
nix fmt # format Nix files with alejandra
nix build .#docs # build this book into ./result
A devenv.nix with Nix language support is available for direnv/devenv users.
Adding a language
-
Add the language and its features to
nixvim/languages/features.nix:zig = ["lsp" "format"];This creates
zenvim.languages.zig.enable,…lsp.enableand…format.enable, importsnixvim/languages/zig.nixand makes theminimalprofile disable it. -
Create
nixvim/languages/zig.nixand guard each feature with its option:{config, lib, pkgs, ...}: let cfg = config.zenvim.languages.zig; in { config = lib.mkMerge [ (lib.mkIf cfg.lsp.enable { lsp.servers.zls = { enable = true; packageFallback = true; }; }) (lib.mkIf cfg.format.enable { plugins.conform-nvim.settings.formatters_by_ft.zig = ["zigfmt"]; }) ]; }Use
packageFallback = truefor language servers andextraPackagesAfterfor other tools, so project versions take precedence. -
Shared plugins such as conform or nvim-lint switch on automatically through
anyLanguageUses "<feature>", which is passed to every module as an argument bynixvim/languages/default.nix.
A new feature kind (besides lsp, format, lint, dap, test, render)
also needs a description in featureDescriptions in
nixvim/languages/default.nix.
Adding a plugin
Create nixvim/plugins/<name>.nix and import it in
nixvim/plugins/default.nix. A plugin that is part of every profile simply sets
its configuration. An optional plugin declares a switch that defaults to true:
{config, lib, ...}: {
options.zenvim.<name>.enable = lib.mkEnableOption "<description>" // {default = true;};
config = lib.mkIf config.zenvim.<name>.enable {
plugins.<name>.enable = true;
};
}
Then add <name> = disabled; to profiles/minimal.nix and document the option
in Options. The
option reference
picks it up automatically.
Keymaps belong to the module of the plugin they use and always get an
options.desc, which is shown by which-key. Add them to Keymaps
as well.
Documentation
This book is written with mdBook. The
sources are in docs/, the configuration in book.toml. nix/docs.nix
generates the pages under docs/reference/ from the option declarations with
nixosOptionsDoc before running mdbook build; they are not checked in.
nix build .#docs
xdg-open result/index.html
A GitHub Actions workflow (.github/workflows/docs.yml) builds the book on
every push to main and publishes it to GitHub Pages; pull requests only build
it.
Note: The deployment only works once the repository’s Pages source is set to “GitHub Actions” (Settings → Pages → Build and deployment → Source).
Versioning and changelog
The project follows Semantic Versioning; the current
version is stored in version. Changes are recorded in the
changelog, and commit messages follow
Conventional Commits.
Changelog
All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.3.0 - 2026-11-11
Added
- Documentation website built with mdBook and published to GitHub Pages, including an option reference generated from the module declarations.
- Flake output
packages.<system>.docsthat builds the documentation website.
Changed
nixosModules.zenvimcarries its file location, so evaluation errors point tomodules/nixos.nix.
0.2.1 - 2026-11-06
Added
- The project is licensed under the MIT license.
0.2.0 - 2026-11-06
Added
- Flake outputs
nixvimModules.fullandnixosModules.zenvimas named equivalents ofnixvimModules.defaultandnixosModules.default.
Changed
- The code moved from
src/tonixvim/(Nixvim configuration),profiles/(fullandminimal) andmodules/(NixOS module). The built packages are unchanged.
0.1.0 - 2026-11-06
Added
- Neovim configuration built with Nixvim, ported from the previous
nix-wrapper-modulessetup with Lua dotfiles: options, autocommands, smart relative line numbers, Neovide settings and all keymaps. - Plugins: autoclose, blink.cmp with ripgrep source, bufferline, claudecode, conform, nvim-cursorline, nvim-dap with dap-ui and Python/Go/LLDB adapters, gitsigns, nvim-lint, lualine with navic, mini.map, neotest with Python/Plenary/Rust adapters and nvim-coverage, neo-tree, noice, persistence, render-markdown, vim-sleuth, snacks with todo-comments, tiny-inline-diagnostic, treesitter with all grammars, rustaceanvim, crates, typescript-tools, lazydev, which-key, nvim-surround, direnv, rose-pine as well as cellular-automaton, duck, pentatonic and tacho.
- Language servers for Lua, Python, Go, Svelte, HTML, SQL, Tailwind CSS, JSON,
YAML, Dockerfile, Bash and Nix, plus formatters, linters and CLI tools. All of
them are appended to
PATH, so project-local versions take precedence. - Boolean options under
zenvim.*:zenvim.languages.<language>.enablefor 16 languages (Lua, Nix, Python, Go, Rust, Shell, HTML, CSS, TypeScript, Svelte, Tailwind CSS, JSON, YAML, SQL, Docker, Markdown) with sub-options for LSP, formatter, linter, DAP, test adapter and Markdown rendering, plus switches for claudecode, bufferline, mini.map, noice, persistence, tacho, pentatonic, duck and cellular-automaton. Shared tooling (LSP core, conform, nvim-lint, nvim-dap, neotest) and language-specific tools are only included when a language needs them. - Profiles
full(everything enabled) andminimal(no languages, only the essentials) as packagesfull,minimalanddefault(=full), with one Nixvim check per profile. - Flake outputs
nixvimModules.defaultandnixvimModules.minimalto build custom variants. - NixOS module
nixosModules.defaultwithprograms.zenvim.enable,profile,settings(Nixvim module on top of the profile),defaultEditorand the read-onlypackage.
Changed
- Plugins are no longer lazy-loaded; lze/lzextras and the
nixInfoglue are gone. - Treesitter parsers are provided by Nix only; on-demand parser installation was dropped.
- claudecode logs only warnings and errors.
Fixed
<leader>lfformats asynchronously as intended (async = trueinstead of the string"true").- nvim-coverage reloads automatically (
auto_reloadinstead of the misspelledauto_reafter). - The LLDB debug adapter points to the
codelldbbinary from nixpkgs.
Legal Notice
Information pursuant to § 5 DDG

Germany
Contact
Email: webmaster@farbzen.de
Responsible for the content pursuant to § 18 (2) MStV
Liability for links
This site contains links to external third-party websites whose content is beyond our control. The respective provider or operator of those pages is always responsible for their content. Links will be removed immediately as soon as any infringement becomes known.