Incapaz de ter duas palavras-pesquisa na conclusão TAB do Zsh para o Homem
-
20-08-2019 - |
Pergunta
Problema: para ter uma conclusão de tabulação que leva duas palavras e calcula o melhor jogo a partir deles para o homem, e retorna os melhores jogos
Exemplo: O pseudo-código a seguir deve me dar pelo menos -command-reverse-menu completo do Zsh. Agora, eu não posso procurar manuais dentro manuais sem zgrep.
man zsh:reverse <TAB>
onde ":". É o separador que eu quero
Problema inicial:? Quais arquivos não a conclusão TAB prazo quando eu pressionar TAB para uma palavra na busca manuais por Zsh
Solução
vou tentar fornecer uma visão de como zsh funciona o sistema de conclusão e uma incompleta ir a este problema.
O arquivo que é executado quando você usar a conclusão de TAB para man
em zsh está localizado sob o diretório /usr/share/zsh/${ZSH_VERSION}/functions
. A árvore varia entre as distribuições, mas o arquivo é chamado _man
, e fornece conclusão para man
, apropos
e whatis
.
Depois de _man é invocado, ele funciona da seguinte forma (descrição aproximada):
- se completando para
man
e--local-file
foi especificado como primeira bandeira, chamar a conclusão de arquivos padrão (_files
) - variável construção
manpath
a partir de um conjunto de defaults /$MANPATH
. Este é o local onde as páginas de manual será procurado - determinar se invocado
man
com um parâmetro número da seção, se sim - apenas as seções serão pesquisados ?? - Se foi utilizado o
zstyle ':completion:*:manuals' separate-sections true
, seções separadas em saída (não se misturam entre eles) - invoke
_man_pages
para fornecer uma lista de páginas de manual para o jogo -
_man_pages
agora faz um pouco de magia comcompfiles -p pages '' '' "$matcher" '' dummy '*'
.pages
é a variável com todas as pastas contendo manpages para secção requerido (s). O padrão de englobamento real é construído a partir do$PREFIX
parâmetro implícito eo último parâmetro paracompfiles
-*
neste caso. Isso resulta em/usr/share/man/man1
para ser transformado em/usr/share/man/man1/foo*
- A nova lista de padrões glob é globbed, a obtenção de todos os arquivos que correspondem ao padrão
-
_man_pages
seguida, retira quaisquer sufixos dos arquivos e os adiciona à lista de widget de conclusão de opções usandocompadd
Agora, como você pode ver, a lista de manpages está diretamente determinada pela variável $PREFIX
. A fim de fazer zsh:foo
a lista apenas as páginas homem de zsh*
que contêm a palavra foo
, ele precisa ser dividida entre caráter :
(se houver).
O seguinte aditamento no _man_pages
parcialmente resolver a questão (zsh 4.3.4):
Original:
_man_pages() {
local matcher pages dummy sopt
zparseopts -E M+:=matcher
if (( $#matcher )); then
matcher=( ${matcher:#-M} )
matcher="$matcher"
else
matcher=
fi
pages=( ${(M)dirs:#*$sect/} )
compfiles -p pages '' '' "$matcher" '' dummy '*'
pages=( ${^~pages}(N:t) )
(($#mrd)) && pages[$#pages+1]=($(awk $awk $mrd))
# Remove any compression suffix, then remove the minimum possible string
# beginning with .<->: that handles problem cases like files called
# `POSIX.1.5'.
[[ $OSTYPE = solaris* ]] && sopt='-s '
if ((CURRENT > 2)) ||
! zstyle -t ":completion:${curcontext}:manuals.$sect" insert-sections
then
compadd "$@" - ${pages%.(?|<->*(|.gz|.bz2|.Z))}
else
compadd "$@" -P "$sopt$sect " - ${pages%.(?|<->*(|.gz|.bz2|.Z))}
fi
}
Modificado (olhar para ## comentários mod):
_man_pages() {
local matcher pages dummy sopt
zparseopts -E M+:=matcher
if (( $#matcher )); then
matcher=( ${matcher:#-M} )
matcher="$matcher"
else
matcher=
fi
pages=( ${(M)dirs:#*$sect/} )
##mod
# split components by the ":" character
local pref_words manpage_grep orig_prefix
# save original prefix (just in case)
orig_prefix=${PREFIX}
# split $PREFIX by ':' and make it an array
pref_words=${PREFIX//:/ }
set -A pref_words ${=pref_words}
# if there are both manpage name and grep string, use both
if (( $#pref_words == 2 )); then
manpage_grep=$pref_words[2]
# PREFIX is used internally by compfiles
PREFIX=$pref_words[1]
elif (( $#pref_words == 1 )) && [[ ${PREFIX[1,1]} == ":" ]]; then
# otherwise, prefix is empty and only grep string exists
PREFIX=
manpage_grep=$pref_words[1]
fi
compfiles -p pages '' '' "$matcher" '' dummy '*'
##mod: complete, but don't strip path names
pages=( ${^~pages} )
(($#mrd)) && pages[$#pages+1]=($(awk $awk $mrd))
##mod: grep pages
# Build a list of matching pages that pass the grep
local matching_pages
typeset -a matching_pages
# manpage_grep exists and not empty
if (( ${#manpage_grep} > 0 )); then
for p in ${pages}; do
zgrep "${manpage_grep}" $p > /dev/null
if (( $? == 0 )); then
#echo "$p matched $manpage_grep"
matching_pages+=($p)
fi
done
else
# there's no manpage_grep, so all pages match
matching_pages=( ${pages} )
fi
#echo "\nmatching manpages: "${matching_pages}
pages=( ${matching_pages}(N:t) )
# keep the stripped prefix for now
#PREFIX=${orig_prefix}
# Remove any compression suffix, then remove the minimum possible string
# beginning with .<->: that handles problem cases like files called
# `POSIX.1.5'.
[[ $OSTYPE = solaris* ]] && sopt='-s '
if ((CURRENT > 2)) ||
! zstyle -t ":completion:${curcontext}:manuals.$sect" insert-sections
then
compadd "$@" - ${pages%.(?|<->*(|.gz|.bz2|.Z))}
else
compadd "$@" -P "$sopt$sect " - ${pages%.(?|<->*(|.gz|.bz2|.Z))}
fi
}
No entanto, é ainda não totalmente funcional (se você remover o comentário da linha #echo "$p matched $manpage_grep"
, você pode ver que ele não criar a lista de) - Eu suspeito que em algum lugar internamente, o sistema de conclusão vê que, por exemplo, "zshcompctl" não é correspondida pelo prefixo "zsh: foo", e não exibir as correspondências resultantes. Eu tentei manter $PREFIX
como é após a remoção da cadeia de grep, mas ainda não quer trabalho.
De qualquer forma, isso pelo menos deve começar.