Vim

Vim is a modal text editor. Mainstream text editing paradigms dictate that the keyboard constantly inputs text, relying on modifier keys (Ctrl, Alt) to invoke commands. Vim inverts this architecture. The keyboard’s default state is Command mode: every key functions as a verb, a motion, or an operator. You enter text only by explicitly stepping into Insert mode. When the edit is complete, you leave.

This inversion drives Vim’s efficiency. It operates like a biological reflex arc: Normal mode is the resting state; Insert mode is deliberate action. The transition between them is fast, intentional, and entirely reversible. Mainstream editors optimize for typing. Vim optimizes for navigating and transforming code, aligning perfectly with how developers actually spend their time.

Neovim is the modern continuation of Vim. It preserves complete compatibility while introducing a native Lua API, a built-in LSP client, Tree-sitter integration, asynchronous job control, and a vastly superior plugin ecosystem. For any new setup, Neovim is the required standard. All commands in this manual apply to Neovim unless explicitly stated otherwise.


1. Installation

Alpine Linux

# Vim
apk add vim

# Neovim
apk add neovim

Arch Linux

# Vim
sudo pacman -S vim

# Neovim
sudo pacman -S neovim

# Optional: language support dependencies
sudo pacman -S python-pynvim nodejs npm

Ubuntu / Debian

# Vim
sudo apt install vim

# Neovim (stable from apt -- may be outdated)
sudo apt install neovim

# Neovim (latest release via PPA -- recommended)
sudo add-apt-repository ppa:neovim-ppa/stable
sudo apt update && sudo apt install neovim

# Python provider
sudo apt install python3-neovim
pip3 install pynvim

Rocky Linux / RHEL

# Vim
sudo dnf install vim-enhanced

# Neovim (via EPEL or direct)
sudo dnf install epel-release
sudo dnf install neovim

# Neovim latest (download AppImage from GitHub releases as alternative)
curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.appimage
chmod +x nvim-linux-x86_64.appimage
sudo mv nvim-linux-x86_64.appimage /usr/local/bin/nvim

Post-Install: Check Health (Neovim)

nvim
:checkhealth

This runs Neovim’s self-diagnostic. Fix any warnings about providers or missing tools.


2. The Modal System

Vim has several modes. Understanding them completely is understanding Vim.

ModeHow to EnterDescription
NormalEsc / Ctrl+[Default. Keys are commands, not text.
Inserti, a, o, I, A, O, s, cKeys input text.
Visualv, V, Ctrl+VSelect text regions.
Visual LineVSelect whole lines.
Visual BlockCtrl+VSelect rectangular blocks.
Command:Run Ex commands.
ReplaceROverwrite characters as you type.
Operator-pendingAfter an operator (d, y, c)Waiting for a motion.

Always return to Normal mode with Esc. Get in the habit. In Neovim, Ctrl+[ is equivalent and faster on standard keyboards.


3. Opening and Closing

Open a file:

vim filename
nvim filename

Open multiple files in buffers:

nvim file1 file2 file3

Open in a split:

nvim -O file1 file2      # vertical split
nvim -o file1 file2      # horizontal split

Open at a specific line:

nvim +42 filename
nvim +/searchterm filename    # open at first match

Open read-only:

nvim -R filename
view filename

Closing

Quit (no unsaved changes):

:q

Save and quit:

:wq
:x       (save only if changed, then quit -- preferred)
ZZ       (same as :x from Normal mode)

Quit without saving:

:q!
ZQ

Quit all buffers:

:qa
:qa!     (discard all)

Save without quitting:

:w
:w filename       (save to a new file)
:w !sudo tee %    (save as root when you forgot to open with sudo)

4. Movement – Normal Mode

Character Movement

h   left
l   right
j   down
k   up

Word Movement

w   forward to start of next word
W   forward to start of next WORD (space-delimited)
b   backward to start of previous word
B   backward to start of previous WORD
e   forward to end of current/next word
E   forward to end of current/next WORD
ge  backward to end of previous word

Word = alphanumeric + underscore. WORD = any non-whitespace sequence.

Line Movement

0   start of line (column 0)
^   first non-blank character of line
$   end of line
g_  last non-blank character of line
|   column 1 (or N| for column N)

Screen / File Movement

gg    go to first line
G     go to last line
42G   go to line 42
:42   go to line 42 (command mode)
H     top of screen (High)
M     middle of screen (Middle)
L     bottom of screen (Low)
Ctrl+F    page forward (down)
Ctrl+B    page backward (up)
Ctrl+D    half-page down
Ctrl+U    half-page up
Ctrl+E    scroll screen down one line (cursor stays)
Ctrl+Y    scroll screen up one line (cursor stays)
zz    centre current line on screen
zt    scroll current line to top
zb    scroll current line to bottom

Search-Based Movement

f{char}   find {char} forward on line (cursor lands on it)
F{char}   find {char} backward on line
t{char}   till {char} forward (cursor stops before it)
T{char}   till {char} backward
;         repeat last f/F/t/T forward
,         repeat last f/F/t/T backward

Paragraph / Section Movement

{         move to previous empty line (paragraph start)
}         move to next empty line (paragraph end)
        move to previous section (function, etc.)
        move to next section
[]        move to end of previous section
][        move to start of next section

Marks

m{a-z}    set mark {a-z} at cursor (local to file)
m{A-Z}    set global mark (works across files)
'{mark}   jump to line of mark
`{mark}   jump to exact position of mark
''        jump back to position before last big jump
``        exact position of last big jump
'.        jump to last edit position

5. Entering Insert Mode

Every method puts you in insert mode at a different cursor position:

i     insert before cursor
a     insert after cursor (append)
I     insert at first non-blank of line
A     insert at end of line
o     open new line below and insert
O     open new line above and insert
s     substitute character (delete char under cursor, enter insert)
S     substitute line (clear line, enter insert) -- same as cc
gi    insert at last insert position

Useful shortcuts inside Insert mode:

Ctrl+H    delete character before cursor (backspace)
Ctrl+W    delete word before cursor
Ctrl+U    delete to beginning of line
Ctrl+T    indent current line
Ctrl+D    dedent current line
Ctrl+N    autocomplete (next match)
Ctrl+P    autocomplete (previous match)
Ctrl+R {reg}  insert content of register
Ctrl+O    execute one Normal mode command, return to Insert

6. Operators, Motions, and Text Objects

Vim’s grammar: [count] operator [motion/text object]

Operators

d     delete (cuts to register)
y     yank (copy)
c     change (delete + enter insert)
>     indent right
<     indent left
=     auto-indent
!     filter through external command
gU    uppercase
gu    lowercase
g~    toggle case

Operator + Motion Examples

dw        delete from cursor to start of next word
db        delete back to start of previous word
d$        delete to end of line
d^        delete to first non-blank of line
d0        delete to start of line
5dw       delete 5 words
yy        yank current line
y3j       yank current line + 3 below (4 lines total)
cc        change entire line
cw        change word (from cursor to end of word)
>>        indent current line
3>>       indent 3 lines
gU$       uppercase to end of line

Text Objects

Text objects are semantic selections – they understand brackets, quotes, words, paragraphs.

Pattern: {operator}i{object} (inside) or {operator}a{object} (around/including delimiters).

iw    inner word
aw    a word (includes surrounding whitespace)
iW    inner WORD
aW    a WORD
is    inner sentence
as    a sentence
ip    inner paragraph
ap    a paragraph
i(    inner parentheses  (also: ib)
a(    around parentheses (includes the parens)
i[    inner square brackets
a[    around square brackets
i{    inner curly braces  (also: iB)
a{    around curly braces
i"    inner double quotes
a"    around double quotes (includes the quotes)
i'    inner single quotes
a'    around single quotes
i`    inner backticks
a`    around backticks
it    inner tag (HTML/XML)
at    around tag

Examples:

di"     delete everything inside double quotes
da"     delete including the double quotes
ci(     change everything inside parentheses
yi{     yank everything inside curly braces
>ap     indent a paragraph
=i{     auto-indent contents of curly braces

7. Deleting, Yanking, Pasting

Deleting

x       delete character under cursor
X       delete character before cursor
dd      delete current line
D       delete from cursor to end of line
dG      delete from current line to end of file
dgg     delete from current line to start of file

Yanking (Copying)

yy      yank current line (also: Y)
yw      yank from cursor to start of next word
y$      yank to end of line
y^      yank to first non-blank
yG      yank to end of file

Pasting

p     paste after cursor (or below current line for line yanks)
P     paste before cursor (or above current line for line yanks)
gp    paste after and move cursor to end of pasted text
gP    paste before and move cursor to end of pasted text
]p    paste with indentation adjusted to current context

Registers

Vim/Neovim have named registers – persistent clipboards.

"{a-z}   use named register a-z for next yank/delete/paste
""       unnamed register (default)
"0       yank register (only updated by y, not d)
"*       system primary clipboard (X11 selection)
"+       system clipboard (Ctrl+C/Ctrl+V clipboard)
".       last inserted text
"%       current file name
"#       alternate file name
":       last command
"/       last search pattern
"_       black hole register (discard)

Examples:

"ayy     yank current line into register a
"ap      paste from register a
"+yy     yank current line to system clipboard
"+p      paste from system clipboard
"_dd     delete line without overwriting default register

View register contents:

:registers
:reg a b c       (show specific registers)

8. Undo and Redo

u         undo
Ctrl+R    redo
U         undo all changes on current line (one step in undo tree)

Vim and Neovim have a full undo tree – branching history, not linear. The undotree plugin makes this visual.

Persistent undo (changes survive file close):

set undofile
set undodir=~/.vim/undodir      " Vim
-- Neovim: vim.opt.undodir = vim.fn.stdpath('data') .. '/undodir'

9. Visual Mode

Visual mode lets you select a region, then apply an operator.

v         character-wise visual
V         line-wise visual
Ctrl+V    block-wise visual
gv        re-select last visual selection
o         move to other end of selection
O         in block mode: move to other corner

Visual Mode Operations

d / x     delete selection
y         yank selection
c         change selection (delete + insert)
>         indent selection
<         dedent selection
=         auto-indent selection
!         filter through external command
U         uppercase selection
u         lowercase selection
~         toggle case
r{char}   replace all chars in selection with {char}

Visual Block Mode (Ctrl+V)

Extremely useful for editing columns, comment/uncomment multiple lines, and inserting text at identical positions.

Insert the same text on multiple lines:

Ctrl+V    select block (move down with j)
I         enter insert at block start
type text
Esc       text is inserted on all selected lines

Append text at end of multiple lines:

Ctrl+V    select block
$         extend selection to end of each line
A         append mode
type text
Esc

10. Search and Replace

Searching

/pattern      search forward
?pattern      search backward
n             next match
N             previous match
*             search forward for word under cursor
#             search backward for word under cursor
g*            search forward for partial word under cursor
gd            go to definition of word under cursor (local)
gD            go to definition (global, file scope)

Clear search highlight:

:nohl
:nohlsearch

Patterns

Vim uses its own regex dialect. Key patterns:

.         any character
*         zero or more (greedy)
\+        one or more
\?        zero or one
\{n,m\}   n to m repetitions
^         start of line
$         end of line
\<        start of word
\>        end of word
\b        word boundary (Neovim/very magic)
[abc]     character class
[^abc]    negated class
\d        digit
\w        word character
\s        whitespace
\n        newline
\t        tab

Very magic mode (closer to PCRE – fewer escapes needed):

/\vpattern    very magic (+ ? { } ( ) | don't need escaping)

Substitution

Basic substitute:

:s/old/new/       replace first match on current line
:s/old/new/g      replace all on current line
:%s/old/new/g     replace all in file
:%s/old/new/gc    replace all with confirmation
:1,20s/old/new/g  replace in lines 1-20

Substitute flags:

g     global (all occurrences per line)
c     confirm each
i     case-insensitive
I     case-sensitive (override ignorecase)
n     count matches without replacing
e     don't error if no match

Substitute with capture groups:

:%s/\(foo\)\(bar\)/\2\1/g    swap foo and bar
:%s/\v(foo)(bar)/\2\1/g      same with very magic

Case modifiers in replacement:

\u    uppercase next character
\l    lowercase next character
\U    uppercase until \E
\L    lowercase until \E
\E    end case modification

:%s/\v(\w+)/\u\1/g    capitalise first letter of every word

Delete blank lines:

:g/^$/d

Delete lines matching a pattern:

:g/pattern/d

Execute a command on all matching lines:

:g/pattern/command
:g/TODO/yank A          (append all TODO lines to register a)

Execute on non-matching lines:

:v/pattern/d            (delete lines that do NOT match)
:g!/pattern/d           (same)

11. Multiple Files: Buffers, Windows, Tabs

Buffers

A buffer is an in-memory file. You can have hundreds open simultaneously.

:e filename       open a file (new buffer)
:ls               list all buffers
:buffers          same
:b filename       switch to buffer by name (tab-complete)
:b2               switch to buffer 2
:bn               next buffer
:bp               previous buffer
:bd               delete (close) current buffer
:bd 2             close buffer 2
:bufdo %s/foo/bar/g   run substitution on all buffers

Buffer status flags in :ls:

a   active (loaded and visible)
h   hidden (loaded, not visible)
%   current buffer
#   alternate buffer (last used)
+   modified (unsaved changes)

Windows (Splits)

:sp filename      horizontal split, open file
:vsp filename     vertical split, open file
Ctrl+W s          horizontal split (same file)
Ctrl+W v          vertical split (same file)
Ctrl+W h/j/k/l    navigate to split in direction
Ctrl+W H/J/K/L    move current split to direction
Ctrl+W =          equalise all split sizes
Ctrl+W _          maximise current split height
Ctrl+W |          maximise current split width
Ctrl+W +/-        increase/decrease split height
Ctrl+W >/<        increase/decrease split width
Ctrl+W r          rotate splits
Ctrl+W o          close all other windows (keep current)
:q                close current window/split

Tabs

Tabs in Vim are layout containers, not file containers. Each tab can hold multiple splits.

:tabnew           open new tab
:tabnew filename  open file in new tab
:tabn             next tab
:tabp             previous tab
:tabclose         close current tab
:tabonly          close all other tabs
gt                next tab (Normal mode)
gT                previous tab (Normal mode)
{n}gt             go to tab n
:tabs             list all tabs

12. Macros

Macros record a sequence of keystrokes and replay them.

Record a macro into register a:

qa    start recording into register a
... keystrokes ...
q     stop recording

Replay the macro:

@a    replay register a
@@    replay last-used macro
10@a  replay 10 times

Recursive macro (replays itself until it fails):

qq    record into q
... do some action...
@q    (at the end of the macro, call itself)
q     stop
@q    run -- it repeats until a motion fails (e.g., j at end of file)

Edit a macro: yanks into a buffer, edit, yank back:

"ap      paste macro a to a line
... edit it ...
"ayy     yank the corrected macro back into register a

13. Indentation and Formatting

Indent Commands

>>        indent current line
<<        dedent current line
>%        indent to matching bracket
=%        auto-indent to matching bracket
=G        auto-indent from cursor to end of file
gg=G      auto-indent entire file
>ap       indent a paragraph

Indentation Settings

set tabstop=4         " tab character = 4 spaces wide
set shiftwidth=4      " indent operation (>>) = 4 spaces
set expandtab         " expand tab key to spaces
set softtabstop=4     " backspace deletes 4 spaces at once
set smartindent       " language-aware auto-indent
set autoindent        " carry indent from previous line

Neovim (Lua):

vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.expandtab = true
vim.opt.softtabstop = 4
vim.opt.smartindent = true

Formatting Prose

Reflow current paragraph to textwidth:

gqap

Reflow visual selection:

gq

Set text width:

set textwidth=80

14. Folding

Folds collapse sections of the file to reduce visual noise.

zf{motion}    create a fold manually
zd            delete fold at cursor
zD            delete all folds at cursor recursively
za            toggle fold open/closed
zo            open fold
zc            close fold
zO            open all folds under cursor recursively
zC            close all folds under cursor recursively
zR            open all folds in file
zM            close all folds in file
zj            move to next fold
zk            move to previous fold
[z            move to start of current fold
]z            move to end of current fold

Fold methods:

set foldmethod=indent     " fold by indentation (Python-friendly)
set foldmethod=syntax     " fold by syntax (requires filetype plugin)
set foldmethod=marker     " fold by {{{ and }}} markers
set foldmethod=manual     " manually defined folds
set foldmethod=expr       " fold by expression (Neovim tree-sitter)

15. The Command Line (:)

The : command line is where Ex commands run. It is a full command language.

File Operations

:e!               reload file from disk (discard changes)
:w                write/save
:wa               write all buffers
:r filename       read file content and insert below cursor
:r !command       insert output of shell command
:!command         run shell command (pause editor)
:.!command        filter current line through shell command
:%!command        filter entire file through shell command

Ranges

:1,10 command     lines 1 to 10
:.,$  command     current line to end
:'a,'b command    from mark a to mark b
:%    command     entire file (shorthand for 1,$)

Useful Ex Commands

:sort             sort lines
:sort!            sort reversed
:sort u           sort and remove duplicates
:sort n           sort numerically
:center 80        centre lines within 80 columns
:left
:right
:retab            convert tabs to spaces (or vice versa, per settings)
:set list         show hidden characters (tabs, trailing spaces)
:set nolist
:echo &option     print value of an option
:set option?      same
:verbose set option?   show where option was set (debug)

Shell Integration

:!ls -la          run ls in a shell, display output
:r !ls -la        insert ls output into buffer
:%!python3        filter entire file through python3
:shell            open a subshell (Ctrl+D to return)
:terminal         open a terminal buffer (Neovim)

16. Configuration

Vim: ~/.vimrc

" General
set nocompatible
set encoding=utf-8
set fileencoding=utf-8
set history=1000
set undofile
set undodir=~/.vim/undodir

" Appearance
set number
set relativenumber
set cursorline
set signcolumn=yes
set scrolloff=8
set colorcolumn=80
set termguicolors
syntax on
set background=dark

" Indentation
set tabstop=4
set shiftwidth=4
set expandtab
set smartindent
set autoindent

" Search
set hlsearch
set incsearch
set ignorecase
set smartcase

" Splits
set splitright
set splitbelow

" Line wrapping
set nowrap
set linebreak

" Performance
set lazyredraw
set updatetime=250
set timeoutlen=500

" File handling
set noswapfile
set nobackup
set autoread

" Clipboard
set clipboard=unnamedplus

" Wildmenu
set wildmenu
set wildmode=longest:full,full

" Show matching brackets
set showmatch

" Status line
set laststatus=2
set ruler
set showcmd

" Mouse
set mouse=a

Neovim: ~/.config/nvim/

Neovim uses Lua. Recommended structure:

~/.config/nvim/
├── init.lua
└── lua/
    └── config/
        ├── options.lua
        ├── keymaps.lua
        ├── autocmds.lua
        └── plugins/
            └── init.lua     (lazy.nvim plugin spec)

init.lua

require("config.options")
require("config.keymaps")
require("config.autocmds")
require("config.plugins")

options.lua

local opt = vim.opt

-- Encoding
opt.encoding = "utf-8"
opt.fileencoding = "utf-8"

-- Appearance
opt.number = true
opt.relativenumber = true
opt.cursorline = true
opt.signcolumn = "yes"
opt.scrolloff = 8
opt.colorcolumn = "80"
opt.termguicolors = true
opt.background = "dark"
opt.showmode = false          -- mode shown by statusline instead

-- Indentation
opt.tabstop = 4
opt.shiftwidth = 4
opt.expandtab = true
opt.smartindent = true
opt.autoindent = true

-- Search
opt.hlsearch = true
opt.incsearch = true
opt.ignorecase = true
opt.smartcase = true

-- Splits
opt.splitright = true
opt.splitbelow = true

-- Line wrap
opt.wrap = false
opt.linebreak = true

-- Performance
opt.updatetime = 100
opt.timeoutlen = 400

-- Files
opt.swapfile = false
opt.backup = false
opt.undofile = true
opt.undodir = vim.fn.stdpath("data") .. "/undodir"
opt.autoread = true

-- Clipboard
opt.clipboard = "unnamedplus"

-- Completion
opt.completeopt = { "menuone", "noselect" }
opt.pumheight = 10

-- Wildmenu
opt.wildmode = "longest:full,full"

-- Folding (tree-sitter-aware in Neovim)
opt.foldmethod = "expr"
opt.foldexpr = "nvim_treesitter#foldexpr()"
opt.foldenable = false         -- start with all folds open
opt.foldlevel = 99

-- Misc
opt.mouse = "a"
opt.showmatch = true
opt.laststatus = 3             -- global statusline (Neovim 0.7+)
opt.conceallevel = 0
opt.pumblend = 10
opt.winblend = 10

keymaps.lua

local map = vim.keymap.set

-- Leader
vim.g.mapleader = " "
vim.g.maplocalleader = " "

-- Normal mode
map("n", "<Esc>", "<cmd>nohlsearch<CR>")              -- clear search highlight
map("n", "<leader>w", "<cmd>w<CR>")                    -- save
map("n", "<leader>q", "<cmd>q<CR>")                    -- quit
map("n", "<leader>Q", "<cmd>qa!<CR>")                  -- force quit all

-- Better 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")

-- Resize splits
map("n", "<C-Up>", ":resize +2<CR>")
map("n", "<C-Down>", ":resize -2<CR>")
map("n", "<C-Left>", ":vertical resize -2<CR>")
map("n", "<C-Right>", ":vertical resize +2<CR>")

-- Buffer navigation
map("n", "<S-l>", ":bnext<CR>")
map("n", "<S-h>", ":bprevious<CR>")
map("n", "<leader>bd", ":bd<CR>")

-- Move lines in visual mode
map("v", "J", ":m '>+1<CR>gv=gv")
map("v", "K", ":m '<-2<CR>gv=gv")

-- Keep selection after indent
map("v", "<", "<gv")
map("v", ">", ">gv")

-- Paste without replacing clipboard
map("v", "p", '"_dP')

-- Better line navigation (treat wrapped lines as separate)
map("n", "j", "gj")
map("n", "k", "gk")

-- Centre screen on navigation
map("n", "<C-d>", "<C-d>zz")
map("n", "<C-u>", "<C-u>zz")
map("n", "n", "nzzzv")
map("n", "N", "Nzzzv")

-- Diagnostics (LSP)
map("n", "[d", vim.diagnostic.goto_prev)
map("n", "]d", vim.diagnostic.goto_next)
map("n", "<leader>e", vim.diagnostic.open_float)
map("n", "<leader>dl", vim.diagnostic.setloclist)

17. Plugins – lazy.nvim (Neovim)

lazy.nvim is the modern plugin manager for Neovim. It supports lazy-loading, dependency management, and lockfiles.

Bootstrap

-- In init.lua or plugins/init.lua
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("config.plugins")

Essential Plugin Spec (~/.config/nvim/lua/config/plugins/init.lua)

return {
  -- Colour scheme
  {
    "catppuccin/nvim",
    name = "catppuccin",
    priority = 1000,
    config = function()
      require("catppuccin").setup({ flavour = "mocha" })
      vim.cmd.colorscheme("catppuccin")
    end,
  },

  -- Treesitter (syntax highlighting, folding, text objects)
  {
    "nvim-treesitter/nvim-treesitter",
    build = ":TSUpdate",
    config = function()
      require("nvim-treesitter.configs").setup({
        ensure_installed = { "lua", "python", "bash", "vim", "html", "css", "json" },
        highlight = { enable = true },
        indent = { enable = true },
      })
    end,
  },

  -- LSP
  {
    "neovim/nvim-lspconfig",
    dependencies = {
      "williamboman/mason.nvim",
      "williamboman/mason-lspconfig.nvim",
    },
  },

  -- Autocompletion
  {
    "hrsh7th/nvim-cmp",
    dependencies = {
      "hrsh7th/cmp-nvim-lsp",
      "hrsh7th/cmp-buffer",
      "hrsh7th/cmp-path",
      "L3MON4D3/LuaSnip",
      "saadparwaiz1/cmp_luasnip",
    },
  },

  -- Fuzzy finder
  {
    "nvim-telescope/telescope.nvim",
    branch = "0.1.x",
    dependencies = { "nvim-lua/plenary.nvim" },
    keys = {
      { "<leader>ff", "<cmd>Telescope find_files<cr>" },
      { "<leader>fg", "<cmd>Telescope live_grep<cr>" },
      { "<leader>fb", "<cmd>Telescope buffers<cr>" },
      { "<leader>fh", "<cmd>Telescope help_tags<cr>" },
    },
  },

  -- File tree
  {
    "nvim-tree/nvim-tree.lua",
    dependencies = { "nvim-tree/nvim-web-devicons" },
    keys = { { "<leader>e", "<cmd>NvimTreeToggle<cr>" } },
  },

  -- Status line
  {
    "nvim-lualine/lualine.nvim",
    dependencies = { "nvim-tree/nvim-web-devicons" },
    config = true,
  },

  -- Git integration
  {
    "lewis6991/gitsigns.nvim",
    config = true,
  },

  -- Comment toggling
  {
    "numToStr/Comment.nvim",
    config = true,
    keys = { "gc", "gb" },
  },

  -- Autopairs
  {
    "windwp/nvim-autopairs",
    event = "InsertEnter",
    config = true,
  },

  -- Which-key (keybinding helper)
  {
    "folke/which-key.nvim",
    event = "VeryLazy",
    config = true,
  },

  -- Indent guides
  {
    "lukas-reineke/indent-blankline.nvim",
    main = "ibl",
    config = true,
  },

  -- Undo tree visualiser
  {
    "mbbill/undotree",
    keys = { { "<leader>u", "<cmd>UndotreeToggle<cr>" } },
  },

  -- Surround motions
  {
    "kylechui/nvim-surround",
    event = "VeryLazy",
    config = true,
  },
}

lazy.nvim Commands

:Lazy              open the plugin dashboard
:Lazy update       update all plugins
:Lazy sync         install missing + update + clean
:Lazy clean        remove unused plugins
:Lazy restore      restore from lockfile

vim-plug (Vim)

" Install vim-plug
call plug#begin('~/.vim/plugged')

Plug 'catppuccin/vim', { 'as': 'catppuccin' }
Plug 'preservim/nerdtree'
Plug 'vim-airline/vim-airline'
Plug 'tpope/vim-fugitive'
Plug 'tpope/vim-surround'
Plug 'tpope/vim-commentary'
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'

call plug#end()
:PlugInstall
:PlugUpdate
:PlugClean

18. LSP in Neovim

The built-in LSP client (:help lsp) connects Neovim to language servers for diagnostics, completion, go-to-definition, and refactoring.

Install Language Servers via Mason

:Mason

Search and install servers: pylsp, lua_ls, bashls, html, cssls, jsonls.

LSP Keymaps (applied on LspAttach)

vim.api.nvim_create_autocmd("LspAttach", {
  callback = function(ev)
    local map = function(keys, func) vim.keymap.set("n", keys, func, { buffer = ev.buf }) end
    map("gd", vim.lsp.buf.definition)
    map("gD", vim.lsp.buf.declaration)
    map("gi", vim.lsp.buf.implementation)
    map("gr", vim.lsp.buf.references)
    map("K",  vim.lsp.buf.hover)
    map("<C-k>", vim.lsp.buf.signature_help)
    map("<leader>rn", vim.lsp.buf.rename)
    map("<leader>ca", vim.lsp.buf.code_action)
    map("<leader>f",  function() vim.lsp.buf.format({ async = true }) end)
  end,
})

LSP Diagnostics

:lua vim.diagnostic.open_float()    -- show diagnostic at cursor
]d  /  [d                           -- next / previous diagnostic
:lua vim.diagnostic.setqflist()     -- send all diagnostics to quickfix list

19. Neovim Terminal Mode

Neovim has a built-in terminal:

:terminal
:vsp | terminal      -- vertical split terminal
:sp | terminal       -- horizontal split terminal

Enter terminal mode: automatically active when terminal buffer is opened.
Exit terminal mode to normal: Ctrl+\ Ctrl+N.

Useful mapping:

map("t", "<Esc>", "<C-\\><C-N>")   -- escape to normal from terminal

20. Spellcheck

set spell
set spelllang=en_gb      " British English
set spellfile=~/.vim/spell/en.utf-8.add
]s    next misspelled word
[s    previous misspelled word
z=    suggest corrections
zg    add word to dictionary (good word)
zw    mark word as bad
zug   undo good-word addition

21. Quickfix and Location Lists

The quickfix list is a global list of positions (errors, search results, grep output). The location list is window-local.

:copen      open quickfix list
:cclose     close it
:cnext      next entry
:cprev      previous entry
:cfirst
:clast
:cc {n}     jump to entry n

Grep into quickfix:

:grep pattern files
:vimgrep /pattern/gj **/*.py     " Vim's built-in grep (slow but portable)

With ripgrep and Telescope, this is largely replaced by :Telescope live_grep.


22. Useful Commands Reference

.         repeat last change
&         repeat last substitution
@:        repeat last command-line command
Ctrl+A    increment number under cursor
Ctrl+X    decrement number under cursor
ga        show ASCII/Unicode value of char under cursor
g8        show UTF-8 encoding of char under cursor
gf        go to file under cursor (open it)
gx        open URL under cursor in browser
K         look up word under cursor in man (default) or LSP hover
J         join current line with next line
gJ        join without adding space
~         toggle case of character under cursor
g~~       toggle case of line

23. Vim vs Neovim Differences

FeatureVimNeovim
Config.vimrc (Vimscript)init.lua (Lua preferred) or init.vim
Plugin managervim-plug, Vundlelazy.nvim (recommended)
LSPVia plugins (coc.nvim)Built-in
Tree-sitterVia pluginBuilt-in
TerminalBasic :terminalFull terminal mode
Async jobsLimitedFull async API
Lua APINoFull Lua 5.1 runtime
GUI protocolNoExt UI / msgpack RPC
Config location~/.vimrc~/.config/nvim/init.lua
Data directory~/.vim/~/.local/share/nvim/

24. Quick Reference Cheatsheet

Movement

h j k l         ← ↓ ↑ →
w / b           next / prev word
e               end of word
0 / ^ / $       line start / first non-blank / line end
gg / G          file start / file end
{N}G            line N
Ctrl+D/U        half page down/up
Ctrl+F/B        full page down/up
f{c} / t{c}     find char / till char on line
* / #           search word under cursor fwd/bwd

Operators

d   delete      y   yank     c   change
>   indent      <   dedent   =   auto-indent
gU  uppercase   gu  lowercase

Text Objects

iw/aw  word       is/as  sentence   ip/ap  paragraph
i(/a(  parens     i[/a[  brackets   i{/a{  braces
i"/a"  dquotes    i'/a'  squotes    it/at  HTML tag

Insert Entry Points

i   before cursor     a   after cursor
I   line start        A   line end
o   line below        O   line above
s   substitute char   S   substitute line

Save / Quit

:w   save       :q   quit       :wq / ZZ   save+quit
:q!  force quit ZQ   force quit :qa!       quit all

  • 202601162325: Vim is the default editor for git commits. Configure with git config --global core.editor nvim.
  • tmux Manual: Run Neovim inside a tmux pane for persistent editor sessions.
  • 202603161901: Use :terminal or :! to run process management commands without leaving the editor.