A personal Neovim setup built on lazy.nvim, for Go and web work (TypeScript, Svelte, Vue, Astro) with LaTeX and Markdown alongside. The same config runs on WSL, a desktop Arch or Ubuntu box, and a headless VPS over SSH; it detects which one it is on and sets up the clipboard accordingly.
Leader is Space. Local leader is backslash.
Neovim 0.12 or later. The treesitter setup uses the main branch API, which does not exist on
older versions.
| Tool | Needed for |
|---|---|
git, curl, tar |
lazy.nvim bootstrap, parser downloads |
tree-sitter CLI 0.26.1+ |
compiling treesitter parsers (install from your package manager, not npm) |
| a C compiler | same |
ripgrep |
Telescope live_grep |
viu or chafa |
terminal image preview |
latexmk, okular |
vimtex compile and forward search |
wl-clipboard, xclip or xsel |
system clipboard on native Linux |
win32yank.exe |
system clipboard on WSL |
Mason installs the language servers, formatters and linters on first launch.
init.lua bootstrap -> options -> keymaps -> lazy.setup
lua/bootstrap.lua clones lazy.nvim if missing
lua/options.lua UI, indentation, clipboard, diagnostics, autocmds
lua/keymaps.lua global keymaps
lua/utils/env.lua is_wsl(), has_clipboard_tool()
lua/utils/preview.lua image rendering via viu or chafa
lua/plugins/ one file per plugin or group
lua/plugins/init.lua the list of spec files lazy loads
lua/plugins/init.lua requires each spec file by hand. A new plugin file is not picked up until
it is added there.
lua/utils/env.lua detects the host and three places branch on it.
Clipboard (options.lua):
- WSL:
win32yank.exebridges to the Windows clipboard,clipboard=unnamedplus. - Native Linux with
wl-copy,xcliporxsel:clipboard=unnamedplus. - Headless VPS or plain SSH:
clipboard=unnamed, and the OSC52 autocmd exports every yank to the terminal running your session.
OSC52 (osc52.lua) loads everywhere except WSL, where win32yank already covers it. Under
tmux it needs set -g allow-passthrough on. Pasting back in is Ctrl+Shift+V, since OSC52 paste
is not widely supported.
The 99 AI agent (99.lua) is only added to the plugin list on WSL, where the opencode and
claude CLIs are available.
Treesitter installs parsers for lua, go, javascript, typescript, tsx, html, css, python,
markdown, markdown_inline, astro and templ. Highlighting starts from a FileType autocmd that
calls vim.treesitter.start(). LaTeX deliberately has no parser; vimtex owns tex highlighting.
LSP goes through lsp-zero plus mason-lspconfig, which enables every installed server
automatically. gopls gets semanticTokens turned on. stylua is excluded, because conform already
runs it as a formatter and it would otherwise be enabled a second time as a language server.
Formatting is conform.nvim, on save with a 5 second timeout, falling back to the LSP when no formatter is configured. Configured formatters are clang-format for C and C++, stylua for lua, gofmt for go, and prettier for javascript and typescript.
Diagnostics have virtual text off. Errors show in the sign column, and the message appears in
a floating window on gl.
Editing and movement:
| Key | Action |
|---|---|
<Esc> |
clear search highlight |
<Tab> / <S-Tab> |
next / previous buffer |
<leader>w |
write file |
<leader>y |
yank whole file to system clipboard |
<leader>r |
scaffold :%s///g with the cursor in the pattern field |
<leader>n |
toggle relative numbers |
<leader>f |
format buffer |
<leader>z |
Zen Mode |
Files and navigation:
| Key | Action |
|---|---|
<C-p> |
find files |
<C-f> |
live grep |
<leader>pa |
find files including ignored ones |
<leader>pg |
live grep including ignored ones |
<leader>e |
toggle file tree |
<leader>a |
Harpoon add |
<leader>h |
Harpoon menu |
<A-1> to <A-4> |
jump to Harpoon slot |
<leader>A / <leader>H |
Harpoon remove / clear |
LSP and diagnostics:
| Key | Action |
|---|---|
gd |
go to definition |
gr |
references |
gl |
show diagnostic in a float |
]d / [d |
next / previous diagnostic |
<A-k> (insert) |
signature help |
<C-Space> |
trigger completion |
Go, via gomodifytags on the struct under the cursor:
| Key | Action |
|---|---|
<leader>gj |
add JSON tags with omitempty |
<leader>gt |
add TOML tags |
<leader>gx |
clear all tags |
Images, LaTeX, and the file tree:
| Key | Action |
|---|---|
<leader>ip |
preview the current buffer if it is an image |
<leader>p (in nvim-tree) |
preview the file under the cursor |
<CR> (in nvim-tree) |
open, or preview if the file is an image |
\ll |
compile LaTeX |
nvim-tree keeps all its default mappings, so rename, delete, create, cut, copy and paste work as
documented upstream. Image preview sits on <leader>p because p, gp and P are taken.
Some plugins have no keymap and are reached by command: :UndotreeToggle, :Trouble,
:CellularAutomaton, and :Git or :G for fugitive.
On WSL only, 9v in visual mode sends the selection to the AI agent and 9s stops in-flight
requests.
Treesitter is on the main branch. There is no nvim-treesitter.configs module and no
configs.setup(). Parsers are installed with require("nvim-treesitter").install() and
highlighting is started per buffer. The spec is deliberately not lazy, because the FileType
autocmd has to exist before the first file is read.
Parsers in ~/.local/share/nvim/lazy/nvim-treesitter/parser/ shadow the ones Neovim ships,
since lazy plugin directories come earlier in the runtimepath. A parser left there from an old
install, combined with newer bundled queries, produces errors like
Query error ... Invalid field name. The fix is to delete the stale .so and reinstall.
mason-lspconfig is v2. handlers and setup_handlers were removed, and unrecognised keys
are ignored silently rather than raising an error. Per-server settings go through
vim.lsp.config().
The LSP spec loads on BufReadPre, not BufEnter. vim.lsp.enable() attaches through a
FileType autocmd, and BufEnter fires after FileType for the buffer named on the command
line, so nvim main.go would start with no client attached until you switched buffers.
refactoring.nvim depends on lewis6991/async.nvim, not plenary. It currently loads on every
BufRead without any keymap bound to it.
Telescope is pinned to 0.1.6. master broke mouse_click, and 0.1.7 and later have
path_expand and ft_to_lang problems. Treesitter is disabled in the previewer for the same
reason.
:Inspectfor the highlight groups under the cursor:checkhealth vim.lspfor attached clients and enabled configurations:Masonfor tool installation status:Lazyfor plugin statusnvim --startuptime /tmp/nvim-startup.logfor startup profiling
:checkhealth vim.lsp reports roughly twenty Unknown filetype warnings. They come from the
filetype lists that tailwindcss, emmet and gopls advertise for frameworks with nothing here
registering them. They are cosmetic.