Bash completion does not work in ZSH/Oh-My-ZSH because COMP_WORDS is not an array

Viewed 1054

I'm writing a bash command line tool for which I want to enable bash completion using completely.

I have the following bash completion. After I eval "$(./cli completion)" (which outputs the below), completions work fine in bash:

#!/usr/bin/env bash

# This bash completions script was generated by
# completely (https://github.com/dannyben/completely)
# Modifying it manually is not recommended
_cli_completions() {
  local cur=${COMP_WORDS[COMP_CWORD]}
  local comp_line="${COMP_WORDS[*]:1}"

  case "$comp_line" in
    'completions'*) COMPREPLY=($(compgen -W "--help -h" -- "$cur")) ;;
    'download'*) COMPREPLY=($(compgen -W "--force --help -f -h" -- "$cur")) ;;
    ''*) COMPREPLY=($(compgen -W "--help --version -h -v completions download" -- "$cur")) ;;
  esac
}

complete -F _cli_completions cli

However, they do not work right in ZSH. I have identified the problem to be that COMP_WORDS is not an array when I'm inside ZSH, but it is inside bash. This then breaks the line local comp_line="${COMP_WORDS[*]:1}".

E.g. in the situation ./cli download <tab>, comp_line should be download, but in ZSH it's /cli download (only the . is removed), so I always end up in the last case ''*.

I'm using ZSH 5.8 with Oh-My-ZSH. Bash completions generally seem to work in ZSH. Oh-My-ZSH sets up completions using

fpath=($ZSH/functions $ZSH/completions $fpath)
autoload -U compaudit compinit

I have tried setting setopt shwordsplit, hoping that that would cause COMP_WORDS to be treated as an array, but it had no effect.

Is there a ZSH configuration that I can change to make this completion script work? Or is there a bash compatible change to the completion script that would make it work in both shells?

1 Answers

This issue is discussed in this Github ticket.

ZSH should be totally capable of handling bash completions when it is configured to do so by adding these two lines in ~/.zshrc:

# Load bash completion functions
autoload -Uz +X compinit && compinit
autoload -Uz +X bashcompinit && bashcompinit

In addition, the script in question, generated by the completely gem, seems to have a problem.

Replacing * with @ in the below line seems to make it work on both bash and zsh.

local comp_line="${COMP_WORDS[*]:1}"   # broken
local comp_line="${COMP_WORDS[@]:1}"   # works
Related