How to give correct suggestions to tab complete when my words contains colons
Asked Answered
D

1

5

I am writing a bash tab completion file for a utility that some times requires full URLs on the form: protocol://host:port. This contains two colons, which have proven to be problematic for tab completion. This is because the colons are treated as word breaks. I have read that I should not change COMP_WORDBREAKS directly, so I want to use the _get_comp_words_by_ref and __ltrim_colon_completions as suggested here: How to reset COMP_WORDBREAKS without effecting other completion script?

This works for a single colon, but the second colon causes a small problem as demonstrated in this minimal example:

This example shows the problem. It occurs for any number of colons in the suggestions.

[root@2e3e8853cc0c /]# cat /etc/bash_completion.d/foo 
_foo()
{
    local cur
    COMPREPLY=()
    _get_comp_words_by_ref -n : -c cur

    COMPREPLY=( $(compgen -W "http://host:1234/aaa http://host:1234/bbb http://host:1234/ccc" -- ${cur}) )
    __ltrim_colon_completions "$cur"
    return 0
}
complete -F _foo foo

Hitting tab after foo successfully completes the common part. Hitting tab twice after that, yields the following suggestions:

[root@2e3e8853cc0c /]# foo http://host:1234/
1234/aaa  1234/bbb  1234/ccc

The desired result is ofcourse:

[root@2e3e8853cc0c /]# foo http://host:1234/
http://host:1234/aaa  http://host:1234/bbb  http://host:1234/ccc

After that, hitting a, b, or c plus tab works as expected, it completes the full URL.

Any suggestions to how I can produce the right output? Do I need to manually change the COMPREPLY variable, or am I just using the functions wrong?

Douai answered 12/2, 2015 at 13:52 Comment(4)
I've removed @, : and = from COMP_WORDBREAKS and I can do without them. :)Electrolyze
I can do without them as well, but this utility will be installed on several hosts, not just mine. When using scp it is normal to have a colon between a remote host and the file path, if I remove colon this will behave differently.Douai
Whenever I want filename auto completion after : (or =) I would just input a SPACE after :. After the filename completion finishes I'll move the cursor back and delete the SPACE. I'm using Bash's vi editing mode so this almost adds no cost.Electrolyze
This is ok when you are modifying your own environment, but I do not want to add hacks like this to my package. I need a way that will solve my problem without changing the behavior of auto completion for other tools at the system.Douai
E
2

I came up with a solution based on one trick I'm always using. Hope it would help.

_bar()
{
    local CUR=$2
    local cur
    local -a compreply=()
    local -a urls=(ftp://gnu.org \
                   http://host1:1234/aaa \
                   http://host2:1234/bbb \
                   http://host2:1234/ccc)

    _get_comp_words_by_ref -n : -c cur

    compreply=( $(compgen -W "${urls[*]}" -- "$cur") )
    COMPREPLY=( "${compreply[@]}" )
    __ltrim_colon_completions "$cur"

    if [[ ${#COMPREPLY[@]} -gt 1 ]]; then
        local common_prefix
        common_prefix=$( printf '%s\n' "${COMPREPLY[@]}" \
                         | sed '$q;N;s/^\(.*\).*\n\1.*$/\1/;h;G;D' )
        if [[ $common_prefix == "$CUR" ]]; then
            COMPREPLY=( "${compreply[@]}" " " )
        fi
    fi

    return 0
}

complete -F _bar bar

Following is what it would look like (tested with Bash 4.3.33):

[STEP 101] $ bar <TAB><TAB>
                       http://host1:1234/aaa  http://host2:1234/ccc
ftp://gnu.org          http://host2:1234/bbb
[STEP 101] $ bar f<TAB>
[STEP 101] $ bar ftp://gnu.org␣
[STEP 101] $ bar ftp://gnu.org <ENTER>
bash: bar: command not found
[STEP 102] $ bar h<TAB>
[STEP 102] $ bar http://host
[STEP 102] $ bar http://host<TAB><TAB>
                       http://host2:1234/bbb
http://host1:1234/aaa  http://host2:1234/ccc
[STEP 102] $ bar http://host2<TAB>
[STEP 102] $ bar http://host2:1234/
[STEP 102] $ bar http://host2:1234/<TAB><TAB>
                       http://host2:1234/bbb  http://host2:1234/ccc
[STEP 102] $ bar http://host2:1234/b<TAB>
[STEP 102] $ bar http://host2:1234/bbb␣
[STEP 102] $ bar http://host2:1234/bbb <ENTER>
bash: bar: command not found
[STEP 103] $

And actually the problem is not specific about two or more colons. One colon has the similar problem too.

Electrolyze answered 21/2, 2015 at 16:4 Comment(6)
Thanks! It seems to work great. I will test it a bit more to see if I can find some corner cases. Can you explain a bit what is happening in the code? You are right about the single colon. I have updated the question.Douai
I would also like to know more about how this code works, as I'm encountering the same problem as OP but unsure of how you've fixed it. Would you consider adding in some explanatory comments whenever it's convenient, pynexj?Obloquy
@Obloquy i never use the bash-completion pkg myself. totally forgot what funcs like _get_comp_words_by_ref are doing. which specific part you dont understand?Electrolyze
It's not clear to me what _get_comp_words_by_ref and __ltrim_colon_completions are actually doing in the function. As far as I can tell they're both called before lowercase cur is assigned to anything. I'd love to know how they change the behaviour of the _bar() function so that it handles colon completion.Obloquy
those funcs are from bash-completion pkg. you can refer to its official doc.Electrolyze
Thanks, I have done so. I'm currently getting bash: _get_comp_words_by_ref: command not found. Do you know if you had to install anything to get this working originally?Obloquy

© 2022 - 2024 — McMap. All rights reserved.