DEV Community

loading...

Fish-style abbreviations in Zsh

Martin Frost
・2 min read

A few years ago, I tried using The Friendly Interactive SHell as my default shell.

It has a lot of nice things, like syntax highlighting on the command line, reverse-history-search by pressing the up/down arrows, and a few more things, but it kept rubbing me the wrong way that it is not POSIX compatible (like bash or zsh), so I ended up going back to zsh.

I did, however, bring a few features with me because they are awesome, and abbreviations is one of those features. They are sort of like aliases but better, at least in my opinion.

Abbreviations vs aliases

In all shells there is something called aliases.
Aliases work like this, you define some alias, like alias gst="git status". After this, if I type gst into my terminal and hit enter, the terminal will run the git status command and display the output as expected.

In fish, I can also define an abbreviation like this:

abbr -a -g gst git status

It works mostly the same way as an alias but with one very important exception: if I type gst in my terminal and hit space, the terminal will expand the abbreviation. If I hit enter instead of space, it will expand the abbreviation and run the command.

This means that it's easier to see what commands have been run by looking at the history and seeing a bunch of lines with git status instead of just gst, and it also means that I can copy commands that I ran directly from my terminal, send them to a collegue, and they can just paste and run them directly if they want to. They don't need to know anything about what aliases I happen to have in my shell config.

How to get this in Zsh?

I simply add these functions in my ~/.zshrc and then I can start listing abbreviations:

# declare a list of expandable aliases to fill up later
typeset -a ealiases
ealiases=()

# write a function for adding an alias to the list mentioned above
function abbrev-alias() {
    alias $1
    ealiases+=(${1%%\=*})
}

# expand any aliases in the current line buffer
function expand-ealias() {
    if [[ $LBUFFER =~ "\<(${(j:|:)ealiases})\$" ]]; then
        zle _expand_alias
        zle expand-word
    fi
    zle magic-space
}
zle -N expand-ealias

# Bind the space key to the expand-alias function above, so that space will expand any expandable aliases
bindkey ' '        expand-ealias
bindkey '^ '       magic-space     # control-space to bypass completion
bindkey -M isearch " "      magic-space     # normal space during searches

# A function for expanding any aliases before accepting the line as is and executing the entered command
expand-alias-and-accept-line() {
    expand-ealias
    zle .backward-delete-char
    zle .accept-line
}
zle -N accept-line expand-alias-and-accept-line

Then I can start adding abbreviations like so:

abbrev-alias g="git"
abbrev-alias gst="git status"
abbrev-alias gcb="git checkout --branch"
abbrev-alias ll="ls -l"

Discussion (0)