Vim is a terminal-native, modal text editor with over three decades of active development. It runs on any POSIX system, requires no graphical environment, and produces no telemetry. Neovim is a refactored fork of Vim that introduces Lua as a first-class configuration language, an embedded LSP client, and a maintained plugin API. Both operate without licencing fees, vendor lock-in, or mandatory account registration.
Proprietary editors including VS Code, JetBrains IDEs, and Sublime Text collect usage data, enforce update channels, and tie configuration to corporate-controlled extension marketplaces. Vim and Neovim place all configuration, all data, and all tooling decisions under the user's direct control. The configuration is a plaintext file. The environment is fully reproducible. The toolchain is yours.
This manual covers both editors in parallel. The configuration section presents my working Neovim init.lua and a functionally equivalent Vim .vimrc.
Installation
Neovim
| Platform |
Command |
| Arch Linux |
sudo pacman -S neovim |
| Alpine Linux |
apk add neovim |
| Ubuntu / Debian (PPA) |
sudo add-apt-repository ppa:neovim-ppa/unstable && sudo apt update && sudo apt install neovim |
| Rocky Linux / RHEL |
sudo dnf install epel-release && sudo dnf install neovim |
| AppImage (universal) |
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 |
Arch Linux is the preferred platform. The package is current, the AUR provides any supplementary tooling, and the system remains under direct user control.
Vim
Vim ships with most base system installations. When installation is required:
| Platform |
Command |
| Arch Linux |
sudo pacman -S vim |
| Alpine Linux |
apk add vim |
| Ubuntu / Debian |
sudo apt install vim |
| Rocky Linux / RHEL |
sudo dnf install vim-enhanced |
Post-Install Verification
| Editor |
Command |
Expected Output |
| Neovim |
nvim --version |
NVIM vX.X.X |
| Vim |
vim --version |
VIM - Vi IMproved X.X |
Run :checkhealth inside Neovim after first launch. The output identifies missing clipboard providers, Python support, and Node binaries. Install only what your workflow requires.
The Modal Editing Model
Vim operates across distinct modes. Each mode governs how the editor interprets keyboard input.
Mode Reference
| Mode |
Entry |
Function |
| Normal |
Escape or Ctrl-[ |
Command input. The default and resting state. Every key executes an operation. |
| Insert |
i, a, o, and variants |
Text entry. Characters typed appear in the buffer. |
| Visual |
v, V, Ctrl-v |
Text selection. Operators apply to the selected region. |
| Command-line |
: |
Ex command execution. File operations, settings, search-replace, shell access. |
| Replace |
R |
Overstrike entry. Typed characters overwrite existing text at the cursor position. |
Normal mode is the primary state. Insert mode serves a single purpose: entering text. Return to Normal immediately after each edit. Remaining in Insert mode removes access to all motion commands and operators.
Insert Mode Entry Points
| Key |
Behaviour |
i |
Insert before cursor |
I |
Insert at first non-blank character of line |
a |
Append after cursor |
A |
Append at end of line |
o |
Open new line below, enter Insert |
O |
Open new line above, enter Insert |
s |
Delete character under cursor, enter Insert |
S |
Delete entire line content, enter Insert |
c{motion} |
Delete through motion, enter Insert |
Navigation
Navigation in Vim is compositional. Motions combine with operators to construct precise edit operations. Learning the motion atoms enables construction of any compound move.
Character Movement
| Key |
Direction |
h |
Left |
j |
Down |
k |
Up |
l |
Right |
Word Motions
Vim distinguishes between a word (alphanumeric characters and underscores) and a WORD (any sequence of non-whitespace characters). The string foo.bar constitutes two words and one WORD.
| Key |
Behaviour |
w |
Start of next word |
W |
Start of next WORD |
b |
Start of previous word |
B |
Start of previous WORD |
e |
End of current or next word |
E |
End of current or next WORD |
ge |
End of previous word |
Line Navigation
| Key |
Behaviour |
0 |
Column zero (hard line start) |
^ |
First non-blank character of line |
$ |
End of line |
g_ |
Last non-blank character of line |
f{char} |
Jump to next occurrence of char on current line |
F{char} |
Jump to previous occurrence of char on current line |
t{char} |
Jump to one column before next occurrence of char |
T{char} |
Jump to one column after previous occurrence of char |
; |
Repeat last f, F, t, or T in same direction |
, |
Repeat last f, F, t, or T in reverse direction |
Document Navigation
| Key |
Behaviour |
gg |
First line of file |
G |
Last line of file |
{N}G |
Line N |
{N}gg |
Line N |
Ctrl-d |
Scroll down half a screen |
Ctrl-u |
Scroll up half a screen |
Ctrl-f |
Scroll down one full screen |
Ctrl-b |
Scroll up one full screen |
H |
Cursor to top of visible screen |
M |
Cursor to middle of visible screen |
L |
Cursor to bottom of visible screen |
zz |
Centre screen on cursor line |
zt |
Scroll so cursor line is at top of screen |
zb |
Scroll so cursor line is at bottom of screen |
Jump List
The jump list records significant cursor positions across files. It is session-persistent.
| Key |
Behaviour |
Ctrl-o |
Jump to previous position in jump list |
Ctrl-i |
Jump to next position in jump list |
:jumps |
Display full jump list |
Editing — Operators, Motions, and Text Objects
The Grammar
Vim editing follows a consistent composable grammar:
[count] {operator} {motion or text-object}
Operators define the action. Motions or text objects define the range. Counts multiply either.
| Composed Command |
Result |
d3w |
Delete three words |
ci" |
Change contents inside double quotes |
ya( |
Yank everything inside and including parentheses |
>ip |
Indent the inner paragraph |
gUiw |
Uppercase the inner word |
Operators
| Key |
Action |
d |
Delete (cut to register) |
y |
Yank (copy to register) |
c |
Change (delete then enter Insert mode) |
> |
Indent right |
< |
Indent left |
= |
Auto-indent |
~ |
Toggle case |
gu |
Lowercase |
gU |
Uppercase |
Doubling an operator applies it to the current line. dd deletes the line. yy yanks the line. cc changes the line.
Text Objects
Text objects describe a structural region of the buffer, independent of cursor position. The prefix i selects the inner region. The prefix a selects around (includes delimiters or surrounding whitespace).
| Key |
Object |
iw / aw |
Inner word / around word |
is / as |
Inner sentence / around sentence |
ip / ap |
Inner paragraph / around paragraph |
i" / a" |
Inner double quotes / including quotes |
i' / a' |
Inner single quotes / including quotes |
i) / a) |
Inner parentheses / including parentheses |
i] / a] |
Inner square brackets / including brackets |
i} / a} |
Inner curly braces / including braces |
it / at |
Inner HTML tag content / including tags |
Common Single-Key Edits
| Key |
Action |
x |
Delete character under cursor |
X |
Delete character before cursor |
r{char} |
Replace character under cursor with char |
R |
Enter Replace mode |
J |
Join current line with line below |
gJ |
Join lines without whitespace adjustment |
u |
Undo |
Ctrl-r |
Redo |
. |
Repeat last change |
p |
Paste register contents after cursor |
P |
Paste register contents before cursor |
The . command repeats the last change at the current cursor position. The efficient editing pattern is: make one change, move, repeat with ..
Visual Mode
Visual mode selects a region of the buffer before applying an operator.
Entry
| Key |
Selection Type |
v |
Character-wise |
V |
Line-wise |
Ctrl-v |
Block (column-wise) |
gv |
Reselect previous visual selection |
Operators in Visual Mode
Any operator applied in Visual mode acts on the selected region.
| Key |
Action on Selection |
d |
Delete |
y |
Yank |
c |
Change |
> / < |
Indent right / left |
~ |
Toggle case |
u / U |
Lowercase / uppercase |
Block Visual Editing
Block visual mode (Ctrl-v) enables column-level operations across multiple lines.
Procedure for inserting text at start of a column:
| Step |
Action |
| 1 |
Enter block visual with Ctrl-v |
| 2 |
Select the column across target lines |
| 3 |
Press I to insert at block start |
| 4 |
Type the text |
| 5 |
Press Escape to apply across all selected lines |
This procedure comments out multiple lines with # or // in a single operation.
Search and Replace
Search
| Key |
Behaviour |
/{pattern} |
Search forward |
?{pattern} |
Search backward |
n |
Next match |
N |
Previous match |
* |
Search forward for word under cursor |
# |
Search backward for word under cursor |
:noh |
Clear search highlights |
Vim uses its own regular expression flavour.
| Pattern |
Meaning |
. |
Any single character |
\w |
Word character (alphanumeric or underscore) |
\d |
Digit |
\s |
Whitespace character |
^ |
Start of line |
$ |
End of line |
\<word\> |
Whole word boundary match |
.* |
Any sequence of characters (greedy) |
With ignorecase set, search is case-insensitive. With smartcase additionally set, a pattern containing any uppercase character becomes case-sensitive automatically.
Substitution Syntax
:{range}s/{pattern}/{replacement}/{flags}
| Component |
Options |
| Range |
(empty) = current line, % = whole file, 5,10 = lines 5 to 10, '<,'> = visual selection |
| Flags |
g = all occurrences per line, i = case-insensitive, c = confirm each, e = suppress no-match error |
Common substitutions:
| Command |
Effect |
:%s/foo/bar/g |
Replace all occurrences in file |
:%s/foo/bar/gc |
Replace with per-match confirmation |
:%s/\s\+$//e |
Strip trailing whitespace from all lines |
:%s/ubuntu/alpine/gi |
Case-insensitive global replace |
:g/^$/d |
Delete all blank lines |
Global Command
The :g command executes an ex command on every line matching a pattern.
| Command |
Effect |
:g/TODO/d |
Delete every line containing TODO |
:g/error/p |
Print every line containing error |
:g/^#/m$ |
Move all lines beginning with # to end of file |
Buffers, Windows, and Tabs
These three constructs are distinct.
| Construct |
Definition |
| Buffer |
A file loaded into memory. It exists regardless of whether it is displayed. |
| Window |
A viewport onto a buffer. Multiple windows display simultaneously. Multiple windows can show the same buffer. |
| Tab |
A named collection of window layouts. Tabs preserve split arrangements, not individual files. |
Buffer Commands
| Command |
Action |
:ls or :buffers |
List all open buffers |
:b{N} |
Switch to buffer N |
:b {name} |
Switch to buffer by partial name match |
:bn |
Next buffer |
:bp |
Previous buffer |
:bd |
Unload current buffer |
:bd{N} |
Unload buffer N |
:ba |
Open all buffers in split windows |
Window Commands
| Command |
Action |
:sp or Ctrl-w s |
Horizontal split |
:vsp or Ctrl-w v |
Vertical split |
Ctrl-w h/j/k/l |
Move focus to adjacent window |
Ctrl-w H/J/K/L |
Move window to screen edge |
Ctrl-w = |
Equalise all window sizes |
Ctrl-w _ |
Maximise current window height |
Ctrl-w \| |
Maximise current window width |
Ctrl-w + / - |
Increase / decrease window height |
Ctrl-w > / < |
Increase / decrease window width |
:q |
Close current window |
:only |
Close all windows except current |
Tab Commands
| Command |
Action |
:tabnew |
Open a new empty tab |
:tabnew {file} |
Open file in a new tab |
gt |
Next tab |
gT |
Previous tab |
{N}gt |
Go to tab N |
:tabclose |
Close current tab |
:tabonly |
Close all other tabs |
:tabs |
List all open tabs |
The Command Line
: enters Command-line mode from Normal mode. Ex commands govern file operations, settings, shell access, and plugin interfaces.
File Operations
| Command |
Action |
:w |
Write current buffer to disk |
:w {name} |
Write to a new filename |
:wa |
Write all modified buffers |
:q |
Close current window |
:qa |
Close all windows |
:wq or :x |
Write and close |
:q! |
Close without writing |
:e {file} |
Open a file into a buffer |
:e! |
Reload current file from disk, discarding changes |
:r {file} |
Insert file contents below cursor |
:r !{cmd} |
Insert shell command output below cursor |
Shell Access
| Command |
Action |
:!{cmd} |
Execute shell command, display output |
:r !{cmd} |
Execute command, insert output into buffer |
:terminal |
Open embedded terminal buffer (Neovim) |
Runtime Option Changes
| Command |
Action |
:set number |
Enable line numbers |
:set number! |
Toggle line numbers |
:set number? |
Query current value |
:set spell |
Enable spellcheck |
:set spelllang=en_gb |
Set spellcheck language |
Marks, Registers, and Macros
Marks
Marks store cursor positions for recall. Lowercase marks are file-local. Uppercase marks are global and persist across files.
| Command |
Action |
m{a-z} |
Set local mark |
m{A-Z} |
Set global mark |
`{mark} |
Jump to exact position of mark |
'{mark} |
Jump to line of mark |
`. |
Jump to position of last change |
`^ |
Jump to position of last Insert mode exit |
`[ |
Start of last yanked or changed text |
`] |
End of last yanked or changed text |
:marks |
List all marks |
:delmarks {mark} |
Delete a mark |
Registers
Registers are named storage locations for text. Vim maintains multiple registers simultaneously.
| Register |
Contents |
"" |
Unnamed register. Receives all d, y, c output. |
"0 |
Last yank. Unaffected by delete operations. |
"1 to "9 |
Rolling delete history. |
"a to "z |
Named registers under user control. |
"A to "Z |
Append variants of named registers. |
"+ |
System clipboard. |
"* |
Primary selection (X11 / Wayland). |
". |
Last inserted text. |
"% |
Current buffer filename. |
": |
Last executed ex command. |
"/ |
Last search pattern. |
Usage:
| Command |
Action |
"ayy |
Yank current line into register a |
"ap |
Paste from register a |
"+yy |
Yank current line to system clipboard |
"+p |
Paste from system clipboard |
:registers |
Display all register contents |
Setting clipboard=unnamedplus routes the unnamed register through the system clipboard. y and p then operate on the system clipboard without any register prefix. This requires wl-clipboard on Wayland or xclip / xsel on X11.
Macros
Macros record a sequence of Normal mode operations into a register and replay them.
Recording and Playback:
| Command |
Action |
q{a-z} |
Begin recording into register |
q |
Stop recording |
@{a-z} |
Execute macro from register |
{N}@{a-z} |
Execute macro N times |
@@ |
Repeat last executed macro |
A macro stops automatically when it encounters an error, such as a failed search or end of file. Supplying a count larger than the number of remaining targets is safe: the macro halts cleanly.
Configuration
Neovim — init.lua Annotated
Configuration file location: ~/.config/nvim/init.lua
-- CORE SETTINGS
-- leader key: space is reachable with either thumb
-- all custom keymaps use this as their prefix
vim.g.mapleader = " "
vim.g.maplocalleader = " "
-- absolute line numbers give fixed references
-- relative numbers show motion counts to any visible line
vim.opt.number = true
vim.opt.relativenumber = true
-- mouse support in all modes
-- useful for resizing splits without disrupting keyboard flow
vim.opt.mouse = "a"
-- ignorecase: search is case-insensitive by default
-- smartcase: pattern with any uppercase letter becomes case-sensitive
vim.opt.ignorecase = true
vim.opt.smartcase = true
-- updatetime: milliseconds before CursorHold fires
-- affects diagnostic display latency across plugins
vim.opt.updatetime = 250
-- termguicolors: enables 24-bit rgb colour in the terminal
-- required for accurate colourscheme rendering
vim.opt.termguicolors = true
-- clipboard: routes the unnamed register through the system clipboard
-- requires wl-clipboard on wayland: pacman -S wl-clipboard
vim.opt.clipboard = "unnamedplus"
-- spelllang: english dictionary for spellcheck
-- activate per session with :set spell
vim.opt.spelllang = "en_gb"
-- PACKAGE MANAGER
-- lazy.nvim bootstraps itself from github on first run
-- subsequent launches load it from the local data directory
-- lazy-lock.json pins every plugin to an exact commit hash,
-- making the environment fully reproducible across machines
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)
-- PLUGINS
require("lazy").setup({
-- COLOURSCHEME
-- flexoki-dark is a warm, low-contrast dark theme
-- priority = 1000 loads it before all other plugins
-- background is overridden to pure black for high-contrast output
{
"kepano/flexoki-neovim",
name = "flexoki",
priority = 1000,
config = function()
vim.cmd("colorscheme flexoki-dark")
vim.api.nvim_set_hl(0, "Normal", { bg = "#000000" })
vim.api.nvim_set_hl(0, "NormalFloat", { bg = "#000000" })
end,
},
-- SYNTAX
-- treesitter parses source into an abstract syntax tree
-- highlighting derives from structure, not regex pattern matching
-- build = ":TSUpdate" keeps parsers current on plugin update
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
config = function()
require("nvim-treesitter").install({ "lua", "bash", "markdown", "markdown_inline" })
-- attach treesitter highlighting when neovim identifies the filetype
vim.api.nvim_create_autocmd("FileType", {
pattern = { "lua", "sh", "markdown" },
callback = function()
vim.treesitter.start()
end,
})
end,
},
-- COMPLETION
-- nvim-cmp is a completion engine with pluggable sources
-- sources here are local only: filesystem paths and open buffer text
-- no network calls, no corporate api endpoints, no telemetry
{
"hrsh7th/nvim-cmp",
dependencies = {
"hrsh7th/cmp-path",
"hrsh7th/cmp-buffer",
},
config = function()
local cmp = require("cmp")
cmp.setup({
mapping = cmp.mapping.preset.insert({
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-Space>"] = cmp.mapping.complete(),
["<C-e>"] = cmp.mapping.abort(),
["<CR>"] = cmp.mapping.confirm({ select = true }),
}),
sources = cmp.config.sources({
{ name = "path" },
{ name = "buffer" },
})
})
end,
},
-- BUFFER LINE
-- renders open buffers as a top bar with ordinal numbering
-- shift-h and shift-l cycle through buffers
-- leader-x unloads the current buffer from memory
{
"akinsho/bufferline.nvim",
config = function()
require("bufferline").setup({
options = {
mode = "buffers",
numbers = "ordinal",
show_buffer_close_icons = false,
show_close_icon = false,
separator_style = "thin",
enforce_regular_tabs = true,
always_show_bufferline = true,
}
})
vim.keymap.set("n", "<S-h>", "<cmd>BufferLineCyclePrev<CR>", { desc = "previous buffer" })
vim.keymap.set("n", "<S-l>", "<cmd>BufferLineCycleNext<CR>", { desc = "next buffer" })
vim.keymap.set("n", "<leader>x", "<cmd>bdelete<CR>", { desc = "close buffer" })
end,
},
-- MARKDOWN RENDERING
-- renders markdown structure visually inside neovim
-- headings display as weighted blocks, code fences receive backgrounds
-- sign = false removes sign column decoration
-- code.width = "block" extends fence background to window width
{
"MeanderingProgrammer/render-markdown.nvim",
dependencies = { "nvim-treesitter/nvim-treesitter" },
opts = {
heading = {
sign = false,
icons = { "H1 ", "H2 ", "H3 ", "H4 ", "H5 ", "H6 " },
},
code = {
sign = false,
width = "block",
right_pad = 1,
},
},
},
-- ZEN MODE
-- removes all chrome and centres text at 80 columns
-- leader-z toggles the mode
{
"folke/zen-mode.nvim",
config = function()
require("zen-mode").setup({
window = {
width = 80,
options = {
number = false,
relativenumber = false,
}
},
})
vim.keymap.set("n", "<leader>z", "<cmd>ZenMode<CR>", { desc = "toggle zen mode" })
end
}
})
Vim — .vimrc Equivalent
Configuration file location: ~/.vimrc
Install vim-plug first:
curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
Open Vim and run :PlugInstall to install all declared plugins.
" CORE SETTINGS
" leader key: space, consistent with neovim config
let mapleader = " "
let maplocalleader = " "
" line numbers: absolute and relative simultaneously
set number
set relativenumber
" mouse support in all modes
set mouse=a
" case-insensitive search with smart-case override
set ignorecase
set smartcase
" reduce key code timeout delay
set updatetime=250
" 24-bit colour support
set termguicolors
" route unnamed register through system clipboard
" requires wl-clipboard on wayland or xclip/xsel on x11
set clipboard=unnamedplus
" english spellcheck dictionary
" activate with :set spell
set spelllang=en_gb
" QUALITY OF LIFE
" display command being constructed in the status area
set showcmd
" status line always visible
set laststatus=2
" faster terminal redraws
set ttyfast
" write swap data to memory only; version control is the recovery mechanism
set noswapfile
" soft wrap at word boundaries
set wrap
set linebreak
" INDENTATION
set autoindent
set smartindent
set expandtab
set tabstop=2
set shiftwidth=2
set softtabstop=2
" SCROLLING
" maintain context lines above and below the cursor
set scrolloff=8
set sidescrolloff=8
" SPLITS
" new splits open to the right and below
set splitright
set splitbelow
" UNDO
" persistent undo survives session boundaries
set undofile
set undodir=~/.vim/undodir
" create the undo directory if it does not exist
silent !mkdir -p ~/.vim/undodir
" COMMAND LINE COMPLETION
set wildmenu
set wildmode=longest:full,full
" PLUGINS
call plug#begin('~/.vim/plugged')
" COLOURSCHEME
" flexoki-neovim is compatible with Vim
Plug 'kepano/flexoki-neovim'
" BUFFER LINE
" vim-airline renders a buffer bar and status line
" operates without nerd fonts; icon dependency removed
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
" ZEN MODE
" goyo centres text at a fixed column width
" limelight dims inactive paragraphs during composition
Plug 'junegunn/goyo.vim'
Plug 'junegunn/limelight.vim'
" COMPLETION
" asyncomplete provides async buffer and path completion
" all sources are local; no external api calls
Plug 'prabirshrestha/asyncomplete.vim'
Plug 'prabirshrestha/asyncomplete-buffer.vim'
Plug 'prabirshrestha/asyncomplete-file.vim'
" MARKDOWN SYNTAX
" vim-markdown extends native markdown highlighting and folding
Plug 'preservim/vim-markdown'
call plug#end()
" COLOURSCHEME
colorscheme flexoki-dark
" override background to pure black
highlight Normal guibg=#000000
highlight NormalFloat guibg=#000000
" AIRLINE
" enable the buffer tab bar
let g:airline#extensions#tabline#enabled = 1
" display buffer numbers for :b{N} navigation
let g:airline#extensions#tabline#buffer_nr_show = 1
" plain separators; no powerline glyphs required
let g:airline#extensions#tabline#left_sep = ' '
let g:airline#extensions#tabline#left_alt_sep = '|'
let g:airline_theme = 'dark'
" buffer cycling: shift-h and shift-l, consistent with neovim config
nnoremap <S-h> :bprevious<CR>
nnoremap <S-l> :bnext<CR>
" leader-x closes current buffer
nnoremap <leader>x :bdelete<CR>
" GOYO
" 80 column writing window
let g:goyo_width = 80
" limelight activates and deactivates with goyo
autocmd! User GoyoEnter Limelight
autocmd! User GoyoLeave Limelight!
" leader-z toggles zen mode, consistent with neovim config
nnoremap <leader>z :Goyo<CR>
" ASYNCOMPLETE
call asyncomplete#register_source(asyncomplete#sources#buffer#get_source_options({
\ 'name': 'buffer',
\ 'allowlist': ['*'],
\ 'completor': function('asyncomplete#sources#buffer#completor'),
\ }))
call asyncomplete#register_source(asyncomplete#sources#file#get_source_options({
\ 'name': 'file',
\ 'allowlist': ['*'],
\ 'completor': function('asyncomplete#sources#file#completor')
\ }))
" tab and shift-tab navigate the completion popup
inoremap <expr> <Tab> pumvisible() ? "\<C-n>" : "\<Tab>"
inoremap <expr> <S-Tab> pumvisible() ? "\<C-p>" : "\<S-Tab>"
inoremap <expr> <CR> pumvisible() ? asyncomplete#close_popup() : "\<CR>"
" MARKDOWN
" folding disabled
let g:vim_markdown_folding_disabled = 1
" strikethrough syntax enabled
let g:vim_markdown_strikethrough = 1
" KEY MAPS
" clear search highlights
nnoremap <leader>n :noh<CR>
" file operations
nnoremap <leader>w :w<CR>
nnoremap <leader>q :q<CR>
nnoremap <leader>Q :qa!<CR>
" split navigation
nnoremap <leader>h <C-w>h
nnoremap <leader>j <C-w>j
nnoremap <leader>k <C-w>k
nnoremap <leader>l <C-w>l
" open splits
nnoremap <leader>v :vsp<CR>
nnoremap <leader>s :sp<CR>
" toggle spellcheck
nnoremap <leader>p :set spell!<CR>
The Plugin Ecosystem
lazy.nvim
lazy.nvim is the Neovim package manager. It defers plugin loading until the plugin is triggered, keeping startup time constant regardless of plugin count. The lazy-lock.json file records the exact commit hash of every installed plugin. This makes the environment fully reproducible: cloning the config repository and running :Lazy restore produces an identical setup on any machine.
| Command |
Action |
:Lazy |
Open the lazy.nvim interface |
:Lazy update |
Update all plugins to latest commits |
:Lazy restore |
Sync all plugins to lockfile hashes |
:Lazy clean |
Remove plugins no longer declared |
kepano/flexoki-neovim
Flexoki is a warm, paper-derived colour palette. It provides accurate highlighting across a wide range of filetypes without relying on the terminal's 16-colour palette. The background override in the config sets it to pure black (#000000) for maximum contrast in terminal environments.
nvim-treesitter
Treesitter constructs a concrete syntax tree from source code and derives highlighting from structural nodes. It correctly handles edge cases that break regex-based highlighting: strings containing quote characters, heredoc blocks, nested interpolations, and multiline constructs. Adding parsers is straightforward.
| Command |
Action |
:TSInstall {language} |
Install a parser |
:TSUpdate |
Update all installed parsers |
:TSBufToggle highlight |
Toggle treesitter highlighting for current buffer |
nvim-cmp
nvim-cmp is a completion framework. It accepts sources as dependencies and routes their output through a unified completion popup. The configuration here uses two offline sources: cmp-buffer draws words from open buffers, cmp-path completes filesystem paths. Both operate without network access. No AI completion endpoints. No data leaves the machine.
bufferline.nvim
bufferline renders the open buffer list as a styled top bar with ordinal numbering. nvim-web-devicons has been removed from the dependency list; icon fonts are decorative and introduce a font dependency that creates inconsistency across terminal environments.
render-markdown.nvim
render-markdown.nvim applies visual structure to markdown files inside the editor. Headings render as weighted labels. Code fences receive a filled background at block width. The plugin depends on treesitter for its markdown parser. The Nerd Font heading glyphs from the original config have been replaced with plain text labels (H1 through H6) to remove the font dependency.
zen-mode.nvim / goyo.vim
Both plugins remove editor chrome and centre the buffer at a fixed column width. zen-mode.nvim operates in Neovim. goyo.vim is the equivalent for Vim. Both are triggered by Space-z. The 80-column width enforces typographic discipline for long-form writing.
Cheatsheet
Modes
| Key |
Mode |
i |
Insert before cursor |
I |
Insert at line start |
a |
Append after cursor |
A |
Append at line end |
o |
New line below, Insert |
O |
New line above, Insert |
v |
Visual character |
V |
Visual line |
Ctrl-v |
Visual block |
R |
Replace |
: |
Command line |
Escape |
Return to Normal |
Navigation
| Key |
Action |
h j k l |
Left, down, up, right |
w / W |
Next word / WORD |
b / B |
Previous word / WORD |
e / E |
End of word / WORD |
0 |
Line start (column 0) |
^ |
First non-blank character |
$ |
Line end |
f{c} / F{c} |
Find char forward / backward on line |
t{c} / T{c} |
To char forward / backward on line |
; / , |
Repeat f/t forward / reverse |
gg / G |
File start / end |
{N}G |
Line N |
Ctrl-d / Ctrl-u |
Scroll half-screen down / up |
Ctrl-f / Ctrl-b |
Scroll full-screen down / up |
H / M / L |
Screen top / middle / bottom |
zz / zt / zb |
Centre / top / bottom screen on cursor |
Ctrl-o / Ctrl-i |
Jump back / forward in jump list |
* / # |
Search word under cursor forward / backward |
Operators
| Key |
Action |
d |
Delete |
y |
Yank |
c |
Change |
> / < |
Indent right / left |
= |
Auto-indent |
gU / gu |
Uppercase / lowercase |
~ |
Toggle case |
Text Objects
| Key |
Object |
iw / aw |
Word / word with space |
ip / ap |
Paragraph inner / around |
i" / a" |
Double quotes inner / around |
i' / a' |
Single quotes inner / around |
i) / a) |
Parentheses inner / around |
i] / a] |
Brackets inner / around |
i} / a} |
Braces inner / around |
it / at |
Tag inner / around |
Composed Commands
| Command |
Action |
dw |
Delete word |
d$ |
Delete to end of line |
ci" |
Change inside double quotes |
yip |
Yank inner paragraph |
da( |
Delete including parentheses |
>ip |
Indent paragraph |
gUiw |
Uppercase inner word |
Common Edits
| Key |
Action |
x |
Delete character |
r{c} |
Replace character |
J |
Join line with line below |
u |
Undo |
Ctrl-r |
Redo |
. |
Repeat last change |
p / P |
Paste after / before cursor |
dd / yy / cc |
Delete / yank / change line |
Search and Replace
| Command |
Action |
/{pat} |
Search forward |
?{pat} |
Search backward |
n / N |
Next / previous match |
:noh |
Clear highlights |
:%s/a/b/g |
Replace all in file |
:%s/a/b/gc |
Replace with confirmation |
:'<,'>s/a/b/g |
Replace in selection |
:g/{pat}/d |
Delete all matching lines |
Buffers and Windows
| Command |
Action |
:ls |
List buffers |
:b{N} |
Go to buffer N |
:bn / :bp |
Next / previous buffer |
:bd |
Unload buffer |
Ctrl-w s / v |
Horizontal / vertical split |
Ctrl-w h/j/k/l |
Navigate splits |
Ctrl-w = |
Equalise splits |
:only |
Close other windows |
Shift-h / Shift-l |
Previous / next buffer (this config) |
Space-x |
Close buffer (this config) |
Marks and Registers
| Command |
Action |
m{a} |
Set mark |
`{a} |
Jump to mark |
'{a} |
Jump to mark's line |
:marks |
List marks |
"ayy |
Yank line to register a |
"ap |
Paste from register a |
"+yy / "+p |
Yank to / paste from system clipboard |
:registers |
List all registers |
Macros
| Command |
Action |
q{a} |
Begin recording into register a |
q |
Stop recording |
@{a} |
Execute macro |
{N}@{a} |
Execute macro N times |
@@ |
Repeat last macro |
File and Session Commands
| Command |
Action |
:w |
Write buffer |
:wa |
Write all buffers |
:q / :q! |
Close / force close |
:wq |
Write and close |
:e {file} |
Open file |
:e! |
Reload from disk |
:r !{cmd} |
Insert command output |
:checkhealth |
Neovim diagnostic report |
:Lazy |
Plugin manager interface |
:Lazy update |
Update all plugins |
:Lazy restore |
Sync to lockfile |
This Config's Custom Keymaps
| Key |
Action |
Space-z |
Toggle zen mode |
Space-x |
Close current buffer |
Shift-h |
Previous buffer |
Shift-l |
Next buffer |
Space-w |
Write file (Vim) |
Space-q |
Quit (Vim) |
Space-n |
Clear search highlights (Vim) |
Space-v / Space-s |
Vertical / horizontal split (Vim) |
Space-h/j/k/l |
Navigate splits (Vim) |
Space-p |
Toggle spellcheck (Vim) |