Zsh’s bindkey command lets you remap keys to actions, but most people don’t realize it’s a full-fledged macro language, not just a simple key-to-command mapping.
Let’s see it in action. Imagine you’re constantly typing git status and then git diff. We can bind Ctrl+s to type git status\n and Ctrl+d to type git diff\n.
# In your ~/.zshrc
# Bind Ctrl+s to type "git status" and press Enter
bindkey '^s' 'git status\n'
# Bind Ctrl+d to type "git diff" and press Enter
bindkey '^d' 'git diff\n'
After sourcing your ~/.zshrc (or opening a new terminal), pressing Ctrl+s will execute git status and Ctrl+d will execute git diff.
This works by associating a key sequence with a string that Zsh then interprets as if you typed it. The \n represents the Enter key, so it not only types the command but also executes it.
The real power comes when you go beyond simple strings. bindkey can execute shell commands, call Zsh functions, and even manipulate the command line buffer directly. This is where the "macro language" aspect shines.
Consider a more complex scenario: binding Alt+g to stage all modified and deleted files in Git. This requires more than just typing text; it needs to run a command and insert its output.
# In your ~/.zshrc
# Function to stage all modified and deleted files
stage_all_git() {
BUFFER="git add -u"
zle accept-line
}
# Bind Alt+g to the stage_all_git function
bindkey '^[g' stage_all_git
Here, ^[g represents Alt+g. When you press Alt+g:
- The
stage_all_gitfunction is executed. BUFFER="git add -u"directly modifies the command line buffer, setting its content togit add -u.zle accept-linesimulates pressing Enter, executing the command now in the buffer.
This allows for dynamic actions. You’re not just typing a fixed string; you’re executing logic. The BUFFER variable is Zsh’s internal representation of your current command line, and zle (Zsh Line Editor) commands allow you to manipulate it.
You can also bind keys to existing Zsh widgets (built-in functions like accept-line, backward-kill-word, etc.) or even create complex scripts that interact with the terminal. For instance, you could bind Ctrl+x Ctrl+p to a function that prompts you for a filename, then inserts the current date and time into the buffer at the cursor.
# In your ~/.zshrc
# Function to insert date and time
insert_datetime() {
BUFFER+=" $(date '+%Y-%m-%d %H:%M:%S')"
zle redisplay
}
# Bind Ctrl+x Ctrl+p to the insert_datetime function
bindkey '^x^p' insert_datetime
zle redisplay is crucial here; it tells Zsh to refresh the screen to show the changes made to BUFFER.
The common misconception is that bindkey is just for simple text insertion. The reality is that bindkey is a gateway to Zsh’s powerful line-editing capabilities, allowing you to automate complex workflows and tailor your shell experience to an extreme degree. You can access a vast array of built-in widgets and create your own custom functions to handle almost any command-line task.
The next step is to explore the wealth of built-in Zsh widgets available by running bindkey -L.