A minimal, coherent, production-ready Neovim configuration for NixOS and Home Manager. Built on mini.nvim with a "one tool per task" philosophy.
This configuration follows three core principles:
- Minimal - Only essential tools. One plugin per task.
- Coherent - Clear, interconnected modules. Easy to understand and extend.
- mini.nvim-first - Uses mini.nvim's lightweight, focused ecosystem for UI, navigation, and sessions.
- Tier 1 (Builtins) - Neovim's builtin LSP, diagnostics, terminal, DAP
- Tier 2 (mini.nvim) - UI (clue, notify), navigation (pick, files), sessions
- Tier 3 (Specialized) - Language tools, formatters, linters, git integration
| Feature | Tool | Included |
|---|---|---|
| UI and Navigation | mini.nvim (clue, pick, files, notify) | Yes |
| Code Completion | nvim-cmp with nvim-lspconfig | Yes |
| Formatting | conform-nvim | Yes |
| Linting | nvim-lint | Yes |
| Debugging | nvim-dap and nvim-dap-ui | Yes |
| Testing | neotest (Python, Rust, Vitest) | Yes |
| Git | gitsigns, neogit, diffview | Yes |
| Syntax Highlighting | nvim-treesitter with context | Yes |
| Terminal | Builtin (no plugin) | Yes |
| Sessions | mini.sessions | Yes |
- NixOS or Home Manager (Linux/macOS)
- Flakes enabled (
experimental-features = nix-command flakes) - Neovim 0.11+ (automatically installed via Nix)
Test the configuration without modifying your system:
cd /path/to/nix_neovim_v2
nix flake show
nix flake develop
nvim --versionAdd to your Home Manager configuration:
{
imports = [
nix-neovim-v2.homeManagerModules.default
];
home.stateVersion = "23.11";
}Then apply:
home-manager switch --flake ".#your-host"This section guides you through testing all configuration features.
cd /home/andrew/Documents/Projects/IDE/nix_neovim_v2
nix developThis provides Neovim 0.11+, all LSP servers, formatters, linters, and test runners.
Start Neovim:
nvimVerify that it starts without errors. Check:
- Status line appears at the bottom
- No error messages or red highlights
- No diagnostic markers in the gutter
Test basic keybindings:
| Key | Action | Expected |
|---|---|---|
<space> |
Show available commands | Menu appears with key hints |
<C-s> |
Save | File is written |
<leader>h |
Clear search highlight | Highlights disappear |
<leader>q |
Quit | Neovim exits |
Test file finding and buffer switching:
| Key | Action | Test |
|---|---|---|
<leader>ff |
Find files | Type "README", should show README.md |
<leader>fg |
Live grep | Type "mini", shows matching lines |
<leader>fb |
Find buffers | Shows open buffers |
<leader>e |
Toggle file explorer | Opens/closes sidebar |
In the file explorer:
- Press
<leader>eto open - Navigate to
nvimdirectory - Press Enter to expand
- Find
init.luaand open it - Press
<leader>eagain to close
Create a test file:
# In Neovim:
:e test.luaType this code with an intentional error:
local function greet(name)
print("Hello " .. nam) -- typo: 'nam' instead of 'name'
end
greet("World")Save the file:
:w test.lua
Check for diagnostics:
- A red underline appears under
nam - The line number shows "E" in the gutter
Test diagnostic navigation:
| Key | Action |
|---|---|
]d |
Jump to next error |
[d |
Jump to previous error |
<leader>x |
Open quickfix list |
Test code completion:
- Position cursor after the opening parenthesis:
greet( - Press
<C-n>to trigger completion - A menu should appear with available variables
- Use arrow keys to navigate and Enter to select
Fix the typo:
- Change
namtoname - Save the file with
<C-s> - The error should disappear
Clean up:
:bd test.luaTest the terminal toggle:
| Key | Action |
|---|---|
<A-i> |
Toggle terminal |
Test sequence:
- Press
<A-i>to open the terminal at the bottom (15 lines) - Type:
echo "Hello from terminal" - Press Enter - the command executes
- Press
<A-i>to close the terminal - Press
<A-i>again - the terminal reopens with history preserved
Open a file in this git repository:
# In Neovim:
:e nvim/lua/config/options.luaCheck for git gutter signs:
- Look at the left edge (sign column)
- Changed lines show
│(pipe) - Deleted lines show
_ - Top-deleted lines show
‾
Test git keybindings:
| Key | Action |
|---|---|
]h |
Next hunk |
[h |
Previous hunk |
<leader>gp |
Preview hunk (shows diff) |
<leader>gb |
Show blame for current line |
<leader>gd |
Show diff for current file |
Test hunk operations:
- Position cursor in a changed hunk
- Press
<leader>gsto stage the hunk - Press
<leader>guto undo staging
Test saving and restoring sessions:
1. Open multiple files:
:e nvim/lua/config/options.lua
:e nvim/lua/config/keymaps.lua
2. Navigate to specific lines in each file
3. Save session:
<leader>qs
4. Quit Neovim:
<leader>qq
5. Restart:
nvim
6. Restore session:
<leader>qr
Select the session from the picker
Verify:
- All files reopen
- Cursor positions are preserved
- Window layout is restored
Test runtime configuration toggles:
| Key | Action |
|---|---|
<leader>ul |
Toggle line numbers |
<leader>ur |
Toggle relative numbers |
<leader>uw |
Toggle word wrap |
<leader>uh |
Toggle inlay hints |
<leader>uf |
Toggle format on save |
Each toggle should take effect immediately without requiring a restart.
Create a test file:
# In Neovim:
:e test_format.pyType poorly formatted code:
def hello( x,y ):
return x+yTest auto-formatting:
- Save with
<C-s> - The code should reformat automatically
- If it doesn't, verify formatting is enabled:
<leader>uf(should show "enabled")
Test linting:
- Add an unused import:
import os - Save the file
- A diagnostic marker should appear on the import line
- Open the quickfix list with
<leader>xto see all linting errors
Clean up:
:bd test_format.pyCreate a test script:
# In Neovim:
:e test_debug.pyType this code:
def add(a, b):
result = a + b # We'll break here
return result
print(add(2, 3))Test breakpoints and debugging:
- Position cursor on the
result = a + bline - Press
<leader>dbto set a breakpoint - Press
<leader>dcto start debugging - The debugger should pause at the breakpoint
- The DAP UI panel should open showing local variables
Test stepping:
| Key | Action |
|---|---|
<leader>dO |
Step over (execute current line) |
<leader>di |
Step into (enter function) |
<leader>do |
Step out (exit function) |
<leader>dt |
Terminate debug session |
View variables:
- Look at the DAP UI panel on the left
- Local variables (a, b, result) should be visible
Clean up:
:bd test_debug.pyNote: DAP setup requires language-specific adapters. Python uses debugpy (installed). Rust and C/C++ use lldb. Other languages may need additional configuration.
Create a test file:
# In Neovim:
:e test_sample.pyType these tests:
def test_addition():
assert 2 + 2 == 4
def test_failure():
assert 1 + 1 == 3 # This will failRun tests:
| Key | Action |
|---|---|
<leader>tt |
Run all tests in file |
<leader>tr |
Run test under cursor |
<leader>ts |
Toggle test summary |
<leader>to |
Toggle test output |
Check results:
- Passing tests show with a check mark
- Failing tests show with an X
- Output panel shows detailed results
Clean up:
:bd test_sample.pyIf you have an Obsidian vault:
:ObsidianOpenThis opens your configured Obsidian vault. You can also create new notes with:
:ObsidianNew NoteNamenix_neovim_v2/
├── flake.nix # Flake inputs and outputs
├── hm-module.nix # Home Manager module
├── plugins.nix # Plugin list
├── nvim/
│ ├── init.lua # Entry point
│ ├── lua/
│ │ ├── config/
│ │ │ ├── init.lua # Loader
│ │ │ ├── options.lua # Vim options
│ │ │ ├── keymaps.lua # Keybindings
│ │ │ └── autocmds.lua # Auto-commands
│ │ └── plugins/
│ │ ├── init.lua # Loader
│ │ ├── lsp.lua # LSP configuration
│ │ ├── format.lua # Formatting configuration
│ │ ├── lint.lua # Linting configuration
│ │ ├── mini.lua # Mini.nvim modules
│ │ ├── git.lua # Git integration
│ │ ├── dap.lua # Debugging configuration
│ │ ├── test.lua # Testing configuration
│ │ ├── treesitter.lua # Syntax highlighting
│ │ ├── tools.lua # Additional tools
│ │ └── codecompanion.lua # AI companion
│ └── plugins/ # Mini.nvim specs
├── README.md # This file
└── ARCHITECTURE.md # Design documentation
| Key | Action |
|---|---|
<leader>ff |
Find files |
<leader>fg |
Live grep |
<leader>fb |
Find buffers |
<leader>fr |
Recent files |
<leader>e |
Toggle file explorer |
| Key | Action |
|---|---|
<C-s> |
Save |
<C-d> |
Half-page down (centered) |
<C-u> |
Half-page up (centered) |
| Key | Action |
|---|---|
[d |
Previous diagnostic |
]d |
Next diagnostic |
<leader>x |
Diagnostics to quickfix |
<leader>uh |
Toggle inlay hints |
| Key | Action |
|---|---|
]h |
Next hunk |
[h |
Previous hunk |
<leader>gs |
Stage hunk |
<leader>gr |
Reset hunk |
<leader>gp |
Preview hunk |
<leader>gb |
Blame line |
<leader>gd |
Diff (working) |
<leader>gD |
Diff (cached) |
| Key | Action |
|---|---|
<leader>db |
Toggle breakpoint |
<leader>dc |
Continue |
<leader>di |
Step into |
<leader>do |
Step out |
<leader>dO |
Step over |
<leader>dt |
Terminate |
| Key | Action |
|---|---|
<leader>tt |
Run file tests |
<leader>tr |
Run nearest test |
<leader>ts |
Toggle summary |
<leader>to |
Toggle output |
| Key | Action |
|---|---|
<leader>qs |
Save session |
<leader>qr |
Restore session |
<leader>qq |
Quit all |
| Key | Action |
|---|---|
<leader>ul |
Toggle line numbers |
<leader>ur |
Toggle relative numbers |
<leader>uw |
Toggle word wrap |
<leader>uh |
Toggle inlay hints |
<leader>uf |
Toggle format on save |
| Key | Action |
|---|---|
<A-i> |
Toggle terminal |
| Language | Server |
|---|---|
| Lua | lua-language-server |
| Rust | rust-analyzer |
| Python | pyright |
| Go | gopls |
| TypeScript/JavaScript | typescript-language-server |
| Bash | bash-language-server |
| Nix | nil |
| C/C++ | clangd |
Edit nvim/lua/config/keymaps.lua:
local map = vim.keymap.set
map("n", "<leader>mc", function()
print("Custom command!")
end, { desc = "My custom command" })Edit nvim/lua/config/options.lua:
local opt = vim.opt
opt.tabstop = 2
opt.shiftwidth = 2
opt.expandtab = true- Add to
plugins.nix:
vp.your-plugin-name- Create
nvim/lua/plugins/your-plugin.lua:
require("your-plugin").setup({
-- options
})- Import in
nvim/lua/plugins/init.lua:
require("plugins.your-plugin")- Add package to
hm-module.nix:
your-language-server- Configure in
nvim/lua/plugins/lsp.lua:
vim.lsp.config("your-server", {
on_attach = on_attach,
settings = { }
})
vim.lsp.enable("your-server")Look at the line number in the error. Usually indicates a typo in a config file.
Validate Lua syntax:
luacheck nvim/lua/config/options.luaCheck which servers are enabled in nvim/lua/plugins/lsp.lua.
Verify installation:
lua-language-server --version
pyright --version
rust-analyzer --versionTry enabling virtual text:
:lua vim.diagnostic.config({ virtual_text = true })Try enabling true color support:
:set termguicolorsThe flake may be out of sync:
nix flake update
nix flake develop- mini.nvim documentation: https://github.com/echasnovski/mini.nvim
- Neovim LSP guide:
:help lsp - Neovim options:
:help options
This configuration is provided as-is. Feel free to fork, modify, and adapt.
Production-ready. All 31 critical issues have been fixed and verified.
See ARCHITECTURE.md for design documentation.