lib/mix/complete/zsh.ex

defmodule Mix.Tasks.Complete.Zsh do
  use Mix.Task

  @shortdoc "Output zsh completion source for mix"
  def run(_) do
    Mix.Tasks.Complete.run([])

    IO.puts("""
    #compdef mix

    _mix() {
      local options completion_line filtered_options
      local -a completions
      local current_word=${words[-1]}
      local previous_word=${words[-2]}

      if [[ -f 'mix.exs' && -f '.mix_complete.cache' ]]; then
        options=$(cat .mix_complete.cache)
      elif [[ ! -z "$XDG_CACHE_HOME" && -f "$XDG_CACHE_HOME/.mix_complete.cache" ]]; then
        options=$(cat "$XDG_CACHE_HOME/.mix_complete.cache")
      elif [[ -f "$HOME/.cache/.mix_complete.cache" ]]; then
        options=$(cat "$HOME/.cache/.mix_complete.cache")
      else
        options=""
      fi

      filtered_options=$(echo "$options" | grep "^$current_word")

      while IFS='\n' read -r completion_line; do
        if [ -n "$completion_line" ]; then
          # zsh's _describe needs a : instead of a TAB.
          # So we first escape any : as part of the completion itself.
          completion_line=${completion_line//:/\\:}

          local tab="$(printf '\t')"
          completion_line=${completion_line//$tab/:}

          completions+=${completion_line}
        fi
      done <<<"$filtered_options"

      if [[ $previous_word == 'mix' ]]; then
        _describe "completions" completions
      else
        _arguments '*:filenames:_files'
      fi
    }
    """)
  end
end