Search code examples
zshzsh-completion

How to complete a variable number of arguments containing spaces


I've build a command line tool and I need to complete arguments with zsh. I never wrote a zsh completion function so I looked in the scripts provided with zsh but I missed something so that it could work properly.

So, mytool can take a variable number of values and two options. Here are some call examples:

mytool ONE
mytool ONE TWO
mytool AAA BBB CCC DDD EEE --info

In order to complete the values, I hava another executable that outputs all possible lines to stdout, like this simplified script named getdata:

#!/usr/local/bin/zsh

echo ONE
echo TWO ONE
echo TWO TWO
# ... a lot of lines
echo OTHER ONE
echo ONE ANOTHER LINE
echo AAA BBB CCC DDD EEE

Each completion must match to a whole line, so in my getdata example, it will not be possible to just complete with the value TWO because this whole line does not exist, it must be TWO ONE or TWO TWO.

As this script is quite time consuming, I would like to use zsh caching feature. So, here is my zsh complete script:

compdef _complete_mytool mytool
__mytool_caching_policy() {
    oldp=( "$1"(Nmh+1) )     # 1 hour
    (( $#oldp ))
}

__mytool_deployments() {
    local cache_policy

    zstyle -s ":completion:${curcontext}:" cache-policy cache_policy
    if [[ -z "$cache_policy" ]]; then
        zstyle ":completion:${curcontext}:" cache-policy __mytool_caching_policy
    fi

    if ( [[ ${+_mytool_values} -eq 0 ]] || _cache_invalid mytool_deployments ) \
        && ! _retrieve_cache mytool_deployments;
    then
        local -a lines
        _mytool_values=(${(f)"$(_call_program values getdata)"})
        _store_cache mytool_deployments _mytool_values
    fi
    _describe "mytool values" _mytool_values
}

_complete_mytool() {
    integer ret=1
    local -a context expl line state state_descr args
    typeset -A opt_args

    args+=(
    '*:values:->values'
    '--help[show this help message and exit]'
    '(-i --info)'{-i,--info}'[display info about values and exit]'
    '(-v --version)'{-v,--version}'[display version about values and exit]'
    )

    _call_function res __mytool_deployments

    return ret
}

But when I try to complete, spaces are escaped with backslash, and I don't want this behaviour.

mytool OTHER\ ONE

The options seem not to be completed too... So, any help will be greatly appreciated.


Solution

  • Thanks to okdana on the freenode zsh channel who helped me a lot.

    So, the solution is:

    compdef _complete_mytool mytool
    __mytool_caching_policy() {
        oldp=( "$1"(Nmh+1) )     # 1 hour
        (( $#oldp ))
    }
    
    __mytool_deployments() {
        local cache_policy
    
        zstyle -s ":completion:${curcontext}:" cache-policy cache_policy
        if [[ -z "$cache_policy" ]]; then
            zstyle ":completion:${curcontext}:" cache-policy __mytool_caching_policy
        fi
    
        if ( [[ ${+_mytool_values} -eq 0 ]] || _cache_invalid mytool_deployments ) \
            && ! _retrieve_cache mytool_deployments;
        then
            local -a lines
            _mytool_values=(${(f)"$(_call_program values getdata)"})
            _store_cache mytool_deployments _mytool_values
        fi
        _describe "mytool values" _mytool_values -Q
    }
    
    _complete_mytool() {
      _arguments : \
        ': :__mytool_deployments' \
        '--help[show this help message and exit]' \
        '(-i --info)'{-i,--info}'[display info about values and exit]' \
        '(-v --version)'{-v,--version}'[display version about values and exit]'
    }