# Shell Line Editing with Zsh Zsh provides powerful line editing capabilities through the ZLE (Zsh Line Editor) system. Understanding how to configure and use it will transform your command-line efficiency. ## What is Shell Line Editing? When you type in your terminal, you're not directly sending characters to programs. Instead, the **shell** (zsh, bash, etc.) captures your keystrokes and lets you edit the line before executing it. This is **Layer 3** in the [[Dev Environment Stack]]. ## Two Editing Modes: Emacs vs Vi Zsh supports two editing modes: ### Emacs Mode (Default) Uses Ctrl-based shortcuts: - Ctrl+A/E for line start/end - Ctrl+W to delete words - Ctrl+R for reverse search **This is what most people use** and what [[Terminal Keyboard Shortcuts]] covers. ### Vi Mode Mimics Vim's modal editing in the shell: - Normal mode: Navigate with h/j/k/l - Insert mode: Type normally - Press Esc to switch to normal mode **Enable vi mode:** ```bash # Add to ~/.zshrc set -o vi ``` ## Customizing Line Editing ### Check Current Mode ```bash # See current keybindings bindkey # Check if in vi or emacs mode bindkey -l # List keymaps ``` ### Emacs Mode Customizations Add these to `~/.zshrc`: ```bash # Ensure emacs mode bindkey -e # Custom keybindings bindkey '^X^E' edit-command-line # Ctrl+X Ctrl+E: Edit in $EDITOR bindkey '^P' up-line-or-search # Ctrl+P: Previous with search bindkey '^N' down-line-or-search # Ctrl+N: Next with search # Make Option+arrows work on Mac bindkey "^[^[[D" backward-word # Option+Left bindkey "^[^[[C" forward-word # Option+Right ``` ### Vi Mode Power User Setup ```bash # Add to ~/.zshrc set -o vi # Show mode in prompt (insert vs normal) function zle-line-init zle-keymap-select { VIM_PROMPT="%{$fg_bold[yellow]%} [% NORMAL]% %{$reset_color%}" RPS1="${${KEYMAP/vicmd/$VIM_PROMPT}/(main|viins)/} $EPS1" zle reset-prompt } zle -N zle-line-init zle -N zle-keymap-select # Better vi mode bindings bindkey -M vicmd 'k' history-beginning-search-backward bindkey -M vicmd 'j' history-beginning-search-forward ``` ## Advanced ZLE Features ### Edit Command in Your Editor Sometimes a command is too complex to edit on one line. Open it in your full editor: ```bash # Add to ~/.zshrc autoload -U edit-command-line zle -N edit-command-line bindkey '^X^E' edit-command-line # Ctrl+X Ctrl+E # Now when you press Ctrl+X Ctrl+E: # 1. Opens your $EDITOR (nvim, vim, etc.) # 2. You edit the command with full editor features # 3. Save and quit → command executes ``` ### History Substring Search Search history based on what you've already typed: ```bash # Install the plugin brew install zsh-history-substring-search # Add to ~/.zshrc source /opt/homebrew/share/zsh-history-substring-search/zsh-history-substring-search.zsh # Bind to arrow keys bindkey '^[[A' history-substring-search-up # Up arrow bindkey '^[[B' history-substring-search-down # Down arrow ``` **Usage:** ```bash # Type partial command docker run # Press ↑ → Cycles through all 'docker run' commands in history # Much better than Ctrl+R for recent commands! ``` ### Syntax Highlighting See your command highlighted as you type (like a modern IDE): ```bash # Install brew install zsh-syntax-highlighting # Add to ~/.zshrc (must be at END of file) source /opt/homebrew/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh ``` **What you get:** - ✅ Valid commands show green - ❌ Invalid commands show red - 📁 Paths show underlined when they exist - 🔧 Options and flags show in different colors ### Autosuggestions (Fish-like) Get suggestions based on history as you type: ```bash # Install brew install zsh-autosuggestions # Add to ~/.zshrc source /opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh # Accept suggestion: Right arrow or Ctrl+E ``` **Usage:** ```bash # You start typing: docker r # Immediately shows gray suggestion: docker run -it mycontainer bash ↑ (from your history) # Press → to accept ``` ## Complete Recommended Setup Here's a production-ready `~/.zshrc` line editing section: ```bash # ============================================ # ZSH Line Editing Configuration # ============================================ # Use emacs mode (Ctrl-based shortcuts) bindkey -e # Custom keybindings bindkey '^X^E' edit-command-line # Ctrl+X Ctrl+E: Edit in editor bindkey '^P' up-line-or-search # Ctrl+P: Search up bindkey '^N' down-line-or-search # Ctrl+N: Search down # Fix Option+arrows on Mac (if needed) bindkey "^[^[[D" backward-word bindkey "^[^[[C" forward-word # Load edit-command-line widget autoload -U edit-command-line zle -N edit-command-line # ============================================ # Enhanced Features (requires brew install) # ============================================ # Syntax highlighting (must be near end of .zshrc) source /opt/homebrew/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh # Autosuggestions (Fish-like) source /opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh # History substring search source /opt/homebrew/share/zsh-history-substring-search/zsh-history-substring-search.zsh bindkey '^[[A' history-substring-search-up bindkey '^[[B' history-substring-search-down # ============================================ # History Configuration # ============================================ HISTFILE=~/.zsh_history HISTSIZE=50000 # Lines in memory SAVEHIST=50000 # Lines saved to file setopt EXTENDED_HISTORY # Save timestamp setopt SHARE_HISTORY # Share between sessions setopt HIST_IGNORE_DUPS # Don't save duplicates setopt HIST_IGNORE_SPACE # Ignore commands starting with space setopt HIST_VERIFY # Show before executing ! ``` ## Troubleshooting ### Option+Arrows Insert Weird Characters **Problem:** Option+← inserts `^[b` instead of moving back one word **Solution:** Configure your terminal to send Option as Meta/Esc+: - **Ghostty:** Check preferences for "Option as Meta" - **iTerm2:** Preferences → Profiles → Keys → Use Option as Meta key - **Alacritty:** Add to config: ```yaml key_bindings: - { key: Left, mods: Alt, chars: "\x1bb" } - { key: Right, mods: Alt, chars: "\x1bf" } ``` ### Ctrl+W Deletes Entire Path **Problem:** `Ctrl+W` on `/path/to/file` deletes the whole thing, not just `file` **Solution:** Customize word boundaries: ```bash # Add to ~/.zshrc # Define word boundaries to treat / as a separator WORDCHARS='*?_-.[]~=&;!#$%^(){}<>' # (removed / from default WORDCHARS) ``` ### Commands Don't Save to History **Check:** ```bash # Is history enabled? echo $SAVEHIST # Are you starting commands with a space? (HIST_IGNORE_SPACE) echo "This won't save" echo "This will save" ``` ## Comparison: Emacs Mode vs Vi Mode | Feature | Emacs Mode | Vi Mode | |---------|------------|---------| | **Learning curve** | Easier (familiar Ctrl shortcuts) | Steeper (modal editing) | | **Efficiency** | Good for most users | Better for Vim experts | | **Muscle memory** | Works everywhere | Conflicts with non-vi tools | | **Recommendation** | Start here | Switch once you master Vim | **Bottom line:** Use emacs mode unless you're already a Vim power user. The Ctrl-based shortcuts work in most text fields (browsers, IDEs, etc.), while vi mode only helps in the shell. ## Power User Workflow **My recommended progression:** 1. **Week 1:** Master basic shortcuts from [[Terminal Keyboard Shortcuts]] - Ctrl+A/E, Ctrl+W, Ctrl+R 2. **Week 2:** Install syntax highlighting and autosuggestions ```bash brew install zsh-syntax-highlighting zsh-autosuggestions ``` 3. **Week 3:** Configure history substring search - Type `docker run`, press ↑ to cycle through docker run commands 4. **Week 4:** Learn edit-command-line (Ctrl+X Ctrl+E) - Edit complex one-liners in Neovim 5. **Advanced:** Consider vi mode if you're a Vim expert **Related:** - [[Terminal Keyboard Shortcuts]] - Complete shortcut reference - [[Dev Environment Stack]] - How shell fits into the bigger picture - [[Neovim/Neovim - The Vim Ecosystem]] - If you want to go all-in on vi mode