覆盖和重用 zsh 中的现有函数

pse*_*ert 6 zsh function

我想稍微扩展一个 zsh 完成功能。

我想避免将完整的函数体放入我的 homedir 中,只更改一行。相反,我想拦截它的调用,然后自己调用原始函数。在准代码中:

<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 $@
}
Run Code Online (Sandbox Code Playgroud)

在我的具体案例中,我几乎在所有项目中都有一个 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
}  
Run Code Online (Sandbox Code Playgroud)

所以缺少的是加载_cmake_define_common_property_names并为其分配一个新的函数名称。

这里我试过

autoload -Uz +X _cmake_define_common_property_names
Run Code Online (Sandbox Code Playgroud)

但这失败了(该函数不是在它自己的文件中定义的,而是在_cmake.

注意:我没有分配新的函数名称以避免必须修改原始版本中调用函数的位置。

部分有效的是,autoload -Uz +X _cmake但这只能确保_cmake加载(我通过调用验证functions _cmake)。它不加载辅助函数_cmake_define_common_property_names

所以我的两个问题是

  1. 如何从$fpath文件中加载函数
  2. 一旦我加载了一个函数。如何在脚本中复制它/分配新的函数名称?

Gil*_*il' 8

如何修补函数

函数的代码存储在关联数组中functions。这是带有规范化空白且没有注释的源代码(zsh 已经进行了词法分析并漂亮地打印了标记)。您可以通过修改functions数组的条目来更改函数的代码。例如,要在开头添加额外的代码:

functions[_cmake_define_common_property_names]="
    … # your extra code here
    $functions[_cmake_define_common_property_names]"
Run Code Online (Sandbox Code Playgroud)

您还可以使用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 "$@"
}
Run Code Online (Sandbox Code Playgroud)

加载所有功能

从为自动加载创建的文件中加载所有函数的唯一可靠方法是执行该函数,前提是您可以安排在没有副作用的情况下执行它。对于完成函数,只需运行错误重定向到位桶的函数。它只会抱怨它没有在完成上下文中执行。

_cmake 2>/dev/null
# Now _cmake_xxx is defined
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

如果你真的不想执行顶层函数,你有几个选择:

  • 补丁定义_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
    
    Run Code Online (Sandbox Code Playgroud)