5 分で読了 #tools

Zsh 環境

zsh の設定は200行程度で、プロンプトは starship、エイリアスは多数あるが、これらについて語ることはほとんどない。唯一手間をかけ、かつ書く価値があるのは補完だ。私は Tab キーでプレビュー付きのメニュー表示にし、そのために carapace と少し格闘した。

補完の優先順位は、ネイティブな compinit を基盤とし、carapace はフォールバックとしてのみ機能する。この順序は重要だ。carapace のデフォルト初期化では compdef を使って700以上のコマンドを乗っ取るが、これは一見楽に見えるが、実際には zsh ネイティブのより細かな補完や、fzf-tab が依存するサブコマンドのコンテキスト(例えば git checkout でブランチごとにプレビューを表示するなど)をすべて上書きしてしまう。そのため、carapace の compdef をフィルタリングし、ネイティブ補完が失敗した場合のみ carapace が呼ばれるようにしている:

source <(carapace _carapace zsh | grep -v '^compdef ')
zstyle ':completion:*' completer _complete _carapace _ignored

補完が完了すると、fzf-tab が候補を fzf に渡し、コマンドごとに異なるプレビューを表示する。ファイルは bat でレンダリング、ディレクトリは eza で一覧表示、kill はプロセスを表示、systemctl はユニットのステータスを直接表示、git checkout は最近のコミットを5件表示、git add は diff を表示する。これが日常の操作性を最も向上させる部分だ。多くの場合、コマンドを打ち切って Enter を押して結果を見る必要はなく、Tab でプレビューを見るだけで判断できる。

zstyle ':fzf-tab:complete:systemctl:*' fzf-preview 'systemctl status --no-pager $word | head -30'
zstyle ':fzf-tab:complete:git-(checkout|switch):*' fzf-preview 'git log --oneline -n 5 $word'

このプレビュー機能は華やかに見えるが、落とし穴も多い。すべてコメントに記した。fzf が認識しないフラグを与えると、補完リストが突然空になったり fzf が終了したりする。また、fzf-min-height を大きすぎると、候補が少ない場合に下部に空白が生じる。そのため、--no-sortfzf-pad 0 といった設定は単にコピーしたのではなく、実際に調整して導き出したものだ。

補完以外にも、シェルを「手に馴染む」ようにする小さな工夫がいくつかある。それぞれ一言で説明する。

zsh-autosuggestions は fish シェルのような履歴のグレー表示ヒントを提供する。入力の途中に、直近で一致したコマンド全体をグレー文字で後ろに補完し、 または Ctrl+f で受け入れる。非同期で候補を取得するため(ZSH_AUTOSUGGEST_USE_ASYNC)、履歴が長くてもキー入力がブロックされない。

zsh-syntax-highlighting は入力しながら検証を行う。コマンドが存在すれば緑、存在しなければ赤、引用符が閉じていなければ即座に色が変わるため、Enter を押す前にタイプミスに気づける。このプラグインは**.zshrc の最後に source する必要がある**⁠。すべての ZLE 部品を包む最外層フックに設定する必要があるため、先に読み込むと後続のプラグインに上書きされてしまう。

starship はプロンプトとして使用する。単一の TOML 設定でシェルの種類を問わず通用し、ディレクトリのコンテキストに応じて git ブランチ/ステータス、言語バージョン、k8s コンテキストなどを表示する。関連する時のみ表示され、常に表示されて画面を埋め尽くすことはない。

ssh 補完のソースを変更した。デフォルトでは known_hosts(多数の IP アドレス、ハッシュ化されているため読めないものもある)を参照するが、これを ~/.ssh/configHost エイリアスのみから補完するように変更した。ssh <TAB> を押すと、私が名前をつけたマシンが直接リストされ、誰が誰だか一目でわかる。

zoxidez によるディレクトリジャンプ、ls の eza への完全置換、git pager の delta への切り替えなど、これらの詳細は CLI ツールボックス のセクションで解説する。