zsh自动补全的复用

zsh的自动补全

zsh-completions是zsh官方支持的自动补全包

使用antigen管理zsh插件的话,zsh-completions的路径在~/.antigen/bundles/zsh-users/zsh-completions/src,其下有众多文件名如_xxx,分别定义了xxx命令的补全。

机理

zsh 有一数组 $fpath,内容如

declare -p fpath
typeset -a fpath=( /usr/local/share/zsh/site-functions /Users/mac/.autojump/functions /usr/share/zsh/site-functions /usr/share/zsh/5.3/functions /Users/mac/.antigen/bundles/robbyrussell/oh-my-zsh/lib /Users/mac/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/autojump /Users/mac/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip /Users/mac/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/svn-fast-info /Users/mac/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/colorize /Users/mac/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/github /Users/mac/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/python /Users/mac/.antigen/bundles/zsh-users/zsh-autosuggestions /Users/mac/.antigen/bundles/zsh-users/zsh-completions /Users/mac/.antigen/bundles/Vifon/deer /Users/mac/.antigen/bundles/willghatch/zsh-cdr /Users/mac/.antigen/bundles/zsh-users/zsh-syntax-highlighting /Users/mac/.antigen/bundles/zsh-users/zsh-completions/src )

表示zsh启动时会从这些路径下加载_xxx文件,当zsh加载fpath所有文件后,会生成相应的_xxx函数,例如

~/.antigen/bundles/zsh-users/zsh-completions/src/_tmuxp文件定义了_tmuxp自动补全函数,(未定义_tmux函数),而zsh加载后会自动生成_tmux函数

复用zsh的自动补全函数

参见

基本原理

  • 直接复用:bar 函数 要 复用foo函数的自动补全

    # 函数 bar的定义可写在其前
    compdef _foo bar
    # 函数 bar的定义也可写在其后
  • 复用带参的:bar 函数 要 复用 foo <一个或多个参数> 的自动补全

    # 创建一个空函数foo_copy_comp,使其补全与foo同
    foo_copy_comp() { : }
    compdef _foo foo_copy_comp
    # zsh的自动补全支持alias,故而bar的自动补全与'foo <一个或多个参数>'命令同
    alias bar='foo_copy_comp <一个或多个参数>'
    
    # 函数 bar的定义要写在其后
    bar(){
      # ...
    }

    当bar函数定义后,alias bar与bar函数共存:

    • which bar 返回的是" alias bar=‘foo_copy_comp <一个或多个参数>’ "
    • bar <TAB键> 会显示foo <一个或多个参数>命令之后的自动补全
    • 运行bar <其他参数>命令,会执行bar 函数,而非alias bar

配置示例

  • 关闭全局的compinit:在全局zshrc文件( mac: /etc/zshrc, linux:/etc/zsh/zshrc ) 中,搜compinit,若发现如下内容,则将skip_global_compinit=1解除注释;若没发现compinit,则不管。

    # If you don't want compinit called here, place the line
    # skip_global_compinit=1    # 这行要解注释
    # in your $ZDOTDIR/.zshenv or $ZDOTDIR/.zprofile
    if [[ -z "$skip_global_compinit" ]]; then
      autoload -U compinit
      compinit
    fi

    这样设置的原因是:在~/.zshrc中需要运行autoload -U compinit && compinit(如下一段),来使得compdef有定义。但是运行这个比较花时间,全局zshrc文件没必要也运行一遍。

    注: 每次启动zsh(不论登录、非登录,交互、非交互),先会运行全局zshrc文件,再运行~/.zshrc

  • 配置自动补全:在~/.zshrc中写

# 初始化zsh的自动补全,从而conpdef函数有定义了
autoload -U compinit && compinit

# ----------直接复用--------------
# auto_rsync 函数 复用 rsync的补全函数
compdef _rsync auto_rsync

# ---------复用带参的--------------
# itm 函数 复用 'tmux -CC attach -t' 的补全函数
tmux_itm() {   :;    }
compdef _tmux tmux_itm
alias itm='tmux_itm -CC attach -t'

## 注意, 两个alias 不能共用同一个 tmux_itm, 不然这两个alias对应的函数会冲突
## 例如下面这样是不行的
# alias itm1='tmux_itm -CC attach -t'
# alias itm2='tmux_itm -CC attach -t'
## 后面定义 itm1函数 和 itm2函数,
# 则 itm1函数 和  itm2函数 会冲突, 即两个函数名, 对应到同一个函数体

# 允许在有`alias foo=...`时,再定义函数`foo() {  .... }`
set -o ALIAS_FUNC_DEF > /dev/null 2>&1
  • 定义补全函数:在~/.zshrc 配置自动补全的后面, source ~/.aliases , 而 source ~/.aliases 中定义补全函数
# ----------直接复用--------------
# 带断点续传的rsync
auto_rsync() {
    local retry_time=180
    while [ 1 ]
    do
        rsync -aHhvzP $*
        if [ "$?" = "0" ]
        then
            echo "rsync completed normally"
            break
        else
            echo "Rsync failure. Backing off and retrying in $retry_time seconds..."
            sleep $retry_time
        fi
    done
}

# ---------复用带参的--------------
# itm 函数覆盖 alias itm='tmux_itm -CC attach -t'
itm() {
    # 注意:若已经定义了alias itm,需要把前三个参数`-CC attach -t`过掉
    if [ "$(alias itm 2> /dev/null)" != '' ]; then
        for i in {1..3}; do shift; done
    fi

    if [ $# = 0 ]; then
        tmux -CC attach || tmux -CC new -s default
    else
        local session="$1"
        tmux -CC attach -t "${session}" || tmux -CC new -s "${session}"
    fi
}

注意:复用foo <n个参数>,要仿照上面的代码,把前n个参数过掉。