3

I'm the creator and maintainer of a PHP based command line application for developers. My command's syntax follows the norms of other PHP command line applications, and looks like this

pestle.phar some:command:name ...arguments and options here...
pestle.phar some:command      ...arguments and options here...    

That is -- colons are used in the command name. This presented problems when I tried using the stock autocomplete features of bash.

Thanks to some other answers I was able to determine this was bash treating the : as a word separator, and that the bash-completion project (brew install bash-completion) could help. I've been able to pull a script together that sort of works -- but I'm stuck on a few things and don't understand the system well enough to diagnose on my own. Also, my bash scripting skills are rusty.

Here's a sample of something that sort of works.

#File: /usr/local/etc/bash_completion.d/pestle-autocomplete.sh
#!/bin/bash

_commandList ()
{
    echo "codecept:convert-selenium-id-for-codecept"
    echo "magento2:check-templates"
    echo "magento2:generate:acl"
    #.. other comand names..
    return 0;
}

_pestleAutocomplete ()
{
    local cur
    local all
    _get_comp_words_by_ref -n : cur
    all=$(_commandList)
    COMPREPLY=( $(compgen -W "$all" $cur) )
    __ltrim_colon_completions "$cur"
    return 0
}
complete -F _pestleAutocomplete -o filenames pestle.phar

If I type $ pestle.phar mag and then hit tab, the follow is output in my shell

$ pestle.phar magento2\:    

That is, magento2: auto completes, but the : is escaped with a slash. If I complete a command name, this "works", but looks a little funny. So question 1 -- how do I get rid of that slash, and where's it coming from.

Second -- I don't understand what the __ltrim_colon_completions command does. In the examples I've seen, its usually used something like

_mytool()
{
    local cur
    _get_comp_words_by_ref -n : cur

    # my implementation here

    __ltrim_colon_completions "$cur"
}
complete -F _mytool mytool

However, to my naive eyes -- that seems to be doing nothing. Its an extraneous call at the end of of the script using a local variable. I assume it changes some state in the bash-completion system, but again, my knowledge there is incomplete. I'm not sure if its related to my escaping problem.

If anyone here has a concrete answer, or can get me pointed in the right direction, I'd appreciate it. OS X 10.11, El Cap, stock bash -- if it matters.

Community
  • 1
  • 1
Alana Storm
  • 164,128
  • 91
  • 395
  • 599
  • I presume that changing `COMP_WORDBREAKS` globally is not an option in case your users already rely on it? Is that why you've not accepted the answer below? Please let us know if you found a solution. – Keeely Jan 21 '19 at 08:53
  • Oh god -- I started re-reading this question and -- I don't even know what I was asking back then? I know I "solved" this problem by letting someone smart fix the the autocomplete in my GitHub project (https://github.com/astorm/pestle) but I have no idea what they did or how it works or how anything works anymore, and I'm so sorry because I've been in your exact position before @keeely, looking at old Stack Overflow questions and wondering why they're abandoned, and I guess I've become that person now. I hope you can forgive me. – Alana Storm Jan 22 '19 at 01:22
  • no probs and thanks for replying but it will be very useful if you can point me to the pestle commit that fixed it. – Keeely Jan 22 '19 at 09:18
  • @keely Here's the two files that make up pestle's auto-commits -- git blame backwards through history to see how they've evolved. (we may have just sidestepped this issue, so no promises) Good luck! https://github.com/astorm/pestle/blob/master/pestle-autocomplete.sh https://github.com/astorm/pestle/blob/master/gen-event-list.sh – Alana Storm Jan 22 '19 at 11:37
  • I'm trying to fix the same problem nearly two years later - I'd be grateful if you did manage to break down the solution that ultimately fixed your problem and post it as an answer. I'm looking at your code now and I admit I'm not too clear from looking what it does or how it fixes the colon issue. – Lou Nov 30 '20 at 17:25
  • @Lou Sadly I've lost the context of all this to middle aged memory -- but I believe a contributor to my repo came along and made all this work. You might have luck fishing into the histories of the above `astorm/pestle` commits. Good luck. – Alana Storm Nov 30 '20 at 18:56

1 Answers1

1

It's possible to adjust the COMP_WORDBREAKS variable in a way that makes the colon behave differently from it's normal completion behavior:

COMP_WORDBREAKS=${COMP_WORDBREAKS//:}

Removing : from that value is enough to make the colon not special to completion (which may or may not be what you are wanting). I suspect that it gets the backslash \: in front of it is because of the way you have excluded it with _get_comp_words_by_ref -n : cur. You can also quote the colon with a backslash to achieve the same result temporarily.

Filename completion (and word completion in general) may appear to behave improperly if there is a colon in the word to be completed.

The colon is special to readline's word completion code: it is one of the characters that breaks words for the completer. Readline uses these characters in sort of the same way that bash uses $IFS: as they break or separate the words the completion code hands to the application-specific or default word completion functions. The original intent was to make it easy to edit colon-separated lists (such as $PATH in bash) in various applications using readline for input.

This is complicated by the fact that some versions of the popular `bash-completion' programmable completion package have problems with the default completion behavior in the presence of colons.

The current set of completion word break characters is available in bash as the value of the COMP_WORDBREAKS variable. Removing `:' from that value is enough to make the colon not special to completion.

__ltrim_colon_completions

This function basically just trims the completion so that the word returned is only the prefix ( or whatever is remaining to the left of the last colon on the right ). An example would be a command such as hello:world and I started typing in he and pressed TAB, the hello: portion is what will be returned. The function is precisely this:

# Define preload__ltrim_colon_completions="false", if the function
# __perf__ltrim_colon_completions() is required instead.
preload__ltrim_colon_completions="true"

if [ $preload__ltrim_colon_completions = "true" ]; then
    type __ltrim_colon_completions &>/dev/null ||
    preload__ltrim_colon_completions="false"
fi
[ $preload__ltrim_colon_completions = "true" ] ||
__perf___ltrim_colon_completions()
{
    if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
        # Remove colon-word prefix from COMPREPLY items
        local colon_word=${1%"${1##*:}"}
        local i=${#COMPREPLY[*]}
        while [[ $((--i)) -ge 0 ]]; do
            COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
            echo $COMPREPLY[$i]
        done
        echo $colon_word
    fi
}

Some of the other variables in the completion code can be set to handle words differently depending on the application; maybe try the method at the very top and if that isn't what you're looking for then maybe browse through the code responsible for controlling it.

l'L'l
  • 44,951
  • 10
  • 95
  • 146