问题:要制作标签,需要两个单词并为他们计算最佳匹配,然后返回最佳匹配
示例:以下伪代码应该至少给我Zsh的reverse-menu-complete -command。现在,如果没有zgrep,我无法在手册中搜索手册。
man zsh:reverse <TAB>
其中“:”是我想要的分隔符。
初始问题:在按Zsh搜索手册中按TAB一个单词时,TAB完成运行的文件是什么?
答案 0 :(得分:2)
我将尝试深入了解zsh完成系统的工作原理,并解决此问题。
在zsh中为man
使用TAB完成时运行的文件位于/usr/share/zsh/${ZSH_VERSION}/functions
目录下。树在不同的发行版中有所不同,但文件名为_man
,并提供man
,apropos
和whatis
的完成。
调用_man后,它的工作原理如下(粗略描述):
man
并且--local-file
被指定为第一个标志,则调用标准文件完成(_files
)manpath
构造$MANPATH
变量。这是搜索联机帮助页的地方man
,如果是,则仅搜索这些节zstyle ':completion:*:manuals' separate-sections true
,则输出中的单独部分(不要在它们之间混合)_man_pages
以提供匹配的手册页列表_man_pages
现在对compfiles -p pages '' '' "$matcher" '' dummy '*'
做了一些魔术。 pages
是变量,其中所有目录都包含所请求部分的联机帮助页。在这种情况下,实际的globbing模式是从隐式参数$PREFIX
和最后一个参数compfiles
- *
构建的。这会导致/usr/share/man/man1
转换为/usr/share/man/man1/foo*
_man_pages
然后从文件中删除所有后缀,并使用compadd
现在,正如您所看到的,联机帮助页列表由$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
}
修改(查找## 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
}
然而,它仍然没有完全正常工作(如果你取消注释#echo "$p matched $manpage_grep"
行,你可以看到它确实构建了列表) - 我怀疑在内部某处,完成系统会看到,例如,“zshcompctl “与前缀”zsh:foo“不匹配,并且不显示生成的匹配项。在剥离grep字符串之后,我试图保持$PREFIX
,但它仍然不想工作。
无论如何,这至少应该让你开始。