Skip to main content
Navigation
HomeTechnical ReferenceJournalGitHubGitHub
Sidebar — toggle document categories via the logo
Categories

Vim

Overview

Vim is a modal text editor designed for efficient keyboard-driven editing. It ships by default on virtually every Unix system and, once configured, rivals modern editors in productivity. This reference covers the essential keybindings, a practical .vimrc, and workflows for daily development.

Modes

ModeKey to enterPurpose
NormalEsc (default)Navigation, commands, manipulation
Inserti, a, o, O, I, ATyping text
Visualv (char), V (line), Ctrl+v (block)Selecting text
Command:Ex commands (save, quit, search/replace)
ReplaceROverwrite mode

Essential navigation

Movement is the foundation of Vim — everything else builds on getting the cursor where you need it. These keys navigate without leaving Normal mode, so you never need to touch the mouse.

h j k l " left, down, up, right
w b " next / previous word start
e ge " next / previous word end
0 ^ $ " start / first non-blank / end of line
gg G " top / bottom of file
42G :42 " go to line 42
Ctrl+f Ctrl+b " page down / up
Ctrl+d Ctrl+u " half-page down / up
% " jump to matching bracket/paren
f<char> F<char> " find char forward / backward on line
t<char> T<char> " find until char forward / backward
; , " repeat last f/t forward / backward

Editing

Editing commands change text around the cursor. Most follow the operator + motion pattern: dw deletes a word, d3w deletes three, and c<motion> deletes the target then drops you into Insert mode.

i a " insert before / after cursor
I A " insert at start / end of line
o O " new line below / above
dd D " delete (cut) line / to end of line
dw d3w " delete word / 3 words
yy Y " yank (copy) line
yw " yank word
p P " paste after / before cursor
u Ctrl+r " undo / redo
. " repeat last change
r<char> " replace single character
c<motion> " change (delete + enter insert)
x X " delete char under / before cursor
>> << " indent / un-indent line
J " join next line to current
~ " toggle case

Visual mode

Visual mode lets you select text first and act on it afterward. Pick a selection, then press d, y, or c to delete, copy, or change exactly the highlighted region.

v V Ctrl+v " character / line / block select
d y c " delete / yank / change selection
> < " indent / un-indent selection
gv " reselect last visual selection

Search and replace

Searching jumps you to matches across the file, while :s runs substitutions on the current line. Prefix a substitute with a range (like % for the whole file) to control where replacements apply.

/pattern " search forward
?pattern " search backward
n N " next / previous match
* # " search word under cursor forward / backward

:s/old/new/g " replace in current line
:%s/old/new/g " replace in entire file
:%s/old/new/gc " replace with confirmation
:5,10s/old/new/g " replace in lines 5-10
:%s/\s\+$//e " strip trailing whitespace

Window and buffer management

Windows are viewports into your buffers, letting you view several files side by side. These commands open, navigate, and resize splits, plus switch between the buffers backing each window.

:vsp :sp " vertical / horizontal split
:vsp file.txt " open file in vertical split
Ctrl+w w " cycle between splits
Ctrl+w h/j/k/l " move to left/down/up/right split
Ctrl+w = " equalize split sizes
Ctrl+w _ " maximize height
Ctrl+w | " maximize width
Ctrl+w q " close split

:e file.txt " open file in current buffer
:bnext :bprev " next / previous buffer
:ls " list buffers
:b1 " switch to buffer 1
:bd " delete current buffer
:bn :bp " next / previous buffer

Marks and jumps

Marks bookmark positions within a file so you can return to them instantly, while the jump and change lists remember everywhere you have been.

ma " set mark 'a' at cursor
'a " jump to line of mark 'a'
`a " jump to exact position of mark 'a'
:marks " list marks
'' " jump back to last position

Ctrl+o Ctrl+i " go to older / newer cursor position
g; g, " go to older / newer change position

Macros

Macros record a sequence of keystrokes into a register and replay it on demand, turning repetitive multi-key edits into a single @ command.

qa " start recording macro 'a'
... " perform actions
q " stop recording
@a " execute macro 'a'
5@a " execute macro 'a' five times
@@ " repeat last macro

Registers

Registers are named clipboards. Every yank, delete, and paste reads and writes one, and named registers let you stash several snippets at once instead of overwriting the last copy.

:reg " list all registers
"ayy " yank line into register 'a'
"ap " paste from register 'a'
"+y " yank to system clipboard
"+p " paste from system clipboard
"_dd " delete without storing (black hole)

Configuration (.vimrc)

The .vimrc is sourced every time Vim launches, which makes it the place to define persistent behavior. This example sets sane defaults across core behavior, appearance, search, indentation, mappings, and file-specific tweaks.

" ── Core ──
set nocompatible " use Vim defaults, not legacy vi behavior
filetype plugin indent on " load per-filetype plugins and indentation rules
syntax on " enable syntax highlighting
set encoding=utf-8 " read and write files as UTF-8

" ── Appearance ──
set number " show absolute line numbers in the gutter
set relativenumber " also show distance from the cursor for easy jumps
set cursorline " highlight the line under the cursor
set colorcolumn=80,100 " draw a ruler at columns 80 and 100
set showmatch " flash the matching bracket while typing
set listchars=tab:→\ ,trail:· " visibly render tabs and trailing whitespace
set list " enable the whitespace markers configured above

" ── Search ──
set hlsearch " keep matches highlighted after the search ends
set incsearch " highlight matches live while you type the pattern
set ignorecase smartcase " case-insensitive unless the pattern has capitals
nnoremap <leader><space> :noh<CR> " clear search highlight with Space Space

" ── Indent ──
set expandtab " insert spaces instead of literal tab characters
set tabstop=2 " width of a tab character in the file
set shiftwidth=2 " width used by >>, << and auto-indentation
set autoindent " copy indentation from the previous line
set smartindent " add extra, language-aware indentation

" ── Behavior ──
set backspace=indent,eol,start " backspace over auto-indent, line breaks, insert point
set hidden " switch buffers without forcing a save first
set history=1000 " keep 1000 ex-commands and search patterns
set undofile undodir=~/.vim/undo " persist undo history between sessions
set undolevels=1000 " keep up to 1000 levels of undo per session
set mouse=a " enable mouse in normal, visual, and insert mode
set clipboard=unnamedplus " share the default register with the system clipboard
set splitbelow splitright " open new splits below and to the right

" ── Key mappings ──
let mapleader = " " " use Space as the <leader> key
nnoremap <leader>w :w<CR> " <leader>w → write the current file
nnoremap <leader>q :q<CR> " <leader>q → quit the current window
nnoremap <leader>x :x<CR> " <leader>x → write and quit
nnoremap <leader>h :noh<CR> " <leader>h → clear search highlighting
nnoremap <leader>e :Explore<CR> " <leader>e → open the file explorer
nnoremap <C-h> <C-w>h " Ctrl+h/j/k/l → move to the split in that direction
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l

" Move the selected lines down (J) / up (K) while keeping the selection
vnoremap J :m '>+1<CR>gv=gv
vnoremap K :m '<-2<CR>gv=gv

" ── File-specific ──
autocmd FileType python setlocal ts=4 sw=4 " Python: 4-space indentation
autocmd FileType yaml setlocal ts=2 sw=2 " YAML: 2-space indentation
autocmd FileType markdown setlocal spell wrap linebreak " Markdown: spellcheck + soft wrap

" ── Status line ──
set laststatus=2 " always display the status line
set statusline=%f\ %m%r%h%w\ [%{&ff}]\ [%Y]\ [%l/%L,%c] " file, flags, type, position

Common workflows

Find and replace across files

To change a symbol everywhere, search first and let the quickfix list hold the matches — then :cdo (or :cfdo) applies a substitution to each line (or file) in the list.

" Use :grep then :cdo to replace across matches
:grep "old_function" src/**/*.ts
:cdo %s/old_function/new_function/g | update
:cclose

" Or interactive with :cfdo
:cfdo %s/old_function/new_function/gc | update

Editing remote files

Vim ships with the netrw plugin, which can open files over SSH, SCP, and HTTP. Use a URL-style path to edit a remote file in place, then save normally — Vim handles the transfer.

:e scp://user@host//etc/nginx/nginx.conf

See also