The line editor (ZLE) and custom widgets

Zsh · Advanced · 12 min

ZLE is what makes zsh's command line programmable: any editing action can be bound to any key.

Everything you type at the prompt goes through ZLE, the Zsh Line Editor. Every action, from moving the cursor to searching history, is a widget, and keymaps bind key sequences to widgets.

  • bindkey -e selects emacs-style keys (the default unless your EDITOR contains "vi"); bindkey -v selects vi mode.
  • bindkey alone lists all bindings; bindkey '^R' shows one. zle -al lists all widgets.
  • Press Ctrl-V and then a key to see the sequence it sends, such as ^[[A for Up in most terminals.

Useful bundled widgets: up-line-or-beginning-search makes Up search history for lines starting with what you already typed; edit-command-line opens the current line in $EDITOR. Bundled widget functions must be loaded with autoload -Uz name and registered with zle -N name before binding.

A custom widget is a shell function registered with zle -N. Inside it you can read and change special variables: BUFFER (the whole line), LBUFFER and RBUFFER (text left and right of the cursor) and CURSOR (position). zle -M "text" shows a message under the prompt, and calling zle accept-line runs the line. Key bindings and widgets belong in .zshrc, and should be set before plugins like syntax highlighting that wrap existing widgets.

Example

bindkey -e                                  # emacs keys (-v for vi mode)
autoload -Uz up-line-or-beginning-search down-line-or-beginning-search
zle -N up-line-or-beginning-search
zle -N down-line-or-beginning-search
bindkey '^[[A' up-line-or-beginning-search  # Up: history matching the typed prefix
bindkey '^[[B' down-line-or-beginning-search
autoload -Uz edit-command-line && zle -N edit-command-line
bindkey '^X^E' edit-command-line            # edit the line in $EDITOR
# A custom widget: Alt-s puts sudo in front of the current line
prepend-sudo() {
  [[ $BUFFER == sudo\ * ]] || BUFFER="sudo $BUFFER"
  CURSOR=$#BUFFER                           # cursor to the end
  zle -M "sudo added"
}
zle -N prepend-sudo
bindkey '^[s' prepend-sudo
bindkey '^R'                                # "^R" history-incremental-search-backward

Remember

  • Widgets are actions; bindkey maps key sequences to them
  • Custom widget = function + zle -N + bindkey
  • BUFFER, LBUFFER, RBUFFER and CURSOR expose the command line

Watch out for

  • Binding a bundled widget like edit-command-line without autoload and zle -N, which gives 'No such widget'.

Check yourself

  1. What are the three steps to bind your own function `myw` to Ctrl-G?
  2. Inside a widget, which variable holds the text to the left of the cursor?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html

Builds on

Unlocks

Nothing builds on this yet.