Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

OutputPurpose
packages.<system>.full / .defaultNeovim with every feature enabled
packages.<system>.minimalNeovim with core editing only
packages.<system>.docsThis book as a static website
nixvimModules.full / .defaultThe complete Nixvim configuration, to build your own variant
nixvimModules.minimalProfile module that switches all optional features off
nixosModules.zenvim / .defaultAdds 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) and minimal (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

Prerequisites

  • Nix with the nix-command and flakes experimental features enabled.
  • A Nerd Font in your terminal; icons in the statusline, bufferline, file explorer and diagnostics rely on it.
  • An x86_64-linux or aarch64-linux system.

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’s flake.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:

ToolNeeded for
claudeclaudecode (zenvim.ai.claudecode)
rust-analyzerRust LSP via rustaceanvim
rustfmtRust formatting
gofmtGo formatting (together with bundled golines)
staticcheckGo linting
ruff, isortPython 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.

ProfileModulePackageContents
fullnixvimModules.fullpackages.<system>.fullAll languages with LSP, formatter, linter, DAP and test adapters, claudecode, bufferline and all extras.
minimalnixvimModules.minimalpackages.<system>.minimalCore 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.

LanguageSub-optionsTooling
lualsp, format, testlua_ls + lazydev, stylua, neotest-plenary
nixlsp, format, lintnil, alejandra, statix
pythonlsp, format, lint, dap, testpyright, isort + ruff_format, ruff, nvim-dap-python, neotest-python
golsp, format, lint, dapgopls, gofmt + golines, staticcheck, nvim-dap-go
rustlsp, format, dap, testrustaceanvim + crates, rustfmt, nvim-dap-lldb (codelldb), neotest-rust
shelllsp, format, lintbash-language-server, shfmt, shellcheck
htmllsp, formatvscode-html-language-server, prettierd (html, htmldjango)
cssformat, lintprettierd, stylelint
typescriptlsp, format, linttypescript-tools, deno fmt (JS/TS), eslint
sveltelsp, format, lintsvelte-language-server, prettierd, eslint
tailwindlsptailwindcss-language-server
jsonlsp, formatvscode-json-language-server, deno fmt
yamllspyaml-language-server
sqllspsqls
dockerlspdockerfile-language-server
markdownformat, renderdeno 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

OptionEnables
zenvim.ai.claudecode.enableclaudecode in a floating snacks terminal (<C-,>)
zenvim.bufferline.enablebufferline with <M-h>, <M-l>, <M-1>…<M-9>, <M-$>
zenvim.miniMap.enablemini.map minimap (<leader>mm)
zenvim.noice.enablenoice UI, macro recording status in the winbar, <leader>na
zenvim.persistence.enablesession management (<leader>ql, <leader>qL, <leader>qd)
zenvim.tacho.enabletyping speed in the winbar
zenvim.pentatonic.enablepentatonic typing sounds
zenvim.duck.enableduck.nvim (<leader>dh, <leader>dc, <leader>dC)
zenvim.cellularAutomaton.enablecellular-automaton (<leader>fml, <leader>gol)

NixOS module

nixosModules.zenvim (also available as nixosModules.default) provides programs.zenvim:

OptionTypeDefaultDescription
programs.zenvim.enableboolfalseInstall zenvim.
programs.zenvim.profile"full" or "minimal""full"Profile the configuration starts from.
programs.zenvim.settingsNixvim module{}Applied on top of the profile, e.g. to toggle zenvim.*.
programs.zenvim.defaultEditorbooltrueSet EDITOR to nvim.
programs.zenvim.packagepackage (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.

KeyAction
<C-o>Open file explorer (neo-tree, floating)
<C-M-o>List recent projects
<leader>ffFind file
<leader>fgFind content in files
<leader>fwFind current word or selection (n, x)
<leader>boShow open buffers
<leader>bwClose current buffer
<leader>bcClose 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>sOpen scratchpad
<leader>SSelect scratchpad
<leader>mmToggle minimap

LSP, diagnostics and formatting

KeyAction
KHover (when an LSP is attached)
<C-k>Signature documentation (LSP)
gdGo to definition
<leader>lrShow LSP references
<leader>lsShow LSP symbols
<leader>lRRestart LSP
<leader>thToggle LSP inlay hints
<leader>lfFormat current file
<leader>ldShow diagnostics for current buffer
<leader>lDShow diagnostics
<leader>llShow location list
<leader>lqShow quickfix list
<leader>ltShow TODO comments
<leader>luShow undo history

Git

KeyAction
<leader>ggOpen Lazygit
<leader>gbToggle git blame on current line
<leader>ghpPreview git hunk
<leader>ghPPreview git hunk inline
<leader>ghsStage git hunk
<leader>ghrReset git hunk

Tests and debugging

KeyAction
<leader>trRun current test(s)
<leader>tdDebug current test(s)
<leader>taAttach to current test(s)
<leader>toToggle test output panel
<leader>tsToggle test summary
<leader>tcsShow test coverage
<leader>tccClear test coverage
<leader>tcSShow 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>dbDAP: Toggle breakpoint
<leader>drDAP: Open REPL
<leader>duDAP: Open UI

Tools and UI

KeyAction
<C-,>Toggle Claude Code (also hides it in terminal mode)
<leader>ttToggle terminal
<leader>dsDim code around the current block
<leader>mpOpen markdown preview
<leader>mtToggle markdown rendering
<leader>nhShow notification history
<leader>naShow Noice message history
<leader>qlLoad the last session
<leader>qLSelect a session to load
<leader>qdDisable persistence (no save on exit)
<C-=>Zoom in (Neovide only)
<C-->Zoom out (Neovide only)

Just for fun

KeyAction
<leader>fmlMake it rain
<leader>golGame of life
<leader>dhHatch a duck
<leader>dcCook a duck
<leader>dCCook all ducks

Completion (blink.cmp)

KeyAction
<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

  • :W and :Q behave like :w and :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 of PATH, 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 to PATH.
  • Tools marked as not bundled below must come from the system or the project environment.

Overview

LanguageLSPFormatLintDAPTest
lualua_ls + lazydevstylua––neotest-plenary
nixnilalejandrastatix––
pythonpyrightisort + ruff_format (not bundled)ruff (not bundled)nvim-dap-pythonneotest-python
gogoplsgofmt (not bundled) + golinesstaticcheck (not bundled)nvim-dap-go–
rustrustaceanvim + cratesrustfmt (not bundled)–nvim-dap-lldb (codelldb)neotest-rust
shellbash-language-servershfmtshellcheck––
htmlvscode-html-language-serverprettierd–––
css–prettierdstylelint––
typescripttypescript-toolsdeno fmteslint––
sveltesvelte-language-serverprettierdeslint––
tailwindtailwindcss-language-server––––
jsonvscode-json-language-serverdeno fmt–––
yamlyaml-language-server––––
sqlsqls––––
dockerdockerfile-language-server––––
markdown–deno fmt–––

Details

Lua

  • LSP: lua_ls with signature help; vim and hl are known globals and the missing-fields diagnostic 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 codelldb adapter 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 html and htmldjango.

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>mp opens 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.

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

PluginPurpose
blink.cmpCompletion from LSP, path, snippets, buffer and ripgrep; Rust fuzzy matcher
blink-ripgrepProject-wide word completion (from 3 characters, up to 1 MB files, ignore case)
treesitterAll grammars, highlighting, indentation and folding (foldlevel = 99)
autocloseAuto-pairs, disabled when the cursor touches a character
nvim-surroundAdd, change and delete surrounding pairs
nvim-cursorlineHighlights the cursor line number and underlines the word under the cursor (from 3 characters)
todo-commentsHighlights TODO, FIXME & co. and lists them in a picker (<leader>lt)
vim-sleuthDetects indentation from the file
direnvLoads .envrc environments, so project tools are found
which-keyShows available mappings after pressing <leader>

Language tooling

These plugins are only enabled if at least one language uses the respective feature.

PluginEnabled when any language usesNotes
lspconfig + inlay hintslspK hover, <C-k> signature help, <leader>lR restart
conform.nvimformatFormatting on demand with <leader>lf (async)
nvim-lintlintRuns on BufReadPost, BufWritePost and InsertLeave
nvim-dap + nvim-dap-uidapUI at the bottom with console, watches and breakpoints
neotest + nvim-coveragetestRounded 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; lazygit is 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

PluginOptionWhat it does
tacho.nvimzenvim.tacho.enableWords per minute in the winbar
pentatonic.nvimzenvim.pentatonic.enablePlays pentatonic notes while typing (volume 5 %)
duck.nvimzenvim.duck.enableA duck that walks across the buffer
cellular-automatonzenvim.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

SettingValueEffect
mapleader, maplocalleaderSpaceLeader for all custom mappings
clipboardunnamedplusYank and paste use the system clipboard
tabstop, shiftwidth4Default indentation (vim-sleuth adapts it per file)
expandtabonSpaces instead of tabs
wrapoffNo soft wrapping
numberonLine numbers, see smart numbers
cursorlineoptnumberOnly the line number of the cursor line is highlighted
ignorecase, smartcaseonCase-insensitive search unless it contains capitals
hlsearchonHighlight search matches
inccommandsplitLive preview of :s in a split
undofileonPersistent undo history
breakindentonWrapped lines keep their indentation
exrconLoads trusted project-local .nvim.lua
mousemoveeventonHover effects (e.g. bufferline close button)
termguicolorson24-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

  • formatoptions loses c, r and o for 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.
  • :W and :Q behave like :w and :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) with Noto Color Emoji as fallback and a line spacing of 5,
  • 16 px padding, rounded floating windows and slight transparency,
  • the pixiedust cursor 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

  1. Check that the language and the feature are enabled, e.g. zenvim.languages.go.lint.enable. The minimal profile disables all languages.
  2. Check whether the tool is bundled. claude, rust-analyzer, rustfmt, gofmt, staticcheck, ruff and isort are not – install them in the system or the project environment (see Languages).
  3. Run :checkhealth vim.lsp or :ConformInfo for 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

PathContent
flake.nixFlake outputs, built with flake-parts and the Nixvim flake module
nixvim/*.nixGeneral 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.nixImports everything from nixvim/
profiles/minimal.nixSets all optional features to lib.mkDefault false
modules/nixos.nixThe NixOS module programs.zenvim
nix/docs.nixBuilds this book, including the generated option reference
docs/, book.tomlSources 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

  1. Add the language and its features to nixvim/languages/features.nix:

    zig = ["lsp" "format"];
    

    This creates zenvim.languages.zig.enable, …lsp.enable and …format.enable, imports nixvim/languages/zig.nix and makes the minimal profile disable it.

  2. Create nixvim/languages/zig.nix and 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 = true for language servers and extraPackagesAfter for other tools, so project versions take precedence.

  3. Shared plugins such as conform or nvim-lint switch on automatically through anyLanguageUses "<feature>", which is passed to every module as an argument by nixvim/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>.docs that builds the documentation website.

Changed

  • nixosModules.zenvim carries its file location, so evaluation errors point to modules/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.full and nixosModules.zenvim as named equivalents of nixvimModules.default and nixosModules.default.

Changed

  • The code moved from src/ to nixvim/ (Nixvim configuration), profiles/ (full and minimal) and modules/ (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-modules setup 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>.enable for 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) and minimal (no languages, only the essentials) as packages full, minimal and default (= full), with one Nixvim check per profile.
  • Flake outputs nixvimModules.default and nixvimModules.minimal to build custom variants.
  • NixOS module nixosModules.default with programs.zenvim.enable, profile, settings (Nixvim module on top of the profile), defaultEditor and the read-only package.

Changed

  • Plugins are no longer lazy-loaded; lze/lzextras and the nixInfo glue are gone.
  • Treesitter parsers are provided by Nix only; on-demand parser installation was dropped.
  • claudecode logs only warnings and errors.

Fixed

  • <leader>lf formats asynchronously as intended (async = true instead of the string "true").
  • nvim-coverage reloads automatically (auto_reload instead of the misspelled auto_reafter).
  • The LLDB debug adapter points to the codelldb binary from nixpkgs.

Legal Notice

Information pursuant to § 5 DDG

Name and address
Germany

Contact

Email: webmaster@farbzen.de

Responsible for the content pursuant to § 18 (2) MStV

Name and address

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.