有没有办法以编程方式访问并保存 Zsh 中的完成候选项列表?

有没有办法以编程方式访问并保存 Zsh 中的完成候选项列表?

在 Zsh 中,默认情况下,该tab键绑定到expand-or-complete。我想以编程方式访问按 产生的完成候选列表tab,以便我可以编写自己的函数并自行过滤列表。我知道 Zsh 附带一个“完成框架”,但我想自己做。

还有list-choices产生相同输出expand-or-complete但不提供选项卡循环功能的功能/小部件。

我在 Google 上进行了相当广泛的搜索,也浏览了 Zsh 源代码,但一无所获。任何帮助都将不胜感激。

答案1

我偶然发现了一个解决方案:zsh-capture-completion。其实有 其他Unix Stack Exchange 网站上的问题几乎完全相同,我在这里都给出了答案。

脚本源代码zsh-capture-completion可以在这里找到:

#!/bin/zsh

zmodload zsh/zpty || { echo 'error: missing module zsh/zpty' >&2; exit 1 }

# spawn shell
zpty z zsh -f -i

# line buffer for pty output
local line

setopt rcquotes
() {
    zpty -w z source $1
    repeat 4; do
        zpty -r z line
        [[ $line == ok* ]] && return
    done
    echo 'error initializing.' >&2
    exit 2
} =( <<< '
# no prompt!
PROMPT=
# load completion system
autoload compinit
compinit -d ~/.zcompdump_capture
# never run a command
bindkey ''^M'' undefined
bindkey ''^J'' undefined
bindkey ''^I'' complete-word
# send a line with null-byte at the end before and after completions are output
null-line () {
    echo -E - $''\0''
}
compprefuncs=( null-line )
comppostfuncs=( null-line exit )
# never group stuff!
zstyle '':completion:*'' list-grouped false
# don''t insert tab when attempting completion on empty line
zstyle '':completion:*'' insert-tab false
# no list separator, this saves some stripping later on
zstyle '':completion:*'' list-separator ''''
# we use zparseopts
zmodload zsh/zutil
# override compadd (this our hook)
compadd () {
    # check if any of -O, -A or -D are given
    if [[ ${@[1,(i)(-|--)]} == *-(O|A|D)\ * ]]; then
        # if that is the case, just delegate and leave
        builtin compadd "$@"
        return $?
    fi
    # ok, this concerns us!
    # echo -E - got this: "$@"
    # be careful with namespacing here, we don''t want to mess with stuff that
    # should be passed to compadd!
    typeset -a __hits __dscr __tmp
    # do we have a description parameter?
    # note we don''t use zparseopts here because of combined option parameters
    # with arguments like -default- confuse it.
    if (( $@[(I)-d] )); then # kind of a hack, $+@[(r)-d] doesn''t work because of line noise overload
        # next param after -d
        __tmp=${@[$[${@[(i)-d]}+1]]}
        # description can be given as an array parameter name, or inline () array
        if [[ $__tmp == \(* ]]; then
            eval "__dscr=$__tmp"
        else
            __dscr=( "${(@P)__tmp}" )
        fi
    fi
    # capture completions by injecting -A parameter into the compadd call.
    # this takes care of matching for us.
    builtin compadd -A __hits -D __dscr "$@"
    setopt localoptions norcexpandparam extendedglob
    # extract prefixes and suffixes from compadd call. we can''t do zsh''s cool
    # -r remove-func magic, but it''s better than nothing.
    typeset -A apre hpre hsuf asuf
    zparseopts -E P:=apre p:=hpre S:=asuf s:=hsuf
    # append / to directories? we are only emulating -f in a half-assed way
    # here, but it''s better than nothing.
    integer dirsuf=0
    # don''t be fooled by -default- >.>
    if [[ -z $hsuf && "${${@//-default-/}% -# *}" == *-[[:alnum:]]#f* ]]; then
        dirsuf=1
    fi
    # just drop
    [[ -n $__hits ]] || return
    # this is the point where we have all matches in $__hits and all
    # descriptions in $__dscr!
    # display all matches
    local dsuf dscr
    for i in {1..$#__hits}; do
        # add a dir suffix?
        (( dirsuf )) && [[ -d $__hits[$i] ]] && dsuf=/ || dsuf=
        # description to be displayed afterwards
        (( $#__dscr >= $i )) && dscr=" -- ${${__dscr[$i]}##$__hits[$i] #}" || dscr=
        echo -E - $IPREFIX$apre$hpre$__hits[$i]$dsuf$hsuf$asuf$dscr
    done
}
# signal success!
echo ok')

zpty -w z "$*"$'\t'

integer tog=0
# read from the pty, and parse linewise
while zpty -r z; do :; done | while IFS= read -r line; do
    if [[ $line == *$'\0\r' ]]; then
        (( tog++ )) && return 0 || continue
    fi
    # display between toggles
    (( tog )) && echo -E - $line
done

return 2

以下是脚本使用的示例:

══► % cd ~/.zsh_plugins
══► % zsh ./zsh-capture-completion/capture.zsh 'cd '
zaw/
zsh-capture-completion/
zsh-syntax-highlighting/
zsh-vimode-visual/

请注意上述命令中的空格字符。使用空格,脚本会提供可从当前目录进入的文件夹列表cd。如果没有空格,脚本将提供以 开头的所有命令的补全cd

我还应该指出,即使提供的脚本/插件的作者也认为他的解决方案是“黑客式的”。如果有人知道更短或更直接的解决方案,我会很高兴接受它作为答案。

相关内容