Bash – Custom bash tab completion showing possible completions, but not actually completing input

autocompletebash

I have tried very hard to figure this one out before actually posting here, but I can't seem to find any other examples of people solving this particular issue.

I am running Ubuntu 17.10

I have written a custom function to handle tab completion for one of my scripts. The intention is that when I type in the name of my script and hit [TAB] that it list all of the files in /opt/use that end in ".use" (without displaying the ".use" or the leading path). I seem to have made that work so far.

The files in /opt/use are as follows:

blender-2.79.use
chrome.use
clarisse-3.6.use
unity-2017.3.0p2.use

The code for the function and the completion is:

_use () {
    files=( `compgen -f -X "!*.use" /opt/use/` )
    output=()
    for file in "${files[@]}"; do
        fileBaseName=`basename $file .use`
        output+=("$fileBaseName")
    done
    COMPREPLY=( ${output[@]} )
}
complete -F _use use

Please don't judge me too harshly, I'm a graphic artist, not a programmer. 🙂

Also, my "use" script is actually an alias for the following command:

source /opt/scripts/use.sh

Now when I type:

use[SPACE][TAB][TAB]

I successfully get a list of the files in /opt/use that end in ".use".

So far so good. For example, I type "use[SPACE][TAB][TAB]", this is what it looks like:

bvz@bvz-xps15-linux:~$ use 
blender-2.79      chrome            clarisse-3.6      unity-2017.3.0p2  

My first question is why I have to hit [TAB] twice? The first time just beeps. The second time it shows me my options. That isn't an issue for me, I just wonder if it is a clue as to my problem, which is this:

If I type enough letters to be completely unique, the tab completion does not actually "complete" the line. It just leaves the entry exactly as I typed it and re-shows me the list of files in /opt/use. So, for example, if I type:

use clar[TAB][TAB]

instead of filling out the line to read:

use clarisse-3.6

which is what I would expect (and what I am after) it leaves the line as:

use clar

and shows me underneath the full list of possible completions. Here is a sample:

bvz@bvz-xps15-linux:~$ use clar[TAB][TAB]
blender-2.79      chrome            clarisse-3.6      unity-2017.3.0p2  
bvz@bvz-xps15-linux:~$ use clar

Notice how it didn't actually complete the line to read "clarisse-3.6" even though that is the only possible completion.

Can anyone enlighten me as to what I have done wrong? Also, if this is a duplicate I apologize. I have looked around for several days but can't find any examples where someone has even run into this issue, much less solved it.

Thanks

Best Answer

Thanks to Muru for pointing me to the solution.

Apparently my issue was that every time my function was called it always returned the full list of possible matches. I needed to only return those possible completions that match what the user has typed so far. Once I return only a single item, then bash will auto-complete the rest of the line.

This is the function that works. Before adding each possible completion, I do a quick check to see if the completion starts with the word that was typed originally. I do this using regular expressions. I also had to test to see if they haven't entered anything yet - in which case I always return everything.

I also changed it to make some more variables "local".

_use () {
    local word="${COMP_WORDS[$COMP_CWORD]}"
    local files=( `compgen -f -X "!*.use" /opt/use/` )
    local output=()
    for file in "${files[@]}"; do
        fileBaseName=`basename $file .use`
        if [[ $fileBaseName =~ ^$word.* ]] || [ "$word" == "" ]; then
            output+=("$fileBaseName")
        fi
    done
    COMPREPLY=( ${output[@]} )
}
Related Question