Есть ли способ программно получить доступ и сохранить список кандидатов на завершение в Zsh?

265
John Lunzer

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

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

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

2
Интересный вопрос. Вы смотрели на эту [реализацию Bash той же концепции] (https://brbsix.github.io/2015/11/29/accessing-tab-completion-programmatics-in-bash/)? JakeGould 5 лет назад 0

1 ответ на вопрос

2
John Lunzer

Косвенно благодаря JakeGould я наткнулся на одно решение: 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 ; do # add a dir suffix? (( dirsuf )) && [[ -d $__hits[$i] ]] && dsuf=/ || dsuf= # description to be displayed afterwards (( $#__dscr >= $i )) && dscr=" -- ${$##$__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.

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

Отличная работа! Добавлен исходный код самого скрипта, потому что - в конце концов - код достаточно короткий, всегда лучше опубликовать его в ответе, поскольку ссылки (и их содержимое) часто могут исчезать. JakeGould 5 лет назад 1
Спасибо, я был как бы на грани добавления. Я думаю, что это просто на грани того, чтобы быть «достаточно коротким» или «слишком длинным». Учитывая ваш опыт, я подчиняюсь вашему мнению. John Lunzer 5 лет назад 0
Нет проблем. Обратите внимание, как исходный код размещается в элементе прокрутки. Таким образом, длина не является полной проблемой, если она действительно длинная и неуправляемая. JakeGould 5 лет назад 0
Кроме того, в исходном коде есть потенциально оскорбительный / нечувствительный язык. John Lunzer 5 лет назад 1
Хлоп! По крайней мере, это был просто комментарий, и я удалил его. JakeGould 5 лет назад 0