Autocompletion in Bash for a simple CLI
Tags: cli, bash, linux
Published: 2021-05-02 02:32:41

:zipper_mouth_face:

Currently, I've been developing a CLI for [DEV Community](https://dev.to) in Go. During this journey I've learned a lot of really cool stuffs :sunglasses:. After having the [devto](https://github.com/Gealber/devto-cli) almost completed, I started wondering, "How can I make autocompletion possible?", so I started looking for information related. Here I'll try to explain how to achieve this in Bash, no idea how would be the case for `cmd` or `PowerShell` :thinking:.

# CLI commands

Now, [devto-cli](https://github.com/Gealber/devto-cli) has these commands:
* articles
* auth
* comments 
* followers 
* listings 
* organizations 
* podcasts 
* reading_lists 
* tags 
* webhooks

and other sub commands as well, but for simplicity I'll focus on how to achieve the autocompletion with these commands only.

## Wanted behavior

The idea is to give the user some feedback after running `devto <TAB>`, for example in this case, the user would see the following suggestions:

`articles auth comments followers listings organizations podcasts reading_lists tags webhooks`

Which are all the commands available, but if the user start typing `devto a<TAB>`, the suggestions would be the commands:
`articles auth`

Given that these two are the only ones that start with the letter `a`.

The general idea is that the completion is triggered after `<TAB>` been pressed.

## The programmable completion feature in Bash

One of the many cool features in bash is the __programmable completion__, bash have two _builtins_ that will help me, one is *complete* and the other one is *compgen*. The documentation of these two commands can be found in `man bash`.

## Jumping to code

After reading several articles, including the mentioned previously, I could manage to adapt that information to my problem. In other words, I made use of the ancestral technique of *Copy, Paste and Adapt*. The third step of this technique is crucial, given that it is on this step, when you really start understanding other people code. Never skip this part, even if your *Copy and Paste* works at the first.


Get prepare to read other people code, here we go:

```bash

_complete_devto () {
    # word that is been typed
    local word=${COMP_WORDS[COMP_CWORD]}
    local line=${COMP_LINE}

    # local variable to store the list of commands
    # that are going to be passed to compgen
    local xpat

    # List of all the commands available
    local commands="articles auth comments followers listings organizations podcasts reading_lists tags webhooks"

    # simple switch case when program is devto
    case "$1" in 
    devto)
        case "$line" in
        *)
            xpat="$commands"
            ;;
        esac
    esac

    # commands to reply to the user
    COMPREPLY=($(compgen -W "$xpat" -- "${word}"))
}

complete -F _complete_devto devto
```

Several variables in detail here:


* `COMP_WORDS`
> An array variable consisting of the individual words in the current command line.

* `COMP_LINE`
> The current command line.

* `COMP_CWORD`
> An index into `${COMP_WORDS}` of the word containing the current cursor position.

* `COMPREPLY`
> An array variable from which bash reads the possible completions generated by a shell function invoked by the  programmable  
> completion facility.

All the definitions taken from `man bash`.

Now just one final step, store that code on a file, let's call it `devto.sh` and source the file.

```bash
source devto.sh
```

Just try to type `devto a<Tab>`. This should give you **articles** and **auth** as suggestions.

That's all folks👋

# Bibliography

* [More on Using the Bash Complete Command](https://www.linuxjournal.com/content/more-using-bash-complete-command)
* [Appendix J. An Introduction to Programmable Completion](https://tldp.org/LDP/abs/html/tabexpansion.html)
* [What does `${1#*=}` mean in Bash?](https://stackoverflow.com/questions/31307210/what-does-1-mean-in-bash/31307237)
* Bash manpage, just `man bash`