Create a gist now

Instantly share code, notes, and snippets.

zsh の補完関数の自作導入編

zsh の補完関数の自作導入編

あまり深く理解してないので識者のツッコミ大歓迎

補完を有効にする

取り敢えず最低限だけ

# 補完を有効にする
autoload -U compinit
compinit -u

# 補完メッセージを読みやすくする
zstyle ':completion:*' verbose yes
zstyle ':completion:*' format '%B%d%b'
zstyle ':completion:*:warnings' format 'No matches for: %d'
zstyle ':completion:*' group-name ''

補完関数のルール

$fpath の通ったディレクトリに補完関数を置く
nyan コマンドの補完関数なら _nyan という名前で保存する
補完関数の先頭には #compdef nyan と補完対象のコマンド名を書く
ファイル名と同じ名前の関数を作りその中に補完関数を書く

~/zsh/functions/_nyan

#compdef nyan

_nyan() {
  # 補完関数
}

あるいは下記のように書いたファイルを source ~/zsh/lib/_nyan.zsh と読み込むことで補完関数を有効にすることも出来る
compdef _nyan nyan_nyan 関数を nyan コマンドの補完関数として使うという意味になる

~/zsh/lib/_nyan.zsh

_nyan() {
  # 補完関数
}

compdef _nyan nyan

nyan コマンドを作る

まずは猫を表示してくれる可愛い nyan コマンドを作る

~/bin/nyan

#!/usr/bin/env zsh

function nyan() {
    typeset -a modes

    help() {
        echo 'Usage: nyan <mode>'
        echo
        echo 'Modes:'
        echo '  neko, usagi, kuma, github'
    }

    neko() {
        echo ' ∧_∧'
        echo "ฅ'ω'ฅ < ニャーン"
    }

    usagi() {
        echo '    /) /)'
        echo '_( _ ๑❛ᴗ❛)_ < ニャーン'
    }

    kuma() {
        echo '(´(ェ)`) < ニャーン'
    }

    github() {
        echo '=͟͟͞͞=͟͟͞͞=͟͟͞͞ (\ (\'
        echo '=͟͟͞͞=͟͟͞͞=͟͟͞͞(,, 0 ω 0) < ニャーン'
        echo '=͟͟͞͞=͟͟͞͞=͟͟͞͞(/ (/ \) \)'
    }

    while [ $1 ]; do
        case $1 in
            -h|--help)
                help
                return
                ;;
            *)
                modes=($modes[*] $1)
                ;;
        esac
        shift
    done

    if (( $#modes == 0 )); then
        neko
        return
    fi

    for mode in $modes; do
        if type $mode > /dev/null 2>&1; then
            $mode
        else
            echo "No such mode \"$mode\""
        fi
    done
}

nyan $@

動かしてみる

$ nyan
 ∧_∧
ฅ'ω'ฅ < ニャーン

$ nyan wan
No such mode "wan"

$ nyan --help
Usage: nyan <mode>

Modes:
  neko, usagi, kuma, github

$ nyan usagi
    /) /)
_( _ ๑❛ᴗ❛)_ < ニャーン

$ nyan kuma github
(´(ェ)`) < ニャーン
=͟͟͞͞=͟͟͞͞=͟͟͞͞ (\ (\
=͟͟͞͞=͟͟͞͞=͟͟͞͞(,, 0 ω 0) < ニャーン
=͟͟͞͞=͟͟͞͞=͟͟͞͞(/ (/ \) \)

neko, usagi, kuma, github の4つのモードと
--help の1つのオプションがある
モードは複数指定することが出来る

nyan 補完関数を作る

まずは4つの全てのモードを補完候補に出してみる

~/zsh/functions/_nyan

#compdef nyan

_nyan() {
  _values \
    'mode' \
    'neko' \
    'usagi' \
    'kuma' \
    'github'
}

image

_values 関数を使うとひとつ目の引数が補完候補のグループ名、その後の引数が補完候補となる

_values で指定した補完候補は複数かつ同じものを何度でも補完出来る
もしひとつの候補を一度しか入力させたくない場合は _values 関数に -w オプションを渡せば良い

ついでに説明文も表示してみる

#compdef nyan

_nyan() {
    _values \
        'mode' \
        'neko[kawaii normal neko]' \
        'usagi[kawaii usa-neko]' \
        'kuma[kawaii kuma-neko]' \
        'github[kawaii octcat]'
}

image

[] 内に説明を書くことで、補完候補の横に説明が表示される

次は --help オプションを補完候補に出してみる

#compdef nyan

_nyan() {
    _arguments \
        {-h,--help}'[show help]' \
        '*: :__nyan_modes'
}

__nyan_modes() {
    _values \
        'mode' \
        'neko[kawaii normal neko]' \
        'usagi[kawaii usa-neko]' \
        'kuma[kawaii kuma-neko]' \
        'github[kawaii octcat]'

}

image

オプションと補完候補に同時に出すために _arguments 関数を使う

オプションの指定は optname[desc] と指定する
同じ意味のオプションがある場合は {optname1,optiname2} とカンマ区切りで書く

引数の指定は n:message:action と指定する

n は何番目の引数に対して動作するかを表す
何回でも同じ引数を補完出来て良いので * と指定しておく

message はその補完候補の説明を表す
今回は引数を省略する(理由は後述)

action はどういう動作をするかを表す
*: :__nyan_modes と書くことで、全ての補完候補で __nyan_modes 関数が使われる
__nyan_modes 関数内の _values 関数で mode というグループ名(説明)指定しているため message を省略してる

説明を出すため/見通しをよくするためモードの補完候補を別関数に分けたが、説明を伴わない場合は '*:mode:(neko usagi kuma github)' のように書くことも出来る

出来れば --help が入力された時はそこで補完を終了させたい
最後にそのための改良をする

#compdef nyan

_nyan() {
    _arguments \
        '(- *)'{-h,--help}'[show help]' \
        '*: :->modes'

    case $state in
        modes)
            _values \
                'mode' \
                'neko[kawaii normal neko]' \
                'usagi[kawaii usa-neko]' \
                'kuma[kawaii kuma-neko]' \
                'github[kawaii octcat]'
            ;;
    esac
}

image

nyan コマンドの補完関数完全版である

'(- *)' で排他指定をしている
- の部分で --help-h)が補完された時に -h--help)を補完候補から除く
* の部分で --help-h)が補完された時にそれ以降の補完を終了している

その他の便利な補完関数

_arguments は他にもオプションや指定がたくさんあったり
_files でマッチしたファイルのみ候補に出したり
_alternative_describe を使って複数のグループの補完候補を表示したり
_store_cache を使って補完候補をキャッシュしたり

他にもたくさんあるけどイマイチ理解が浅いので気が向いた時に続編を書くかも書かないかも

その他

補完関数は先人たちが書いたものがたくさんある
git の補完関数はでかいが参考になる
自分が普段よく使うコマンドの補完関数を読んでみると良い

後、これを入れておくと便利
zsh-users/zsh-completions
Mac で homebrew なら brew install zsh-completions で入る

参考文献

zsh: 20. Completion System(英語)
多分これに全て書かれてるけど要所しか読んでない

zsh補完関数の書き方(訳)
大体の雰囲気が分かる

zsh補完関数を自作すると便利 - はこべブログ♨
ひとつの補完関数を書く過程を書かれてて分かりやすい

@ghost
ghost commented Feb 10, 2014

Sorry, I have pushed 'Report as abuse' by my mistake. But, I don't know what erase my act... Your post is nice . Really sorry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment