- BashとZshで、すでに補完済みの単語にも説明を表示するタブ補完機能を実装する方法を紹介
- BashとZshは異なるタブ補完APIを使用しており、Zshのみがデフォルトで補完候補に説明を表示する機能を提供
_generate_foo_completionsで候補を生成し、BashではCOMPREPLY、Zshではcompaddで返す構成を実装
- Zshの説明表示機能をBashでも実現するため、候補文字列に説明を含め、単一候補のときだけ説明を取り除く方式で処理
- 単一候補の場合でも意図的に曖昧さを追加し、
<TAB>入力時に説明が表示されるよう改善
- 最終スクリプトは両方のシェルで同一のユーザー体験を提供し、部分補完・完全補完・候補説明の表示をすべてサポート
問題の背景
- タブ補完(tab-completion) はコマンドやフラグを探す際に便利で、とくにAPIやCLIツールを初めて使うときに役立つ
- Zshはデフォルトで複数候補があるときだけ説明を表示し、Bashは別途設定した場合にのみ可能
- しかしすでに補完済みの単語については、どちらのシェルも説明を表示しない
- そのため、説明を見るにはユーザーが次のような面倒な手順を踏む必要がある
- 一部の文字を削除して複数候補が一致する状態にする
<TAB>キーを押して候補一覧を確認
- 目的の説明を目で探す
- 削除した文字を再入力してコマンドを実行
解決方法の概要
- 単一候補の場合でもダミー候補(dummy completion) を追加して候補を曖昧にする
- これによりBashとZshの両方で候補一覧とともに説明を出力できる
- ただし、実際のコマンドに説明テキストが挿入されないよう注意が必要
基本概念
- タブ補完は
<TAB>入力時に現在の単語とカーソル位置を受け取り、可能な候補一覧を返す仕組みで動作する
- Bash:
COMPREPLY配列に候補を代入
- Zsh:
compaddコマンドで候補を登録
_generate_foo_completions関数は候補文字列を出力し、実運用ではCLIの状態に基づいて動的生成も可能
BashとZshを同時にサポートする
_complete_foo_bashと_complete_foo_zsh関数で、それぞれのシェルに合わせて実装
if [ -n "${ZSH_VERSION:-}" ]; then ... elif [ -n "${BASH_VERSION:-}" ]; then ... fiで分岐
- ユーザーはスクリプトを
.bashrcまたは.zshrcに登録して適用する
Zshでの説明表示
- 候補文字列に
名前: 説明形式を使用
- Zsh:
compadd -d raw -- $trimmedで名前と説明を並列配列として渡す
- Bash: 説明部分を取り除いた候補だけを
COMPREPLYに渡す(デフォルトでは説明をサポートしないため)
Bashで説明を実装する
- 複数候補の場合は説明を含む文字列をそのまま表示
- 単一候補の場合にだけ説明を削除
- Bashの補完が共通接頭辞だけを挿入する動作を利用し、説明が実際の入力に含まれないようにする
単一候補でも説明を表示
- 補完済みの単語に
<TAB>を入力したときでも説明を見せるため、ダミー候補を追加して曖昧さを誘発
- Zsh: 2つの並列配列(
raw, trimmed)の両方にダミー候補を追加
- Bash: 単一候補の場合、
trimmedに名前だけを追加
最終結果
1件のコメント
Hacker Newsの意見
fish_update_completionsを実行するだけでよい~/.cache/fish/generated_completions/に生成するcurlの-L→ 'Follow redirects'、-O→ 'Write output to file named as remote file'car TABがblkdiscardに展開される非接頭辞自動補完のせいで、cargoがPATHになくても誤動作する点は改善されてほしい--helpしか提供しないプログラムについては、fishにzshの_gnu_genericやbashのcomplete -F _longoptのような機能があるのか気になるzypper search fish-completionを実行すると200個を超えるパッケージが出てきて、何か怪しいと感じたcomplete-filenameのように、文脈を無視してファイル名だけを補完する関数があるlsで補完してからコマンドを打ち替えることで回避しているcomplete -rを実行したあとで望みどおりに動くのを見ると、bash-completionスクリプト側に問題があるようだ$fpathへインストールするとキャッシュされ、起動速度が速くなる_gnu_genericで--helpベースの簡単な補完が可能--generateオプション、PHP Symfonyのランタイム補完生成などの事例がある--completion標準があるとよいcomplete_kill_1配列にシグナル名を入れると、killコマンドの第1引数補完を提供できるset-java-home関数で~/apps/java/*の一覧をバージョンとして補完するよう_describeを使用