TL;DR
Vim is a modal text editor â you switch between Normal, Insert, Visual, and Command modes. The learning curve is steep but the payoff is massive: editing at the speed of thought without leaving the keyboard. This guide covers everything from exiting Vim to recording macros, configuring Neovim, and building a modern IDE-like workflow with plugins like Telescope and LSP.
Key Takeaways
- Vim has five modes â Normal is the default; always return to it with Escape
- Learn text objects (
diw,ci",da{}) â they are the most force-multiplying feature - Use
.(dot) to repeat the last change â combine with text objects for powerful edits - Macros (
q/@) automate repetitive multi-step edits across many lines :%s/old/new/gcwith thecflag lets you confirm each replacement interactively- Neovim with Lua config (
init.lua) + Lazy.nvim is the modern approach for a full IDE setup - Splits + buffers + marks replace tabs in most workflows â learn
Ctrl+wnavigation - Invest 1 week to break the initial barrier â productivity increases exponentially after that
Introduction: Why Learn Vim in 2026?
Vim has been a developer staple since 1991, and its influence has only grown. Every major IDE â VS Code, JetBrains, Xcode â offers a Vim emulation plugin as one of its most-installed extensions. SSH sessions, production servers, and container environments almost always have vi available. Understanding Vim gives you a universal editing superpower.
The core idea is modal editing: instead of using modifier keys (Ctrl, Alt) for every command, Vim switches between modes. In Normal mode, every key is a command. In Insert mode, keys type text. This sounds counterintuitive but enables extremely fast, precise text manipulation using mnemonic single-character commands.
Neovim is a modernized fork of Vim that adds built-in LSP support, async processing, a Lua API, and a vibrant plugin ecosystem. This guide covers both Vim and Neovim, noting differences where they exist. If you are starting fresh, Neovim is the recommended choice.
1. Vim Modes Explained
Understanding Vim's modal system is the single most important concept to grasp. Every confusion beginners face traces back to accidentally being in the wrong mode.
Normal Mode â The Command Hub
Normal mode is the default mode when you open a file. Your keystrokes are interpreted as commands, not text input. Always return here with Escape (or Ctrl+[ as a faster alternative). Most of your time in Vim should be spent in Normal mode â it is the hub you return to between every edit.
Insert Mode â Typing Text
Insert mode works like a conventional text editor: keys type characters. Enter it from Normal mode with several commands, each placing the cursor differently:
| Key | Action |
|---|---|
i | Insert before cursor |
I | Insert at beginning of line |
a | Append after cursor |
A | Append at end of line |
o | Open new line below and insert |
O | Open new line above and insert |
s | Delete character and insert |
S | Delete line and insert |
c{motion} | Change (delete motion, then insert) |
gi | Insert at last insert position |
Visual Mode â Selecting Text
Visual mode highlights text for operations. There are three variants:
| Key | Mode | Description |
|---|---|---|
v | Character Visual | Select character by character |
V | Line Visual | Select entire lines |
Ctrl+v | Block Visual | Rectangular column selection â insert/delete in columns simultaneously |
gv | Re-select | Re-select the previous visual selection |
Command-line Mode â Ex Commands
Entered by pressing : (colon) in Normal mode. This is where you run Ex commands: saving, quitting, substitutions, running shell commands, setting options. Press Enter to execute and Escape to cancel.
:w " Save
:q " Quit
:wq " Save and quit
:q! " Quit without saving (discard changes)
:e filename " Open file
:set number " Enable line numbers
:!ls -la " Run shell command
:help motion " Open help for 'motion'Replace Mode
Entered with R from Normal mode. Like Insert mode but overwrites existing characters rather than inserting. Press r (lowercase) to replace a single character and return to Normal mode immediately.
2. Essential Navigation
Effective navigation is what separates Vim novices from experts. The goal is to move to your target with the fewest keystrokes possible.
Basic Movement: h/j/k/l
The four cardinal movement keys work in Normal mode. They were chosen because they are on the home row:
h â left
j â down
k â up
l â right
" All accept a count prefix:
5j " move 5 lines down
10h " move 10 characters leftWord Motions
| Key | Motion |
|---|---|
w | Move to start of next word (stops at punctuation) |
W | Move to start of next WORD (space-delimited) |
b | Move backward to start of word |
B | Move backward to start of WORD |
e | Move to end of word |
E | Move to end of WORD |
ge | Move to end of previous word |
Line Navigation
0 " Move to start of line (column 0)
^ " Move to first non-whitespace character
$ " Move to end of line
g_ " Move to last non-whitespace character
+ " Move to first non-blank of next line
- " Move to first non-blank of previous line
" Jump to specific column
5| " Move to column 5File Navigation
gg " Go to first line of file
G " Go to last line of file
42G " Go to line 42
42gg " Also go to line 42
Ctrl+f " Scroll forward one full page
Ctrl+b " Scroll backward one full page
Ctrl+d " Scroll forward half page
Ctrl+u " Scroll backward half page
Ctrl+e " Scroll screen down one line (cursor stays)
Ctrl+y " Scroll screen up one line (cursor stays)
zz " Center cursor line on screen
zt " Put cursor line at top of screen
zb " Put cursor line at bottom of screenCharacter Search on Current Line
f{char} " Find next occurrence of char on line (cursor on char)
F{char} " Find previous occurrence
t{char} " Till: move cursor before next char
T{char} " Till backward
; " Repeat last f/F/t/T in same direction
, " Repeat last f/F/t/T in opposite direction
" Example: to delete from cursor to the next comma:
dt, " Delete till comma (not including it)
df, " Delete through comma (including it)Search Navigation
/pattern " Search forward for pattern
?pattern " Search backward for pattern
n " Next match (same direction)
N " Previous match (opposite direction)
* " Search forward for word under cursor
# " Search backward for word under cursor
g* " Search for partial word under cursor
:%s/foo//gn " Count number of occurrences of 'foo'Jumps and Position History
Ctrl+o " Jump to previous position in jump list
Ctrl+i " Jump to next position in jump list
:jumps " Show jump list
'' " Jump to position before last big jump
'. " Jump to last edited position
'^ " Jump to last insert position3. Core Editing Commands
Vim's editing commands follow a verb + motion grammar. Once you internalize this pattern, commands become composable and predictable.
Delete, Yank, and Put
" === DELETE ===
x " Delete character under cursor (like Del key)
X " Delete character before cursor (like Backspace)
dd " Delete entire line
D " Delete from cursor to end of line
d$ " Same as D
d0 " Delete from cursor to start of line
dw " Delete word (from cursor)
db " Delete word backward
d3w " Delete 3 words
dG " Delete from current line to end of file
dgg " Delete from current line to beginning of file
" === YANK (copy) ===
yy " Yank (copy) current line
Y " Yank current line (same as yy)
yw " Yank word
y$ " Yank to end of line
y0 " Yank to start of line
yG " Yank to end of file
y3w " Yank 3 words
" === PUT (paste) ===
p " Put (paste) after cursor / below current line
P " Put before cursor / above current line
gp " Put after cursor and move cursor after pasted text
"+p " Paste from system clipboard (+ register)
"0p " Paste the last explicitly yanked textChange Commands
cc " Change entire line (delete and enter Insert mode)
C " Change from cursor to end of line
cw " Change word from cursor
cb " Change word backward
c$ " Change to end of line
c0 " Change to beginning of line
s " Substitute character (delete + Insert mode)
S " Substitute line (same as cc)Undo and Redo
u " Undo last change
U " Undo all changes on current line
Ctrl+r " Redo (undo the undo)
5u " Undo last 5 changes
:undolist " Show undo history tree (Neovim: telescope undo)
" Neovim persistent undo (add to init.lua / .vimrc):
" set undofile
" set undodir=~/.vim/undoReplace and Case
r{char} " Replace character under cursor with char
R " Enter Replace mode (overwrite characters)
~ " Toggle case of character under cursor
g~w " Toggle case of word
guw " Lowercase word
gUw " Uppercase word
guu " Lowercase entire line
gUU " Uppercase entire line
g~~ " Toggle case of entire line
" Visual mode case operations:
" Select text in visual mode, then:
u " Lowercase selection
U " Uppercase selection
~ " Toggle case of selectionIndentation
>> " Indent current line
<< " Unindent current line
>3j " Indent 3 lines down
>ip " Indent inner paragraph
= " Auto-indent (fix indentation)
== " Auto-indent current line
=G " Auto-indent from cursor to end of file
gg=G " Auto-indent entire file (go to top, indent all)Joining Lines
J " Join current line with line below (space between)
gJ " Join without adding space
3J " Join current line and next 2 lines4. Text Objects â Vim's Superpower
Text objects are the most powerful feature of Vim that most other editors lack. They let you operate on semantic chunks of text â words, sentences, paragraphs, quoted strings, parenthesized expressions, HTML tags â with a single operator.
The syntax is: operator + i/a + object. The i prefix means inner (excludes surrounding delimiter), while a means around (includes surrounding delimiter and trailing whitespace).
Word and Sentence Objects
" w = word, W = WORD, s = sentence, p = paragraph
diw " Delete inner word (just the word, not spaces)
daw " Delete a word (word + surrounding space)
ciw " Change inner word (replaces word, stays in insert)
caw " Change a word including space
yiw " Yank inner word
viw " Visual select inner word
diW " Delete inner WORD (space-delimited)
dis " Delete inner sentence
das " Delete a sentence (including trailing space)
dip " Delete inner paragraph (block separated by blank lines)
dap " Delete a paragraph including surrounding blank linesDelimiter Objects
" Works with: " ' ` ( ) [ ] { } < > t (tag)
ci" " Change inside double quotes: "hello" â ""
ca" " Change around double quotes (removes quotes too)
di' " Delete inside single quotes
da' " Delete around single quotes
ci( " Change inside parentheses: foo(bar) â foo()
ca( " Change around parens: foo(bar) â foo
ci[ " Change inside square brackets
ci{ " Change inside curly braces
ci< " Change inside angle brackets
cit " Change inside HTML/XML tag: <p>text</p> â <p></p>
cat " Change around tag (removes the tag entirely)
" Practical examples:
" Cursor anywhere inside: console.log("hello world")
ci" " â console.log("") (change content in quotes)
" Cursor anywhere inside: function foo(a, b, c)
ci( " â function foo() (clear parameters)
" Cursor anywhere inside: const obj = { key: "value" }
ci{ " â const obj = {} (clear object body)Custom Text Objects (with plugins)
The vim-textobj-user plugin allows creating custom text objects. Popular community text objects include ae/ie (entire file), al/il (current line), ai/ii (indented block), and af/if (function).
5. Visual Mode Operations
Visual mode makes selection explicit before applying operations, which is helpful for beginners and for complex operations.
Character and Line Visual
" Enter visual modes:
v " Character visual
V " Line visual
Ctrl+v " Block (column) visual
gv " Re-select previous visual selection
o " In visual mode: jump to other end of selection
" Operations on visual selection:
d / x " Delete selection
y " Yank (copy) selection
c " Change selection (delete + insert)
> " Indent selection
< " Unindent selection
= " Auto-indent selection
u " Lowercase selection
U " Uppercase selection
~ " Toggle case
!cmd " Filter selection through external command
: " Enter Command mode for range commandsBlock Visual Mode â Column Editing
Block Visual mode (Ctrl+v) is one of Vim's most impressive features, enabling simultaneous editing of multiple lines in a column:
" Example: add a '#' comment to the beginning of 5 lines
1. Position cursor at start of first line
2. Ctrl+v " enter block visual
3. 4j " select 5 lines vertically
4. I " capital I = insert at block start
5. # " type the character(s) to insert
6. Escape " apply to all selected lines
" Example: delete leading whitespace from a column block
1. Ctrl+v to select the whitespace column
2. d to delete it
" Appending to multiple lines:
1. Ctrl+v to select a column
2. $ " extend to end of each line
3. A " append after each line's end
4. type text
5. Escape " applies to all lines6. Search and Replace
Vim's substitute command is one of the most powerful tools in its arsenal, supporting full regex patterns.
The Substitute Command
" Basic syntax: :[range]s/pattern/replacement/[flags]
:s/old/new " Replace first 'old' on current line
:s/old/new/g " Replace ALL on current line (g=global)
:%s/old/new/g " Replace all in entire file (% = all lines)
:%s/old/new/gc " Replace all, confirm each (c=confirm)
:%s/old/new/gi " Replace all, case-insensitive (i)
:%s/old/new/gI " Replace all, case-sensitive (override smartcase)
" Range examples:
:1,10s/foo/bar/g " Lines 1-10
:10,20s/foo/bar/g " Lines 10-20
:'<,'>s/foo/bar/g " Visual selection (auto-filled after V-mode :)
:.,$s/foo/bar/g " Current line to end of file
" Special patterns:
:%s/word/new/g " Whole word only ( = word boundary)
:%s/^/ /g " Add 2 spaces to beginning of every line
:%s/ $//g " Remove trailing whitespace
:%s/s+$//e " Remove trailing whitespace (e=no error if not found)
:%s/
/
/g " Replace double blank lines with single
" Using captured groups:
:%s/(foo)(bar)/\2\1/g " Swap 'foo' and 'bar' â 'barfoo'
:%s/(foo)(bar)/\2\1/g " Same with very magic (, less escaping)Very Magic Mode
" enables 'very magic' mode â most chars have special meaning
" so you need less backslash escaping:
:%s/(https?)://(w+)/[\2](\0)/g " Wrap URLs in markdown
" Pattern reference:
" w = word character, d = digit, s = whitespace
" + = one or more, * = zero or more, ? = zero or one
" | = alternation (OR), () = capture group
" ^ = start of line, $ = end of lineGlobal Command
:g/pattern/command " Run command on every line matching pattern
:g/TODO/d " Delete all lines containing TODO
:g/^s*$/d " Delete all blank lines
:g/pattern/y A " Yank all matching lines into register A
:g!/pattern/d " Delete all lines NOT matching pattern (also :v/)
:v/pattern/d " Same as :g!/pattern/d7. File Operations and Buffers
Saving and Quitting
:w " Write (save) file
:w filename " Write to new filename (save as)
:w! " Force write (overwrite read-only)
:wa " Write all open buffers
:q " Quit (fails if unsaved changes)
:q! " Quit and discard unsaved changes
:wq " Write and quit
:wqa " Write all and quit
:x " Write only if changed, then quit
ZZ " Same as :x (shortcut in Normal mode)
ZQ " Quit without saving (same as :q!)
" Reload file from disk (discard changes):
:e! " Revert to saved version
:e " Reload without the ! if file unchangedWorking with Buffers
A buffer is a file loaded in memory. Buffers persist even when their window is closed. This is Vim's equivalent of open tabs in a conventional editor.
:e filename " Open file into new buffer
:ls " List all buffers (also :buffers, :files)
:b N " Switch to buffer number N
:b filename " Switch to buffer by partial name
:bn " Next buffer
:bp " Previous buffer
:bf " First buffer
:bl " Last buffer
:bd " Close (delete) current buffer
:bd N " Close buffer N
:bw " Wipe buffer (removes from buffer list)
:%bd " Delete all buffers
:ball " Open all buffers in split windows
" Quick navigation with fzf/Telescope (see plugins section):
" :Telescope buffers â fuzzy-find your open buffersWindows (Splits)
:split " Split horizontally (same file)
:split file " Split horizontally, open file
:vsplit " Split vertically (same file)
:vsplit file " Split vertically, open file
:new " New horizontal split with empty buffer
:vnew " New vertical split with empty buffer
" Keyboard shortcuts (all Ctrl+w followed by key):
Ctrl+w h/j/k/l " Navigate to split left/down/up/right
Ctrl+w w " Cycle through windows
Ctrl+w W " Cycle backward through windows
Ctrl+w = " Make all windows equal size
Ctrl+w _ " Maximize current window height
Ctrl+w | " Maximize current window width
Ctrl+w +/- " Increase/decrease window height
Ctrl+w >/< " Increase/decrease window width
Ctrl+w r " Rotate windows
Ctrl+w x " Exchange current window with next
Ctrl+w q " Close current window
Ctrl+w o " Close all windows except current (:only)Tabs
:tabnew " Open new empty tab
:tabnew file " Open file in new tab
:tabclose " Close current tab
:tabonly " Close all other tabs
gt " Go to next tab
gT " Go to previous tab
3gt " Go to tab 3
:tabs " List all tabs and their windows8. Marks and Jumps
Marks let you bookmark positions in files and jump back to them instantly.
Setting and Using Marks
m{a-z} " Set local mark (lowercase = file-local)
m{A-Z} " Set global mark (uppercase = cross-file)
'{mark} " Jump to the line of mark
`{mark} " Jump to exact position (line + column) of mark
:marks " List all marks
" Special automatic marks:
`. " Position of last change
`' " Position before last jump
`< " Start of last visual selection
`> " End of last visual selection
`[ " Start of last yanked/changed text
`] " End of last yanked/changed text
" Cross-file jumps with global marks:
mA " Set global mark A in file1
" (open file2)
`A " Jump back to exact position in file19. Macros â Automating Repetitive Edits
Macros record a sequence of Normal mode commands (including entering/exiting Insert mode, motions, and Ex commands) and replay them. They are Vim's most powerful automation feature for repetitive structural edits.
Recording and Playing Macros
qa " Start recording into register 'a'
" ... perform your sequence of actions ...
q " Stop recording
@a " Play macro 'a' once
@@ " Replay last used macro
5@a " Play macro 'a' 5 times
:%normal @a " Apply macro to every line in file
" View macro contents (paste register):
"ap " Paste register 'a' to see the recorded sequence
" Edit a macro:
" 1. "ap â paste the macro
" 2. Edit the pasted text
" 3. "ayy â yank it back into register 'a'Practical Macro Example
Suppose you have a list of JavaScript variable names and want to wrap each in console.log():
" Initial state (cursor on first line):
myVar
anotherVar
thirdVar
" Record macro:
qa " start recording to 'a'
I " insert at beginning
console.log( " type opening
Escape " back to normal mode
A " append at end
); " type closing
Escape " back to normal mode
j " move to next line
q " stop recording
" Result after @a on first line:
console.log(myVar);
" Apply to all remaining lines:
2@a " or: select all 3 lines and :%normal @aAdvanced Macro Techniques
" Recursive macro (runs until it fails):
qaq " Clear register 'a' (record empty macro)
qa " Start recording
" ... actions ...
@a " Call itself at end (recursive!)
q " Stop recording
@a " This will repeat until an error (e.g., end of file)
" Running macro on visual selection lines:
V5j " Select 6 lines
:normal @a " Apply macro 'a' to each selected line
" Running on lines matching pattern:
:g/pattern/normal @a " Apply macro to lines matching pattern10. Registers
Vim has multiple registers (named clipboards) for storing text and macros. Understanding them unlocks powerful copy-paste workflows.
:reg " List all register contents
:reg a b c " List specific registers
" Named registers (a-z):
"ayy " Yank current line into register 'a'
"ap " Paste from register 'a'
"Ayy " APPEND yank to register 'a' (uppercase appends)
" Special registers:
"" " Unnamed register (default for d, y, c, x)
"0 " Yank register (last explicit yank only, not deletes)
"1-"9 " Numbered registers (recent deletes/changes)
"+ " System clipboard (X11/Wayland primary selection)
"* " System clipboard (middle-click paste on Linux)
"_ " Black hole register (discard, like /dev/null)
". " Last inserted text
": " Last Ex command
"/ " Last search pattern
"% " Current filename
"# " Alternate filename
" Tip: use "0p after multiple deletes to paste last yank
" without the delete overwriting the unnamed register11. Configuration â .vimrc and init.lua
Your configuration file is where you set options, key mappings, and plugins. Vim uses ~/.vimrc (Vimscript). Neovim uses ~/.config/nvim/init.vim (Vimscript) or ~/.config/nvim/init.lua (Lua â recommended for new setups).
Essential .vimrc Settings
" ~/.vimrc â essential settings
" === BASICS ===
set nocompatible " Not Vi-compatible (enable Vim improvements)
filetype plugin indent on " Enable filetype detection, plugins, indenting
syntax on " Enable syntax highlighting
" === UI ===
set number " Show line numbers
set relativenumber " Show relative line numbers (great for motions)
set ruler " Show cursor position in status bar
set showcmd " Show incomplete commands
set showmode " Show current mode
set wildmenu " Enhanced command-line completion
set wildmode=list:longest " Complete like shell (list matches)
set cursorline " Highlight current line
set scrolloff=8 " Keep 8 lines visible above/below cursor
set colorcolumn=80,120 " Highlight columns 80 and 120
" === SEARCH ===
set hlsearch " Highlight search matches
set incsearch " Incremental search (highlight as you type)
set ignorecase " Case-insensitive search
set smartcase " Override ignorecase if uppercase used
" === INDENTATION ===
set tabstop=2 " Tab displays as 2 spaces
set shiftwidth=2 " Indent with 2 spaces
set expandtab " Use spaces instead of tabs
set autoindent " Auto-indent new lines
set smartindent " Smart auto-indenting
" === FILES ===
set encoding=utf-8 " Default encoding
set hidden " Allow background buffers without saving
set noswapfile " Disable swap files
set undofile " Persistent undo across sessions
set undodir=~/.vim/undo " Where to store undo files
set autoread " Auto-reload files changed externally
set backup " Keep backup files
" === PERFORMANCE ===
set lazyredraw " Don't redraw during macros
set ttyfast " Faster terminal connection
" === KEY MAPPINGS ===
let mapleader = ' ' " Space as leader key (very common)
" Quick save and quit
nnoremap <leader>w :w<CR>
nnoremap <leader>q :q<CR>
nnoremap <leader>Q :q!<CR>
" Clear search highlight
nnoremap <leader>h :nohlsearch<CR>
" Better split navigation
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l
" Move lines up/down
nnoremap <A-j> :m .+1<CR>==
nnoremap <A-k> :m .-2<CR>==
vnoremap <A-j> :m '>+1<CR>gv=gv
vnoremap <A-k> :m '<-2<CR>gv=gv
" Keep visual selection after indent
vnoremap > >gv
vnoremap < <gv
" Y should yank to end of line (like D and C)
nnoremap Y y$
" Center screen on search results
nnoremap n nzzzv
nnoremap N NzzzvNeovim init.lua (Modern Lua Config)
-- ~/.config/nvim/init.lua
-- === BASICS ===
vim.g.mapleader = ' '
vim.g.maplocalleader = ' '
-- === OPTIONS ===
local opt = vim.opt
opt.number = true
opt.relativenumber = true
opt.tabstop = 2
opt.shiftwidth = 2
opt.expandtab = true
opt.autoindent = true
opt.smartindent = true
opt.hlsearch = true
opt.incsearch = true
opt.ignorecase = true
opt.smartcase = true
opt.scrolloff = 8
opt.hidden = true
opt.undofile = true
opt.swapfile = false
opt.backup = false
opt.encoding = 'utf-8'
opt.cursorline = true
opt.colorcolumn = '80'
opt.termguicolors = true -- Enable true color support
opt.signcolumn = 'yes' -- Always show sign column (for LSP errors)
opt.updatetime = 250 -- Faster update for CursorHold events
opt.completeopt = 'menuone,noinsert,noselect'
-- === KEY MAPPINGS ===
local map = vim.keymap.set
-- Window navigation
map('n', '<C-h>', '<C-w>h')
map('n', '<C-j>', '<C-w>j')
map('n', '<C-k>', '<C-w>k')
map('n', '<C-l>', '<C-w>l')
-- Quick save/quit
map('n', '<leader>w', ':w<CR>')
map('n', '<leader>q', ':q<CR>')
-- Clear search highlight
map('n', '<leader>h', ':nohlsearch<CR>')
-- Move lines
map('n', '<A-j>', ':m .+1<CR>==')
map('n', '<A-k>', ':m .-2<CR>==')
map('v', '<A-j>', ":m '>+1<CR>gv=gv")
map('v', '<A-k>', ":m '<-2<CR>gv=gv")
-- Keep indent in visual mode
map('v', '>', '>gv')
map('v', '<', '<gv')
-- Y = yank to end of line
map('n', 'Y', 'y$')
-- === PLUGIN MANAGER (Lazy.nvim) ===
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
if not vim.loop.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({
-- Theme
{ 'folke/tokyonight.nvim', priority = 1000, config = function()
vim.cmd.colorscheme 'tokyonight'
end },
-- Fuzzy finder
{ 'nvim-telescope/telescope.nvim', dependencies = { 'nvim-lua/plenary.nvim' } },
-- File tree
{ 'nvim-tree/nvim-tree.lua' },
-- Treesitter (better syntax)
{ 'nvim-treesitter/nvim-treesitter', build = ':TSUpdate' },
-- LSP
{ 'neovim/nvim-lspconfig' },
-- Completion
{ 'hrsh7th/nvim-cmp', dependencies = { 'hrsh7th/cmp-nvim-lsp' } },
-- Git
{ 'tpope/vim-fugitive' },
{ 'lewis6991/gitsigns.nvim' },
-- Status line
{ 'nvim-lualine/lualine.nvim' },
-- Surround
{ 'tpope/vim-surround' },
-- Auto-pairs
{ 'windwp/nvim-autopairs' },
})12. Essential Plugins
Vim's plugin ecosystem transforms it from a text editor into a full development environment. Here are the most impactful plugins every developer should know.
Telescope.nvim â Fuzzy Finder (Neovim)
Telescope is the gold standard for fuzzy finding in Neovim. It searches files, buffers, git commits, LSP symbols, and almost anything else with a beautiful preview.
-- In your init.lua key mappings:
local telescope = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', telescope.find_files) -- find files
vim.keymap.set('n', '<leader>fg', telescope.live_grep) -- grep in project
vim.keymap.set('n', '<leader>fb', telescope.buffers) -- list buffers
vim.keymap.set('n', '<leader>fh', telescope.help_tags) -- search help
vim.keymap.set('n', '<leader>fs', telescope.lsp_document_symbols)
vim.keymap.set('n', '<leader>fr', telescope.oldfiles) -- recent files
vim.keymap.set('n', '<leader>gc', telescope.git_commits) -- git logfzf.vim â Fuzzy Finder (Vim)
" In .vimrc (requires fzf installed separately):
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'
" Key mappings:
nnoremap <C-p> :Files<CR> " Find files
nnoremap <leader>b :Buffers<CR> " List buffers
nnoremap <leader>g :Rg<CR> " Live grep (requires ripgrep)
nnoremap <leader>l :Lines<CR> " Search lines in open buffersNERDTree / nvim-tree â File Explorer
" NERDTree (Vim and Neovim):
Plug 'preservim/nerdtree'
nnoremap <leader>e :NERDTreeToggle<CR>
nnoremap <leader>f :NERDTreeFind<CR> " Reveal current file
" Inside NERDTree:
" o / Enter = open file
" t = open in new tab
" i = open in horizontal split
" s = open in vertical split
" ma = create file/directory
" md = delete
" mm = rename/move
-- nvim-tree (Lua, modern):
require('nvim-tree').setup()
vim.keymap.set('n', '<leader>e', ':NvimTreeToggle<CR>')vim-fugitive â Git Integration
Tim Pope's vim-fugitive is described as "the best Git wrapper of all time." It brings Git commands into Vim seamlessly.
" Essential fugitive commands:
:Git status " (or :G) â interactive git status
:Git add % " Stage current file
:Git commit " Commit with message in split
:Git push " Push to remote
:Git log " Git log
:Git blame " Blame current file (press q to close)
:Git diff " Diff working tree
:Git diff --staged " Diff staged changes
" In :Git status window:
" s = stage file
" u = unstage file
" - = toggle stage
" cc = commit
" ca = amend commit
" dv = diff in vertical splitcoc.nvim â Completion and LSP
" coc.nvim works for both Vim and Neovim:
Plug 'neoclide/coc.nvim', {'branch': 'release'}
" Install language servers:
:CocInstall coc-tsserver " TypeScript/JavaScript
:CocInstall coc-pyright " Python
:CocInstall coc-rust-analyzer " Rust
:CocInstall coc-eslint " ESLint
:CocInstall coc-prettier " Prettier formatting
" Key mappings for coc:
nmap <silent> gd <Plug>(coc-definition) " Go to definition
nmap <silent> gy <Plug>(coc-type-definition) " Go to type def
nmap <silent> gi <Plug>(coc-implementation) " Go to implementation
nmap <silent> gr <Plug>(coc-references) " Find references
nmap <leader>rn <Plug>(coc-rename) " Rename symbol
nmap <leader>f <Plug>(coc-fix-current) " Fix current issue
nmap <leader>ac <Plug>(coc-codeaction) " Code action
K " Show hover docs (press K in normal mode)Neovim Native LSP
-- neovim/nvim-lspconfig setup example:
local lspconfig = require('lspconfig')
-- TypeScript
lspconfig.tsserver.setup({
on_attach = function(client, bufnr)
-- Key mappings when LSP attaches
local opts = { buffer = bufnr }
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, opts)
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts)
vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts)
end,
})
-- Python
lspconfig.pyright.setup({})
-- Rust
lspconfig.rust_analyzer.setup({})vim-surround â Surround Text
" Tim Pope's vim-surround allows changing, adding, deleting surrounding chars
" Change surroundings:
cs'" " Change surrounding ' to " : 'word' â "word"
cs({ " Change surrounding ( to { : (word) â { word }
cs)} " Change surrounding ) to } : (word) â {word}
cst<p> " Change surrounding tag to <p>
" Delete surroundings:
ds' " Delete surrounding ' : 'word' â word
ds( " Delete surrounding ( : (word) â word
dst " Delete surrounding tag
" Add surroundings (normal mode):
ysiw" " Surround inner word with " : word â "word"
ysiw( " Surround inner word with ( ) : word â ( word )
ysip<p> " Surround paragraph with <p>
" Add surroundings (visual mode):
S" " After visual select, surround with "
S{ " After visual select, surround with {}13. Vim vs Neovim Comparison
Both Vim and Neovim are excellent editors. The right choice depends on your needs, workflow, and tolerance for configuration.
| Feature | Vim 9.x | Neovim 0.9+ |
|---|---|---|
| Config language | Vimscript 9 (new syntax) | Lua (primary) + Vimscript |
| Built-in LSP | No (requires coc.nvim) | Yes (vim.lsp) |
| Treesitter | Plugin only | Built-in (nvim-treesitter) |
| Async job control | Limited | Full async API |
| Plugin ecosystem | Large, mature | Very active, Lua-first |
| Defaults | Conservative (Vi compat) | Sensible modern defaults |
| Performance | Excellent | Excellent (faster for Lua plugins) |
| Floating windows | Limited | Full support (used by Telescope etc.) |
| Embeddable | No | Yes (libneovim) |
| GUI support | gvim | Neovide, Goneovim, etc. |
| Universal availability | Pre-installed on most systems | Must install separately |
| Starter configs | vimrc examples online | LazyVim, Kickstart, AstroNvim |
| Best for | Servers, scripting, Vimscript familiarity | Full development workflow, modern IDE features |
Recommendation: If you are new to modal editing, start with Neovim and the kickstart.nvim starter config. It is well-documented and teaches you configuration as you use it. If you primarily edit config files on remote servers, vanilla Vim (which is nearly always present) is perfectly adequate.
14. Developer Workflow Tips
The Dot Command â Most Underrated Feature
" The . command repeats the last change (everything from
" entering Insert mode to leaving it counts as one change)
" Example: add a semicolon to the end of a line
A;<Escape> " Add semicolon at end
j. " Move down, repeat: add semicolon to next line
j. " Repeat again...
" Combined with f/t for powerful workflows:
" Add console.log around all 'response' variables:
/response " find first occurrence
ciw " change inner word
console.log(response) " type new text
<Escape>
n. " find next, repeat changeAbbreviations for Frequent Typos
" In .vimrc / init.lua:
iabbrev teh the
iabbrev recieve receive
iabbrev funciton function
iabbrev calback callback
" Expand while typing (press space after abbreviation)Quickfix List â Navigate Errors and Search Results
:copen " Open quickfix window
:cclose " Close quickfix window
:cnext " Jump to next error/result
:cprev " Jump to previous error/result
:cfirst " Jump to first
:clast " Jump to last
" Populate quickfix with grep results:
:grep -r "pattern" . " Search and load results into quickfix
:Rg pattern " With fzf.vim: results go to quickfix
" Location list (window-local quickfix):
:lopen / :lclose
:lnext / :lprevSpell Checking
:set spell " Enable spell checking
:set spelllang=en_us " Set language
]s " Next misspelled word
[s " Previous misspelled word
z= " Show spelling suggestions
zg " Add word to dictionary
zw " Mark word as misspelledWorking with Multiple Files
" Open multiple files from command line:
vim file1.js file2.js file3.js
" Navigate: :bn :bp :b filename
" Open all JS files in project:
vim **/*.js
" Use argument list:
:args **/*.js " Set argument list
:next / :prev " Navigate args
:argdo %s/foo/bar/g " Apply to all args
:argdo w " Save all args
" Project-wide search and replace (with quickfix):
:grep -r "oldName" .
:cfdo %s/oldName/newName/g | wFrequently Asked Questions
How do I exit Vim?
Press Escape to ensure you are in Normal mode, then: :q (quit, no unsaved changes), :q! (quit, discard changes), :wq or ZZ (save and quit). If everything feels broken, Escape mashed repeatedly then :q! will always work.
What are Vim text objects and why are they powerful?
Text objects let you operate on semantic units: diw deletes a word, ci" changes text inside quotes, da{} deletes a block including braces, yip yanks a paragraph. The pattern is operator + i/a + object. They make editing much faster than manually selecting characters.
How do I search and replace text in Vim?
Use :%s/old/new/g to replace all occurrences in the file. Add c for confirmation (:%s/old/new/gc), i for case-insensitive. Use :s/old/new/g for the current line only. The % means "all lines" and can be replaced with a range like 1,10.
What is the difference between Vim and Neovim?
Neovim is a modernized fork with built-in LSP, Lua configuration, async processing, floating windows, and an active modern plugin ecosystem. Vim is more stable and universally pre-installed. For new development workflows, Neovim is recommended. For quick edits on servers, Vim is always available.
How do Vim macros work?
Record with qa (record into register a), perform actions, stop with q. Replay with @a or @@ for last macro. Apply to multiple lines with a count: 10@a. Apply to all lines: :%normal @a.
How do I split windows and manage buffers?
Split with :split / :vsplit. Navigate splits with Ctrl+w h/j/k/l. List buffers with :ls, switch with :b N or :bn / :bp. Close buffer with :bd. Use Telescope (<leader>fb) for fuzzy buffer switching.
What essential plugins should I install?
For Neovim: Telescope (fuzzy finding), nvim-tree (file explorer), vim-fugitive (Git), nvim-lspconfig + nvim-cmp (LSP + completion), nvim-treesitter (syntax), lualine.nvim (status line), vim-surround (surround text), nvim-autopairs. For a full preset configuration, try LazyVim or Kickstart.nvim.
How do I learn Vim effectively?
Run vimtutor from the terminal â it is a built-in 30-minute interactive tutorial. Focus on one new concept per day rather than trying to learn everything at once. Force yourself to use Vim exclusively for a week; the muscle memory develops quickly. Practice text objects and motions most â they provide the biggest productivity gain.