如何正确地使自定义 zsh 完成“正常工作”?

如何正确地使自定义 zsh 完成“正常工作”?

如果我在这里做了一些愚蠢的事情,请原谅我,文档很大,而且搜索还没有发现任何东西。

我正在尝试为名为 的自定义脚本创建 shell 补全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

但是每当我启动一个新的 shell 时,fab补全就不起作用(来自 的其他功能vendor-completions,例如_docker,都很好)。compinit修复了该特定 shell 的问题。我发现让它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(因为$ZSH指向~/.oh-my-zsh) 中的一个完成。这是 oh-my-zsh 的事情吗?
  4. 如果我要将这些补全打包到可再发行包中或创建安装程序脚本,我应该将 zsh 补全放在哪里?在安装过程中我还应该做什么来确保一切正常工作?

我的目标是 Ubuntu 16.04、18.04 和 19.04,但欢迎提供非发行版特定的信息。我正在 Ubuntu 16.04 上使用 zsh 5.1.1 和最近的 oh-my-zsh 进行测试。

答案1

TL,DR:在正常操作中,只需将文件放入相应的目录即可。测试时,您需要删除缓存文件(.zcompdump默认情况下,但用户可以将其放在不同的位置,oh-my-zsh 确实将其放在不同的位置)。


简单的答案是将完成函数写入第一行所在的文件中#compdef fab。该文件必须位于 上的目录中$fpath

该文件可以包含函数体,也可以包含函数的定义,后跟函数的调用。也就是说,该文件包含类似的内容

#compdef fab
_arguments …

或者

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

$fpath该文件必须在运行之前存在compinit。这意味着您需要注意其中的顺序.zshrc:首先添加任何自定义目录$fpath,然后调用compinit。如果您使用 oh-my-zsh 等框架,请确保$fpath在 oh-my-zsh 代码之前添加任何自定义目录。

compinit是初始化完成系统的函数。它读取所有文件$fpath并检查它们的第一行是否有魔术指令#autoload#compdef

.zcompdump是一个由compinit.~/.zcompdump是默认位置;运行时您可以选择不同的位置compinit。 Oh-my-zsh 调用compinit时可以-d选择使用变量 给出的不同缓存文件名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。这就是您需要做的全部。

不透明的一件事是您是否需要#compdef在升级中更改行,或者删除或重命名某些文件。在这种情况下,用户将需要删除其缓存文件,而这不是您可以从安装在多用户计算机上的软件包中执行的操作。

相关内容