我想稍微扩展一个 zsh 补全功能。
我想避免将完整的函数体放入我的主目录中,只更改一行。相反,我想拦截它的调用,然后自己调用原始函数。在准代码中:
<make sure _the_original_function is loaded>
_backup_of_original_function=_the_original_function
_the_original_function() {
_arguments "-superduper[that one option I always need]"
_backup_of_originial_function $@
}
在我的具体案例中,我几乎所有项目都有一个 cmake 属性,因此我想修改 cmake 完成。在完成时没有这个选项比在不属于该选项的项目中拥有它更让我烦恼。因此,_cmake
我不会在某个地方复制,而是_cmake_define_common_property_names()
在我的.zshrc
:
_cmake_define_common_property_names() {
local properties; properties=(
'MY_GREAT_OPTION:be awesome'
)
_describe -t 'my-property-names' 'my property name' properties $@
**call original _cmake_define_common_property_names here
}
所以缺少的是加载_cmake_define_common_property_names
并为其分配一个新的函数名称。
从这里我试过
autoload -Uz +X _cmake_define_common_property_names
但这失败了(该函数不是在其自己的文件中定义的,而是在_cmake
.
注意:我没有分配新的函数名称,以避免必须修改原始版本中调用函数的位置。
部分有效的是autoload -Uz +X _cmake
,但这只能确保_cmake
已加载(我通过调用来验证functions _cmake
)。它不加载辅助函数_cmake_define_common_property_names
。
所以我的两个问题是
- 如何从
$fpath
文件中加载函数 - 一旦我加载了一个函数。如何将其复制到脚本中/分配新的函数名称?
答案1
如何修补函数
函数的代码存储在关联数组中functions
。这是带有规范化空格且没有注释的源代码(zsh 已完成词法分析并漂亮地打印标记)。您可以通过修改数组的条目来更改函数的代码functions
。例如,要在开头添加额外的代码:
functions[_cmake_define_common_property_names]="
… # your extra code here
$functions[_cmake_define_common_property_names]"
如果您想要进行的更改仅涉及在原始代码之前和之后运行代码(或者更一般地说,例如将其放入条件中),您可以将函数复制到新名称并重新定义函数以调用该新名称。从 zsh 5.8 开始,您可以使用以下命令轻松完成此操作functions -c
。
functions -c _cmake_define_common_property_names _cmake_define_common_property_names_orig
_cmake_define_common_property_names () {
…
_cmake_define_common_property_names_orig "$@"
}
或者,在任何版本的 zsh 中,您也可以使用数组functions
将函数复制到另一个名称。
functions[_cmake_define_common_property_names_orig]=$functions[_cmake_define_common_property_names]
_cmake_define_common_property_names () {
…
_cmake_define_common_property_names_orig "$@"
}
加载所有函数
从自动加载文件中加载所有函数的唯一可靠方法是执行该函数(如果您可以安排执行该函数而不会产生任何副作用)。对于完成函数,只需运行该函数,并将错误重定向到位桶。它只会抱怨它没有在完成上下文中执行,什么也不做。
_cmake 2>/dev/null
# Now _cmake_xxx is defined
autoload -Uz +X _cmake
不起作用的原因是辅助函数的定义在_cmake
函数本身中。
% echo $functions[_cmake]
builtin autoload -XU
% autoload -Uz +X _cmake
% echo $functions[_cmake]
…
(( $+functions[_cmake_define_property_names] )) || _cmake_define_property_names () {
…
}
…
local cmake_command_actions
cmake_command_actions=('-E[CMake command mode]:*:command')
_cmake_command () {
_arguments -C -s - command "$cmake_command_actions[@]"
}
local cmake_suggest_build
cmake_suggest_build=('--build[build]')
if [ $CURRENT -eq 2 ]
then
_arguments -C -s - help "$cmake_help_actions[@]" - command "$cmake_command_actions[@]" - build_opts "$cmake_build_options[@]" - build_cmds "$cmake_suggest_build[@]" && return 0
elif [[ $words[2] = --help* ]]
then
_cmake_help
elif [[ $words[2] != -E ]]
then
_cmake_on_build
else
_cmake_command
fi
如果你确实不想执行顶层函数,你有几种选择:
_cmake_define_common_property_names
修补的定义内的的定义_cmake
。_cmake_define_common_property_names
从 的定义中提取 的定义_cmake
并使用它来定义_cmake_define_common_property_names_orig
或_cmake_define_common_property_names
。修补 的定义
_cmake
以删除函数定义以外的部分,然后执行它。它实际上不能与 一起使用_cmake
,但其他一些完成功能的结构更好。例如,_cvs
纯粹由条件函数定义(例如(( $+functions[_cvs_command] )) || _cvs_command () { … }
)、标题函数的定义以及作为最后一件事的标题函数的调用组成,因此您可以定义所有函数,但不能通过删除最后一行来执行任何内容。autoload -Uz +X _cvs functions[_cvs]=${functions_cvs%'$\n'*} _cvs # Patch auxiliary functions here