Zsh:如何在一组字符后强制文件完成?

时间:2015-09-19 08:11:35

标签: zsh zshrc zsh-completion

我试图找出如何让文件完成在一组字符后在命令行的任何word位置工作。如shell中所列,这些字符为[ =+-\'\"()](空格为制表符和空格)。 Zsh会这样做,但只能在反击后的角色,'`'或$(之后执行。 mksh执行此操作,但不在字符[+-]之后。

通过命令行上的单词位置,我会谈论你输入的每一组字符,这些字符由空格和一些其他字符分隔。例如,

print Hello World

在1-3位有三个words。在第1位,当你第一次输入内容时,完成非常完美。在我提到的所有字符之后,文件完成工作。在第一个word之后,完成系统变得更加有限,因为它很聪明。这对命令很有用,但限制文件完成的位置并不是特别有用。

以下是文件完成对我不起作用的一些示例,但在我看来应该是这样的:

: ${a:=/...}

echo "${a:-/...}"

make LDFLAGS+='-nostdlib /.../crt1.o /.../crti.o ...'

env a=/... b=/... ...

我已经看过重新绑定'^I'(标签),其中包含Zsh附带的一些不同的完成小部件并更改了我的zstyle ':completion:*'行。到目前为止,没有任何方法可以改变这种默认的Zsh行为。我认为我需要创建一个完成函数,我可以将其添加到zstyle ':completion:*' completer ...行的末尾,作为最后的完成。

在完成功能中,一条路线是切断我想要完成的当前word,完成它,然后重新插入完成,如果可能的话。它也可能更像_precommand,它将第二个word转换为第一个word,以便正常的命令完成。

我能够修改_precommand,以便您可以在任意word位置完成命令。这是新文件,我将其命名为_commando并将其目录添加到我的fpath:

#compdef -                                                              

# precommands is made local in _main_complete
precommands+=($words[1,$(( CURRENT -1 ))])

shift words
CURRENT=1

_normal

要使用它,我将其添加到zshrc中':completion:*' completer ...行的末尾,以便它适用于$path中的每个程序。基本上,无论你输入的word被认为是第一个word,所以命令完成在命令行的每个word位置都有效。

我试图想办法为文件完成做同样的事情,但是在第一次赌注时看起来有点复杂。我不确定该去哪里,所以我希望得到一些帮助。

1 个答案:

答案 0 :(得分:2)

我仔细研究了一些Zsh的内置函数,并注意到一些具有特殊完成行为的函数。它们属于typeset组,默认_typeset中的函数为fpath。我只需要为我想做的事情提取几行。这些是我提取的行:

...
elif [[ "$PREFIX" = *\=* ]]; then
compstate[parameter]="${PREFIX%%\=*}"
compset -P 1 '*='
_value
...

这几行允许在命令中的每个斜杠后完成排版完成:

typeset file1=/... file2=~/... file3=/...

我从中推断出来创建以下功能。您可以修改它以放入fpath。我只是在我的zshrc中定义它:

_reallyforcefilecompletion() {
    local prefix_char

    for prefix_char in ' ' $'\t' '=' '+' '-' "'" '"' ')' ':'; do
        if [[ "$PREFIX" = *${prefix_char}* ]]; then
            if [[ "$PREFIX" = *[\'\"]* ]]; then
                compset -q -P "*${prefix_char}"
            else
                compset -P "*${prefix_char}"
            fi
            _value
            break
        fi
    done
}

您可以将其添加到zstyle行,如下所示:

zstyle ':completion:*' completer _complete _reallyforcefilecompletion

这样,它只能作为最后的手段使用,以便更聪明的完成可以在它之前尝试。这里有一个关于函数的一点解释,从少数变量和所涉及的命令开始:

prefix_char:这将设置为我们想要完成的每个前缀字符。例如,env a=123的前缀字符为=

PREFIX:最初这将被设置为从单词的开头到光标位置的当前单词的一部分;它可以被修改为所有匹配的公共前缀。

IPREFIX(未在代码中显示):compset将字符串匹配从PREFIX移至IPREFIX,以便PREFIX的其余部分可以完成。< / p>

compset:此命令简化了特殊参数的修改,而其返回状态允许对它们进行测试。

_value:对此不太确定。文档声明它在完成中起着某种作用。

Documentation for the completion system

功能:在第二行,我们声明prefix_char local以避免可变污染。在第三行中,我们开始for循环,选择我们想要完成的每个prefix_char。在下一个if块中,我们会检查变量PREFIX是否以我们要完成的prefix_chars之一结束,以及PREFIX是否包含任何引号。由于PREFIX包含引号,因此我们使用compset -q基本上允许忽略引号,以便我们可以完成它们。 compset -P剥离PREFIX并将其移至IPREFIX,基本上会被忽略,完成就可以了。

下一个elif语句适用于PREFIX以prefix_char结尾但不包含引号,因此我们只使用compset -P。我添加了return 0来打破循环。使用case语句的更正确的方法是使用compset语句,但我们不使用_value返回值,因此可行。除了word之外,您还没有看到有关文件完成的任何信息。在大多数情况下,我们只是告诉系统忽略部分env TERM=linux PATH=/<---cursor here

基本上这就是函数的作用。我们有一行看起来像:

env TERM=linux /<---cursor here

光标位于该斜杠的末尾。这个函数允许忽略PREFIX,即PATH =,所以我们有:

PATH=

您可以删除PATH=的文件。该函数实际上并没有删除force-list,但它只是将其重新分类为要忽略的内容。

使用此功能,您现在可以完成我在问题中列出的所有示例以及更多内容。

最后要提到的一点是,在zshrc中添加此zstyle ':completion:*' force-list always 行会以某种方式削弱此函数。它仍然有效,但似乎窒息。无论如何,这种新的强制列表功能更好。

self.text=
编辑:有几行忘了复制到函数中。可能应该在发布之前进行检查。我觉得现在好了。