问题描述
整个问题都在 here 和 here 中讨论。由于没有参与这些讨论的人对这个问题有 100% 的把握,我在这里寻求帮助。 (为了完整起见,我将从头开始。)
假设我们有两个脚本(源自 ~/.zshrc
)为 ZSH 设置了一些完成逻辑。现在根据我学到的知识,在脚本中的某个时刻,您需要像这样调用 compinit
和 bashcompinit
(从 NVM 完成脚本复制)
if [[ -n ${ZSH_VERSION-} ]]; then
autoload -U +X compinit && if [[ ${ZSH_disABLE_COMPFIX-} = true ]]; then
compinit -u
else
compinit
fi
autoload -U +X bashcompinit && bashcompinit
fi
显然,根据 ZSH 手册,bashcompinit
必须在 compinit
之后调用,(不确定是否相关)。现在的问题是,当第二个脚本调用 compinit
时,来自第一个脚本的逻辑就消失了(即第一个脚本的完成不可用)。重现这一点的简单片段是(复制自 here):
complete -W "hello world" one
one <tab> # to see autocomplete working
compinit
one <tab> # to see autocomplete NOT working
有人提出 (here) 类似下面的方法来解决这个问题(通过在调用之前检查 compinit
是否已经被调用):
if [[ -n ${ZSH_VERSION-} ]]; then
if ! command -v compinit > /dev/null; then
autoload -U +X compinit && if [[ ${ZSH_disABLE_COMPFIX-} = true ]]; then
compinit -u
else
compinit
fi
fi
autoload -U +X bashcompinit && bashcompinit
fi
另一个想法可能是不在自定义完成脚本中调用 compinit
和 bashcompinit
,而是在 ~/.zshrc
中调用(这会损害 NVM 等工具的自动安装过程)。
我想知道一般设置完成的正确方法是什么(或者特别是关于调用 compinit
)。
谢谢。
解决方法
假设我们有两个脚本(来自 ~/.zshrc)为 ZSH 设置了一些完成逻辑。现在根据我学到的知识,在脚本中的某个时刻,您需要调用 compinit
和 bashcompinit
不,这不是你的脚本应该做的。不是您的脚本,而是 user 应该在其 compinit
文件中调用 .zshrc
以启用 Zsh 的完成系统。此外,对于每个 shell 实例,它应该只调用一次。
相反,第 3 方脚本有两种正确的方式向 Zsh 添加完成功能:
- 如果用户应该为其 shell 的每个实例分别运行您的脚本(这似乎是您的情况),那么
- 指示您的用户在他们的
.zshrc
文件中找到他们执行autoload -Uz compinit && compinit
的点(如果他们不这样做,则添加它)。 - 让他们在该行之前获取您的脚本。
- 让您的脚本将包含完成函数的目录添加到它们的
$fpath
中。
- 指示您的用户在他们的
- 但是,如果您还提供安装程序,则可以改为
- 让安装程序将您的完成功能复制到
/usr/local/share/zsh/site-functions
(默认情况下在 Zsh 的$fpath
中)。 - 指示用户确保他们的
autoload -Uz compinit && compinit
文件中有.zshrc
。
- 让安装程序将您的完成功能复制到
(后者也是安装软件时包管理器应该做的。)
显然,根据 ZSH 手册,bashcompinit
必须在 compinit
之后调用,(不确定是否相关)。
是的,这是相关的,但不,不是您认为的那样。其中,bashcompinit
定义了 complete()
,用户可以使用它向 Zsh 添加 Bash 完成功能。与 compinit
一样,bashcompinit
旨在每个 shell 仅调用一次。
如果您的包没有提供原生 Zsh 补全功能,那么您应该指示用户执行以下操作:
- 在他们的
.zshrc
文件中找到他们执行autoload -Uz compinit && compinit
的点。 - 在该行之后,添加
autoload -Uz bashcompinit && bashcompinit
。 - 然后,在该行之后,添加安装 Bash 补全的步骤。