3

В Zsh по умолчанию клавиша табуляции связана с expand-or-complete . Я хотел бы получить программный доступ к списку кандидатов на завершение, которые были бы созданы с помощью нажатой вкладки, чтобы я мог написать свою собственную функцию и отфильтровать список самостоятельно. Я понимаю, что в Zsh есть "фреймворк завершения", но я бы хотел сделать это сам.

Существует также функция / виджет list-choices choices, которая выдает тот же результат, что и expand-or-complete но не предлагает функции циклического преобразования вкладок.

Я провел достаточно обширный поиск в Google, а также просмотрел источник Zsh, но потерял сознание. Любая помощь будет оценена.

1 ответ1

2

Косвенно благодаря JakeGould я наткнулся на одно решение: zsh-capture-completion complete. На самом деле на сайтах 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 .

Следует также отметить, что даже автор предоставленного скрипта / плагина считает свое решение "хакерским". Если кто-нибудь знает о более коротком или более прямолинейном решении, я был бы рад принять его в качестве ответа.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .