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

Joh*_*zer 5 zsh autocomplete

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

还有一个list-choices函数/小部件,它产生与expand-or-complete标签循环功能相同的输出,但不提供选项卡循环功能。

我已经在 Google 上进行了相当广泛的搜索,并且还浏览了 Zsh 源代码,但结果很枯燥。任何帮助,将不胜感激。

Joh*_*zer 5

我偶然发现了一种解决方案:zsh-capture-completion。实际上,Unix Stack Exchange 站点上还有另外两个几乎相同的问题,这两个问题都有我在这里给出的答案。

\n

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

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

以下是脚本使用的示例:

\n
\xe2\x95\x90\xe2\x95\x90\xe2\x96\xba % cd ~/.zsh_plugins\n\xe2\x95\x90\xe2\x95\x90\xe2\x96\xba % zsh ./zsh-capture-completion/capture.zsh \'cd \'\nzaw/\nzsh-capture-completion/\nzsh-syntax-highlighting/\nzsh-vimode-visual/\n
Run Code Online (Sandbox Code Playgroud)\n

请注意上面命令中的空格字符。cd通过空格,脚本提供了您可以从当前目录进入的文件夹列表。如果没有它,脚本将提供以 开头的命令的所有完成cd

\n

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

\n