Emacs-style Line Editing — origin & must-know subset¶
For non-vim users: this repo provides a chezmoi
enableVimModeprompt (defaulttrue). Set it tofalseand your shells (zsh + bash) drop modal vi editing entirely — they behave exactly the way this guide describes (pure emacs keymap). Seedocs/this_repo/vim-mode.mdfor the opt-out flow.
(stub — sections filled in by subsequent edits)
TL;DR¶
The "weird" Ctrl+A / Ctrl+E / Ctrl+W / Ctrl+R shortcuts you see
everywhere — bash, zsh, psql, python REPL, node, sqlite3,
many TUI input boxes, even macOS native text fields — are all
descendants of the Emacs editor's keymap, propagated via GNU
Readline (bash's line editor) and zsh's ZLE (Zsh Line Editor).
It's not a POSIX standard; it's a layered convention that stuck because it was early, simple, modeless, and well-suited to single-line command editing. You don't need to memorize the whole table — see Tier 1 below for the 8 that pay back their learning cost on day one.
For the practical lookup of every binding active in this repo
(built-in, plugin, custom widget) press Alt+/ in any zsh prompt — see
Zsh Keybindings & the keys-picker widget.
Why every Unix shell shares the same shortcuts¶
The lineage looks like this:
Terminal (xterm / Ghostty / Alacritty / iTerm2 / …)
↓ raw keystrokes / escape sequences
Shell's line editor
├─ bash → GNU Readline (default keymap: emacs)
├─ zsh → ZLE (default keymap: emacs)
└─ fish → fish's own line editor (emacs-flavored defaults)
↓ translates keys → "widgets" (named actions)
Widget (beginning-of-line, kill-line, yank, …)
The default keymap is Emacs in all three. You can switch to vi-mode
with one line (§ Switching modes), but very few
people do — partly because most terminals' editing tasks are short
single-line commands where modeless beats modal, partly because the
Emacs keys are already burned into muscle memory after years of using
them in REPLs, psql, sqlite3, and the macOS Cocoa text system.
It is not POSIX. POSIX sh says nothing about line editing — it's
the optional set -o emacs / set -o vi extension in the bash spec,
and a separate bindkey system in zsh. Why it's near-universal is
historical:
- Early Unix shells had no line editing — you typed, hit Enter, couldn't fix typos.
- Bill Joy's vi (1976) and Stallman's Emacs (1976) both shipped with their own keymap conventions for in-buffer editing.
- Readline was extracted from the bash project in the 1980s as a
reusable library; its author chose Emacs bindings as the default
because Stallman's keymap was already documented and "modeless"
(no
Escdance) — friendlier to novices. - Every CLI tool that wanted history + line editing started linking
against Readline (or its BSD sibling
editline/libedit) — psql, python, sqlite3, node, irb, gdb, lldb, mysql, … - zsh's ZLE was written from scratch but kept Readline-compatible
keybindings on purpose (the
emacskeymap), so users wouldn't have to relearn anything when switching shells.
So the convention isn't "Emacs is more popular" — it's "Readline got
there first and everything plugs into Readline". Even macOS's native
text fields support a subset (Ctrl+A, Ctrl+E, Ctrl+K, Ctrl+Y,
Ctrl+P, Ctrl+N) because Cocoa's text system was inspired by the
same convention.
The 3-tier breakdown¶
Don't try to memorize 40 shortcuts. The bindings split cleanly into must-know / nice-to-know / look-up-when-needed.
Tier 1 — must memorize (8 keys)¶
These pay back their cost on day one. Real high-frequency.
| Key | Action | Mnemonic |
|---|---|---|
Ctrl+A |
Beginning of line | Ahead / start |
Ctrl+E |
End of line | — |
Ctrl+U |
Kill to start of line | "U-turn" back |
Ctrl+K |
Kill to end of line | — |
Ctrl+W |
Kill previous word | — |
Ctrl+R |
Reverse history search (fzf in this repo) | — |
Ctrl+L |
Clear screen (keeps current command) | Light refresh |
Ctrl+X Ctrl+E |
Open current command in $EDITOR |
"Edit" |
If you only ever learn these 8, you've covered ~90% of the daily wins.
Tier 2 — nice to know¶
Worth a second pass once Tier 1 is muscle memory.
| Key | Action |
|---|---|
Alt+B |
Move back one word (Alt = "word-wise") |
Alt+F |
Move forward one word |
Alt+D |
Kill next word (forward Ctrl+W) |
Alt+. |
Insert last argument of previous command |
Ctrl+Y |
Yank (paste) last killed text |
Ctrl+T |
Transpose two characters (fix typo: teh → the) |
Ctrl+_ |
Undo |
Ctrl+P / Ctrl+N |
Previous / next history entry (= ↑/↓) |
Alt+. alone replaces 80% of "I need the last arg again" cases —
e.g. mkdir foo/bar/ then cd <Alt+.>.
Tier 3 — look up when needed¶
Don't memorize. Use Alt+/ (the keys-picker) or
bindings (CLI) to grep when you need them.
| Key | Action |
|---|---|
Ctrl+X Ctrl+U |
Undo (alternate) |
Ctrl+X Ctrl+X |
Swap cursor and mark |
Ctrl+X Ctrl+R |
Re-read inputrc |
Alt+T |
Transpose two words |
Alt+U / Alt+L |
Upper/lower-case next word |
Alt+C |
Capitalize next word |
Alt+\\ |
Delete surrounding whitespace |
Ctrl+V |
Verbatim insert (e.g. literal Ctrl+J) |
Home / End vs Ctrl+A / Ctrl+E¶
On most modern keyboards Home and End work fine in bash/zsh and do
the same thing as Ctrl+A / Ctrl+E. So why bother with the Ctrl
versions?
- They work everywhere.
psql,python,node,sqlite3,irb,gdb,lldb, ssh-into-a-busybox-container, tmux copy-mode, macOS Cocoa text fields — all supportCtrl+A/Ebecause they all embed Readline (or a Readline-compatible editor).Home/Endneed the terminal → app → library chain to agree on the escape sequence, which fails surprisingly often (tmux withoutxterm-keys, screen, serial consoles, recovery shells, …). - Hands stay on home row. No reach to the navigation cluster.
- They compose.
Ctrl+A Ctrl+K= "select all, cut" in one motion.Home Shift+End Deleteis three keys and needs a selection model the shell doesn't have. - SSH-friendly. Over flaky links, single-byte
Ctrlcodes round-trip more reliably than multi-byte\e[H/\e[Fsequences.
Same logic for Ctrl+P / Ctrl+N over ↑ / ↓ — the arrows are
multi-byte escape sequences that some environments mangle.
Why not vi mode by default?¶
zsh and bash both ship set -o vi / bindkey -v. It exists, it works,
and a vocal minority swears by it. So why is emacs the default
everywhere?
- Modeless. Single-line command editing is short. The cost of
"am I in insert or normal mode?" plus an
Escround-trip per edit outweighs the benefit of vi's verb+motion grammar, which shines on multi-line buffers, not 60-char commands. - Discoverability. New users can fumble
Ctrl+Aand see something useful happen.ddin insert mode just types "dd". - Historical inertia. Readline picked emacs in the 80s; everything downstream inherited it.
- REPL ergonomics.
python,node,psql, etc. all ship the emacs keymap by default. Switching shells to vi-mode means living in a split-brain world.
That said — if you live in Vim and want vi-mode in zsh, this repo uses
zsh-vi-mode which is far
better than the built-in bindkey -v (visual mode, surround, yank to
system clipboard, mode indicator). See
docs/shells/zsh.md for the setup. The trade-off: every new
emacs-style binding must be re-applied inside zvm_after_init because
zsh-vi-mode wipes the keymap on init — see the Alt+/ rebind in
dot_zshrc.tmpl
for the pattern.
Kill-ring vs system clipboard¶
Ctrl+K, Ctrl+U, Ctrl+W, Alt+D all "kill" text — and Ctrl+Y
"yanks" it back. This is not the system clipboard. It's an
in-process kill-ring (a stack of recently killed text), inherited
from Emacs.
Implications:
- Killed text does NOT go to
pbpaste/xclip/wl-paste. You can't paste it into a browser. Ctrl+YthenAlt+Y(in some shells) cycles through older kill-ring entries.Ctrl+W(kill word) +Ctrl+Yis a fast "duplicate the last word" pattern — kill it, yank it back, yank it again.
If you want killed text on the system clipboard, you need a wrapper
widget. zsh-vi-mode already does this for vi-mode y / d operations
when configured; for emacs-mode this repo doesn't bridge it (left as a
future TODO).
For Vim users — the Ctrl+X Ctrl+E escape hatch¶
The single most useful binding for vim users stuck in an emacs-keymap shell:
Opens the current command line in $EDITOR (set to nvim in this
repo). Edit it as a normal file — multi-line, syntax-highlighted, full
vim motions, LSP if you've configured it. Save+quit, and the edited
buffer is executed as the next command.
Use it for:
- Long
ffmpeg/awk/jqone-liners that need to be 5 lines. - Pasting a multi-line snippet from somewhere and editing it before running.
- Composing a complex
git commit -m "$(cat <<EOF .... - Editing a
forloop that you mistyped 3 levels deep.
Set EDITOR=nvim in your env (this repo does, see
dot_config/shell/00_env.sh).
With that one binding, you stop fighting the emacs keymap for anything
non-trivial.
Where this convention shows up beyond shells¶
Same keys, same expectations, courtesy of Readline / libedit / native Cocoa support:
| Tool | Notes |
|---|---|
psql, mysql, sqlite3 |
Full Readline |
python (system), ipython |
Readline (system) / prompt_toolkit (ipython, emacs-keymap by default) |
node, irb, lua |
Readline / libedit |
gdb, lldb |
Readline / libedit |
ssh password prompt |
Limited — Ctrl+U works |
tmux command-prompt (prefix + :) |
Emacs keys (settable to vi via set -g status-keys vi) |
| macOS native text fields | Ctrl+A/E/K/Y/P/N/F/B/D/T — Cocoa text system |
| Browser address bars (Chromium/Firefox on Linux+macOS) | Subset works |
| Slack/Discord input boxes | Subset works on macOS via Cocoa |
This is why learning the Tier 1 set is high-leverage — the muscle memory transfers across dozens of tools you already use.
Switching modes¶
Per-shell, in your rc file:
Per-tool (Readline-based, in ~/.inputrc):
Some tools (python's prompt_toolkit, ipython) have their own
%config switch — check tool-specific docs.
Recommended mental model¶
- Learn Tier 1 (8 keys). Two days of conscious use, then they're permanent.
- Set
EDITOR=nvimand useCtrl+X Ctrl+Efor anything longer than one line. - Use
Alt+/(keys-picker) when you sense "there's probably a binding for this" but can't remember it. Don't try to memorize the full table. - Don't bother with vi-mode unless you write multi-line shell blocks frequently AND already use vim daily. The split-brain cost (REPLs stay emacs) usually isn't worth it.
Related¶
- Zsh Keybindings & the keys-picker widget — every
binding active in this repo, with the
Alt+/picker andbindingsCLI. - Zsh setup — plugins (zsh-vi-mode, autosuggestions, syntax-highlighting) and load order.
- Bash setup — ble.sh + oh-my-bash, why some zsh-only widgets don't have a bash port yet.
- GNU Readline manual: https://tiswww.case.edu/php/chet/readline/rluserman.html
- zsh ZLE manual:
man zshzle