我想为接受目录作为参数的命令编写自定义完成。我认为最简单的解释一下如何使用一个例子。
假设我有一个在某处定义的目录列表:
d=(~/somedir/foo ~/somedir/foo/bar ~/other/dir/baz)
对命令的有效调用是
mycmd foo
mycmd bar
mycmd baz
mycmd baz/and/some/subdir
完成应该可以
mycmd f<TAB> => mycmd foo
mycmd baz/and/some/s<TAB> => mycmd baz/and/some/subdir
不知怎的,我在zshcompsys的复杂性方面有点迷失,所以我不确定如何做到最好。
答案 0 :(得分:5)
如果事先知道mycmd
的参数是什么,你可以使用一个非常简单的完成函数,里面的值是硬编码的:
#compdef _mycmd
_arguments "1: :(foo bar baz baz/and/some/subdir)"
这给出了以下内容:
zsh% mycmd<TAB>
bar baz baz/some/other/subdir foo
zsh% mycmd baz<TAB>
baz baz/some/other/subdir
zsh% mycmd baz/<TAB>
zsh% mycmd baz/some/other/subdir
假设foo
的任何子目录都是mycmd
的有效路径:
~/foo/bar # Valid path
~/foo/subdir/baz # Valid path
~/baz/bar # Invalid path
您可以告诉compctl
完成foo
中的任何目录作为mycmd
的参数:
# In your ~/.zshrc
compctl -/ -W ~/foo mycmd
我不知道这对于您编写的mycmd
的任何其他完成函数有多好(例如,如果mycmd
也采用非文件名参数)。它像这样完成:
zsh% mycmd<TAB>
bar/ baz/ caz/
zsh% mycmd baz/s<TAB>
zsh% mycmd baz/subdir/
...
)。如果我们有一个数组d=(foo bar baz)
,其中每个元素都是mycmd
的有效完成,我们可以使用-k
:
→ compctl -k "(foo bar baz)" mycmd
→ mycmd
bar baz foo
使用以下目录结构:
~/.../somedir
~/.../somedir/bar
~/.../somedir/foo
~/.../somedir/foo/invalid
~/.../otherdir
~/.../otherdir/subdir
~/.../otherdir/subdir/baz
-W
的{{1}}选项也将数组作为参数,允许这样:
compctl
我很确定现在符合您的要求
注意: → compctl -/ -W "(/.../otherdir /.../somedir)" mycmd
→ mycmd <TAB>
bar/ foo/ subdir/
选项适用于-W
(请参阅-/
的手册页) - zshcompctl
无法自行运行。
我们假设有效的参数是:
-W
我们在哪里不想要mycmd foo # Valid
mycmd baz # Valid
mycmd baz/and/some/subdir # Valid - subdirectory of `baz`
mycmd foo/subdir # Invalid!!
的子目录,只有foo
的子目录。我们可以通过混合baz
和-k
:
-/ -W
然而,这留下了→ compctl -/ -W "(/.../otherdir)" -k "(/.../somedir/foo)" mycmd
→ mycmd <TAB>
/Users/simont/sandbox/completion/somedir/foo subdir/
→ mycmd foo/<TAB> # No further completion - we can't complete foo/invalid.
→ mycmd subdir/<TAB>
→ mycmd subdir/baz/
的完整路径(与foo
不同,后者将其删除)。要解决此问题,请不要将其放入-W
:
-k
答案 1 :(得分:1)
感谢您的帮助,simont,但我采用了另一种方法,使用完成功能。它主要是基于试验和错误,而不是阅读文档,所以我不知道这是否正确[tm]这样做,但它的工作原理并不需要太多代码。
function _foo {
setopt LOCALOPTIONS
unsetopt AUTOPUSHD
local paths
paths=(/home/dani '/var/log' '/usr/share' '/etc')
if [[ $PREFIX == *'/'* ]] {
for p ($paths) {
if [[ ${PREFIX[(ws:/:)1]} == ${p:t} ]] {
local oldpwd=$PWD
cd -q ${p:h}
local matches
matches=($PREFIX*(/))
if [[ ${#matches} -gt 0 ]] {
compadd -p ${matches[1]:h}'/' -q -S '/' -- ${matches:t}
}
cd -q $oldpwd
return
}
}
} else {
compadd -q -S '/' -- ${^paths:t}
}
}
compdef _foo foo