--- [article] uuid = "9b26c1ed-45c3-4cad-9476-cbf2cf2e4de7" title = "【Zsh】 Composer のカスタムコマンドに対する Zsh 補完で引数にファイルを補完させる" description = "Zsh の Composer に対する補完はカスタムコマンドやその引数を補完しない。カスタムコマンドの引数としてファイルを補完させる方法を調べた。" tags = [ "composer", "php", "zsh", ] [[article.revisions]] date = "2024-04-29" remark = "公開" ---
バージョン情報
はじめに

Composer は PHP のデファクトスタンダードなパッケージマネージャである。 Zsh では、composer コマンドに対する補完が提供されており、composer と入力してタブキーを押すと、利用可能なコマンドやオプションが補完される。 Zsh の補完はシェル関数の形で実装されており、composer コマンドに対応した補完をおこなうのは _composer である。 記事執筆時点での補完関数の定義は、GitHub のミラーリポジトリから参照できる。

発生していた問題

composer コマンドはカスタムコマンド (composer.jsonscripts で定義されたコマンド) に対して補完をおこなわない。 つまり、途中まで入力されたカスタムコマンドを補完しないし、カスタムコマンドの引数も補完しない。 例えば、PHPUnit を呼び出す phpunit というカスタムコマンドを定義し composer phpu まで打ってタブキーを押しても、composer phpunit にはならない。 また、composer phpunit -- -- まで打ってタブキーを押しても、phpunit コマンドのオプションは補完されない。

このことは、先ほどリンクを載せた _composer 関数を定義しているファイルの冒頭にも書かれている。

やりたいこと

確かに、カスタムコマンドに対して完全な補完を提供するのは不可能か、あるいは実現できても遅くなりすぎるだろう。 しかし、不完全なフォールバックを提供するくらいなら可能なはずだ。

この記事では、これらのカスタムコマンドについて、Zsh が提供するデフォルトのファイル・ディレクトリ補完を適用する。 つまり、composer phpunit -- tests/ まで打ってタブキーを押すと、tests ディレクトリの下にあるテストファイルまたはディレクトリが補完される。

解決策

まずは、Zsh で補完関数を提供する場合のボイラープレートコードを書く。 以下は ~/.zshrc にすべて書く前提だが、autoload を設定するなどすれば別ファイルに分離できる (詳細な手順は割愛)。

compdef は Zsh が用意している関数で、第一引数に補完関数の名前、第二引数以降に補完を適用するコマンド名を並べる。 この場合は、composer コマンドや composer.phar コマンドに対して _my_composer を使って補完をおこなうよう定義している。

次に _my_composer を定義する。基本的にはデフォルトの composer コマンドの補完関数 (つまり _composer 関数) を使い、それが何も返さなかった場合に限り、Zsh のファイル・ディレクトリ補完へフォールバックする。

_composer コマンドは何も補完候補がなかったとき非ゼロな exit status で終了するので、そうであったなら _files を呼び出す。 _files は、Zsh がデフォルトで用意しているファイル・ディレクトリの補完をおこなう関数である。

まとめ

これらの設定をおこなうことで、部分的ながら Composer のカスタムコマンドに対して補完をおこなうことができる。 特に、PHPUnit や PHPStan などの対象ファイル・ディレクトリを引数に取るようなコマンドを使う場合に有用であろう。