Existe uma maneira de acessar programaticamente e salvar uma lista de candidatos de conclusão no Zsh?

3

Em Zsh por padrão, a tecla tab está vinculada a expand-or-complete . Eu gostaria de acessar programaticamente a lista de candidatos de conclusão que teria sido produzida pela aba pressionada , para que eu possa escrever minha própria função e filtrar a lista por conta própria. Eu entendo que existe uma "estrutura de conclusão" que vem com o Zsh, mas eu gostaria de fazer isso sozinho.

Existe também o list-choices function / widget que produz a mesma saída que expand-or-complete , mas não oferece a funcionalidade de tabulação.

Eu fiz uma pesquisa razoavelmente extensa no Google e também pesquisei a fonte do Zsh, mas fiquei seco. Qualquer ajuda seria apreciada.

    
por John Lunzer 10.09.2018 / 17:45

1 resposta

2

Indiretamente, graças a JakeGould, deparei com uma solução: zsh-capture-completion . Na verdade, existem dois other perguntas quase idênticas nos sites Unix Stack Exchange, ambas com a resposta que eu tenho dado aqui.

O código fonte do script para zsh-capture-completion pode ser encontrado aqui:

#!/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 - $''
══► % cd ~/.zsh_plugins
══► % zsh ./zsh-capture-completion/capture.zsh 'cd '
zaw/
zsh-capture-completion/
zsh-syntax-highlighting/
zsh-vimode-visual/
'' } 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 == *$'
#!/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 - $''
══► % cd ~/.zsh_plugins
══► % zsh ./zsh-capture-completion/capture.zsh 'cd '
zaw/
zsh-capture-completion/
zsh-syntax-highlighting/
zsh-vimode-visual/
'' } 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 == *$'%pre%\r' ]]; then (( tog++ )) && return 0 || continue fi # display between toggles (( tog )) && echo -E - $line done return 2
\r' ]]; then (( tog++ )) && return 0 || continue fi # display between toggles (( tog )) && echo -E - $line done return 2

Aqui está um exemplo de uso de script:

%pre%

Observe o caractere de espaço no comando acima. Com o espaço, o script fornece a lista de pastas às quais você pode cd no diretório atual. Sem ele, o script forneceria todas as conclusões para os comandos que começam com cd .

Também devo observar que até mesmo o autor do script / plug-in fornecido considera sua solução "hacky". Se alguém souber de uma solução mais curta ou mais direta, ficaria muito feliz em aceitá-la como resposta.

    
por 10.09.2018 / 19:26