Targets the latest stable Neovim (0.12) on Debian/Ubuntu, x86_64 or aarch64. Everything that can live in ~/.local is installed there by a script, no root needed except for a handful of apt packages.
- plugin manager: lazy.nvim, locked versions in
lazy-lock.json - colors: kanagawa by default; tokyonight, catppuccin, rose-pine and gruvbox are a keypress away (
ton the dashboard, previewed live, remembered across restarts). statusline: lualine - syntax: nvim-treesitter (main branch, parsers built with the tree-sitter cli)
- language servers: native
vim.lspwith nvim-lspconfig, installed by mason; per-server settings inlsp/ - completion: blink.cmp with lsp, path, snippet and buffer sources
- fuzzy finding: telescope with the native fzf sorter
- files: oil.nvim (edit directories like buffers, replaces netrw)
- formatting on demand: conform.nvim
- markdown rendered in place with plain glyphs: render-markdown.nvim; no browser, no terminal image protocol, works over ssh
- quarto: completion, hover and diagnostics inside code chunks with quarto-nvim and otter.nvim
- debugging: nvim-dap with a ui, adapters for python, rust, c/c++, go, javascript/typescript, haskell
- pinned files: harpoon, todo highlighting: todo-comments
- zen mode (snacks), built-in undo tree and difftool
- git: gitsigns, fugitive, lazygit through snacks
- sessions per directory: auto-session
- dashboard with a cow and a random quote (like vim-startify, no cowsay needed), notifications, indent guides: snacks.nvim
- keymap hints: which-key
- plain text throughout, no nerd-font icons, so any monospace font works
- tmux: shared pane navigation, matching
dotfiles/.tmux.conf - languages: Lua, LaTeX, Python, R, Quarto, Julia, OCaml, Haskell, Rust, Go, Zig, C/C++, Fortran, Bash, TypeScript/JavaScript, Markdown, TOML, YAML, JSON, SQL, CSV
git clone https://github.com/cb-g/nvim ~/nvim
~/nvim/scripts/install.shThe script downloads the latest release of neovim, the tree-sitter cli, ripgrep, fd, stylua, lazygit and yq into ~/.local/opt and ~/.local/bin, symlinks ~/.config/nvim and ~/.tmux.conf to this repository and clones the tmux plugin manager. Any terminal with true color works; the config uses no icons, so any monospace font does too. Rerun it to update those tools. Make sure ~/.local/bin is on your PATH.
Nothing else is required: a stock Ubuntu already has the compiler, curl, git and unzip that parsers and servers need. The following are optional, each unlocks one thing:
| apt package | unlocks |
|---|---|
tmux |
the tmux integration and dotfiles/.tmux.conf |
wl-clipboard (wayland) or xclip (x11) |
the "+ register in a local session; over ssh neovim uses OSC 52 through the terminal instead |
nodejs npm |
the bash, typescript, eslint, html, json, yaml, docker and sql servers and the javascript debug adapter |
golang-go |
gopls and the go debug adapter |
gfortran |
a compiler for fortls to check against |
clangd |
C and C++ on aarch64, where mason has no build |
python3-venv python3-pip |
lets mason install basedpyright and black; not needed if you use uv tool install basedpyright black |
zathura biber chktex texlive-extra-utils |
LaTeX: pdf viewer with synctex, bibliography, texlab's chktex diagnostics, latexindent formatting |
Everything at once:
sudo apt install tmux wl-clipboard xclip nodejs npm clangd python3-venv python3-pip zathura biber chktex texlive-extra-utilsThen start nvim. lazy.nvim installs the plugins, treesitter compiles the parsers and mason installs the servers whose runtime it can find (see language servers). :checkhealth tells you what is missing.
Shell aliases that go well with this:
alias vim=nvim
alias vimc='cd ~/nvim'
alias cleantex=~/nvim/scripts/rm_latex_aux.shinit.lua leader keys, loads lua/config
lua/config/options.lua editor options
lua/config/keymaps.lua global keymaps
lua/config/autocmds.lua autocommands
lua/config/lazy.lua lazy.nvim bootstrap and settings
lua/config/tools.lua helper: is an executable available
lua/config/theme.lua colorscheme picker and the remembered choice
lua/config/fortune.lua the cow and its quotes for the dashboard
lua/plugins/ui.lua kanagawa, lualine, which-key, snacks
lua/plugins/editor.lua oil, telescope, harpoon, todo-comments, autopairs, surround, auto-session
lua/plugins/treesitter.lua
lua/plugins/completion.lua blink.cmp
lua/plugins/lsp.lua lspconfig, mason, mason-lspconfig, lazydev, mason-tool-installer
lua/plugins/dap.lua nvim-dap, dap-ui, adapters
lua/plugins/format.lua conform
lua/plugins/git.lua gitsigns, fugitive
lua/plugins/tmux.lua nvim-tmux-navigation
lua/plugins/lang.lua vimtex, R.nvim, quarto, julia-vim, vim-ocaml, haskell-tools, render-markdown, rainbow_csv
lsp/<server>.lua per-server settings, picked up by vim.lsp.config
after/ftplugin/<ft>.lua buffer-local keymaps and options (tex, ocaml, markdown, quarto)
dotfiles/ .tmux.conf
scripts/install.sh linux bootstrap
scripts/rm_latex_aux.sh delete latex auxiliary files in the cwd
nvimin a project directory. The dashboard shows the cow, pinned actions and recent files;srestores the last session for that directory,tpicks a theme.<leader>fsto jump to a file,<leader>fgto grep for a symbol,-to browse and reorganise the directory in oil.- Language servers attach on their own once the file type is known:
Kfor docs,gdto the definition,grrfor references,grnto rename,grafor code actions,<leader>eto read a diagnostic,<leader>cfto format. <leader>apins the files you keep coming back to,<leader>1to<leader>4jump between them,<C-e>shows the list.<leader>gsfor staging and committing in fugitive,<leader>ggfor lazygit,]hand<leader>hpto walk through changes.<leader>dtsets a breakpoint,<F5>starts the debugger with the ui,<F10>steps.- In LaTeX
<leader>llcompiles on every save and<leader>lvopens the pdf; in markdown and quarto<leader>cptoggles the rendered view;<leader>zfor writing without distractions. - Sessions save on exit, so the next
nvimin that directory picks up where you left off.
<leader>fk lists every keymap with its description, and pressing <leader> and waiting shows the groups. Everything below is also visible there.
Leader is space, local leader is backslash. j and k are swapped: j moves up, k moves down, in normal, visual and select mode. The same orientation is used for pane navigation and resizing in tmux and for moving through telescope results.
| key | action |
|---|---|
<C-h/j/k/l> |
move between splits and tmux panes (j up, k down) |
<C-\>, <C-Space> |
last / next pane |
<leader>|, <leader>- |
split right / below |
<leader>m, <leader>n |
next / previous buffer |
<leader>x |
close buffer |
<leader>w, - |
file explorer (oil) in the current file's directory; edit the listing like text and :w to rename, move or delete, <C-r> refreshes, q closes |
<leader>y, <leader>Y |
yank to the system clipboard |
<leader>p |
(visual) paste over selection without losing the register |
<leader>/ |
toggle comment |
<C-d>, <C-u>, n, N |
as usual, with the cursor kept centered |
<Esc> |
clear search highlight |
<leader>u |
undo tree |
<leader>z, <leader>Z |
zen mode, zoom the current window |
<leader>a, <C-e> |
harpoon: pin the current file, open the pinned list |
<leader>1 … <leader>4 |
harpoon: jump to pinned file 1 to 4 |
]t, [t, <leader>ft |
next / previous todo comment, list them |
<leader>nh, <leader>nd |
notification history, dismiss notifications |
<leader>L |
lazy |
<leader>tt |
pick a colorscheme (also t on the dashboard) |
q |
close help, quickfix, man and fugitive windows |
| key | action |
|---|---|
<leader>fs |
files |
<leader>fv |
git files |
<leader>fg |
live grep |
<leader>fw |
grep word under cursor |
<leader>fo |
recent files |
<leader>fb |
buffers |
<leader>fh |
help tags |
<leader>fd |
diagnostics |
<leader>fk |
keymaps |
<leader>fr |
resume last picker |
<leader>fS |
sessions |
Inside a picker <C-j> moves up and <C-k> moves down.
Neovim's built-in lsp keymaps apply: K hover, grn rename, gra code action, grr references, gri implementation, grt type definition, gO document symbols, [d ]d diagnostics, <C-s> signature help in insert mode. On top of that:
| key | action |
|---|---|
gd, gD |
definition, declaration |
<leader>cr |
rename |
<leader>ca |
code action |
<leader>cf |
format buffer or selection (conform, lsp fallback) |
<leader>cd, <leader>cR |
definitions, references (telescope) |
<leader>cs, <leader>cS |
document, workspace symbols (telescope) |
<leader>e |
line diagnostics |
<leader>q |
diagnostics to the location list |
<leader>th |
toggle inlay hints |
<leader>td |
toggle diagnostics |
| key | action |
|---|---|
<CR> |
accept |
<C-n>, <C-p>, arrows |
next / previous item |
<C-Space> |
open menu, toggle documentation |
<C-e> |
close menu |
<Tab>, <S-Tab> |
jump through snippet fields |
<C-b>, <C-f> |
scroll documentation |
<C-k> |
toggle signature help |
| key | action |
|---|---|
<leader>gg |
lazygit |
<leader>gl, <leader>gf |
git log, git log for the current file |
<leader>gs |
:Git (fugitive status) |
<leader>gb |
:Git blame |
<leader>gd |
diff against the index |
<leader>gD |
:DiffTool for two files or directories, type the paths and press enter |
]h, [h |
next / previous hunk |
<leader>hs, <leader>hr |
stage / reset hunk (also in visual mode) |
<leader>hS, <leader>hR |
stage / reset buffer |
<leader>hp |
preview hunk |
<leader>hb |
blame line |
<leader>hd |
diff the current file against the index (gitsigns) |
<leader>tb |
toggle current line blame |
ih |
hunk text object |
| key | action |
|---|---|
<F5>, <leader>dd |
start or continue |
<F10>, <leader>do |
step over |
<F11>, <leader>di |
step into |
<F12>, <leader>dO |
step out |
<leader>dt |
toggle breakpoint |
<leader>dB |
conditional breakpoint |
<leader>de |
evaluate expression under the cursor or selection |
<leader>du |
toggle the debugger ui |
<leader>dR |
repl |
<leader>dl |
run the last configuration |
<leader>dq |
terminate |
Adapters: codelldb for Rust, C and C++ (asks for the executable to launch), debugpy for Python (<leader>dd on a file runs it with the project's interpreter), delve for Go, js-debug-adapter for JavaScript and TypeScript, haskell-tools for Haskell.
| key | action |
|---|---|
<leader>ll |
compile continuously (latexmk, output in build/) |
<leader>ss |
compile once |
<leader>lv |
view pdf (zathura if installed, otherwise xdg-open) |
<leader>lc |
clean the build directory |
<leader>le |
errors |
<leader>lt |
table of contents |
<leader>cl |
delete auxiliary files in the cwd (scripts/rm_latex_aux.sh) |
| key | action |
|---|---|
<leader>cp |
toggle in-place rendering (headings, lists, code blocks, tables, links) |
| key | action |
|---|---|
<leader>cp, <leader>cP |
quarto preview, close it (needs the quarto cli) |
Code chunks get completion, hover and diagnostics from the language servers of R, Python, Julia and Bash. R.nvim's console keys work in R chunks.
| key | action |
|---|---|
<leader>db |
dune build |
<leader>dr |
dune exec ./bin/main.exe |
R.nvim's defaults, all with the local leader (backslash): \rf starts R, \d sends the line, \l sends the line and moves down, \aa sends the file, :RMapsDesc lists everything.
Mason installs these on first start when the language's runtime is present, so nothing fails on a machine that lacks it:
| server | language | needs |
|---|---|---|
| lua_ls | Lua | nothing |
| texlab | LaTeX | nothing (compiling needs texlive and latexmk) |
| markdown_oxide | Markdown | nothing |
| rust_analyzer | Rust | a toolchain from rustup; rustup component add rust-analyzer rust-src is used when present |
| taplo | TOML | nothing |
| zls | Zig | nothing |
| clangd | C, C++ | mason on x86_64, apt install clangd on aarch64 |
| basedpyright | Python | uv tool install basedpyright (no sudo), or python3-venv and python3-pip for mason |
| bashls | Bash | nodejs, npm; shellcheck and shfmt come from mason |
| vtsls, eslint | TypeScript, JavaScript | nodejs, npm |
| html, jsonls, yamlls, dockerls, sqlls | nodejs, npm |
|
| gopls | Go | go |
| fortls | Fortran | python3-venv, python3-pip |
| julials | Julia | julia |
| elixirls, elp | Elixir, Erlang | elixir, erl |
Servers that are already on the path (ocamllsp, clangd, basedpyright, rust-analyzer) are enabled directly and not installed again by mason. Handled outside mason:
- OCaml:
opam install ocaml-lsp-server ocamlformat ocp-indent. The server from the active switch is used, ocp-indent's vim plugin is picked up fromopam var share. - Haskell: install
haskell-language-serverwith ghcup, haskell-tools.nvim starts it. - R: R.nvim ships its own language server,
install.packages("languageserver")is not needed. - Rust: rust-analyzer only works with a toolchain, so install rustup first.
rustfmtcomes with it and is what<leader>cfuses. - Python:
uv tool install basedpyright blackputs both on the path. The interpreter is taken from$VIRTUAL_ENV, then.venv/in the project root, then~/.config/venvs_py/.default, thenpython3.
Formatters used by <leader>cf: stylua (Lua), black (Python), ocamlformat, rustfmt, fourmolu or ormolu (Haskell), shfmt; anything else falls back to the language server. stylua comes from the install script, the others from their toolchains.
# latex
sudo apt install texlive-full latexmk zathura biber chktex texlive-extra-utils
# python
curl -LsSf https://astral.sh/uv/install.sh | sh
uv tool install basedpyright black
# rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup component add rust-analyzer rust-src
# r
sudo apt install r-base
# go
sudo apt install golang-go
# fortran
sudo apt install gfortran
# quarto
# https://quarto.org/docs/get-started/
# julia
curl -fsSL https://install.julialang.org | sh
# haskell
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
# ocaml
bash -c "sh <(curl -fsSL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)"
opam init && opam install ocaml-lsp-server odoc ocamlformat ocp-indent utopdotfiles/.tmux.conf, symlinked to ~/.tmux.conf by the install script. Prefix is ctrl-s.
| key | action |
|---|---|
prefix |, prefix - |
split right / below in the current path |
ctrl-h/j/k/l |
move between panes, also from inside nvim (j up, k down) |
prefix h/j/k/l |
resize the pane |
prefix r |
reload the config |
prefix I |
install plugins |
v, y in copy mode |
select, copy to the system clipboard |
The window-name plugin needs mikefarah's yq, which the install script provides (the yq in apt is a different program).
# update plugins (writes lazy-lock.json)
nvim "+Lazy sync"
# update parsers and servers
nvim "+TSUpdate" "+MasonUpdate"
# format the lua files
stylua .Reset everything the config generated:
rm -rf ~/.local/share/nvim ~/.local/state/nvim ~/.cache/nvim