Impossible d'effectuer une recherche de deux mots dans l'achèvement de la tabulation de Zsh pour Man
-
20-08-2019 - |
Question
Problème: : la complétion par un onglet prend deux mots et en calcule la meilleure correspondance pour Man, puis renvoie les meilleures correspondances
Exemple: Le pseudo-code suivant doit me fournir au moins la commande reverse-menu-complete de Zsh. Pour le moment, je ne peux pas rechercher de manuels dans les manuels sans zgrep.
man zsh:reverse <TAB>
où " " est le séparateur que je veux.
Problème initial: Quels fichiers l'achèvement de la tabulation est-il exécuté lorsque j'appuie sur TAB pour un mot dans la recherche de manuels par Zsh?
La solution
Je vais essayer de donner un aperçu du fonctionnement du système d'achèvement de zsh et de résoudre ce problème de manière incomplète.
Le fichier qui s'exécute lorsque vous utilisez la complétion TAB pour man
dans zsh se trouve dans le répertoire /usr/share/zsh/${ZSH_VERSION}/functions
. L’arborescence varie d’une distribution à l’autre, mais le fichier s’appelle _man
et permet de compléter apropos
, whatis
et --local-file
.
Après l'appel de _man, cela fonctionne comme suit (description approximative):
- si terminer pour
_files
et quemanpath
était spécifié comme premier indicateur, invoquer l'achèvement des fichiers standard ($MANPATH
) - construit
zstyle ':completion:*:manuals' separate-sections true
une variable à partir d'un ensemble de valeurs par défaut /_man_pages
. C’est là que les pages de manuel seront recherchées - détermine si nous avons appelé
compfiles -p pages '' '' "$matcher" '' dummy '*'
avec un paramètre de numéro de section, si oui, seules les sections faisant l'objet de la recherche seront recherchées - si le
pages
a été utilisé, séparez les sections en sortie (ne mélangez pas entre elles) - invoque
$PREFIX
pour fournir une liste de pages de manuel pour la correspondance -
compfiles
fait maintenant un peu de magie avec*
./usr/share/man/man1
est la variable avec tous les répertoires contenant les pages de manuel des sections demandées. Le modèle de globalisation actuel est construit à partir du paramètre implicite/usr/share/man/man1/foo*
et du dernier paramètre àcompadd
-zsh:foo
dans ce cas. Cela se traduit parzsh*
transformation enfoo
- La nouvelle liste de modèles glob est globbed, ce qui permet d'obtenir tous les fichiers correspondant au modèle
-
:
supprime ensuite les suffixes des fichiers et les ajoute à la liste de choix du widget d'achèvement en utilisant#echo "$p matched $manpage_grep"
Comme vous pouvez le constater, la liste des pages de manuel est directement déterminée par la variable <=>. Pour que <=> ne répertorie que les pages de manuel de <=> qui contiennent le mot <=>, il doit être divisé en <=> caractères (le cas échéant).
L'ajout suivant dans <=> résoudre partiellement le problème (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
}
Modifié (recherchez ## mod comments):
_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
}
Cependant, cela ne fonctionne toujours pas complètement (si vous supprimez la mise en commentaire de la ligne <=>, vous pouvez voir qu'elle construit la liste). Je suppose que quelque part en interne, le système d'achèvement voit que, par exemple, & " ; zshcompctl " ne correspond pas au préfixe & "; zsh: foo &"; et n'affiche pas les correspondances résultantes. J'ai essayé de garder <=> tel qu'il est après avoir supprimé la chaîne grep, mais cela ne veut toujours pas fonctionner.
Quoi qu'il en soit, cela devrait au moins vous aider à démarrer.