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?

Community
  • 1
  • 1
toftis
  • 1,070
  • 9
  • 26
  • 1
    I've removed `@`, `:` and `=` from `COMP_WORDBREAKS` and I can do without them. :) – pynexj Feb 15 '15 at 01:53
  • 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. – toftis Feb 16 '15 at 09:44
  • 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. – pynexj Feb 16 '15 at 10:27
  • 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. – toftis Feb 16 '15 at 10:51

1 Answers1

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.

pynexj
  • 19,215
  • 5
  • 38
  • 56
  • 1
    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. – toftis Feb 23 '15 at 13:56
  • 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? – Lou Dec 10 '20 at 10:43
  • @Lou 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? – pynexj Dec 10 '20 at 10:57
  • 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. – Lou Dec 10 '20 at 11:17
  • those funcs are from [bash-completion](https://github.com/scop/bash-completion) pkg. you can refer to its official doc. – pynexj Dec 10 '20 at 11:28
  • 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? – Lou Dec 15 '20 at 17:22