基于多个路径的自定义zsh完成

时间:2013-01-11 22:44:34

标签: zsh

我想为接受目录作为参数的命令编写自定义完成。我认为最简单的解释一下如何使用一个例子。

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

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的复杂性方面有点迷失,所以我不确定如何做到最好。

2 个答案:

答案 0 :(得分:5)

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的{​​{1}}选项也将数组作为参数,允许这样:

compctl

我很确定现在符合您的要求 注意: → compctl -/ -W "(/.../otherdir /.../somedir)" mycmd → mycmd <TAB> bar/ foo/ subdir/ 选项适用于-W(请参阅-/的手册页) - zshcompctl无法自行运行。

5。具有部分子目录的数组

我们假设有效的参数是:

-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