web-dev-qa-db-ja.com

カスタムzsh補完を正しく機能させるにはどうすればよいですか?

ここで私が愚かなことをしている場合は、許してください。ドキュメントが膨大で、検索してもまだ何も見つかりません。

fabというカスタムスクリプトのシェル補完を作成しようとしています。 bashの場合は簡単でした。_/etc/bash_completion.d_にドロップするだけで機能します。しかし、ああ少年、zshはPITAです...

私には補完関数__fab_があり、_compdef _fab fab_で有効にすると正常に機能します。 _/usr/share/zsh/vendor-completions/_fab_に既にあった_$fpath_に入れました。ファイルは_#compdef fab_で始まり、_compdef _fab fab_で終わります。いいね:

_$ type _fab
_fab is an autoload Shell function
_

しかし、新しいシェルを開始するたびに、fab補完が機能しませんでした(_vendor-completions_のような__docker_からの他の関数は問題ありませんでした)。 compinitは、特定のシェルについてこれを修正しました。 rm ~/.zcompdump ~/.zcompdump-$(hostname)-5.1.1; compinitが永続的に機能するようにした(5.1.1 =自分のzshバージョン)。

質問:

  1. 何をいつ_~/.zcompdump_を読み取って初期補完を設定しますか?
  2. _man zshall_さんのコメント:

    次のcompinitの呼び出しでは、完全な初期化を実行する代わりに、ダンプされたファイルを読み取ります。

    その場合、compinitは_~/.zcompdump_を削除する前に補完を修正しませんでしたか?何か不足していますか?

  3. ~/.zcompdump-$(hostname)-5.1.1とは何ですか、また_.zcompdump_とどのように関連していますか?唯一の違いは、_~/.oh-my-zsh/completions_にある1つの補完です(_$ZSH_が_~/.oh-my-zsh_を指すため)。ああ、私のzshですか?
  4. これらの補完を再配布可能なパッケージにパッケージ化するか、インストーラースクリプトを作成する場合、zsh補完をどこに配置し、インストール中に他の何をすればすべてが正しく機能することを確認できますか?

私はUbuntu 16.04、18.04、19.04をターゲットにしていますが、ディストリビューションに固有でない情報を歓迎します。私はzsh 5.1.1と最近のoh-my-zshを使ってUbuntu 16.04でこれをテストしています。

3
gronostaj

TL、DR:通常の操作では、ファイルを適切なディレクトリにドロップするだけです。テスト中は、キャッシュファイルを削除する必要があります(デフォルトでは.zcompdumpですが、ユーザーは別の場所に置くことができ、oh-my-zshは別の場所に置きます)。


簡単な答えは、最初の行が #compdef fab であるファイルに補完関数を書き込むことです。ファイルは$fpathのディレクトリにある必要があります。

ファイルには、関数本体、または関数の定義と、それに続く関数の呼び出しを含めることができます。つまり、ファイルには次のようなものが含まれています

#compdef fab
_arguments …

または

#compdef fab
function _fab {
  _arguments …
}
_fab "$@"

compinitを実行する前に、ファイルが$fpathに存在する必要があります。つまり、.zshrcの順序に注意する必要があります。最初に$fpathにカスタムディレクトリを追加してから、compinitを呼び出します。 oh-my-zshなどのフレームワークを使用する場合は、oh-my-zshコードの前の$fpathにカスタムディレクトリを追加してください。

compinit は、補完システムを初期化する関数です。 $fpath内のすべてのファイルを読み取り、マジックディレクティブ #autoloadおよび#compdef の最初の行をチェックします。

.zcompdumpcompinitが使用するキャッシュファイルです。 ~/.zcompdumpはデフォルトの場所です。 compinitを実行するときに別の場所を選択できます。 Oh-my-zshは、-dオプションを使用してcompinitを呼び出し、変数ZSH_COMPDUMPで指定される別のキャッシュファイル名を使用します。これは デフォルトは です。

ZSH_COMPDUMP="${ZDOTDIR:-${HOME}}/.zcompdump-${SHORT_Host}-${ZSH_VERSION}"

ホスト名は、ホームディレクトリがマシン間で共有され、異なるマシンに異なるソフトウェアがインストールされている可能性がある人のために含まれています。キャッシュファイルはバージョン間で互換性がないため、zshバージョンが含まれています(バージョン間で変更されるコードが含まれています)。

すべての問題の原因は古いキャッシュファイルにあると思います(そのため、状況が複雑になりすぎています)。残念ながら、 キャッシュファイルが古くなっているかどうかを判断するzshのアルゴリズム は、おそらく速度を考慮して、完全ではありません。 $fpath上のファイルのコンテンツやタイムスタンプはチェックされません。カウントするだけです。 .zcompdumpファイルは次のような行で始まります

#files: 858     version: 5.1.1

Zshのバージョンとファイル数が正しい場合、zshはキャッシュファイルをロードします。

キャッシュファイルには、コマンド名間の関連付けのみが含まれ、補完関数のコードは含まれません。次に、キャッシュが透過的に機能する一般的なシナリオをいくつか示します。

  • 新しいファイルを$fpathに追加すると、キャッシュが無効になります。
  • より一般的には、$fpathでファイルを追加および削除し、削除されたファイルの合計数が削除されたファイルの合計数と同じでない場合、キャッシュが無効になります。
  • 名前を変更せずに$fpathの別のディレクトリにファイルを移動しても、キャッシュにあるものには影響しないため、キャッシュは正しいままです。
  • 最初の行を変更せずに$fpathのファイルを変更しても、これはキャッシュにあるものには影響しないため、キャッシュは正しいままです。

キャッシュが無効になる一般的なシナリオをいくつか示しますが、zshはそれを認識しません。

  • いくつかのファイルを$fpathに追加し、まったく同じ数のファイルを削除します。
  • $fpathでファイルの名前を変更します。
  • ファイルの上部にある#compdef(または#autoload)行を追加または変更します。

その最後のポイントは、テスト中に噛みがちになるものです。 #compdef行を変更する場合は、.zcompdumpファイルを削除してzshを再起動する(またはcompinitを再実行する)必要があります。

補完を再配布可能なパッケージに入れる場合は、補完ファイルをシステム全体の$fpathにあるディレクトリにドロップするだけです。 Ubuntuパッケージの場合、適切な場所は/usr/share/zsh/vendor-completionsです。 /usr/localの下にインストールされているものは、/usr/local/share/zsh/site-functionsです。それがあなたがする必要があるすべてです。

透過的ではない1つのことは、アップグレードで#compdef行を変更する必要がある場合、または一部のファイルを削除または名前変更した場合です。このような場合、ユーザーはキャッシュファイルを削除する必要がありますが、これはマルチユーザーマシンにインストールされたパッケージから実行できることではありません。