It's a good first dive into zsh completion. The whole thing is quite the large system to wrap ones head around it and I'm still somewhat struggling.

But at work, I've been slowly adding auto completion to our ansible wrapper scripts, like explanations which playbooks to use when, smart `-l` completion based off a possibly selected playbook (so, if the playbook is postgres.yml, it doesn't suggest mariadb groups), tag autocompletion (with a few, admittedly, hardcoded explanations how these tags should be used) and such.

It's somewhat of a friday-afternoon struggle project, but it's making the big ansible project pretty approachable to use.

Could you share how you do it? Ansible playbooks are ran via command `ansible-playbook` command and it surely has its own tab auto completion script.

It kinda does, but I've added a few bells and whistles to it. Mind you, this is zsh code I haven't really cleaned up, so it's kinda messy and most likely buggy in edge cases.

This needs to be in a directory in your FPATH.

At the core, it uses _arguments to dissect the command line. This both suggests that "-t" exists, is called "tags" and later on sets a state variable to "tags" or "limits" if we're completing these arguments.

    #compdef corp-ansible-wrapper
    _arguments  '-t[tags]:tags:->tags' '-l[limit]:limit:->limits' '-D[diffmode]' '-C[checkmode]' '::optional arg:_files'
    detect_playbook
    case "$state" in
        tags)
        ...
        ;;
        limits)
        ...
        ;;

Given this, the limits autocompletion goes one step further. `detect_playbook` mainly goes through $words and looks for a singular argument looking like "*.yml" and sets that as $PLAYBOOK. Then, based on "$PLAYBOOK", it selects a filter-expression for the groups. This ensures that a `./wrapper mariadb.yml -l<TAB>` only sees mariadb-groups, and not postgres-groups.

All of that is shoved into `_values`, and then the usual zsh completion works, so with something like `./wrapper mariadb.yml -l prj4<TAB>`, zsh tries to filter the values based on the word, so this finds stuff like `prj4`, `prj49`, `dc2_prj45`, and so on, but not `prj5`.

    detect_playbook

    # should be an array.
    case $PLAYBOOK in
        postgres.yml)
            FILTER="pg_\|postgres_\|pgbackrest_"
        ;;
        mariadb.yml)
            FILTER="db_\|mariadb_"
        ;;
        # quite a few more
        *)
            FILTER=""
        ;;
    esac

    # probably overly complex
    ANSIBLE_GROUPS_RAW=$( cat $ANSIBLE_INVENTORY/groups | grep -E "$FILTER" | sort | uniq | tr '\n' ',')
    IFS=',' read -r -A ANSIBLE_GROUPS <<< "$ANSIBLE_GROUPS_RAW"
    _values -s: 'groups' "${ANSIBLE_GROUPS[@]}"
For the tags I'm working on a similar thing, but this contains enough ugly shell-script already. However, the key parts there are:

- You can run `ansible-playbook "$WORD" --list-tags` to get all tags a playbook references in the current inventory.

- One can give `_values` descriptions. If `_values` sees 'foo[bar]' as an option, it will show the user something like: "foo: bar" and only auto-completes to foo.

- This means, we can give standard or well-established tags short descriptions in a directory or an array or whatever, and instead of offering just "postgres_client_tls_certs" as possible auto-completions for `-t`, we can give the user a prompt like "postgres_client_tls_certs: Ensures the postgres cluster has valid and up-to-date TLS certificates for mutual TLS with applications".

It took a bit of time to understand all of this. But now documenting a tag in a place that people actually look at is very easy and straightforward.