#compdef pre-commit
# ------------------------------------------------------------------------------
# Description
# -----------
#
#  Completion script for pre-commit 4.2.0 (https://github.com/pre-commit/pre-commit/).
#  Modified from rejected https://github.com/pre-commit/pre-commit/pull/2506
#
# ------------------------------------------------------------------------------
# Authors
# -------
#
#  * Wu Zhenyu <wuzhenyu@ustc.edu>
#
# ------------------------------------------------------------------------------

_pre_commit() {
  typeset -A opt_args
  local context state line curcontext="$curcontext"
  local ret=1

  _arguments -C \
    '(- *)'{-h,--help}'[show help message and exit]' \
    '(- *)'{-V,--version}'[show version number and exit]' \
    '1:command:_pre_commit_commands' \
    '*:: :->args' \
    && ret=0

  case "$state" in
    (args)
      local -a options=(
        '(- : *)'{-h,--help}'[show help message and exit]'
        "--color[Whether to use color in output(default: auto)]:color:(auto always never)"
      )
      local -a hook_types=(
        commit-msg post-checkout post-commit post-merge post-rewrite pre-commit
        pre-merge-commit pre-push pre-rebase prepare-commit-msg
      )
      local -a hook_stages=($hook_types[@] manual)

      case $words[1] in
        (help)
          _pre_commit_commands && ret=0
          return ret
        (autoupdate|init-templatedir|install|install-hooks|migrate-config|run|uninstall)
          options+=(
            '(-c --config)'{-c,--config}'[Path to alternate config file]:config:_files -g "*.yaml"'
          )
          ;|
        (init-templatedir|install|uninstall)
          options+=(
            '(-t --hook-type)'{-t,--hook-type}'[hook type]:type:($hook_types)'
          )
          ;|
        (autoupdate)
          options+=(
            '--bleeding-edge[Update to the bleeding edge of 'HEAD' instead of the latest tagged version]'
            '--freeze[Store "frozen" hashes in "rev" instead of tag names]'
            '*--repo[Only update this repository -- may be specified multiple times]:repos'
            '(-j --jobs)'{-j,--jobs}'[Number of threads to use(default: 1)]:jobs'
          )
          ;;
        (init-templatedir)
          options+=(
            '--no-allow-missing-config[Assume cloned repos should have a "pre-commit" config]'
            '*::dir:_files -/'
          )
          ;;
        (install)
          options+=(
            '(-f --overwrite)'{-f,--overwrite}'[Overwrite existing hooks / remove migration mode]'
            '--install-hooks[Whether to install hook environments for all environments]'
            '--allow-missing-config[Allow a missing "pre-commit" configuration file]'
          )
          ;;
        (run|try-repo)
          options+=(
            '(-v --verbose)'{-v,--verbose}'[verbose mode]'
            '(-a --all-files)'{-a,--all-files}'[Run on all the files in the repo]'
            '--files[Specific filenames to run hooks on]:file:_files'
            '--show-diff-on-failure[When hooks fail, run "git diff" directly afterward]'
            '--hook-stage[The stage during which the hook is fired]:stage:($hook_stages)'
            '--remote-branch[Remote branch ref used by "git push"]:remote_branch:_pre_commit_git_remote_branches'
            '--local-branch[Local branch ref used by "git push"]:local_branch:_pre_commit_git_local_branches'
            '(--from-ref --source -s)'{--from-ref,--source,-s}'[original ref in "from_ref..to_ref" diff expression]:ref:_pre_commit_git_refs'
            '(--to-ref --origin -o)'{--to-ref,--origin,-o}'[destination ref in "from_ref..to_ref" diff expression]:ref:_pre_commit_git_refs'
            '--pre-rebase-upstream[upstream from which the series was forked]:upstream'
            '--pre-rebase-branch[branch being rebased and is not set when rebasing the current branch]:branch'
            '--commit-msg-filename[file name to check when running during "commit-msg"]:file:_files'
            '--prepare-commit-message-source[source of the commit message]:source'
            '--commit-object-name[commit object name]:object_name'
            '--remote-name[Remote name used by "git push"]:name:remote:_pre_commit_git_remotes'
            '--remote-url[Remote url used by "git push"]:url:_urls'
            '--checkout-type[branch check out or a file checkout]:type:_pre_commit_commit_types'
            '--is-squash-merge[use squash merge]:flag:(0 1)'
            '--rewrite-command[specify the command that invoked the rewrite]:command'
            "2:hook_id"
          )
          ;;
      esac

      _arguments $options[@] && ret=0
      ;;
  esac

  return ret
}

_pre_commit_commands() {
  local -a commands=(
    "autoupdate:Auto-update pre-commit config to the latest repos' versions"
    "clean:Clean out pre-commit files"
    "gc:Clean unused cached repos"
    "init-templatedir:Install hook script in a directory intended for use with 'git config init.templateDir'"
    "install:Install the pre-commit script"
    "install-hooks:Install hook environments for all environments in the config file"
    "migrate-config:Migrate list configuration to new map configuration"
    "run:Run hooks"
    "sample-config:Produce a sample .pre-commit-config.yaml file"
    "try-repo:Try the hooks in a repository, useful for developing new hooks"
    "uninstall:Uninstall the pre-commit script"
    "validate-config:Validate .pre-commit-config.yaml files"
    "validate-manifest:Validate .pre-commit-hooks.yaml files"
    "help:Show help for a specific command"
  )

  _describe 'pre-commit commands' commands
}

_pre_commit_git_remote_branches() {
  if (( $+commands[git] )); then
    local -a remote_branches=(${(f)"$(git branch -a 2>/dev/null | \grep 'remotes/' | \grep -v HEAD | awk -F/ '{print $NF}' | uniq)"})
    _values 'branch' $remote_branches
  fi
}

_pre_commit_git_local_branches() {
  if (( $+commands[git] )); then
    local -a local_branches=(${(f)"$(git branch 2>/dev/null | cut -c3-)"})
    _values 'branch' $local_branches
  fi
}

_pre_commit_git_remotes() {
  if (( $+commands[git] )); then
    local -a remote_names=(${(f)"$(git remote 2>/dev/null)"})
    _values 'remote' $remote_names
  fi
}

_pre_commit_commit_types() {
  local -a types=(
    '0:retrieving a file from the index'
    '1:changing branches'
  )

  _describe 'commit types' types
}

_pre_commit_git_refs() {
  if (( $+commands[git] )); then
    local -a refs=(${(f)"$(git branch -a 2>/dev/null | \grep -v HEAD | cut -c3- | sed 's/^remotes\///')"})
    _values 'ref' $refs
  fi
}

_pre_commit "$@"

# Local Variables:
# mode: Shell-Script
# sh-indentation: 2
# indent-tabs-mode: nil
# sh-basic-offset: 2
# End:
# vim: ft=zsh sw=2 ts=2 et
