env.dev

Neovim: A Developer Guide to the Modern Vim

Master Neovim: modes, motions, text objects, registers, LSP, Tree-sitter, Lua configuration, and essential plugins.

Neovim is a modern, extensible fork of Vim that keeps full backward compatibility while adding first-class Lua scripting, a built-in LSP client, Tree-sitter integration, and asynchronous job control. It is designed for developers who want a fast, keyboard-driven editor that can be shaped into a full IDE.

Modes

Neovim is a modal editor — the meaning of each key depends on which mode you are in. Understanding modes is the single most important concept for productive editing.

ModeEnter withPurpose
NormalEscNavigate, delete, copy, paste, run commands
Inserti / a / oType text into the buffer
Visualv / V / Ctrl-vSelect text (character, line, or block)
Command-line:Execute Ex commands, search & replace
Terminal:terminalRun a shell inside a Neovim buffer

Motions & Operators

Neovim commands follow the grammar operator + [count] + motion. Learning a handful of operators and motions gives you a combinatorial explosion of editing power.

Common Operators

OperatorAction
dDelete
cChange (delete + enter Insert mode)
yYank (copy)
>Indent right
<Indent left
gqFormat / rewrap text
gu / gULowercase / uppercase

Common Motions

w / b       → forward / backward by word
e           → end of word
0 / $       → start / end of line
gg / G      → top / bottom of file
f{char}     → jump to next {char} on the line
t{char}     → jump to just before {char}
/{pattern}  → search forward
?{pattern}  → search backward
%           → matching bracket

Combine them: d2w deletes two words, ci" changes the text inside quotes, yG yanks from the cursor to the end of the file.

Text Objects

Text objects let you operate on structured chunks of text. Prefix with i for inner (contents only) or a for around (including delimiters).

iw / aw

Inner / around word

diw

i" / a"

Inside / around double quotes

ci"

i( / a(

Inside / around parentheses

da(

i{ / a{

Inside / around curly braces

yi{

it / at

Inside / around HTML/XML tag

cit

ip / ap

Inner / around paragraph

dap

Registers

Registers are named storage slots for yanked or deleted text. Prefix a command with "{reg} to target a specific register.

RegisterDescription
"Default (unnamed) register
0Last yank
1-9Last 9 deletes (stack)
a-zNamed registers (you choose)
+System clipboard
_Black hole (discard)
.Last inserted text
%Current filename
:Last command-line command

Example: "ayy yanks the current line into register a, and "ap pastes it. Use "+y to copy to the system clipboard.

Buffers, Windows & Tabs

Neovim separates the concepts of files (buffers), viewports (windows), and layouts (tabs). A single file can be displayed in multiple windows across multiple tabs.

# Buffers
:e file.lua          Open a file in a new buffer
:bnext / :bprev      Navigate buffers
:ls                  List all buffers
:bd                  Close current buffer

# Windows (splits)
:split / :vsplit     Horizontal / vertical split
Ctrl-w h/j/k/l      Move between windows
Ctrl-w =             Equalize window sizes
Ctrl-w o             Close all other windows

# Tabs
:tabnew              New tab
gt / gT              Next / previous tab

Built-in LSP Client

Neovim ships with a built-in Language Server Protocol client that provides IDE features like go-to-definition, autocomplete, hover docs, rename, and diagnostics — all without plugins.

-- init.lua: minimal LSP setup (Neovim 0.11+)
vim.lsp.enable('lua_ls')       -- enable lua-language-server
vim.lsp.enable('ts_ls')        -- enable typescript-language-server

-- Common keymaps (set in an LspAttach autocmd)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition)
vim.keymap.set('n', 'gr', vim.lsp.buf.references)
vim.keymap.set('n', 'K',  vim.lsp.buf.hover)
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename)
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action)
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev)
vim.keymap.set('n', ']d', vim.diagnostic.goto_next)

For automatic server installation, the popular mason.nvim and mason-lspconfig.nvim plugins can manage language server binaries for you.

Tree-sitter

Neovim integrates Tree-sitter for fast, accurate syntax highlighting and code-aware text objects. Instead of regex-based highlighting, Tree-sitter builds a real parse tree of your code.

-- Install parsers (via nvim-treesitter plugin)
:TSInstall lua typescript python rust

-- Tree-sitter powered text objects (with nvim-treesitter-textobjects)
-- vaf  → select outer function
-- vic  → select inner class
-- ]m   → jump to next method

Configuration with Lua

Neovim uses init.lua (or the legacy init.vim) located in ~/.config/nvim/. Lua is the recommended language for all new configuration.

-- ~/.config/nvim/init.lua

-- Options
vim.opt.number         = true       -- line numbers
vim.opt.relativenumber = true       -- relative line numbers
vim.opt.tabstop        = 2          -- tab width
vim.opt.shiftwidth     = 2          -- indent width
vim.opt.expandtab      = true       -- spaces instead of tabs
vim.opt.signcolumn     = 'yes'      -- always show sign column
vim.opt.clipboard      = 'unnamedplus'  -- use system clipboard
vim.opt.ignorecase     = true       -- case-insensitive search
vim.opt.smartcase      = true       -- unless uppercase is used

-- Leader key
vim.g.mapleader = ' '

-- Keymaps
vim.keymap.set('n', '<leader>w', ':w<CR>')
vim.keymap.set('n', '<leader>q', ':q<CR>')
vim.keymap.set('n', '<Esc>', ':nohlsearch<CR>')

Plugin Management

lazy.nvim is the de facto standard plugin manager. It supports lazy loading, lockfiles, a UI dashboard, and automatic dependency resolution.

-- Bootstrap lazy.nvim (place at top of init.lua)
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
if not vim.uv.fs_stat(lazypath) then
  vim.fn.system({
    'git', 'clone', '--filter=blob:none',
    'https://github.com/folke/lazy.nvim.git',
    '--branch=stable', lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)

require('lazy').setup({
  { 'nvim-telescope/telescope.nvim',  -- fuzzy finder
    dependencies = { 'nvim-lua/plenary.nvim' } },
  { 'nvim-treesitter/nvim-treesitter', build = ':TSUpdate' },
  { 'neovim/nvim-lspconfig' },         -- LSP configs
  { 'hrsh7th/nvim-cmp' },              -- autocompletion
})

Essential Plugins

telescope.nvim

Fuzzy finder for files, grep, buffers, and more

:Telescope find_files

nvim-cmp

Autocompletion engine with LSP, snippet, and path sources

Ctrl-n / Ctrl-p

oil.nvim

File explorer as an editable buffer

:Oil

gitsigns.nvim

Git signs in the gutter, inline blame, hunk actions

:Gitsigns preview_hunk

conform.nvim

Formatter integration (prettier, stylua, etc.)

:ConformFormat

nvim-lint

Async linter integration (eslint, ruff, etc.)

Runs on save

Macros

Record a sequence of keystrokes and replay them. This is one of Neovim's most powerful features for repetitive edits.

q{reg}       Start recording into register {reg}
q            Stop recording
@{reg}       Play macro from register {reg}
@@           Repeat last played macro
5@a          Play macro 'a' five times

# Example: wrap each line in quotes
qa           Record into register a
0i"<Esc>     Go to start, insert "
A"<Esc>      Go to end, append "
j            Move to next line
q            Stop recording
99@a         Apply to next 99 lines

See the Neovim Cheat Sheet for a quick reference of common keybindings.