更新履歴

  1. : 公開

バージョン情報

  • Composer: 2.7.4
  • PHP: 8.3.6
  • Zsh: 5.9

はじめに

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

発生していた問題

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

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

# - @todo We don't complete custom commands (including script aliases). This is
#   easy to do in the general case, but it probably requires some clever caching
#   to avoid introducing a noticeable lag to every completion operation, due to
#   the way command resolution works and the fact that discovering custom
#   commands requires making slow calls to Composer

やりたいこと

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

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

解決策

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

compdef _my_composer composer composer.phar

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

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

function _my_composer() {
    _composer "$@" || _files "$@"
}

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

まとめ

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