zshで略語展開

zshにはglobal aliasという機能があって, commandの位置(例えば行頭)以外でもaliasが使えるようになっている. 例えば

$ alias -g L='| less'

とすれば

$ some-command L

$ some-command | less

に展開されて実行される. けれどもこれだと予期せず展開されることがあるのと, historyに展開されてない形で記録されるので使い難い.

http://homepage1.nifty.com/blankspace/zsh/zsh.html で紹介されている略語展開はスペースを用いて展開している*1. スペースだと思わぬところで展開されることがあって大文字を入力するのに毎回 C-v を入力しなければならず面倒である. かといって他のキーにbindするにしても, yasnippetの展開は C-. にbindしていてこれはterminalでは使えないので暫く放置していた. (そもそも open-line を使ってないから C-o を展開に使ってもいいかもしれない.)

けれども, zshで略語展開を行なうのは大体行末においてだということに気付いたので C-e にbindして, 行末なら展開, そうでなければ end-of-line するようにしてみた. 以下そのコード*2.

https://gist.github.com/kenoss/8985182

typeset -A myabbrev
myabbrev=(
    "DN"  "&> /dev/null"
    "L"   "| $PAGER "
    "G"   "| grep "
    "S"   "| sed '_|_'"
    "R"   " rm "
    "M"   " mkdir "
    "C"   " cat "
    "TX"  " tar -xvzf "
    "TC"  " tar -cvzf "
    "RS"  "rsync -av --exclude '\#*' --exclude '*~' "
    "FX"  "find _|_ -print0 | xargs -0 "
    "PWD" ""
)

my-expand-abbrev() {
    if [ -z "$RBUFFER" ] ; then
        my-expand-abbrev-aux
    else
        zle end-of-line
    fi
}

my-expand-abbrev-aux() {
    local init last value addleft addright
    init=$(echo -nE "$LBUFFER" | sed -e "s/[_a-zA-Z0-9]*$//")
    last=$(echo -nE "$LBUFFER" | sed -e "s/.*[^_a-zA-Z0-9]\([_a-zA-Z0-9]*\)$/\1/")
    value=${myabbrev[$last]}
    if [[ $value = *_\|_* ]] ; then
        addleft=${value%%_\|_*}
        addright=${value#*_\|_}
    else
        addleft=$value
        addright=""
    fi
    if [ "$last" = "PWD" ] ; then
        LBUFFER=""
        RBUFFER="$PWD"
    else
        LBUFFER=$init${addleft:-$last }
        RBUFFER=$addright$RBUFFER
    fi
}

zle -N my-expand-abbrev
bindkey "^e" my-expand-abbrev

例えば

$ some-command L

で行末にカーソルがあるときに C-e すると

$ some-command | less

に展開される. おまけに

$ FX

だと

$ find  -print0 | xargs -0

に展開されて, 「find」の直後にカーソルが動く. 何気に便利.
(PWDは$PWDの値に展開されるんだけど, 何に使ってたんだっけ...?)

global aliasでないのだしShiftからCtrl押すのはめんどいのでキーは小文字にした方が楽かしら?

*1:省略+トリガー文字列+スペースで展開するというのもある. http://hackerific.net/2009/01/23/zsh-abbreviations/

*2:gistが貼れない...