Я попытаюсь дать представление о том, как работает система завершения zsh, и неполное описание этой проблемы.
Файл, который запускается при использовании завершения TAB для man
в zsh, находится в каталоге /usr/share/zsh/${ZSH_VERSION}/functions
. Дерево варьируется в зависимости от дистрибутива, но файл называется _man
и обеспечивает завершение для man
, apropos
и whatis
.
После вызова _man он работает следующим образом (грубое описание):
- , если для
man
и для первого флага было указано --local-file
, вызывать стандартное завершение файлов (_files
)
- construct
manpath
переменная из набора значений по умолчанию / $MANPATH
. Здесь будут искать справочные страницы
- определить, вызвали ли мы
man
с параметром номера раздела, если да - будет выполняться поиск только по этим разделам
- , если использовался
zstyle ':completion:*:manuals' separate-sections true
, отдельные разделы в выводе (не смешивайте между ними)
- invoke
_man_pages
, чтобы предоставить список man-страниц для матча
_man_pages
теперь делает немного магии с compfiles -p pages '' '' "$matcher" '' dummy '*'
. pages
- это переменная со всеми каталогами, содержащими man-страницы для запрошенных разделов. Фактический шаблон сглаживания создается из неявного параметра $PREFIX
и последнего параметра в compfiles
- *
в этом случае. В результате /usr/share/man/man1
будет преобразовано в /usr/share/man/man1/foo*
- Новый список шаблонов globged, получая все файлы, которые соответствуют шаблону
_man_pages
затем удаляет любые суффиксы из файлов и добавляет их в список вариантов виджетов завершения, используя compadd
Теперь, как вы видите, список manpages напрямую определяется переменной $PREFIX
. Чтобы zsh:foo
отображал только справочные страницы zsh*
, содержащие слово foo
, его необходимо разделить на символ :
(если есть).
Следующее добавление в _man_pages
частично решает проблему (zsh 4.3.4):
Оригинал:
_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
}
Изменено (ищите комментарии ## мода):
_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
}
Тем не менее, он все еще не полностью работает (если вы раскомментируете строку #echo "$p matched $manpage_grep"
, вы можете увидеть, что она строит список) - я подозреваю, что где-то внутри, система завершения видит, например, что "zshcompctl" не соответствует префиксу "zsh: foo" и не отображает полученные совпадения. Я пытался сохранить $PREFIX
как есть после удаления строки grep, но он все равно не хочет работать.
Во всяком случае, это, по крайней мере, должно начать вас.