2

我想为接受目录作为参数的命令编写自定义完成。我认为用一个例子来解释它应该如何工作是最容易的。

假设我在某处定义了一个目录列表:

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 的复杂性中,所以我不确定如何最好地解决这个问题。

4

2 回答 2

7

1. 预定义目录作为参数。

如果您事先知道 的参数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 

2.完成一个目录内的所有子目录

假设 的任何子目录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/

注意:我的路径/命令有点长。我在下面的粘贴中剪掉了一些(替换为...)。

3. 使用预定义的数组。

如果我们有一个数组d=(foo bar baz),其中每个元素都是 的有效补全mycmd,我们可以使用-k

→ compctl -k "(foo bar baz)" mycmd
→ mycmd
bar  baz  foo

4. 数组的子目录。

使用以下目录结构:

~/.../somedir
~/.../somedir/bar
~/.../somedir/foo
~/.../somedir/foo/invalid
~/.../otherdir
~/.../otherdir/subdir
~/.../otherdir/subdir/baz

-W选项compctl也将数组作为参数,允许这样做:

→ compctl -/ -W "(/.../otherdir /.../somedir)" mycmd
→ mycmd <TAB>
bar/     foo/     subdir/

我相当确定这现在符合您的要求。
注意:-W选项适用于-/(阅读手册页zshcompctl) --W不能单独使用。

5. 带有部分子目录的数组

让我们假设有效的论点是:

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

compctl -/ -W "(/.../otherdir)" -k "(foo)" mycmd
于 2013-01-12T07:13:11.087 回答
2

Thanks for your help, simont, but I took another approach using a completion function. It's mostly based on trial and error rather than reading the documentation, so I have no idea if this is the right way[tm] to do it, but it works and does not require much code.

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
于 2013-01-12T22:02:23.053 回答