Zsh Keybindings

Keymap Modes

Emacs vs Vi mode
# Emacs mode (default)
bindkey -e

# Vi mode
bindkey -v
export KEYTIMEOUT=1                      # reduce mode switch delay (10ms)

# Check current keymap
bindkey -l                               # list all keymaps
bindkey -M emacs                         # show emacs bindings
bindkey -M vicmd                         # show vi command mode bindings
bindkey -M viins                         # show vi insert mode bindings

Essential Emacs-Mode Bindings

Default bindings (Ctrl-based)
# Navigation
Ctrl-a          # beginning of line
Ctrl-e          # end of line
Ctrl-f          # forward one char
Ctrl-b          # back one char
Alt-f           # forward one word
Alt-b           # back one word

# Editing
Ctrl-d          # delete char under cursor (or EOF if empty)
Ctrl-h          # backspace
Ctrl-w          # delete word backward
Alt-d           # delete word forward
Ctrl-u          # delete from cursor to beginning of line
Ctrl-k          # delete from cursor to end of line
Ctrl-y          # paste (yank) last deleted text
Ctrl-t          # transpose chars
Alt-t           # transpose words

# History
Ctrl-r          # reverse search history
Ctrl-s          # forward search history
Ctrl-p          # previous command (same as Up)
Ctrl-n          # next command (same as Down)
Alt-.           # insert last argument of previous command
                # (repeat to cycle through older commands)

# Misc
Ctrl-l          # clear screen
Ctrl-c          # cancel current line
Ctrl-z          # suspend current process
Ctrl-x Ctrl-e   # edit current line in $EDITOR

Custom Bindings

bindkey patterns
# Bind to key sequence
bindkey '^[l' clear-screen               # Alt-l to clear
bindkey '^X^E' edit-command-line         # Ctrl-x Ctrl-e to edit in vim

# Bind to widget
autoload -Uz edit-command-line
zle -N edit-command-line
bindkey '^xe' edit-command-line

# Find key codes
# Run: cat -v   then press the key
# Or:  showkey -a
# Common sequences:
#   ^[[A    Up arrow
#   ^[[B    Down arrow
#   ^[[H    Home
#   ^[[F    End
#   ^[[3~   Delete
#   ^[[1;5C Ctrl-Right
#   ^[[1;5D Ctrl-Left

Custom Widgets

ZLE (Zsh Line Editor) widgets
# Insert sudo at beginning of line
insert-sudo() {
  BUFFER="sudo $BUFFER"
  CURSOR=$((CURSOR + 5))
}
zle -N insert-sudo
bindkey '^[s' insert-sudo                # Alt-s to prepend sudo

# Accept and run
accept-and-hold() {
  zle accept-line                        # run command
  # line stays in buffer for editing
}

# Jump to project directory
goto-project() {
  local dir
  dir=$(find ~/atelier -maxdepth 3 -type d -name ".git" 2>/dev/null | \
    sed 's|/.git||' | \
    fzf --prompt="Project: ")
  if [[ -n "$dir" ]]; then
    BUFFER="cd $dir"
    zle accept-line
  fi
}
zle -N goto-project
bindkey '^xp' goto-project              # Ctrl-x p

Vi Mode Enhancements

Making vi mode practical
# Cursor shape changes by mode
function zle-keymap-select {
  if [[ $KEYMAP == vicmd ]]; then
    echo -ne '\e[2 q'                   # block cursor (normal mode)
  else
    echo -ne '\e[6 q'                   # beam cursor (insert mode)
  fi
}
zle -N zle-keymap-select

# Initialize beam cursor for insert mode
zle-line-init() { echo -ne '\e[6 q' }
zle -N zle-line-init

# Keep useful emacs bindings in vi insert mode
bindkey -M viins '^a' beginning-of-line
bindkey -M viins '^e' end-of-line
bindkey -M viins '^r' history-incremental-search-backward
bindkey -M viins '^w' backward-kill-word