高速で設定しやすいZsh/BashプラグインマネージャーSheldonの紹介
ZshのプラグインマネージャーというとAntigenやzplug、Zinit(旧zplugin)などが挙げられます。
私はこれまでZinitを使ってきましたが、作者がGitHubのOrganizationを削除してしまうという事件が起きました。有志がローカルのクローンから復旧させてメンテンスが続いていますが、他のプラグインマネージャーへの乗り換えを検討していたところ、Sheldonを見つけました。
SheldonはRust製のシェルプラグインマネージャーで、ZshだけでなくBashにも対応しています。ライセンスはApache License 2.0またはMIT Licenseを選択するデュアルライセンスで配布されています。
この記事ではmacOS上のZshでの利用を前提に記述していますが、他の環境でもインストール方法とシェルによる機能差分ぐらいしか違いはありません。
インストール
macOSではHomebrewでインストールすることができます。[1]
brew install sheldon
別のインストール方法を選択したい場合や、その他のOSの場合は📦 Installation - sheldon docsをご参照ください。
初期設定
設定ファイルの作成
次のコマンドでSheldonの設定ファイルを生成できます。
sheldon init --shell zsh
設定ファイルはシェルスクリプトではなく、TOML形式のファイルです。SheldonはXDG Base Directory Specificationに対応しており、上記のコマンドを実行すると${XDG_CONFIG_HOME}/sheldon/plugins.toml
が生成されます。
シェル初期化時の設定
シェル起動時にSheldonをロードするため、~/.zshrc
などに次の1行を追加します。
eval "$(sheldon source)"
設定ファイルを生成した直後のsource
サブコマンドは何も出力しません。プラグインを追加するとそれに対応したコマンドが出力されるようになります。
また、source
サブコマンドは~/.sheldon/plugins.lock
を生成します。このロックファイルにインストールされたプラグインの内部的な情報が展開され、2回目以降はこの情報を元に実行することで高速に動作します。
Sheldonのロックファイルは一般的なパッケージマネージャーのロックファイルと性質が異なり、ローカル環境の状態を管理するものであるため、Git管理下に置いて他のマシンと共有するべきではありません。
設定ファイルの編集
プラグインの定義などは上記で作成したplugins.toml
で行います。直接パスを指定してエディターから開いてもよいですが、edit
サブコマンドを実行すると簡単に開くことができます。
sheldon edit
編集に用いるエディターを指定したい場合は環境変数EDITOR
を設定します。
EDITOR=vim sheldon edit
プラグインの追加
プラグインの追加はplugins.toml
を直接編集するか、add
サブコマンドを使います。私はプラグインの追加時に設定を調整したりするため、plugins.toml
を直接編集してしまうことが多いです。
設定ファイルの編集による追加
プラグインはplugins.toml
のplugins
テーブル配下に記述していきます。
例えば、zsh-users/zsh-autosuggestionsを追加する場合は次のように記述します。
[plugins.zsh-autosuggestions]
github = "zsh-users/zsh-autosuggestions"
テーブル名のplugins.zsh-autosuggestions
のzsh-autosuggestions
の部分は一意なものを自分で命名します。ここで指定した名前はプラグインのリポジトリ内のどのファイルを読み込むのかに使われます。これはグローバルオプションのmatch
キーの設定に基づいています。match
キーにはデフォルトで次の値が設定されており、順番にパターンとマッチしているかチェックして一番最初にマッチしたファイルを読み込みます。
match = [
"{{ name }}.plugin.zsh",
"{{ name }}.zsh",
"{{ name }}.sh",
"{{ name }}.zsh-theme",
"*.plugin.zsh",
"*.zsh",
"*.sh",
"*.zsh-theme"
]
この{{ name }}
の部分にテーブル名で指定した名前が入るようになっています。もしファイル名と一致しない名前を定義したとしても、後続のワイルドカード指定のパターンに一致するため、特殊なケースでない限り問題ないですが、合わせておいた方がわかりやすいです。
そしてgithub
キーで値にGitHubのOwner名を含んだリポジトリ名を指定します。
プラグインを追加するとsource
サブコマンドがプラグインを読み込むためのコマンドを出力するようになります。
source "/Users/john/.sheldon/repos/github.com/zsh-users/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh"
コマンドによるプラグインの追加
同様のことをadd
サブコマンドで行うには次のようにします。
sheldon add zsh-autosuggestions --github zsh-users/zsh-autosuggestions
add
サブコマンドを実行するとplugins.toml
に指定されたプラグインが追加されます。add
サブコマンドにはplugins.toml
のプラグインの設定で指定可能なキーをすべて引数として渡すことができます。
プラグインのバージョン固定
また、タグ(tag
)やブランチ(branch
)、コミットID(rev
)を指定してバージョンを固定することもできます。
[plugins.zsh-autosuggestions]
github = "zsh-users/zsh-autosuggestions"
tag = "v0.7.0"
プラグインの取得元の指定
他にも、プラグインの取得先としてGitリポジトリ(git
)やGist(gist
)、HTTP/HTTPS上のファイル(remote
)、ローカルのファイル(local
)が指定できます。
プラグインの遅延読み込み
プラグインの読み込み処理が遅いとシェルの起動時間も遅くなってしまうため、遅延読み込みをしておきたいです。遅延読み込みの設定は💡 Examples - sheldon docsのDeferred loading of plugins in Zshに設定例が記載されていますので、これを設定していきます。
Sheldon本体に遅延読み込みの機能はありませんが、zsh-deferとsource
サブコマンドが出力するコマンドをカスタマイズできるテンプレート機能を組み合わせることで実現できます。
組み込みテンプレートとして、デフォルトの挙動であるsource
でプラグインを読み込むコマンドを出力するもの、PATH
やpath
、fpath
にディレクトリを追加するコマンドを出力するものがあります(テンプレート名はそれぞれsource
、PATH
、path
、fpath
です)。
自分でカスタムテンプレートを作成する場合は、template
テーブル配下に定義を追加します。
遅延読み込み用のテンプレートを追加する前に、plugins
テーブル配下にzsh-deferをプラグインとして追加しておきます。
[plugins.zsh-defer]
github = "romkatv/zsh-defer"
次にtemplates
テーブル配下にdefer
というキーで遅延読み込みのテンプレートを追加します。
[templates]
defer = "{% for file in files %}zsh-defer source \"{{ file }}\"\n{% endfor %}"
そして、遅延読み込みをしたいプラグインの定義に、このdefer
テンプレートを値に持つapply
キーを追加します。
[plugins.zsh-autosuggestions]
github = 'zsh-users/zsh-autosuggestions'
apply = ["defer"]
すると、source
サブコマンドの出力にzsh-defer
が付与されるようになり、対象のプラグインはzsh-deferを使った遅延読み込みが行われるようになります。
zsh-defer source "/Users/john/.sheldon/repos/github.com/zsh-users/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh"
読み込むファイルの指定
プラグインのリポジトリ内のどのファイルが読み込まれるかはmatch
キーで定義されているということを上述しました。ただ、特定のプラグインで読み込むファイルを変更したい場合、グローバルオプションであるmatch
キーの値を変更することは適切ではありません。
プラグイン単位で読み込むファイルのパターンを指定するにはuse
キーを使います。
例えばzsh-autosuggestionsの場合、デフォルトのmatch
キーによるファイル検出ではzsh-autosuggestions.plugin.zsh
が先にマッチします。しかし、zsh-autosuggestions.plugin.zsh
はzsh-autosuggestions.zsh
をsource
しているだけなので、use
キーを使ってzsh-autosuggestions.zsh
を読み込むように設定できます。
[plugins.zsh-autosuggestions]
github = 'zsh-users/zsh-autosuggestions'
use = ['{{ name }}.zsh']
apply = ["defer"]
これにより、source
サブコマンドの出力は次のようになります。
zsh-defer source "/Users/john/.sheldon/repos/github.com/zsh-users/zsh-autosuggestions/zsh-autosuggestions.zsh"
Zshの補完定義プラグインの設定
Zshの補完定義がプラグインとして提供されている場合、プラグイン側の*.plugin.zsh
内でfpath
に追加する処理が書かれているため、通常はそのまま追加すれば問題ありません。
Sheldon組み込みのfpath
テンプレートを使うと、プラグインとして提供されていないリポジトリやGist上のファイルを追加することができます。
ここでは例としてnnao45/zsh-kubectl-completion
の_kubectl
を追加する方法を示します。
[plugins.kubectl-completion]
remote = "https://raw.githubusercontent.com/nnao45/zsh-kubectl-completion/master/_kubectl"
apply = ["fpath"]
github
キーの代わりにremote
キーを使ってリポジトリ上の_kubectl
のURLを指定します。そしてapply
キーを追加してfpath
テンプレートを指定します。
このように設定するとsource
サブコマンドは次のようにfpath
への追加コマンドを出力します。
fpath=( "/Users/john/.sheldon/downloads/raw.githubusercontent.com/nnao45/zsh-kubectl-completion/master" $fpath )
プラグインの更新
プラグインの更新はlock
サブコマンドに--update
オプションを付けて実行します。
sheldon lock --update
--update
オプションの代わりに--reinstall
オプションを付けて実行すると強制的にプラグインが再インストールされます。
sheldon lock --reinstall
オプションなしのlock
サブコマンドはロックファイルを再生成し、plugins.toml
で指定された通りにプラグインがインストールされているかチェックし、指定された通りになっていなければインストール・削除を行います。
sheldon lock
ロックファイルが存在しない場合はsource
サブコマンドの実行でもlock
サブコマンドと同等の動作をします。
実行速度
実行速度についてはSheldonの作者による各種Zshプラグインマネージャーのベンチマーク結果を比較するリポジトリが参考になります。
ここでは26個のプラグインを追加して遅延読み込みなしでベンチマークが測定されています。
Load timeより引用
この結果によれば、Sheldonのロード時間は高速な部類に入ります。
実行速度は追加するプラグインに依存しますし、遅延読み込みを利用できるものであればシェル起動時には影響を受けなくなりますので、ベンチマークを鵜呑みにせずに自身の環境で測定しながら試行錯誤することが大事です。
参考として、hyperfineを使って簡易的に実行時間を測定してみました。
私の場合、この記事の執筆時点では5つのプラグインを追加した状態でsheldon source
の実行時間は7msぐらいです。
❯ hyperfine --shell=none 'sheldon source'
Benchmark 1: sheldon source
Time (mean ± σ): 7.5 ms ± 1.1 ms [User: 4.2 ms, System: 1.7 ms]
Range (min … max): 6.3 ms … 13.9 ms 215 runs
ほとんどのプラグインを遅延読み込みしており、sheldon source
の結果をZshが読み込む部分まで含めると10msぐらいになっています。
❯ hyperfine --shell "zsh --no-rcs" 'source <(sheldon source)'
Benchmark 1: source <(sheldon source)
Time (mean ± σ): 9.6 ms ± 1.7 ms [User: 6.9 ms, System: 2.6 ms]
Range (min … max): 6.9 ms … 16.8 ms 186 runs
私の環境だとその他の設定も含めてZshの起動時間は140msぐらいです。何もロードしない素のZshだと起動時間は5msぐらいです。
❯ hyperfine --shell=none 'zsh -i -c exit' 'zsh --no-rcs -c exit'
Benchmark 1: zsh -i -c exit
Time (mean ± σ): 137.4 ms ± 24.5 ms [User: 48.7 ms, System: 58.0 ms]
Range (min … max): 119.2 ms … 214.9 ms 13 runs
Benchmark 2: zsh --no-rcs -c exit
Time (mean ± σ): 4.8 ms ± 1.3 ms [User: 1.1 ms, System: 1.1 ms]
Range (min … max): 3.7 ms … 14.9 ms 392 runs
頻繁にtmuxでセッションを生成していますが、体感的にも気にならないくらい速いです。
おわりに
Sheldonは従来のZshプラグインマネージャーに比べ、設定項目自体が直感的でわかりやすく、設定ファイルがTOML形式になっていることもあり可読性も高いです。それでいて実行速度も高速です。
他にない特徴としてはテンプレート機能によってスクリプトの読み込み処理を柔軟に拡張できることです。これによってプラグイン化されていないものも簡単に呼び出すことができます。
かなり使い勝手がよいと思いますので、ぜひ試してみてください。
変更履歴
2022-10-28
-
v0.7.0の破壊的変更を反映
- XDG Base Directoryをデフォルトで利用するように変更
- テンプレート設定から
each
フィールドの削除
-
Intel環境でApple Silicon版をクロスコンパイルするとlibsslへのリンクに失敗する問題があり、現時点でIntel環境しか存在しないGitHub ActionsでCIを実行しているため、公式のビルド済みバイナリはIntel版しか提供されていません。一方、HomebrewではApple Silicon上でビルドしたBottleが提供されているため、Apple Silicon版のビルド済みバイナリをインストールすることができます。 ↩︎
Discussion