完成没有root的文件名,只有扩展名隐藏文件

问题描述

我正在尝试为一个命令编写一个完成,该命令的文件名没有根/基本部分,只有扩展名(例如“foo/.ext”)。隐藏文件。 (特别是 direnv 的“.envrc”。)

在fish的安装中有几个例子可以使用 __fish_complete_suffix 用于具有根 扩展名的文件,例如 fish 本身:

complete -c fish -k -x -a "(__fish_complete_suffix .fish)"

同样的事情对于隐藏文件,没有 root 的文件,不起作用。

# completions/foo.fish
complete -c foo -k -x -a "(__fish_complete_suffix .bar)"

# functions/foo.fish
function foo
    echo $argv
end

> mkdir -p a/b; touch a/1.bar a/2.bar a/.bar
> foo a/<TAB>
a/1.bar  a/2.bar  a/b/

有没有办法用这个或其他一些功能来实现这一点?

解决方法

fish doesn't add hidden files to the list of completions unless the token being completed 已经以“.”开头。

您应该能够编写满足您要求的自定义完成,但不幸的是 long-standing bug 使这变得不可能。

,

我在这上面花了太多时间,它可能过于复杂,可能需要优化和简化,但它几乎适用于所有情况。一个没有的情况是@Zanchey 提到的突出错误。

# completions/foo.fish
complete -c foo -k -x -a "(__fish_complete_dotfile_or_subdirectories .bar)"

function __fish_complete_dotfile_or_subdirectories -d "Complete dotfile or directories"

    set -l token
    set -l suffix
    switch (count $argv)
        case 1
            set token (commandline -ct)
            set suffix $argv
        case 2
            set token $argv[1]
            set suffix $argv[2]
    end

    set -l suffix_len (string length $suffix)

    # trim leading . from suffix if present
    if test (string sub -l 1 $suffix) = "."
        set suffix_len (math $suffix_len - 1)
        set suffix (string sub -l $suffix_len -s "-"$suffix_len $suffix)
    end

    set -l root_name (string split -rm 1 "/" $token)
    set -l root $root_name[1..-2]"/"
    set -l name $root_name[-1]

    # build regex
    # $suffix = foo  ->  '^(?:|\.|\.f|\.fo|\.foo)$'
    # $suffix = bar  ->  '^(?:|\.|\.b|\.ba|\.bar)$'
    set -l re (
        echo -n "^(?:|\."
        for i in (seq $suffix_len)
            echo -n "|\."(string sub -l $i $suffix)
        end
        echo ')$'
    )
    if string match -qr $re "$name"
        set -l i
        if test -n "$root"
            set i (printf "%s.%s" $root $suffix)
        else
            set i (printf ".%s" $suffix)
        end
        if test -f "$i"
            printf "%s\n" $i
        end
    end

    # all subdirs except . and ..
    for i in (complete -C "nonexistentcommand $token" |
        string match -er '[^\.]/$')
        printf "%s\n" $i
    end

end

*(complete -C ...inspiration

> mkdir -p a/{.a1,.a2,b1,b2,.b3,.b4} c
> touch .bar .baz a/.bar a/.baz a/foo.bar c/.bar
> tree -aF
.
├── a/
│   ├── .a1/
│   ├── .a2/
│   ├── b1/
│   ├── b2/
│   ├── .b3/
│   ├── .b4/
│   ├── .bar
│   ├── .baz
│   └── foo.bar
├── .bar
├── .baz
└── c/
    └── .bar

> foo <TAB>
> __fish_complete_dotfile_or_subdirectories "" bar
.bar                # .bar is included ...
a/
c/
> foo <TAB>
a/  c/              # ... but is not here because of the long-standing bug
> foo .<TAB>        # completes to `foo .bar`
> foo ./<TAB>
./.bar  ./a/  ./c/  # now it's included
> foo ./.<TAB>      # completes to `foo ./.bar`
> foo a<TAB>        # completes to `foo a/`
> foo a/<TAB>
a/.bar  a/b1/  a/b2/
> foo a/.<TAB>
a/.bar  a/.a1/  a/.a2/  a/.b3/  a/.b4/
> foo a/.b<TAB>
a/.bar  a/.b3/  a/.b4/
> foo a/.ba<TAB>    # completes to `foo a/.bar`
> foo c<TAB>        # completes to `foo c/`
> foo c/<TAB>       # completes to `foo c/.bar`