【问题标题】:Zsh: How to force file completion everywhere following a set of characters?Zsh:如何在一组字符之后强制文件完成?
【发布时间】:2015-12-16 10:07:06
【问题描述】:

我正在尝试弄清楚如何在一组字符之后使文件完成在命令行上的任何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=/... ...

我查看了使用 Zsh 附带的少数不同的完成小部件重新绑定 '^I'(选项卡)并更改了我的 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 位置都有效。

我正在尝试找出一种方法来完成文件完成,但乍一看它看起来有点复杂。我真的不知道该怎么做,所以我希望得到一些帮助。

【问题讨论】:

    标签: zsh zshrc zsh-completion


    【解决方案1】:

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

    ...
    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 的其余部分。

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

    _value: 不太确定这个。该文档指出它在完成中起着某种作用。

    Documentation for the completion system

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

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


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

    env TERM=linux PATH=/<---cursor here
    

    光标位于该斜线的末尾。此函数允许忽略 PREFIX,即 PATH=,因此我们有:

    env TERM=linux /<---cursor here
    

    您可以在此处完成删除 PATH= 的文件。该函数实际上并没有删除PATH=,它只是将它重新归类为可以忽略的东西。

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

    最后要提到的是,在您的 zshrc 中添加此 force-list 行会以某种方式削弱此功能。它仍然有效,但似乎窒息。无论如何,这个新的强制列表功能要好得多。

    zstyle ':completion:*' force-list always
    

    编辑:有几行我忘记复制到函数中。可能应该在发布前检查。我觉得现在挺好的。

    【讨论】:

    • 感谢您的工作!不过,它对我不起作用。你能描述一下把代码放在哪里(函数和zstyle 命令都可以在.zshrc 中吗)?此外,了解您所说的“窒息”是什么意思会很有趣。
    猜你喜欢
    • 2014-10-02
    • 2013-02-14
    • 2021-01-24
    • 2015-04-28
    • 2013-10-18
    • 2013-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多