#compdef git-forgit -p forgit::*
#description Utility tool for using git interactively
#
# forgit completions for zsh
#
# Place this file in your $fpath (e.g. /usr/share/zsh/site-functions) to enable
# tab completions for forgit.

_git-branches() {
    _alternative "branches:branchname:($(git branch -a --format '%(refname:short)'))"
}

_git-checkout-file() {
    _alternative "files:filename:($(git ls-files --modified))"
}

_git-stash-show() {
    _alternative "files:filename:($(git stash list | sed -n -e 's/:.*//p'))"
}

# The completions for git already define a _git-diff completion function, but
# it provides the wrong results when called from _git-forgit because it heavily
# depends on the context it's been called from (usage of $curcontext and
# $CURRENT), so we use a simplified version here which always provides the same
# results independent of the context.
_git-forgit-diff() {
    _alternative \
        'commit-ranges::__git_commit_ranges' \
        'blobs-and-trees-in-treeish::__git_blobs_and_trees_in_treeish' \
        'files::__git_changed-in-working-tree_files' \
        'blobs::__git_blobs '
}

_git-staged() {
    _alternative "files:filename:($(git diff --name-only --staged))"
}

_git-forgit-reflog() {
    declare -a cmds
    cmds=('expire:prune old reflog entries' 'delete:delete entries from reflog' 'show:show log of ref' 'exists:check whether a ref has a reflog')
    _alternative 'cmds:: _describe -t cmds cmd cmds' 'refs:: __git_references'
}

_git-forgit() {
    local subcommand cmd
    subcommand="${words[1]}"
    if [[ "$subcommand" != "forgit"* ]]; then
        # Forgit is obviously called via a git alias. Get the original
        # aliased subcommand and proceed as if it was the previous word.
        cmd=$(git config --get "alias.$subcommand" | cut -d ' ' -f 2)
    else
        # The last word is the relevant command
        cmd=${words[(( ${#words} - 1 ))]}
    fi

    case ${cmd} in
    forgit)
        local -a subcommands
        subcommands=(
            'add:git add selector'
            'blame:git blame viewer'
            'branch_delete:git branch deletion selector'
            'checkout_branch:git checkout branch selector'
            'checkout_commit:git checkout commit selector'
            'checkout_file:git checkout-file selector'
            'checkout_tag:git checkout tag selector'
            'cherry_pick:git cherry-picking'
            'cherry_pick_from_branch:git cherry-picking with interactive branch selection'
            'clean:git clean selector'
            'diff:git diff viewer'
            'fixup:git fixup'
            'ignore:git ignore generator'
            'log:git commit viewer'
            'reflog:git reflog viewer'
            'rebase:git rebase'
            'reset_head:git reset HEAD (unstage) selector'
            'revert_commit:git revert commit selector'
            'reword:git fixup=reword'
            'squash:git squash'
            'stash_show:git stash viewer'
            'stash_push:git stash push selector'
        )
        _describe -t commands 'git forgit' subcommands
        ;;
    add) _git-add ;;
    branch_delete) _git-branches ;;
    checkout_branch) _git-branches ;;
    checkout_commit) __git_recent_commits ;;
    checkout_file) _git-checkout-file ;;
    checkout_tag) __git_tags ;;
    cherry_pick) _git-cherry-pick ;;
    cherry_pick_from_branch) _git-branches ;;
    clean) _git-clean ;;
    diff) _git-forgit-diff ;;
    fixup) __git_branch_names ;;
    log) _git-log ;;
    reflog) _git-forgit-reflog ;;
    rebase) _git-rebase ;;
    reset_head) _git-staged ;;
    revert_commit) __git_recent_commits ;;
    reword) __git_branch_names ;;
    squash) __git_branch_names ;;
    stash_show) _git-stash-show ;;
    show) _git-show ;;
    esac
}

# We're reusing existing completion functions, so load those first
# if not already loaded and check if completion function exists afterwards.
(( $+functions[_git-add] )) || _git
(( $+functions[_git-add] )) || return 1
# Completions for forgit plugin shell functions (also works for aliases)
compdef _git-add forgit::add
compdef _git-branches forgit::branch::delete
compdef _git-branches forgit::checkout::branch
compdef __git_recent_commits forgit::checkout::commit
compdef _git-checkout-file forgit::checkout::file
compdef __git_tags forgit::checkout::tag
compdef _git-cherry-pick forgit::cherry::pick
compdef _git-branches forgit::cherry::pick::from::branch
compdef _git-clean forgit::clean
compdef _git-forgit-diff forgit::diff
compdef __git_branch_names forgit::fixup
compdef _git-log forgit::log
compdef _git-reflog forgit::reflog
compdef _git-rebase forgit::rebase
compdef _git-staged forgit::reset::head
compdef __git_recent_commits forgit::revert::commit
compdef __git_branch_names forgit::reword
compdef __git_branch_names forgit::squash
compdef _git-stash-show forgit::stash::show
compdef _git-show forgit::show

# this is the case of calling the command and pressing tab
# the very first time of a shell session, we have to manually
# call the dispatch function
if [[ $funcstack[1] == "_git-forgit" ]]; then
  _git-forgit "$@"
fi
